From 1b4d0302ee52115c83cee47985ec889ac999363e Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Tue, 23 May 2023 21:12:50 -0400 Subject: [PATCH 1/6] Windows support (WiP) --- .gitignore | 2 + CMakeLists.txt | 17 ++++++--- api/s2n.h | 4 ++ stuffer/s2n_stuffer.h | 4 ++ stuffer/s2n_stuffer_file.c | 5 ++- win_shim/mmap-windows.c | 78 ++++++++++++++++++++++++++++++++++++++ win_shim/win_shim.h | 54 ++++++++++++++++++++++++++ 7 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 win_shim/mmap-windows.c create mode 100644 win_shim/win_shim.h diff --git a/.gitignore b/.gitignore index e6b6e5853c0..23a37341c6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +build_* +cmake-build-* *.o *.a *.dylib diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e27e981038..2405ec31666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,10 @@ file(GLOB_RECURSE TLS_SRC "tls/*.c") file(GLOB UTILS_HEADERS "utils/*.h") file(GLOB UTILS_SRC "utils/*.c") +if (WINDOWS) + list(APPEND UTILS_HEADERS "win_shim/win_shim.h") + list(APPEND UTILS_SRC "win_shim/mmap-windows.c") +endif (WINDOWS) # Always include the top-level pq-crypto/ files file(GLOB PQ_HEADERS "pq-crypto/*.h") @@ -268,11 +272,12 @@ set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) set(CMAKE_C_FLAGS_DEBUGOPT "") -target_compile_options(${PROJECT_NAME} PRIVATE -pedantic -std=gnu99 -Wall -Wimplicit -Wunused -Wcomment -Wchar-subscripts - -Wuninitialized -Wshadow -Wcast-align -Wwrite-strings -Wno-deprecated-declarations -Wno-unknown-pragmas -Wformat-security - -Wno-missing-braces -Wno-strict-prototypes -Wa,--noexecstack -) - +if (NOT MSVC AND NOT CYGWIN AND NOT MSYS AND NOT MINGW) + target_compile_options(${PROJECT_NAME} PRIVATE -pedantic -std=gnu99 -Wall -Wimplicit -Wunused -Wcomment -Wchar-subscripts + -Wuninitialized -Wshadow -Wcast-align -Wwrite-strings -Wno-deprecated-declarations -Wno-unknown-pragmas -Wformat-security + -Wno-missing-braces -Wno-strict-prototypes -Wa,--noexecstack + ) +endif () if (EXPERIMENTAL_TREAT_WARNINGS_AS_ERRORS) target_compile_options(${PROJECT_NAME} PRIVATE -Wsign-compare ) endif() @@ -295,7 +300,7 @@ if(S2N_LTO) endif() endif() -if(NOT APPLE) +if(NOT APPLE AND NOT WINDOWS) set(CMAKE_SHARED_LINKER_FLAGS -Wl,-z,noexecstack,-z,relro,-z,now) endif() diff --git a/api/s2n.h b/api/s2n.h index 29d12143b64..703af20c4dc 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -42,7 +42,11 @@ extern "C" { #include #include #include +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#else #include +#endif /** * Function return code diff --git a/stuffer/s2n_stuffer.h b/stuffer/s2n_stuffer.h index ee9eda12873..a504e69c3ff 100644 --- a/stuffer/s2n_stuffer.h +++ b/stuffer/s2n_stuffer.h @@ -18,7 +18,11 @@ #include #include #include +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#else #include +#endif #include "utils/s2n_blob.h" #include "utils/s2n_result.h" diff --git a/stuffer/s2n_stuffer_file.c b/stuffer/s2n_stuffer_file.c index a41564d54b4..58b69be366a 100644 --- a/stuffer/s2n_stuffer_file.c +++ b/stuffer/s2n_stuffer_file.c @@ -15,10 +15,13 @@ #include #include -#include #include #include +#if defined(_MSC_VER) || defined(__MINGW32__) +#else +#include #include +#endif #include "error/s2n_errno.h" #include "stuffer/s2n_stuffer.h" diff --git a/win_shim/mmap-windows.c b/win_shim/mmap-windows.c new file mode 100644 index 00000000000..3f73bd4f62a --- /dev/null +++ b/win_shim/mmap-windows.c @@ -0,0 +1,78 @@ +/* mmap() replacement for Windows + * + * Author: Mike Frysinger + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#include +#include +#include + +#include "win_shim.h" + +void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +void munmap(void *addr, size_t length) +{ + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} + +#undef DWORD_HI +#undef DWORD_LO diff --git a/win_shim/win_shim.h b/win_shim/win_shim.h new file mode 100644 index 00000000000..9b9954ce1a9 --- /dev/null +++ b/win_shim/win_shim.h @@ -0,0 +1,54 @@ +#ifndef S2N_WIN_SHIM_H +#define S2N_WIN_SHIM_H + +#ifdef WIN32 + +#include + +struct iovec { + size_t iov_len; + void *iov_base; +}; + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* !MIN */ + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif /* !MAX */ + +/* */ + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE + #define PROT_EXEC 0x4 +#else + #define PROT_EXEC 0x0 + #define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +#ifdef __USE_FILE_OFFSET64 + # define DWORD_HI(x) (x >> 32) + # define DWORD_LO(x) ((x) & 0xffffffff) +#else + # define DWORD_HI(x) (0) + # define DWORD_LO(x) (x) +#endif + +void *mmap(void *, size_t, int, int, int, off_t); +void munmap(void *, size_t); + +/* */ + +#endif /* WIN32 */ + +#endif /* !S2N_WIN_SHIM_H */ From d5aed33283fb518e03d45bf8a26e502717c8086e Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Sun, 4 Jun 2023 16:58:58 -0400 Subject: [PATCH 2/6] WiP MSVC support --- CMakeLists.txt | 58 +++++++++++++++++++++++++++++++++++++++----------- vcpkg.json | 8 +++++++ 2 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 vcpkg.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 2405ec31666..7c08a62502d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,9 @@ else() find_package(Threads REQUIRED) endif() +set(gcc_like "$") +set(msvc "$") + if(S2N_NO_PQ_ASM) message(STATUS "S2N_NO_PQ_ASM flag was detected - disabling PQ crypto assembly code") else() @@ -189,7 +192,9 @@ try_compile( FALL_THROUGH_SUPPORTED ${CMAKE_BINARY_DIR} SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/fallthrough.c" - COMPILE_DEFINITIONS "-Werror" + COMPILE_DEFINITIONS + "$<${gcc_like}:$>" + "$<${msvc}:$>" ) # Determine if __restrict__ is available @@ -197,7 +202,9 @@ try_compile( __RESTRICT__SUPPORTED ${CMAKE_BINARY_DIR} SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/__restrict__.c" - COMPILE_DEFINITIONS "-Werror" + COMPILE_DEFINITIONS + "$<${gcc_like}:$>" + "$<${msvc}:$>" ) # Determine if madvise() is available @@ -205,7 +212,9 @@ try_compile( MADVISE_SUPPORTED ${CMAKE_BINARY_DIR} SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/madvise.c" - COMPILE_DEFINITIONS "-Werror" + COMPILE_DEFINITIONS + "$<${gcc_like}:$>" + "$<${msvc}:$>" ) # Determine if minherit() is available @@ -213,7 +222,9 @@ try_compile( MINHERIT_SUPPORTED ${CMAKE_BINARY_DIR} SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/minherit.c" - COMPILE_DEFINITIONS "-Werror" + COMPILE_DEFINITIONS + "$<${gcc_like}:$>" + "$<${msvc}:$>" ) # Determine if clone() is available @@ -221,7 +232,9 @@ try_compile( CLONE_SUPPORTED ${CMAKE_BINARY_DIR} SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/clone.c" - COMPILE_DEFINITIONS "-Werror" + COMPILE_DEFINITIONS + "$<${gcc_like}:$>" + "$<${msvc}:$>" ) if(APPLE) @@ -234,6 +247,9 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(OS_LIBS Threads::Threads kvm) elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") set(OS_LIBS Threads::Threads dl) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + find_package(PThreads4W REQUIRED) + set(OS_LIBS PThreads4W::PThreads4W) else() set(OS_LIBS Threads::Threads dl rt) endif() @@ -283,7 +299,12 @@ if (EXPERIMENTAL_TREAT_WARNINGS_AS_ERRORS) endif() if (UNSAFE_TREAT_WARNINGS_AS_ERRORS) - target_compile_options(${PROJECT_NAME} PRIVATE -Werror ) + target_compile_options( + ${PROJECT_NAME} + PRIVATE + "$<${gcc_like}:$>" + "$<${msvc}:$>" + ) endif () if(BUILD_TESTING AND BUILD_SHARED_LIBS) @@ -409,6 +430,10 @@ endif() if (TARGET crypto) message(STATUS "S2N found target: crypto") set(LINK_LIB "crypto") +elseif(DEFINED Z_VCPKG_EXECUTABLE) + find_package(OpenSSL REQUIRED) + message(STATUS "Using libcrypto from vcpkg") + set(LINK_LIB "OpenSSL::Crypto") else() find_package(crypto REQUIRED) message(STATUS "Using libcrypto from the cmake path") @@ -463,7 +488,9 @@ try_compile( ${CMAKE_BINARY_DIR} SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/evp_kem_kyber_512.c" LINK_LIBRARIES ${LINK_LIB} ${OS_LIBS} - COMPILE_DEFINITIONS "-Werror" + COMPILE_DEFINITIONS + "$<${gcc_like}:$>" + "$<${msvc}:$>" ) if(LIBCRYPTO_SUPPORTS_EVP_KEM_KYBER_512) @@ -638,7 +665,9 @@ if (BUILD_TESTING) find . -name '${test_case_name}.c.o' -exec objcopy --redefine-syms libcrypto.symbols {} \\\; ) endif() - target_compile_options(${test_case_name} PRIVATE -Wno-implicit-function-declaration -Wno-deprecated -D_POSIX_C_SOURCE=200809L -std=gnu99) + if (NOT MSVC AND NOT CYGWIN AND NOT MSYS AND NOT MINGW) + target_compile_options(${test_case_name} PRIVATE -Wno-implicit-function-declaration -Wno-deprecated -D_POSIX_C_SOURCE=200809L -std=gnu99) + endif () if (S2N_LTO) target_compile_options(${test_case_name} PRIVATE -flto) endif() @@ -700,10 +729,15 @@ if (BUILD_TESTING) 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) + target_compile_options( + ${benchmark_name} + PRIVATE + "$<${gcc_like}:$>" + "$<${msvc}:$>" + ) endforeach(benchmark) endif() diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000000..ac28683f597 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,8 @@ +{ + "name": "s2n-tls", + "version-string": "0.11.7", + "dependencies": [ + "openssl", + "pthreads" + ] +} \ No newline at end of file From d47dcd7d30c39439527c10005d22d5ec4690fe53 Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Sat, 23 May 2026 15:44:06 +1200 Subject: [PATCH 3/6] Port to MSVC (confirmed it now builds) --- CMakeLists.txt | 17 +- api/s2n.h | 4 +- bin/common.c | 1149 ++-- bin/echo.c | 962 ++-- bin/policy.c | 120 +- bin/s2nc.c | 1653 +++--- bin/s2nd.c | 1431 ++--- cmake/modules/Findcrypto.cmake | 242 +- crypto/s2n_certificate.c | 1752 +++---- crypto/s2n_dhe.c | 767 +-- crypto/s2n_hkdf.c | 789 +-- crypto/s2n_locking.c | 238 +- crypto/s2n_openssl_x509.c | 299 +- crypto/s2n_pkey_evp.c | 747 +-- error/s2n_errno.c | 1030 ++-- stuffer/s2n_stuffer.c | 939 ++-- stuffer/s2n_stuffer_file.c | 7 + .../s2n_array_insert_harness.c | 123 +- .../s2n_hash_digest_size_harness.c | 65 +- .../s2n_stuffer_raw_write_harness.c | 107 +- .../s2n_stuffer_reserve_harness.c | 131 +- .../s2n_stuffer_reserve_space_harness.c | 127 +- .../s2n_stuffer_reserve_uint16_harness.c | 123 +- .../s2n_stuffer_reserve_uint24_harness.c | 121 +- .../s2n_stuffer_skip_write_harness.c | 97 +- .../s2n_stuffer_wipe_n_harness.c | 133 +- .../s2n_stuffer_write_harness.c | 127 +- .../s2n_stuffer_write_bytes_harness.c | 115 +- .../s2n_stuffer_write_hex_harness.c | 149 +- .../s2n_stuffer_write_uint16_hex_harness.c | 127 +- .../s2n_stuffer_write_uint8_hex_harness.c | 127 +- tests/fuzz/s2n_cert_req_recv_test.c | 201 +- .../s2n_certificate_extensions_parse_test.c | 231 +- tests/fuzz/s2n_client_key_recv_fuzz_test.c | 297 +- tests/fuzz/s2n_select_server_cert_test.c | 559 +- tests/fuzz/s2n_tls13_cert_req_recv_test.c | 163 +- tests/fuzz/s2n_tls13_cert_verify_recv_test.c | 191 +- tests/testlib/s2n_key_schedule_testlib.c | 157 +- tests/testlib/s2n_ktls_test_utils.c | 499 +- tests/testlib/s2n_test_certs.c | 531 +- tests/unit/s2n_async_pkey_test.c | 1451 +++--- tests/unit/s2n_cbc_test.c | 219 +- tests/unit/s2n_cert_chain_and_key_load_test.c | 343 +- tests/unit/s2n_cert_chain_and_key_test.c | 457 +- tests/unit/s2n_cert_status_extension_test.c | 817 +-- .../unit/s2n_cert_validation_callback_test.c | 1153 ++-- tests/unit/s2n_cipher_suite_match_test.c | 2723 +++++----- tests/unit/s2n_client_auth_handshake_test.c | 1065 ++-- tests/unit/s2n_client_extensions_test.c | 2531 ++++----- tests/unit/s2n_client_hello_recv_test.c | 1123 ++-- tests/unit/s2n_client_hello_retry_test.c | 4021 +++++++------- tests/unit/s2n_client_hello_test.c | 4291 +++++++-------- tests/unit/s2n_client_record_version_test.c | 631 +-- .../s2n_client_secure_renegotiation_test.c | 619 +-- tests/unit/s2n_config_test.c | 2769 +++++----- .../s2n_connection_protocol_versions_test.c | 781 +-- tests/unit/s2n_cookie_test.c | 753 +-- tests/unit/s2n_crl_test.c | 1883 +++---- tests/unit/s2n_drain_alert_test.c | 281 +- tests/unit/s2n_handshake_invariant_test.c | 177 +- tests/unit/s2n_handshake_test.c | 1107 ++-- tests/unit/s2n_handshake_type_test.c | 551 +- tests/unit/s2n_key_update_threads_test.c | 537 +- tests/unit/s2n_mem_allocator_test.c | 611 +-- tests/unit/s2n_mem_usage_test.c | 419 +- tests/unit/s2n_mutual_auth_test.c | 1131 ++-- tests/unit/s2n_openssl_x509_test.c | 477 +- tests/unit/s2n_optional_client_auth_test.c | 957 ++-- tests/unit/s2n_pem_rsa_dhe_test.c | 409 +- tests/unit/s2n_pem_test.c | 247 +- tests/unit/s2n_policy_defaults_test.c | 271 +- tests/unit/s2n_post_handshake_recv_test.c | 1031 ++-- tests/unit/s2n_prf_key_material_test.c | 471 +- tests/unit/s2n_quic_support_io_test.c | 1217 ++--- tests/unit/s2n_record_size_test.c | 1045 ++-- tests/unit/s2n_recv_test.c | 1341 ++--- .../unit/s2n_release_non_empty_buffers_test.c | 415 +- tests/unit/s2n_renegotiate_test.c | 1241 ++--- tests/unit/s2n_rsa_pss_test.c | 701 +-- tests/unit/s2n_seccomp_handshake_test.c | 171 +- tests/unit/s2n_self_talk_alerts_test.c | 671 +-- tests/unit/s2n_self_talk_broken_pipe_test.c | 359 +- tests/unit/s2n_self_talk_certificates_test.c | 279 +- tests/unit/s2n_self_talk_custom_io_test.c | 441 +- .../s2n_self_talk_min_protocol_version_test.c | 259 +- tests/unit/s2n_self_talk_nonblocking_test.c | 785 +-- tests/unit/s2n_self_talk_session_id_test.c | 1345 ++--- tests/unit/s2n_self_talk_tls12_test.c | 413 +- tests/unit/s2n_send_key_update_test.c | 395 +- tests/unit/s2n_send_multirecord_test.c | 1165 ++--- tests/unit/s2n_send_test.c | 1617 +++--- tests/unit/s2n_server_hello_retry_test.c | 1253 ++--- tests/unit/s2n_session_ticket_test.c | 3589 ++++++------- tests/unit/s2n_sslv3_test.c | 263 +- tests/unit/s2n_tls12_handshake_test.c | 1117 ++-- tests/unit/s2n_tls13_cert_verify_test.c | 721 +-- .../s2n_tls13_handshake_early_data_test.c | 713 +-- .../s2n_tls13_handshake_state_machine_test.c | 2015 +++---- tests/unit/s2n_tls13_handshake_test.c | 663 +-- tests/unit/s2n_tls13_parse_record_type_test.c | 513 +- tests/unit/s2n_tls13_pq_handshake_test.c | 1429 ++--- tests/unit/s2n_tls13_server_cert_test.c | 399 +- tests/unit/s2n_tls_prf_test.c | 1057 ++-- tests/unit/s2n_wildcard_hostname_test.c | 425 +- .../unit/s2n_x509_intent_verification_test.c | 1273 ++--- ...09_validator_certificate_signatures_test.c | 439 +- tests/unit/s2n_x509_validator_test.c | 4617 +++++++++-------- ...2n_x509_validator_time_verification_test.c | 573 +- tests/viz/s2n_state_machine_viz.c | 229 +- tls/extensions/s2n_client_psk.c | 843 +-- tls/extensions/s2n_client_server_name.c | 205 +- .../s2n_client_supported_versions.c | 407 +- tls/extensions/s2n_extension_type.c | 515 +- tls/extensions/s2n_supported_versions.c | 77 +- tls/policy/s2n_policy_defaults.c | 257 +- tls/policy/s2n_policy_writer.c | 420 +- tls/s2n_alerts.c | 785 +-- tls/s2n_async_pkey.h | 195 +- tls/s2n_cbc.c | 207 +- tls/s2n_cipher_preferences.c | 4236 +++++++-------- tls/s2n_client_hello.c | 2213 ++++---- tls/s2n_client_key_exchange.c | 695 +-- tls/s2n_config.c | 2683 +++++----- tls/s2n_connection.c | 3785 +++++++------- tls/s2n_early_data.c | 871 ++-- tls/s2n_early_data_io.c | 553 +- tls/s2n_ecc_preferences.h | 86 +- tls/s2n_fingerprint_ja4.c | 1203 ++--- tls/s2n_handshake.c | 765 +-- tls/s2n_handshake_io.c | 3503 ++++++------- tls/s2n_handshake_transcript.c | 259 +- tls/s2n_ktls.h | 160 +- tls/s2n_ktls_io.c | 995 ++-- tls/s2n_ocsp_stapling.c | 80 +- tls/s2n_post_handshake.c | 401 +- tls/s2n_prf.c | 1703 +++--- tls/s2n_psk.c | 1423 ++--- tls/s2n_quic_support.c | 365 +- tls/s2n_record_read.c | 575 +- tls/s2n_record_read_stream.c | 187 +- tls/s2n_record_write.c | 1285 ++--- tls/s2n_recv.c | 661 +-- tls/s2n_resume.c | 2187 ++++---- tls/s2n_send.c | 549 +- tls/s2n_server_hello.c | 687 +-- tls/s2n_server_new_session_ticket.c | 887 ++-- tls/s2n_signature_scheme.h | 220 +- tls/s2n_tls13.h | 110 +- tls/s2n_tls13_certificate_verify.c | 423 +- tls/s2n_tls13_handshake.c | 445 +- tls/s2n_tls13_secrets.c | 1555 +++--- tls/s2n_x509_validator.c | 2329 ++++----- utils/s2n_array.c | 443 +- utils/s2n_blob.c | 185 +- utils/s2n_compiler.h | 55 +- utils/s2n_ensure.h | 356 +- utils/s2n_init.c | 322 +- utils/s2n_mem.c | 795 +-- utils/s2n_prelude.h | 103 +- utils/s2n_random.c | 806 +-- utils/s2n_random.h | 72 +- utils/s2n_rfc5952.c | 268 +- utils/s2n_safety.h | 242 +- utils/s2n_safety_macros.h | 1270 ++--- utils/s2n_socket.c | 492 +- win_shim/mmap-windows.c | 156 +- win_shim/win_shim.h | 141 +- 167 files changed, 68136 insertions(+), 67662 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ca9eb8557c..294a6f5a817 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,8 +111,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") set(OS_LIBS Threads::Threads dl) elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - find_package(PThreads4W REQUIRED) - set(OS_LIBS PThreads4W::PThreads4W) + set(OS_LIBS bcrypt) + # pthreads is stubbed for native MSVC else() set(OS_LIBS Threads::Threads dl rt) endif() @@ -176,12 +176,12 @@ endif () if (S2N_WERROR_ALL) target_compile_options(${PROJECT_NAME} PUBLIC "$<${gcc_like}:$>" - "$<${msvc}:$>" + "$<${msvc}:$>" ) elseif (UNSAFE_TREAT_WARNINGS_AS_ERRORS) target_compile_options(${PROJECT_NAME} PRIVATE "$<${gcc_like}:$>" - "$<${msvc}:$>" + "$<${msvc}:$>" ) endif () @@ -253,7 +253,8 @@ if(TSAN OR ASAN OR UBSAN) target_compile_options(${PROJECT_NAME} PUBLIC -fno-omit-frame-pointer -fno-optimize-sibling-calls) endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") +file(TO_CMAKE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules" MODULE_PATH) +set(CMAKE_MODULE_PATH "${MODULE_PATH}") if (COVERAGE) # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html @@ -504,7 +505,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ${OS_LIBS} m) target_include_directories(${PROJECT_NAME} PUBLIC $) target_include_directories(${PROJECT_NAME} PUBLIC $ $) -if (BUILD_TESTING) +if (BUILD_TESTING AND NOT MSVC) enable_testing() ############################################################################ @@ -623,7 +624,7 @@ if (BUILD_TESTING) if (UNSAFE_TREAT_WARNINGS_AS_ERRORS) target_compile_options(${test_case_name} PRIVATE "$<${gcc_like}:$>" - "$<${msvc}:$>" + "$<${msvc}:$>" ) endif() if (S2N_LTO) @@ -644,6 +645,7 @@ if (BUILD_TESTING) ######################### build utility binaries ########################### ############################################################################ +if (NOT MSVC) add_executable(s2nc "bin/s2nc.c" "bin/echo.c" "bin/https.c" "bin/common.c") target_link_libraries(s2nc ${PROJECT_NAME}) target_compile_options(s2nc PRIVATE -std=gnu99) @@ -660,6 +662,7 @@ if (BUILD_TESTING) target_compile_options(s2nc PRIVATE -flto) target_compile_options(s2nd PRIVATE -flto) endif() + endif() if (S2N_INTEG_TESTS) find_package (Python3 COMPONENTS Interpreter Development) diff --git a/api/s2n.h b/api/s2n.h index 8022a43c685..3874df2d405 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -119,7 +119,7 @@ extern "C" { * * @warning To avoid possible confusion, s2n_errno should be cleared after processing an error: `s2n_errno = S2N_ERR_T_OK` */ -S2N_API extern __thread int s2n_errno; +extern __thread int s2n_errno; /** * This function can be used instead of trying to resolve `s2n_errno` directly @@ -2277,7 +2277,6 @@ S2N_API extern int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status */ S2N_API extern ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked); -#ifndef _WIN32 /** * Works in the same way as s2n_sendv_with_offset() but with the `offs` parameter implicitly assumed to be 0. * Therefore in the partial write case, the caller would have to make sure that the `bufs` and `count` fields are modified in a way that takes @@ -2309,7 +2308,6 @@ S2N_API extern ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. */ S2N_API extern ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, ssize_t offs, s2n_blocked_status *blocked); -#endif /** * Decrypts and reads **size* to `buf` data from the associated diff --git a/bin/common.c b/bin/common.c index 66c3459f245..259f33b1d4b 100644 --- a/bin/common.c +++ b/bin/common.c @@ -1,571 +1,578 @@ -/* - * 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 "common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" -uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - -uint8_t default_ticket_key[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, - 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, - 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, - 0xb3, 0xe5 }; - -struct session_cache_entry session_cache[256]; - -static char dhparams[] = - "-----BEGIN DH PARAMETERS-----\n" - "MIIBCAKCAQEAy1+hVWCfNQoPB+NA733IVOONl8fCumiz9zdRRu1hzVa2yvGseUSq\n" - "Bbn6k0FQ7yMED6w5XWQKDC0z2m0FI/BPE3AjUfuPzEYGqTDf9zQZ2Lz4oAN90Sud\n" - "luOoEhYR99cEbCn0T4eBvEf9IUtczXUZ/wj7gzGbGG07dLfT+CmCRJxCjhrosenJ\n" - "gzucyS7jt1bobgU66JKkgMNm7hJY4/nhR5LWTCzZyzYQh2HM2Vk4K5ZqILpj/n0S\n" - "5JYTQ2PVhxP+Uu8+hICs/8VvM72DznjPZzufADipjC7CsQ4S6x/ecZluFtbb+ZTv\n" - "HI5CnYmkAwJ6+FSWGaZQDi8bgerFk9RWwwIBAg==\n" - "-----END DH PARAMETERS-----\n"; - -/* - * Since this is a server, and the mechanism for hostname verification is not defined for this use-case, - * allow any hostname through. If you are writing something with mutual auth and you have a scheme for verifying - * the client (e.g. a reverse DNS lookup), you would plug that in here. - */ -static uint8_t unsafe_verify_host_fn(const char *host_name, size_t host_name_len, void *data) -{ - return 1; -} - -int write_array_to_file(const char *path, uint8_t *data, size_t length) -{ - GUARD_EXIT_NULL(path); - GUARD_EXIT_NULL(data); - - FILE *file = fopen(path, "wb"); - if (!file) { - return S2N_FAILURE; - } - - if (fwrite(data, sizeof(char), length, file) != length) { - fclose(file); - return S2N_FAILURE; - } - fclose(file); - - return S2N_SUCCESS; -} - -int get_file_size(const char *path, size_t *length) -{ - GUARD_EXIT_NULL(path); - GUARD_EXIT_NULL(length); - - FILE *file = fopen(path, "rb"); - if (!file) { - return S2N_FAILURE; - } - - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return S2N_FAILURE; - } - - long file_length = ftell(file); - if (file_length < 0) { - fclose(file); - return S2N_FAILURE; - } - - *length = file_length; - fclose(file); - return S2N_SUCCESS; -} - -int load_file_to_array(const char *path, uint8_t *data, size_t max_length) -{ - GUARD_EXIT_NULL(path); - GUARD_EXIT_NULL(data); - - size_t file_size = 0; - if (get_file_size(path, &file_size) < 0 || file_size > max_length) { - return S2N_FAILURE; - } - - FILE *file = fopen(path, "rb"); - if (!file) { - return S2N_FAILURE; - } - - if (fread(data, sizeof(char), file_size, file) < file_size) { - fclose(file); - return S2N_FAILURE; - } - - fclose(file); - return S2N_SUCCESS; -} - -char *load_file_to_cstring(const char *path) -{ - FILE *pem_file = fopen(path, "rb"); - if (!pem_file) { - fprintf(stderr, "Failed to open file %s: '%s'\n", path, strerror(errno)); - return NULL; - } - - /* Make sure we can fit the pem into the output buffer */ - if (fseek(pem_file, 0, SEEK_END) < 0) { - fprintf(stderr, "Failed calling fseek: '%s'\n", strerror(errno)); - fclose(pem_file); - return NULL; - } - - const ssize_t pem_file_size = ftell(pem_file); - if (pem_file_size < 0) { - fprintf(stderr, "Failed calling ftell: '%s'\n", strerror(errno)); - fclose(pem_file); - return NULL; - } - - rewind(pem_file); - - char *pem_out = malloc(pem_file_size + 1); - if (pem_out == NULL) { - fprintf(stderr, "Failed allocating memory\n"); - fclose(pem_file); - return NULL; - } - - if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < (size_t) pem_file_size) { - fprintf(stderr, "Failed reading file: '%s'\n", strerror(errno)); - free(pem_out); - fclose(pem_file); - return NULL; - } - - pem_out[pem_file_size] = '\0'; - fclose(pem_file); - - return pem_out; -} - -int key_log_callback(void *file, struct s2n_connection *conn, uint8_t *logline, size_t len) -{ - if (fwrite(logline, 1, len, (FILE *) file) != len) { - return S2N_FAILURE; - } - - if (fprintf((FILE *) file, "\n") < 0) { - return S2N_FAILURE; - } - - return fflush((FILE *) file); -} - -/* An inverse map from an ascii value to a hexadecimal nibble value - * accounts for all possible char values, where 255 is invalid value */ -static const uint8_t hex_inverse[256] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 -}; - -int s2n_str_hex_to_bytes(const unsigned char *hex, uint8_t *out_bytes, uint32_t max_out_bytes_len) -{ - GUARD_EXIT_NULL(hex); - GUARD_EXIT_NULL(out_bytes); - - uint32_t len_with_spaces = strlen((const char *) hex); - size_t i = 0, j = 0; - while (j < len_with_spaces) { - if (hex[j] == ' ') { - j++; - continue; - } - - uint8_t high_nibble = hex_inverse[hex[j]]; - if (high_nibble == 255) { - fprintf(stderr, "Invalid HEX encountered\n"); - return S2N_FAILURE; - } - - uint8_t low_nibble = hex_inverse[hex[j + 1]]; - if (low_nibble == 255) { - fprintf(stderr, "Invalid HEX encountered\n"); - return S2N_FAILURE; - } - - if (max_out_bytes_len < i) { - fprintf(stderr, "Insufficient memory for bytes buffer, try increasing the allocation size\n"); - return S2N_FAILURE; - } - out_bytes[i] = high_nibble << 4 | low_nibble; - - i++; - j += 2; - } - - return S2N_SUCCESS; -} - -static int s2n_get_psk_hmac_alg(s2n_psk_hmac *psk_hmac, char *hmac_str) -{ - GUARD_EXIT_NULL(psk_hmac); - GUARD_EXIT_NULL(hmac_str); - - if (strcmp(hmac_str, "SHA256") == 0) { - *psk_hmac = S2N_PSK_HMAC_SHA256; - } else if (strcmp(hmac_str, "SHA384") == 0) { - *psk_hmac = S2N_PSK_HMAC_SHA384; - } else { - return S2N_FAILURE; - } - return S2N_SUCCESS; -} - -static int s2n_setup_external_psk(struct s2n_psk **psk, char *params) -{ - GUARD_EXIT_NULL(psk); - GUARD_EXIT_NULL(params); - - /* duplicate params as strtok will modify the input string */ - char *params_dup = malloc(strlen(params) + 1); - GUARD_EXIT_NULL(params_dup); - strcpy(params_dup, params); - - size_t token_idx = 0; - for (char *token = strtok(params_dup, ","); token != NULL; token = strtok(NULL, ","), token_idx++) { - switch (token_idx) { - case 0: - GUARD_EXIT(s2n_psk_set_identity(*psk, (const uint8_t *) token, strlen(token)), - "Error setting psk identity\n"); - break; - case 1: { - uint32_t max_secret_len = strlen(token) / 2; - uint8_t *secret = malloc(max_secret_len); - GUARD_EXIT_NULL(secret); - GUARD_EXIT(s2n_str_hex_to_bytes((const unsigned char *) token, secret, max_secret_len), "Error converting hex-encoded psk secret to bytes\n"); - GUARD_EXIT(s2n_psk_set_secret(*psk, secret, max_secret_len), "Error setting psk secret\n"); - free(secret); - } break; - case 2: { - s2n_psk_hmac psk_hmac_alg = 0; - GUARD_EXIT(s2n_get_psk_hmac_alg(&psk_hmac_alg, token), "Invalid psk hmac algorithm\n"); - GUARD_EXIT(s2n_psk_set_hmac(*psk, psk_hmac_alg), "Error setting psk hmac algorithm\n"); - } break; - default: - break; - } - } - - free(params_dup); - return S2N_SUCCESS; -} - -int s2n_setup_external_psk_list(struct s2n_connection *conn, char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH], size_t psk_list_len) -{ - GUARD_EXIT_NULL(conn); - GUARD_EXIT_NULL(psk_optarg_list); - - for (size_t i = 0; i < psk_list_len; i++) { - struct s2n_psk *psk = s2n_external_psk_new(); - GUARD_EXIT_NULL(psk); - GUARD_EXIT(s2n_setup_external_psk(&psk, psk_optarg_list[i]), "Error setting external PSK parameters\n"); - GUARD_EXIT(s2n_connection_append_psk(conn, psk), "Error appending psk to the connection\n"); - GUARD_EXIT(s2n_psk_free(&psk), "Error freeing psk\n"); - } - return S2N_SUCCESS; -} - -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"); - - GUARD_EXIT(s2n_config_set_cipher_preferences(config, cipher_prefs), "Error setting cipher prefs"); - - GUARD_EXIT(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache), "Error setting cache store callback"); - - GUARD_EXIT(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache), "Error setting cache retrieve callback"); - - GUARD_EXIT(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache), "Error setting cache retrieve callback"); - - if (conn_settings.enable_mfl) { - GUARD_EXIT(s2n_config_accept_max_fragment_length(config), "Error enabling TLS maximum fragment length extension in server"); - } - - if (s2n_config_set_verify_host_callback(config, unsafe_verify_host_fn, NULL)) { - print_s2n_error("Failure to set hostname verification callback"); - exit(1); - } - - if (conn_settings.session_ticket) { - GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); - } - - if (conn_settings.session_cache) { - GUARD_EXIT(s2n_config_set_session_cache_onoff(config, 1), "Error enabling session cache using id"); - } - - if (conn_settings.session_ticket || conn_settings.session_cache) { - /* Key initialization */ - uint8_t *st_key = NULL; - uint32_t st_key_length = 0; - - if (session_ticket_key_file_path) { - int fd = open(session_ticket_key_file_path, O_RDONLY); - GUARD_EXIT(fd, "Error opening session ticket key file"); - - struct stat st; - GUARD_EXIT(fstat(fd, &st), "Error fstat-ing session ticket key file"); - - st_key = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - POSIX_ENSURE(st_key != MAP_FAILED, S2N_ERR_MMAP); - - st_key_length = st.st_size; - - close(fd); - } else { - st_key = default_ticket_key; - st_key_length = sizeof(default_ticket_key); - } - - if (s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), st_key, st_key_length, 0) != 0) { - fprintf(stderr, "Error adding ticket key: '%s'\n", s2n_strerror(s2n_errno, "EN")); - exit(1); - } - } - return 0; -} - -int s2n_setup_server_connection(struct s2n_connection *conn, int fd, struct s2n_config *config, struct conn_settings settings) -{ - if (settings.deserialize_in) { - GUARD_RETURN(s2n_connection_deserialize_in(conn, settings.deserialize_in), "Failed to deserialize file"); - } - - if (settings.self_service_blinding) { - s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING); - } - - if (settings.mutual_auth) { - GUARD_RETURN(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED), "Error setting client auth type"); - - if (settings.ca_dir || settings.ca_file) { - GUARD_RETURN(s2n_config_set_verification_ca_location(config, settings.ca_file, settings.ca_dir), "Error adding verify location"); - } - - if (settings.insecure) { - GUARD_RETURN(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); - } - } - - GUARD_RETURN(s2n_connection_set_config(conn, config), "Error setting configuration"); - - if (settings.prefer_throughput) { - GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); - } - - if (settings.prefer_low_latency) { - GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); - } - - GUARD_RETURN(s2n_connection_set_fd(conn, fd), "Error setting file descriptor"); - - if (settings.use_corked_io) { - GUARD_RETURN(s2n_connection_use_corked_io(conn), "Error setting corked io"); - } - - GUARD_RETURN( - s2n_setup_external_psk_list(conn, settings.psk_optarg_list, settings.psk_list_len), - "Error setting external psk list"); - - GUARD_RETURN(early_data_recv(conn), "Error receiving early data"); - return 0; -} - -int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) -{ - struct session_cache_entry *cache = ctx; - - POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); - POSIX_ENSURE_INCLUSIVE_RANGE(1, value_size, MAX_VAL_LEN); - - uint8_t idx = ((const uint8_t *) key)[0]; - - memmove(cache[idx].key, key, key_size); - memmove(cache[idx].value, value, value_size); - - cache[idx].key_len = key_size; - cache[idx].value_len = value_size; - - return 0; -} - -int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) -{ - struct session_cache_entry *cache = ctx; - - POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); - - uint8_t idx = ((const uint8_t *) key)[0]; - - POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(*value_size >= cache[idx].value_len, S2N_ERR_INVALID_ARGUMENT); - - *value_size = cache[idx].value_len; - memmove(value, cache[idx].value, cache[idx].value_len); - - for (uint64_t i = 0; i < key_size; i++) { - printf("%02x", ((const uint8_t *) key)[i]); - } - printf("\n"); - - return 0; -} - -int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) -{ - struct session_cache_entry *cache = ctx; - - POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); - - uint8_t idx = ((const uint8_t *) key)[0]; - - if (cache[idx].key_len != 0) { - POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); - } - - cache[idx].key_len = 0; - cache[idx].value_len = 0; - - return 0; -} - -uint8_t unsafe_verify_host(const char *host_name, size_t host_name_len, void *data) -{ - struct verify_data *verify_data = (struct verify_data *) data; - - if (host_name_len > 2 && host_name[0] == '*' && host_name[1] == '.') { - char *suffix = strstr(verify_data->trusted_host, "."); - return (uint8_t) (strcasecmp(suffix, host_name + 1) == 0); - } - - /* If we're connecting to localhost, accept any values that represent localhost */ - bool is_localhost = (strcasecmp(verify_data->trusted_host, "localhost") == 0); - is_localhost |= (strcasecmp(verify_data->trusted_host, "127.0.0.1") == 0); - if (is_localhost) { - bool match = (strcasecmp(host_name, "localhost") == 0); - match |= (strcasecmp(host_name, "127.0.0.1") == 0); - /* Some of our older test certificates use odd common names */ - match |= (strcasecmp(host_name, "s2nTestServer") == 0); - return (uint8_t) match; - } - - return (uint8_t) (strcasecmp(host_name, verify_data->trusted_host) == 0); -} - -int wait_for_shutdown(struct s2n_connection *conn, int fd) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - while (s2n_shutdown(conn, &blocked) != S2N_SUCCESS) { - int errno_val = errno; - switch (s2n_error_get_type(s2n_errno)) { - case S2N_ERR_T_BLOCKED: - GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for shutdown"); - break; - case S2N_ERR_T_CLOSED: - /* We can't control the behavior of our peer. If the peer indicates end-of-stream - * without sending a close_notify, don't treat it as an error, but print a warning. - * - * This is common in our integration tests both because OpenSSL s_server - * never sends a close_notify (see https://github.com/openssl/openssl/issues/1806) - * and because we tend to kill processes rather than waiting for a graceful shutdown. - */ - fprintf(stdout, "Connection closed by peer\n"); - return S2N_SUCCESS; - case S2N_ERR_T_IO: - /* Again, we can't control the behavior of our peer, so just print a warning. - * Killing a process can result in its peer receiving a ECONNRESET. - */ - if (errno_val == ECONNRESET) { - fprintf(stdout, "Connection reset by peer\n"); - return S2N_SUCCESS; - } - /* Otherwise, IO errors are fatal and should be investigated */ - fprintf(stderr, "Unexpected IO error during shutdown: %s\n", strerror(errno_val)); - return S2N_FAILURE; - default: - return S2N_FAILURE; - } - } - return S2N_SUCCESS; -} - -int s2n_connection_serialize_out(struct s2n_connection *conn, const char *file_path) -{ - uint32_t serialize_length = 0; - GUARD_RETURN(s2n_connection_serialization_length(conn, &serialize_length), "Failed to get serialized connection length"); - uint8_t *mem = malloc(serialize_length); - GUARD_RETURN_NULL(mem); - GUARD_RETURN(s2n_connection_serialize(conn, mem, serialize_length), "Failed to get serialized connection"); - GUARD_RETURN(write_array_to_file(file_path, mem, serialize_length), "Failed to write serialized connection to file"); - free(mem); - - return 0; -} - -int s2n_connection_deserialize_in(struct s2n_connection *conn, const char *file_path) -{ - size_t deserialize_length = 0; - GUARD_RETURN(get_file_size(file_path, &deserialize_length), "Failed to read deserialize-in file size"); - ENSURE_RETURN(deserialize_length <= UINT32_MAX, "deserialize-in file size is too large"); - uint8_t *mem = malloc(deserialize_length); - GUARD_RETURN_NULL(mem); - GUARD_RETURN(load_file_to_array(file_path, mem, deserialize_length), "Failed to read deserialize-in file"); - GUARD_RETURN(s2n_connection_deserialize(conn, mem, (uint32_t) deserialize_length), "Failed to deserialize connection"); - free(mem); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "common.h" + +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" +uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + +uint8_t default_ticket_key[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, + 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, + 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, + 0xb3, 0xe5 }; + +struct session_cache_entry session_cache[256]; + +static char dhparams[] = + "-----BEGIN DH PARAMETERS-----\n" + "MIIBCAKCAQEAy1+hVWCfNQoPB+NA733IVOONl8fCumiz9zdRRu1hzVa2yvGseUSq\n" + "Bbn6k0FQ7yMED6w5XWQKDC0z2m0FI/BPE3AjUfuPzEYGqTDf9zQZ2Lz4oAN90Sud\n" + "luOoEhYR99cEbCn0T4eBvEf9IUtczXUZ/wj7gzGbGG07dLfT+CmCRJxCjhrosenJ\n" + "gzucyS7jt1bobgU66JKkgMNm7hJY4/nhR5LWTCzZyzYQh2HM2Vk4K5ZqILpj/n0S\n" + "5JYTQ2PVhxP+Uu8+hICs/8VvM72DznjPZzufADipjC7CsQ4S6x/ecZluFtbb+ZTv\n" + "HI5CnYmkAwJ6+FSWGaZQDi8bgerFk9RWwwIBAg==\n" + "-----END DH PARAMETERS-----\n"; + +/* + * Since this is a server, and the mechanism for hostname verification is not defined for this use-case, + * allow any hostname through. If you are writing something with mutual auth and you have a scheme for verifying + * the client (e.g. a reverse DNS lookup), you would plug that in here. + */ +static uint8_t unsafe_verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +int write_array_to_file(const char *path, uint8_t *data, size_t length) +{ + GUARD_EXIT_NULL(path); + GUARD_EXIT_NULL(data); + + FILE *file = fopen(path, "wb"); + if (!file) { + return S2N_FAILURE; + } + + if (fwrite(data, sizeof(char), length, file) != length) { + fclose(file); + return S2N_FAILURE; + } + fclose(file); + + return S2N_SUCCESS; +} + +int get_file_size(const char *path, size_t *length) +{ + GUARD_EXIT_NULL(path); + GUARD_EXIT_NULL(length); + + FILE *file = fopen(path, "rb"); + if (!file) { + return S2N_FAILURE; + } + + if (fseek(file, 0, SEEK_END) != 0) { + fclose(file); + return S2N_FAILURE; + } + + long file_length = ftell(file); + if (file_length < 0) { + fclose(file); + return S2N_FAILURE; + } + + *length = file_length; + fclose(file); + return S2N_SUCCESS; +} + +int load_file_to_array(const char *path, uint8_t *data, size_t max_length) +{ + GUARD_EXIT_NULL(path); + GUARD_EXIT_NULL(data); + + size_t file_size = 0; + if (get_file_size(path, &file_size) < 0 || file_size > max_length) { + return S2N_FAILURE; + } + + FILE *file = fopen(path, "rb"); + if (!file) { + return S2N_FAILURE; + } + + if (fread(data, sizeof(char), file_size, file) < file_size) { + fclose(file); + return S2N_FAILURE; + } + + fclose(file); + return S2N_SUCCESS; +} + +char *load_file_to_cstring(const char *path) +{ + FILE *pem_file = fopen(path, "rb"); + if (!pem_file) { + fprintf(stderr, "Failed to open file %s: '%s'\n", path, strerror(errno)); + return NULL; + } + + /* Make sure we can fit the pem into the output buffer */ + if (fseek(pem_file, 0, SEEK_END) < 0) { + fprintf(stderr, "Failed calling fseek: '%s'\n", strerror(errno)); + fclose(pem_file); + return NULL; + } + + const ssize_t pem_file_size = ftell(pem_file); + if (pem_file_size < 0) { + fprintf(stderr, "Failed calling ftell: '%s'\n", strerror(errno)); + fclose(pem_file); + return NULL; + } + + rewind(pem_file); + + char *pem_out = malloc(pem_file_size + 1); + if (pem_out == NULL) { + fprintf(stderr, "Failed allocating memory\n"); + fclose(pem_file); + return NULL; + } + + if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < (size_t) pem_file_size) { + fprintf(stderr, "Failed reading file: '%s'\n", strerror(errno)); + free(pem_out); + fclose(pem_file); + return NULL; + } + + pem_out[pem_file_size] = '\0'; + fclose(pem_file); + + return pem_out; +} + +int key_log_callback(void *file, struct s2n_connection *conn, uint8_t *logline, size_t len) +{ + if (fwrite(logline, 1, len, (FILE *) file) != len) { + return S2N_FAILURE; + } + + if (fprintf((FILE *) file, "\n") < 0) { + return S2N_FAILURE; + } + + return fflush((FILE *) file); +} + +/* An inverse map from an ascii value to a hexadecimal nibble value + * accounts for all possible char values, where 255 is invalid value */ +static const uint8_t hex_inverse[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 +}; + +int s2n_str_hex_to_bytes(const unsigned char *hex, uint8_t *out_bytes, uint32_t max_out_bytes_len) +{ + GUARD_EXIT_NULL(hex); + GUARD_EXIT_NULL(out_bytes); + + uint32_t len_with_spaces = strlen((const char *) hex); + size_t i = 0, j = 0; + while (j < len_with_spaces) { + if (hex[j] == ' ') { + j++; + continue; + } + + uint8_t high_nibble = hex_inverse[hex[j]]; + if (high_nibble == 255) { + fprintf(stderr, "Invalid HEX encountered\n"); + return S2N_FAILURE; + } + + uint8_t low_nibble = hex_inverse[hex[j + 1]]; + if (low_nibble == 255) { + fprintf(stderr, "Invalid HEX encountered\n"); + return S2N_FAILURE; + } + + if (max_out_bytes_len < i) { + fprintf(stderr, "Insufficient memory for bytes buffer, try increasing the allocation size\n"); + return S2N_FAILURE; + } + out_bytes[i] = high_nibble << 4 | low_nibble; + + i++; + j += 2; + } + + return S2N_SUCCESS; +} + +static int s2n_get_psk_hmac_alg(s2n_psk_hmac *psk_hmac, char *hmac_str) +{ + GUARD_EXIT_NULL(psk_hmac); + GUARD_EXIT_NULL(hmac_str); + + if (strcmp(hmac_str, "SHA256") == 0) { + *psk_hmac = S2N_PSK_HMAC_SHA256; + } else if (strcmp(hmac_str, "SHA384") == 0) { + *psk_hmac = S2N_PSK_HMAC_SHA384; + } else { + return S2N_FAILURE; + } + return S2N_SUCCESS; +} + +static int s2n_setup_external_psk(struct s2n_psk **psk, char *params) +{ + GUARD_EXIT_NULL(psk); + GUARD_EXIT_NULL(params); + + /* duplicate params as strtok will modify the input string */ + char *params_dup = malloc(strlen(params) + 1); + GUARD_EXIT_NULL(params_dup); + strcpy(params_dup, params); + + size_t token_idx = 0; + for (char *token = strtok(params_dup, ","); token != NULL; token = strtok(NULL, ","), token_idx++) { + switch (token_idx) { + case 0: + GUARD_EXIT(s2n_psk_set_identity(*psk, (const uint8_t *) token, strlen(token)), + "Error setting psk identity\n"); + break; + case 1: { + uint32_t max_secret_len = strlen(token) / 2; + uint8_t *secret = malloc(max_secret_len); + GUARD_EXIT_NULL(secret); + GUARD_EXIT(s2n_str_hex_to_bytes((const unsigned char *) token, secret, max_secret_len), "Error converting hex-encoded psk secret to bytes\n"); + GUARD_EXIT(s2n_psk_set_secret(*psk, secret, max_secret_len), "Error setting psk secret\n"); + free(secret); + } break; + case 2: { + s2n_psk_hmac psk_hmac_alg = 0; + GUARD_EXIT(s2n_get_psk_hmac_alg(&psk_hmac_alg, token), "Invalid psk hmac algorithm\n"); + GUARD_EXIT(s2n_psk_set_hmac(*psk, psk_hmac_alg), "Error setting psk hmac algorithm\n"); + } break; + default: + break; + } + } + + free(params_dup); + return S2N_SUCCESS; +} + +int s2n_setup_external_psk_list(struct s2n_connection *conn, char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH], size_t psk_list_len) +{ + GUARD_EXIT_NULL(conn); + GUARD_EXIT_NULL(psk_optarg_list); + + for (size_t i = 0; i < psk_list_len; i++) { + struct s2n_psk *psk = s2n_external_psk_new(); + GUARD_EXIT_NULL(psk); + GUARD_EXIT(s2n_setup_external_psk(&psk, psk_optarg_list[i]), "Error setting external PSK parameters\n"); + GUARD_EXIT(s2n_connection_append_psk(conn, psk), "Error appending psk to the connection\n"); + GUARD_EXIT(s2n_psk_free(&psk), "Error freeing psk\n"); + } + return S2N_SUCCESS; +} + +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"); + + GUARD_EXIT(s2n_config_set_cipher_preferences(config, cipher_prefs), "Error setting cipher prefs"); + + GUARD_EXIT(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache), "Error setting cache store callback"); + + GUARD_EXIT(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache), "Error setting cache retrieve callback"); + + GUARD_EXIT(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache), "Error setting cache retrieve callback"); + + if (conn_settings.enable_mfl) { + GUARD_EXIT(s2n_config_accept_max_fragment_length(config), "Error enabling TLS maximum fragment length extension in server"); + } + + if (s2n_config_set_verify_host_callback(config, unsafe_verify_host_fn, NULL)) { + print_s2n_error("Failure to set hostname verification callback"); + exit(1); + } + + if (conn_settings.session_ticket) { + GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); + } + + if (conn_settings.session_cache) { + GUARD_EXIT(s2n_config_set_session_cache_onoff(config, 1), "Error enabling session cache using id"); + } + + if (conn_settings.session_ticket || conn_settings.session_cache) { + /* Key initialization */ + uint8_t *st_key = NULL; + uint32_t st_key_length = 0; + + if (session_ticket_key_file_path) { + int fd = open(session_ticket_key_file_path, O_RDONLY); + GUARD_EXIT(fd, "Error opening session ticket key file"); + + struct stat st; + GUARD_EXIT(fstat(fd, &st), "Error fstat-ing session ticket key file"); + + st_key = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + POSIX_ENSURE(st_key != MAP_FAILED, S2N_ERR_MMAP); + + st_key_length = st.st_size; + + close(fd); + } else { + st_key = default_ticket_key; + st_key_length = sizeof(default_ticket_key); + } + + if (s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), st_key, st_key_length, 0) != 0) { + fprintf(stderr, "Error adding ticket key: '%s'\n", s2n_strerror(s2n_errno, "EN")); + exit(1); + } + } + return 0; +} + +int s2n_setup_server_connection(struct s2n_connection *conn, int fd, struct s2n_config *config, struct conn_settings settings) +{ + if (settings.deserialize_in) { + GUARD_RETURN(s2n_connection_deserialize_in(conn, settings.deserialize_in), "Failed to deserialize file"); + } + + if (settings.self_service_blinding) { + s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING); + } + + if (settings.mutual_auth) { + GUARD_RETURN(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED), "Error setting client auth type"); + + if (settings.ca_dir || settings.ca_file) { + GUARD_RETURN(s2n_config_set_verification_ca_location(config, settings.ca_file, settings.ca_dir), "Error adding verify location"); + } + + if (settings.insecure) { + GUARD_RETURN(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); + } + } + + GUARD_RETURN(s2n_connection_set_config(conn, config), "Error setting configuration"); + + if (settings.prefer_throughput) { + GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); + } + + if (settings.prefer_low_latency) { + GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); + } + + GUARD_RETURN(s2n_connection_set_fd(conn, fd), "Error setting file descriptor"); + + if (settings.use_corked_io) { + GUARD_RETURN(s2n_connection_use_corked_io(conn), "Error setting corked io"); + } + + GUARD_RETURN( + s2n_setup_external_psk_list(conn, settings.psk_optarg_list, settings.psk_list_len), + "Error setting external psk list"); + + GUARD_RETURN(early_data_recv(conn), "Error receiving early data"); + return 0; +} + +int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) +{ + struct session_cache_entry *cache = ctx; + + POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); + POSIX_ENSURE_INCLUSIVE_RANGE(1, value_size, MAX_VAL_LEN); + + uint8_t idx = ((const uint8_t *) key)[0]; + + memmove(cache[idx].key, key, key_size); + memmove(cache[idx].value, value, value_size); + + cache[idx].key_len = key_size; + cache[idx].value_len = value_size; + + return 0; +} + +int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) +{ + struct session_cache_entry *cache = ctx; + + POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); + + uint8_t idx = ((const uint8_t *) key)[0]; + + POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(*value_size >= cache[idx].value_len, S2N_ERR_INVALID_ARGUMENT); + + *value_size = cache[idx].value_len; + memmove(value, cache[idx].value, cache[idx].value_len); + + for (uint64_t i = 0; i < key_size; i++) { + printf("%02x", ((const uint8_t *) key)[i]); + } + printf("\n"); + + return 0; +} + +int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) +{ + struct session_cache_entry *cache = ctx; + + POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].key_len != 0) { + POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); + } + + cache[idx].key_len = 0; + cache[idx].value_len = 0; + + return 0; +} + +uint8_t unsafe_verify_host(const char *host_name, size_t host_name_len, void *data) +{ + struct verify_data *verify_data = (struct verify_data *) data; + + if (host_name_len > 2 && host_name[0] == '*' && host_name[1] == '.') { + char *suffix = strstr(verify_data->trusted_host, "."); + return (uint8_t) (strcasecmp(suffix, host_name + 1) == 0); + } + + /* If we're connecting to localhost, accept any values that represent localhost */ + bool is_localhost = (strcasecmp(verify_data->trusted_host, "localhost") == 0); + is_localhost |= (strcasecmp(verify_data->trusted_host, "127.0.0.1") == 0); + if (is_localhost) { + bool match = (strcasecmp(host_name, "localhost") == 0); + match |= (strcasecmp(host_name, "127.0.0.1") == 0); + /* Some of our older test certificates use odd common names */ + match |= (strcasecmp(host_name, "s2nTestServer") == 0); + return (uint8_t) match; + } + + return (uint8_t) (strcasecmp(host_name, verify_data->trusted_host) == 0); +} + +int wait_for_shutdown(struct s2n_connection *conn, int fd) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (s2n_shutdown(conn, &blocked) != S2N_SUCCESS) { + int errno_val = errno; + switch (s2n_error_get_type(s2n_errno)) { + case S2N_ERR_T_BLOCKED: + GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for shutdown"); + break; + case S2N_ERR_T_CLOSED: + /* We can't control the behavior of our peer. If the peer indicates end-of-stream + * without sending a close_notify, don't treat it as an error, but print a warning. + * + * This is common in our integration tests both because OpenSSL s_server + * never sends a close_notify (see https://github.com/openssl/openssl/issues/1806) + * and because we tend to kill processes rather than waiting for a graceful shutdown. + */ + fprintf(stdout, "Connection closed by peer\n"); + return S2N_SUCCESS; + case S2N_ERR_T_IO: + /* Again, we can't control the behavior of our peer, so just print a warning. + * Killing a process can result in its peer receiving a ECONNRESET. + */ + if (errno_val == ECONNRESET) { + fprintf(stdout, "Connection reset by peer\n"); + return S2N_SUCCESS; + } + /* Otherwise, IO errors are fatal and should be investigated */ + fprintf(stderr, "Unexpected IO error during shutdown: %s\n", strerror(errno_val)); + return S2N_FAILURE; + default: + return S2N_FAILURE; + } + } + return S2N_SUCCESS; +} + +int s2n_connection_serialize_out(struct s2n_connection *conn, const char *file_path) +{ + uint32_t serialize_length = 0; + GUARD_RETURN(s2n_connection_serialization_length(conn, &serialize_length), "Failed to get serialized connection length"); + uint8_t *mem = malloc(serialize_length); + GUARD_RETURN_NULL(mem); + GUARD_RETURN(s2n_connection_serialize(conn, mem, serialize_length), "Failed to get serialized connection"); + GUARD_RETURN(write_array_to_file(file_path, mem, serialize_length), "Failed to write serialized connection to file"); + free(mem); + + return 0; +} + +int s2n_connection_deserialize_in(struct s2n_connection *conn, const char *file_path) +{ + size_t deserialize_length = 0; + GUARD_RETURN(get_file_size(file_path, &deserialize_length), "Failed to read deserialize-in file size"); + ENSURE_RETURN(deserialize_length <= UINT32_MAX, "deserialize-in file size is too large"); + uint8_t *mem = malloc(deserialize_length); + GUARD_RETURN_NULL(mem); + GUARD_RETURN(load_file_to_array(file_path, mem, deserialize_length), "Failed to read deserialize-in file"); + GUARD_RETURN(s2n_connection_deserialize(conn, mem, (uint32_t) deserialize_length), "Failed to deserialize connection"); + free(mem); + + return 0; +} diff --git a/bin/echo.c b/bin/echo.c index 75b4fc28a22..961de036c57 100644 --- a/bin/echo.c +++ b/bin/echo.c @@ -1,480 +1,482 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "api/unstable/fingerprint.h" -#include "api/unstable/renegotiate.h" -#include "common.h" -#include "crypto/s2n_pkey.h" -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" - -#define STDIO_BUFSIZE 10240 - -const char *sig_alg_strs[] = { - [S2N_TLS_SIGNATURE_ANONYMOUS] = "None", - [S2N_TLS_SIGNATURE_RSA] = "RSA", - [S2N_TLS_SIGNATURE_ECDSA] = "ECDSA", - [S2N_TLS_SIGNATURE_RSA_PSS_RSAE] = "RSA-PSS-RSAE", - [S2N_TLS_SIGNATURE_RSA_PSS_PSS] = "RSA-PSS-PSS", - [S2N_TLS_SIGNATURE_MLDSA] = "MLDSA", -}; - -const char *sig_hash_strs[] = { - [S2N_TLS_HASH_NONE] = "None", - [S2N_TLS_HASH_MD5] = "MD5", - [S2N_TLS_HASH_SHA1] = "SHA1", - [S2N_TLS_HASH_SHA224] = "SHA224", - [S2N_TLS_HASH_SHA256] = "SHA256", - [S2N_TLS_HASH_SHA384] = "SHA384", - [S2N_TLS_HASH_SHA512] = "SHA512", - [S2N_TLS_HASH_MD5_SHA1] = "MD5_SHA1", -}; - -/* Careful: don't change this without updating the integration tests that check for it! - * Negative cases may stop working but not fail when this is updated. - */ -const char *pq_enabled_note = "PQ key exchange enabled"; - -void print_s2n_error(const char *app_error) -{ - fprintf(stderr, "[%d] %s: '%s' : '%s'\n", getpid(), app_error, s2n_strerror(s2n_errno, "EN"), - s2n_strerror_debug(s2n_errno, "EN")); -} - -/* Poll the given file descriptor for an event determined by the blocked status */ -int wait_for_event(int fd, s2n_blocked_status blocked) -{ - struct pollfd reader = { .fd = fd, .events = 0 }; - - switch (blocked) { - case S2N_NOT_BLOCKED: - return S2N_SUCCESS; - case S2N_BLOCKED_ON_READ: - reader.events |= POLLIN; - break; - case S2N_BLOCKED_ON_WRITE: - reader.events |= POLLOUT; - break; - case S2N_BLOCKED_ON_EARLY_DATA: - case S2N_BLOCKED_ON_APPLICATION_INPUT: - /* This case is not encountered by the s2nc/s2nd applications, - * but is detected for completeness */ - return S2N_SUCCESS; - } - - if (poll(&reader, 1, -1) < 0) { - fprintf(stderr, "Failed to poll connection: %s\n", strerror(errno)); - S2N_ERROR_PRESERVE_ERRNO(); - } - - return S2N_SUCCESS; -} - -int early_data_recv(struct s2n_connection *conn) -{ - uint32_t max_early_data_size = 0; - GUARD_RETURN(s2n_connection_get_max_early_data_size(conn, &max_early_data_size), "Error getting max early data size"); - if (max_early_data_size == 0) { - return S2N_SUCCESS; - } - - ssize_t total_data_recv = 0; - ssize_t data_recv = 0; - bool server_success = 0; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint8_t *early_data_received = (uint8_t *) malloc(max_early_data_size); - GUARD_EXIT_NULL(early_data_received); - - do { - server_success = (s2n_recv_early_data(conn, early_data_received + total_data_recv, - max_early_data_size - total_data_recv, &data_recv, &blocked) - >= S2N_SUCCESS); - total_data_recv += data_recv; - } while (!server_success); - - if (total_data_recv > 0) { - fprintf(stdout, "Early Data received: "); - for (ssize_t i = 0; i < total_data_recv; i++) { - fprintf(stdout, "%c", early_data_received[i]); - } - fprintf(stdout, "\n"); - } - - free(early_data_received); - - return S2N_SUCCESS; -} - -int early_data_send(struct s2n_connection *conn, uint8_t *data, uint32_t len) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - ssize_t total_data_sent = 0; - ssize_t data_sent = 0; - bool client_success = 0; - do { - client_success = (s2n_send_early_data(conn, data + total_data_sent, - len - total_data_sent, &data_sent, &blocked) - >= S2N_SUCCESS); - total_data_sent += data_sent; - } while (total_data_sent < len && !client_success); - - return S2N_SUCCESS; -} - -int print_connection_info(struct s2n_connection *conn) -{ - int client_hello_version = 0; - int client_protocol_version = 0; - int server_protocol_version = 0; - int actual_protocol_version = 0; - - if ((client_hello_version = s2n_connection_get_client_hello_version(conn)) < 0) { - fprintf(stderr, "Could not get client hello version\n"); - POSIX_BAIL(S2N_ERR_CLIENT_HELLO_VERSION); - } - if ((client_protocol_version = s2n_connection_get_client_protocol_version(conn)) < 0) { - fprintf(stderr, "Could not get client protocol version\n"); - POSIX_BAIL(S2N_ERR_CLIENT_PROTOCOL_VERSION); - } - if ((server_protocol_version = s2n_connection_get_server_protocol_version(conn)) < 0) { - fprintf(stderr, "Could not get server protocol version\n"); - POSIX_BAIL(S2N_ERR_SERVER_PROTOCOL_VERSION); - } - if ((actual_protocol_version = s2n_connection_get_actual_protocol_version(conn)) < 0) { - fprintf(stderr, "Could not get actual protocol version\n"); - POSIX_BAIL(S2N_ERR_ACTUAL_PROTOCOL_VERSION); - } - printf("CONNECTED:\n"); - printf("Handshake: %s\n", s2n_connection_get_handshake_type_name(conn)); - printf("Client hello version: %d\n", client_hello_version); - printf("Client protocol version: %d\n", client_protocol_version); - printf("Server protocol version: %d\n", server_protocol_version); - printf("Actual protocol version: %d\n", actual_protocol_version); - - if (s2n_get_server_name(conn)) { - printf("Server name: %s\n", s2n_get_server_name(conn)); - } - - if (s2n_get_application_protocol(conn)) { - printf("Application protocol: %s\n", s2n_get_application_protocol(conn)); - } - - const char *kem = s2n_connection_get_kem_name(conn); - if (strcmp(kem, "NONE") != 0) { - printf("Legacy TLS1.2 KEM: %s (%s, but the use of PQ key exchange with " - "TLS1.2 was experimental and is neither stable nor on a path to " - "standardization. The TLS1.3 version should be used instead: " - "https://aws.github.io/s2n-tls/usage-guide/ch15-post-quantum.html)\n", - kem, pq_enabled_note); - } - - const char *kem_group = s2n_connection_get_kem_group_name(conn); - if (strcmp(kem_group, "NONE") != 0) { - printf("KEM Group: %s (%s)\n", kem_group, pq_enabled_note); - } else { - printf("Curve: %s\n", s2n_connection_get_curve(conn)); - } - - uint32_t length = 0; - const uint8_t *status = s2n_connection_get_ocsp_response(conn, &length); - if (status && length > 0) { - printf("OCSP response received, length %u\n", length); - } - - printf("Cipher negotiated: %s\n", s2n_connection_get_cipher(conn)); - - s2n_tls_signature_algorithm server_sig_alg = 0, client_sig_alg = 0; - s2n_tls_hash_algorithm server_sig_hash = 0, client_sig_hash = 0; - GUARD_EXIT(s2n_connection_get_selected_signature_algorithm(conn, &server_sig_alg), - "Error getting server signature algorithm"); - GUARD_EXIT(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &client_sig_alg), - "Error getting client signature algorithm"); - GUARD_EXIT(s2n_connection_get_selected_digest_algorithm(conn, &server_sig_hash), - "Error getting server signature hash algorithm"); - GUARD_EXIT(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &client_sig_hash), - "Error getting client signature hash algorithm"); - printf("Server signature negotiated: %s+%s\n", sig_alg_strs[server_sig_alg], sig_hash_strs[server_sig_hash]); - if (client_sig_alg != S2N_TLS_SIGNATURE_ANONYMOUS) { - printf("Client signature negotiated: %s+%s\n", sig_alg_strs[client_sig_alg], sig_hash_strs[client_sig_hash]); - } - - bool session_resumed = s2n_connection_is_session_resumed(conn); - if (session_resumed) { - printf("Resumed session\n"); - } - - uint16_t identity_length = 0; - GUARD_EXIT(s2n_connection_get_negotiated_psk_identity_length(conn, &identity_length), "Error getting negotiated psk identity length from the connection\n"); - if (identity_length != 0 && !session_resumed) { - uint8_t *identity = (uint8_t *) malloc(identity_length); - GUARD_EXIT_NULL(identity); - GUARD_EXIT(s2n_connection_get_negotiated_psk_identity(conn, identity, identity_length), "Error getting negotiated psk identity from the connection\n"); - printf("Negotiated PSK identity: %.*s\n", identity_length, identity); - free(identity); - } - - s2n_early_data_status_t early_data_status = (s2n_early_data_status_t) 0; - GUARD_EXIT(s2n_connection_get_early_data_status(conn, &early_data_status), "Error getting early data status"); - const char *status_str = NULL; - switch (early_data_status) { - case S2N_EARLY_DATA_STATUS_OK: - status_str = "IN PROGRESS"; - break; - case S2N_EARLY_DATA_STATUS_NOT_REQUESTED: - status_str = "NOT REQUESTED"; - break; - case S2N_EARLY_DATA_STATUS_REJECTED: - status_str = "REJECTED"; - break; - case S2N_EARLY_DATA_STATUS_END: - status_str = "ACCEPTED"; - break; - } - GUARD_EXIT_NULL(status_str); - printf("Early Data status: %s\n", status_str); - - struct s2n_client_hello *ch = s2n_connection_get_client_hello(conn); - if (ch && client_hello_version > S2N_SSLv2) { - uint8_t ja3[16] = { 0 }; - uint32_t ja3_size = 0, str_size = 0; - GUARD_EXIT(s2n_client_hello_get_fingerprint_hash(ch, S2N_FINGERPRINT_JA3, - sizeof(ja3), ja3, &ja3_size, &str_size), - "Error calculating JA3"); - printf("JA3: "); - for (size_t i = 0; i < ja3_size; i++) { - printf("%02x", ja3[i]); - } - printf("\n"); - } - - printf("Wire bytes in: %" PRIu64 "\n", s2n_connection_get_wire_bytes_in(conn)); - printf("Wire bytes out: %" PRIu64 "\n", s2n_connection_get_wire_bytes_out(conn)); - - return 0; -} - -int negotiate(struct s2n_connection *conn, int fd) -{ - s2n_blocked_status blocked; - while (s2n_negotiate(conn, &blocked) != 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")); - if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { - fprintf(stderr, "Alert: %d\n", - s2n_connection_get_alert(conn)); - } - S2N_ERROR_PRESERVE_ERRNO(); - } - - if (wait_for_event(fd, blocked) != S2N_SUCCESS) { - S2N_ERROR_PRESERVE_ERRNO(); - } - } - - print_connection_info(conn); - - printf("s2n is ready\n"); - return 0; -} - -int renegotiate(struct s2n_connection *conn, int fd) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint8_t buffer[STDIO_BUFSIZE] = { 0 }; - ssize_t data_read = 0; - - GUARD_RETURN(s2n_renegotiate_wipe(conn), "Unable to prepare connection for renegotiate"); - GUARD_RETURN(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); - - fprintf(stdout, "RENEGOTIATE\n"); - fflush(stdout); - - while (s2n_renegotiate(conn, buffer, sizeof(buffer), &data_read, &blocked) != S2N_SUCCESS) { - uint8_t *data_ptr = buffer; - while (data_read > 0) { - ssize_t data_written = write(STDOUT_FILENO, data_ptr, data_read); - GUARD_RETURN(data_written, "Error writing to stdout\n"); - data_read -= data_written; - data_ptr += data_written; - } - - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Failed to renegotiate: '%s'. %s\n", s2n_strerror(s2n_errno, NULL), - s2n_strerror_debug(s2n_errno, NULL)); - if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { - fprintf(stderr, "Alert: %d\n", s2n_connection_get_alert(conn)); - } - return S2N_FAILURE; - } - - GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for renegotiate"); - } - - print_connection_info(conn); - printf("s2n is ready, again\n"); - return S2N_SUCCESS; -} - -void send_data(struct s2n_connection *conn, int sockfd, const char *data, uint64_t len, s2n_blocked_status *blocked) -{ - uint64_t bytes_remaining = len; - const char *data_ptr = data; - do { - ssize_t send_len = MIN(bytes_remaining, SSIZE_MAX); - ssize_t bytes_written = s2n_send(conn, data_ptr, send_len, blocked); - if (bytes_written < 0) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Error writing to connection: '%s'\n", - s2n_strerror(s2n_errno, "EN")); - exit(1); - } - - GUARD_EXIT(wait_for_event(sockfd, *blocked), "Unable to send data"); - continue; - } - - bytes_remaining -= bytes_written; - data_ptr += bytes_written; - - } while (bytes_remaining > 0); -} - -int echo(struct s2n_connection *conn, int sockfd, bool *stop_echo) -{ - struct pollfd readers[2]; - - readers[0].fd = sockfd; - readers[0].events = POLLIN; - readers[1].fd = STDIN_FILENO; - readers[1].events = POLLIN; - - /* Reset errno so that we can't inherit the errno == EINTR exit condition. */ - errno = 0; - - /* Act as a simple proxy between stdin and the SSL connection */ - int p = 0; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - do { - /* echo will send and receive Application Data back and forth between - * client and server, until stop_echo is true or stdin EOF is reached. */ - while (!(*stop_echo) && (p = poll(readers, 2, -1)) > 0) { - char buffer[STDIO_BUFSIZE]; - ssize_t bytes_read = 0; - - if (readers[0].revents & POLLIN) { - s2n_errno = S2N_ERR_T_OK; - bytes_read = s2n_recv(conn, buffer, STDIO_BUFSIZE, &blocked); - if (bytes_read == 0) { - return 0; - } - if (bytes_read < 0) { - switch (s2n_error_get_type(s2n_errno)) { - case S2N_ERR_T_BLOCKED: - /* Wait until poll tells us data is ready */ - continue; - case S2N_ERR_T_ALERT: - fprintf(stderr, "Received alert: %d\n", s2n_connection_get_alert(conn)); - break; - default: - fprintf(stderr, "Error reading from connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); - break; - } - exit(1); - } - - char *buf_ptr = buffer; - do { - ssize_t bytes_written = write(STDOUT_FILENO, buf_ptr, bytes_read); - if (bytes_written < 0) { - fprintf(stderr, "Error writing to stdout\n"); - exit(1); - } - - bytes_read -= bytes_written; - buf_ptr += bytes_written; - } while (bytes_read > 0); - } - - if (readers[1].revents & POLLIN) { - size_t bytes_available = 0; - - if (ioctl(STDIN_FILENO, FIONREAD, &bytes_available) < 0) { - bytes_available = 1; - } - - do { - /* We can only read as much data as we have space for. So it may - * take a couple loops to empty stdin. */ - size_t bytes_to_read = bytes_available; - if (bytes_available > sizeof(buffer)) { - bytes_to_read = sizeof(buffer); - } - - bytes_read = read(STDIN_FILENO, buffer, bytes_to_read); - if (bytes_read < 0 && errno != EINTR) { - fprintf(stderr, "Error reading from stdin\n"); - exit(1); - } - if (bytes_read == 0) { - fprintf(stderr, "Exiting on stdin EOF\n"); - return 0; - } - bytes_available -= bytes_read; - - /* We may not be able to write all the data we read in one shot, so - * keep sending until we have cleared our buffer. */ - send_data(conn, sockfd, buffer, bytes_read, &blocked); - - } while (bytes_available || blocked); - } - - if (readers[1].revents & POLLHUP) { - /* The stdin pipe hanged up, and we've handled all read from it above */ - return 0; - } - - if (readers[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { - fprintf(stderr, "Error polling from socket: err=%d hup=%d nval=%d\n", - (readers[0].revents & POLLERR) ? 1 : 0, - (readers[0].revents & POLLHUP) ? 1 : 0, - (readers[0].revents & POLLNVAL) ? 1 : 0); - POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); - } - - if (readers[1].revents & (POLLERR | POLLNVAL)) { - fprintf(stderr, "Error polling from socket: err=%d nval=%d\n", - (readers[1].revents & POLLERR) ? 1 : 0, - (readers[1].revents & POLLNVAL) ? 1 : 0); - POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); - } - } - } while (p < 0 && errno == EINTR); - - return 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 +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "api/unstable/fingerprint.h" +#include "api/unstable/renegotiate.h" +#include "common.h" +#include "crypto/s2n_pkey.h" +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" + +#define STDIO_BUFSIZE 10240 + +const char *sig_alg_strs[] = { + [S2N_TLS_SIGNATURE_ANONYMOUS] = "None", + [S2N_TLS_SIGNATURE_RSA] = "RSA", + [S2N_TLS_SIGNATURE_ECDSA] = "ECDSA", + [S2N_TLS_SIGNATURE_RSA_PSS_RSAE] = "RSA-PSS-RSAE", + [S2N_TLS_SIGNATURE_RSA_PSS_PSS] = "RSA-PSS-PSS", + [S2N_TLS_SIGNATURE_MLDSA] = "MLDSA", +}; + +const char *sig_hash_strs[] = { + [S2N_TLS_HASH_NONE] = "None", + [S2N_TLS_HASH_MD5] = "MD5", + [S2N_TLS_HASH_SHA1] = "SHA1", + [S2N_TLS_HASH_SHA224] = "SHA224", + [S2N_TLS_HASH_SHA256] = "SHA256", + [S2N_TLS_HASH_SHA384] = "SHA384", + [S2N_TLS_HASH_SHA512] = "SHA512", + [S2N_TLS_HASH_MD5_SHA1] = "MD5_SHA1", +}; + +/* Careful: don't change this without updating the integration tests that check for it! + * Negative cases may stop working but not fail when this is updated. + */ +const char *pq_enabled_note = "PQ key exchange enabled"; + +void print_s2n_error(const char *app_error) +{ + fprintf(stderr, "[%d] %s: '%s' : '%s'\n", getpid(), app_error, s2n_strerror(s2n_errno, "EN"), + s2n_strerror_debug(s2n_errno, "EN")); +} + +/* Poll the given file descriptor for an event determined by the blocked status */ +int wait_for_event(int fd, s2n_blocked_status blocked) +{ + struct pollfd reader = { .fd = fd, .events = 0 }; + + switch (blocked) { + case S2N_NOT_BLOCKED: + return S2N_SUCCESS; + case S2N_BLOCKED_ON_READ: + reader.events |= POLLIN; + break; + case S2N_BLOCKED_ON_WRITE: + reader.events |= POLLOUT; + break; + case S2N_BLOCKED_ON_EARLY_DATA: + case S2N_BLOCKED_ON_APPLICATION_INPUT: + /* This case is not encountered by the s2nc/s2nd applications, + * but is detected for completeness */ + return S2N_SUCCESS; + } + + if (poll(&reader, 1, -1) < 0) { + fprintf(stderr, "Failed to poll connection: %s\n", strerror(errno)); + S2N_ERROR_PRESERVE_ERRNO(); + } + + return S2N_SUCCESS; +} + +int early_data_recv(struct s2n_connection *conn) +{ + uint32_t max_early_data_size = 0; + GUARD_RETURN(s2n_connection_get_max_early_data_size(conn, &max_early_data_size), "Error getting max early data size"); + if (max_early_data_size == 0) { + return S2N_SUCCESS; + } + + ssize_t total_data_recv = 0; + ssize_t data_recv = 0; + bool server_success = 0; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t *early_data_received = (uint8_t *) malloc(max_early_data_size); + GUARD_EXIT_NULL(early_data_received); + + do { + server_success = (s2n_recv_early_data(conn, early_data_received + total_data_recv, + max_early_data_size - total_data_recv, &data_recv, &blocked) + >= S2N_SUCCESS); + total_data_recv += data_recv; + } while (!server_success); + + if (total_data_recv > 0) { + fprintf(stdout, "Early Data received: "); + for (ssize_t i = 0; i < total_data_recv; i++) { + fprintf(stdout, "%c", early_data_received[i]); + } + fprintf(stdout, "\n"); + } + + free(early_data_received); + + return S2N_SUCCESS; +} + +int early_data_send(struct s2n_connection *conn, uint8_t *data, uint32_t len) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t total_data_sent = 0; + ssize_t data_sent = 0; + bool client_success = 0; + do { + client_success = (s2n_send_early_data(conn, data + total_data_sent, + len - total_data_sent, &data_sent, &blocked) + >= S2N_SUCCESS); + total_data_sent += data_sent; + } while (total_data_sent < len && !client_success); + + return S2N_SUCCESS; +} + +int print_connection_info(struct s2n_connection *conn) +{ + int client_hello_version = 0; + int client_protocol_version = 0; + int server_protocol_version = 0; + int actual_protocol_version = 0; + + if ((client_hello_version = s2n_connection_get_client_hello_version(conn)) < 0) { + fprintf(stderr, "Could not get client hello version\n"); + POSIX_BAIL(S2N_ERR_CLIENT_HELLO_VERSION); + } + if ((client_protocol_version = s2n_connection_get_client_protocol_version(conn)) < 0) { + fprintf(stderr, "Could not get client protocol version\n"); + POSIX_BAIL(S2N_ERR_CLIENT_PROTOCOL_VERSION); + } + if ((server_protocol_version = s2n_connection_get_server_protocol_version(conn)) < 0) { + fprintf(stderr, "Could not get server protocol version\n"); + POSIX_BAIL(S2N_ERR_SERVER_PROTOCOL_VERSION); + } + if ((actual_protocol_version = s2n_connection_get_actual_protocol_version(conn)) < 0) { + fprintf(stderr, "Could not get actual protocol version\n"); + POSIX_BAIL(S2N_ERR_ACTUAL_PROTOCOL_VERSION); + } + printf("CONNECTED:\n"); + printf("Handshake: %s\n", s2n_connection_get_handshake_type_name(conn)); + printf("Client hello version: %d\n", client_hello_version); + printf("Client protocol version: %d\n", client_protocol_version); + printf("Server protocol version: %d\n", server_protocol_version); + printf("Actual protocol version: %d\n", actual_protocol_version); + + if (s2n_get_server_name(conn)) { + printf("Server name: %s\n", s2n_get_server_name(conn)); + } + + if (s2n_get_application_protocol(conn)) { + printf("Application protocol: %s\n", s2n_get_application_protocol(conn)); + } + + const char *kem = s2n_connection_get_kem_name(conn); + if (strcmp(kem, "NONE") != 0) { + printf("Legacy TLS1.2 KEM: %s (%s, but the use of PQ key exchange with " + "TLS1.2 was experimental and is neither stable nor on a path to " + "standardization. The TLS1.3 version should be used instead: " + "https://aws.github.io/s2n-tls/usage-guide/ch15-post-quantum.html)\n", + kem, pq_enabled_note); + } + + const char *kem_group = s2n_connection_get_kem_group_name(conn); + if (strcmp(kem_group, "NONE") != 0) { + printf("KEM Group: %s (%s)\n", kem_group, pq_enabled_note); + } else { + printf("Curve: %s\n", s2n_connection_get_curve(conn)); + } + + uint32_t length = 0; + const uint8_t *status = s2n_connection_get_ocsp_response(conn, &length); + if (status && length > 0) { + printf("OCSP response received, length %u\n", length); + } + + printf("Cipher negotiated: %s\n", s2n_connection_get_cipher(conn)); + + s2n_tls_signature_algorithm server_sig_alg = 0, client_sig_alg = 0; + s2n_tls_hash_algorithm server_sig_hash = 0, client_sig_hash = 0; + GUARD_EXIT(s2n_connection_get_selected_signature_algorithm(conn, &server_sig_alg), + "Error getting server signature algorithm"); + GUARD_EXIT(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &client_sig_alg), + "Error getting client signature algorithm"); + GUARD_EXIT(s2n_connection_get_selected_digest_algorithm(conn, &server_sig_hash), + "Error getting server signature hash algorithm"); + GUARD_EXIT(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &client_sig_hash), + "Error getting client signature hash algorithm"); + printf("Server signature negotiated: %s+%s\n", sig_alg_strs[server_sig_alg], sig_hash_strs[server_sig_hash]); + if (client_sig_alg != S2N_TLS_SIGNATURE_ANONYMOUS) { + printf("Client signature negotiated: %s+%s\n", sig_alg_strs[client_sig_alg], sig_hash_strs[client_sig_hash]); + } + + bool session_resumed = s2n_connection_is_session_resumed(conn); + if (session_resumed) { + printf("Resumed session\n"); + } + + uint16_t identity_length = 0; + GUARD_EXIT(s2n_connection_get_negotiated_psk_identity_length(conn, &identity_length), "Error getting negotiated psk identity length from the connection\n"); + if (identity_length != 0 && !session_resumed) { + uint8_t *identity = (uint8_t *) malloc(identity_length); + GUARD_EXIT_NULL(identity); + GUARD_EXIT(s2n_connection_get_negotiated_psk_identity(conn, identity, identity_length), "Error getting negotiated psk identity from the connection\n"); + printf("Negotiated PSK identity: %.*s\n", identity_length, identity); + free(identity); + } + + s2n_early_data_status_t early_data_status = (s2n_early_data_status_t) 0; + GUARD_EXIT(s2n_connection_get_early_data_status(conn, &early_data_status), "Error getting early data status"); + const char *status_str = NULL; + switch (early_data_status) { + case S2N_EARLY_DATA_STATUS_OK: + status_str = "IN PROGRESS"; + break; + case S2N_EARLY_DATA_STATUS_NOT_REQUESTED: + status_str = "NOT REQUESTED"; + break; + case S2N_EARLY_DATA_STATUS_REJECTED: + status_str = "REJECTED"; + break; + case S2N_EARLY_DATA_STATUS_END: + status_str = "ACCEPTED"; + break; + } + GUARD_EXIT_NULL(status_str); + printf("Early Data status: %s\n", status_str); + + struct s2n_client_hello *ch = s2n_connection_get_client_hello(conn); + if (ch && client_hello_version > S2N_SSLv2) { + uint8_t ja3[16] = { 0 }; + uint32_t ja3_size = 0, str_size = 0; + GUARD_EXIT(s2n_client_hello_get_fingerprint_hash(ch, S2N_FINGERPRINT_JA3, + sizeof(ja3), ja3, &ja3_size, &str_size), + "Error calculating JA3"); + printf("JA3: "); + for (size_t i = 0; i < ja3_size; i++) { + printf("%02x", ja3[i]); + } + printf("\n"); + } + + printf("Wire bytes in: %" PRIu64 "\n", s2n_connection_get_wire_bytes_in(conn)); + printf("Wire bytes out: %" PRIu64 "\n", s2n_connection_get_wire_bytes_out(conn)); + + return 0; +} + +int negotiate(struct s2n_connection *conn, int fd) +{ + s2n_blocked_status blocked; + while (s2n_negotiate(conn, &blocked) != 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")); + if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { + fprintf(stderr, "Alert: %d\n", + s2n_connection_get_alert(conn)); + } + S2N_ERROR_PRESERVE_ERRNO(); + } + + if (wait_for_event(fd, blocked) != S2N_SUCCESS) { + S2N_ERROR_PRESERVE_ERRNO(); + } + } + + print_connection_info(conn); + + printf("s2n is ready\n"); + return 0; +} + +int renegotiate(struct s2n_connection *conn, int fd) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t buffer[STDIO_BUFSIZE] = { 0 }; + ssize_t data_read = 0; + + GUARD_RETURN(s2n_renegotiate_wipe(conn), "Unable to prepare connection for renegotiate"); + GUARD_RETURN(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); + + fprintf(stdout, "RENEGOTIATE\n"); + fflush(stdout); + + while (s2n_renegotiate(conn, buffer, sizeof(buffer), &data_read, &blocked) != S2N_SUCCESS) { + uint8_t *data_ptr = buffer; + while (data_read > 0) { + ssize_t data_written = write(STDOUT_FILENO, data_ptr, data_read); + GUARD_RETURN(data_written, "Error writing to stdout\n"); + data_read -= data_written; + data_ptr += data_written; + } + + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Failed to renegotiate: '%s'. %s\n", s2n_strerror(s2n_errno, NULL), + s2n_strerror_debug(s2n_errno, NULL)); + if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { + fprintf(stderr, "Alert: %d\n", s2n_connection_get_alert(conn)); + } + return S2N_FAILURE; + } + + GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for renegotiate"); + } + + print_connection_info(conn); + printf("s2n is ready, again\n"); + return S2N_SUCCESS; +} + +void send_data(struct s2n_connection *conn, int sockfd, const char *data, uint64_t len, s2n_blocked_status *blocked) +{ + uint64_t bytes_remaining = len; + const char *data_ptr = data; + do { + ssize_t send_len = MIN(bytes_remaining, SSIZE_MAX); + ssize_t bytes_written = s2n_send(conn, data_ptr, send_len, blocked); + if (bytes_written < 0) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Error writing to connection: '%s'\n", + s2n_strerror(s2n_errno, "EN")); + exit(1); + } + + GUARD_EXIT(wait_for_event(sockfd, *blocked), "Unable to send data"); + continue; + } + + bytes_remaining -= bytes_written; + data_ptr += bytes_written; + + } while (bytes_remaining > 0); +} + +int echo(struct s2n_connection *conn, int sockfd, bool *stop_echo) +{ + struct pollfd readers[2]; + + readers[0].fd = sockfd; + readers[0].events = POLLIN; + readers[1].fd = STDIN_FILENO; + readers[1].events = POLLIN; + + /* Reset errno so that we can't inherit the errno == EINTR exit condition. */ + errno = 0; + + /* Act as a simple proxy between stdin and the SSL connection */ + int p = 0; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + do { + /* echo will send and receive Application Data back and forth between + * client and server, until stop_echo is true or stdin EOF is reached. */ + while (!(*stop_echo) && (p = poll(readers, 2, -1)) > 0) { + char buffer[STDIO_BUFSIZE]; + ssize_t bytes_read = 0; + + if (readers[0].revents & POLLIN) { + s2n_errno = S2N_ERR_T_OK; + bytes_read = s2n_recv(conn, buffer, STDIO_BUFSIZE, &blocked); + if (bytes_read == 0) { + return 0; + } + if (bytes_read < 0) { + switch (s2n_error_get_type(s2n_errno)) { + case S2N_ERR_T_BLOCKED: + /* Wait until poll tells us data is ready */ + continue; + case S2N_ERR_T_ALERT: + fprintf(stderr, "Received alert: %d\n", s2n_connection_get_alert(conn)); + break; + default: + fprintf(stderr, "Error reading from connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); + break; + } + exit(1); + } + + char *buf_ptr = buffer; + do { + ssize_t bytes_written = write(STDOUT_FILENO, buf_ptr, bytes_read); + if (bytes_written < 0) { + fprintf(stderr, "Error writing to stdout\n"); + exit(1); + } + + bytes_read -= bytes_written; + buf_ptr += bytes_written; + } while (bytes_read > 0); + } + + if (readers[1].revents & POLLIN) { + size_t bytes_available = 0; + + if (ioctl(STDIN_FILENO, FIONREAD, &bytes_available) < 0) { + bytes_available = 1; + } + + do { + /* We can only read as much data as we have space for. So it may + * take a couple loops to empty stdin. */ + size_t bytes_to_read = bytes_available; + if (bytes_available > sizeof(buffer)) { + bytes_to_read = sizeof(buffer); + } + + bytes_read = read(STDIN_FILENO, buffer, bytes_to_read); + if (bytes_read < 0 && errno != EINTR) { + fprintf(stderr, "Error reading from stdin\n"); + exit(1); + } + if (bytes_read == 0) { + fprintf(stderr, "Exiting on stdin EOF\n"); + return 0; + } + bytes_available -= bytes_read; + + /* We may not be able to write all the data we read in one shot, so + * keep sending until we have cleared our buffer. */ + send_data(conn, sockfd, buffer, bytes_read, &blocked); + + } while (bytes_available || blocked); + } + + if (readers[1].revents & POLLHUP) { + /* The stdin pipe hanged up, and we've handled all read from it above */ + return 0; + } + + if (readers[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fprintf(stderr, "Error polling from socket: err=%d hup=%d nval=%d\n", + (readers[0].revents & POLLERR) ? 1 : 0, + (readers[0].revents & POLLHUP) ? 1 : 0, + (readers[0].revents & POLLNVAL) ? 1 : 0); + POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); + } + + if (readers[1].revents & (POLLERR | POLLNVAL)) { + fprintf(stderr, "Error polling from socket: err=%d nval=%d\n", + (readers[1].revents & POLLERR) ? 1 : 0, + (readers[1].revents & POLLNVAL) ? 1 : 0); + POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); + } + } + } while (p < 0 && errno == EINTR); + + return 0; +} diff --git a/bin/policy.c b/bin/policy.c index 1252360a20f..6ea7d7b0368 100644 --- a/bin/policy.c +++ b/bin/policy.c @@ -1,59 +1,61 @@ -/* - * 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 "api/s2n.h" -#include "tls/policy/s2n_policy_feature.h" -#include "tls/s2n_security_policies.h" - -static int usage() -{ - printf("policy \n" - "example: policy default_tls13\n\n"); - return 0; -} - -int main(int argc, char *const *argv) -{ - if (argc != 2) { - usage(); - exit(1); - } - - if (s2n_init() != S2N_SUCCESS) { - fprintf(stderr, "Error: Failed to initialize s2n\n"); - exit(1); - } - - const char *policy_name = argv[1]; - const struct s2n_security_policy *policy = NULL; - if (s2n_find_security_policy_from_version(policy_name, &policy) != S2N_SUCCESS) { - fprintf(stderr, "Error: Failed to find security policy\n"); - s2n_cleanup(); - exit(1); - } - - uint32_t output_size = 0; - if (s2n_security_policy_write_fd(policy, S2N_POLICY_FORMAT_DEBUG_V1, STDOUT_FILENO, &output_size) != S2N_SUCCESS) { - s2n_cleanup(); - exit(1); - } - - s2n_cleanup(); - return 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 +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "tls/policy/s2n_policy_feature.h" +#include "tls/s2n_security_policies.h" + +static int usage() +{ + printf("policy \n" + "example: policy default_tls13\n\n"); + return 0; +} + +int main(int argc, char *const *argv) +{ + if (argc != 2) { + usage(); + exit(1); + } + + if (s2n_init() != S2N_SUCCESS) { + fprintf(stderr, "Error: Failed to initialize s2n\n"); + exit(1); + } + + const char *policy_name = argv[1]; + const struct s2n_security_policy *policy = NULL; + if (s2n_find_security_policy_from_version(policy_name, &policy) != S2N_SUCCESS) { + fprintf(stderr, "Error: Failed to find security policy\n"); + s2n_cleanup(); + exit(1); + } + + uint32_t output_size = 0; + if (s2n_security_policy_write_fd(policy, S2N_POLICY_FORMAT_DEBUG_V1, STDOUT_FILENO, &output_size) != S2N_SUCCESS) { + s2n_cleanup(); + exit(1); + } + + s2n_cleanup(); + return 0; +} diff --git a/bin/s2nc.c b/bin/s2nc.c index 552109eba6f..c06e0a5ee84 100644 --- a/bin/s2nc.c +++ b/bin/s2nc.c @@ -1,824 +1,829 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#ifndef S2N_INTERN_LIBCRYPTO - #include - #include -#endif - -#include "api/s2n.h" -#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" - -#define OPT_TICKET_IN 1000 -#define OPT_TICKET_OUT 1001 -#define OPT_SEND_FILE 1002 -#define OPT_RENEG 1003 -#define OPT_NPN 1004 -#define OPT_PREFER_LOW_LATENCY 1005 -#define OPT_PREFER_THROUGHPUT 1006 -#define OPT_BUFFERED_SEND 1007 -#define OPT_SERIALIZE_OUT 1008 -#define OPT_DESERIALIZE_IN 1009 - -/* This should match the final cert in the s2nd default_certificate_chain */ -const char default_trusted_cert[] = - "-----BEGIN CERTIFICATE-----" - "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" - "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" - "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" - "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" - "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" - "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" - "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" - "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" - "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" - "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" - "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" - "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" - "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" - "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" - "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" - "MZ8=" - "-----END CERTIFICATE-----"; - -/* - * s2nc is an example client that uses many s2n-tls APIs. - * It is intended for testing purposes only, and should not be used in production. - */ -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"); - fprintf(stderr, "\n Options:\n\n"); - fprintf(stderr, " -a [protocols]\n"); - fprintf(stderr, " --alpn [protocols]\n"); - fprintf(stderr, " Sets the application protocols supported by this client, as a comma separated list.\n"); - fprintf(stderr, " -c [version_string]\n"); - fprintf(stderr, " --ciphers [version_string]\n"); - fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); - fprintf(stderr, " --enter-fips-mode\n"); - fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); - fprintf(stderr, " -e,--echo\n"); - fprintf(stderr, " Listen to stdin after TLS Connection is established and echo it to the Server\n"); - fprintf(stderr, " --send-file [file path]\n"); - fprintf(stderr, " Sends the contents of the provided file to the server after connecting.\n"); - fprintf(stderr, " -h,--help\n"); - fprintf(stderr, " Display this message and quit.\n"); - fprintf(stderr, " -n [server name]\n"); - fprintf(stderr, " --name [server name]\n"); - fprintf(stderr, " Sets the SNI server name header for this client. If not specified, the host value is used.\n"); - fprintf(stderr, " -s,--status\n"); - fprintf(stderr, " Request the OCSP status of the remote server certificate\n"); - fprintf(stderr, " -m,--mfl\n"); - fprintf(stderr, " Request maximum fragment length from: 512, 1024, 2048, 4096\n"); - fprintf(stderr, " -f,--ca-file [file path]\n"); - fprintf(stderr, " Location of trust store CA file (PEM format). If neither -f or -d are specified. System defaults will be used.\n"); - fprintf(stderr, " -d,--ca-dir [directory path]\n"); - fprintf(stderr, " Directory containing hashed trusted certs. If neither -f or -d are specified. System defaults will be used.\n"); - fprintf(stderr, " -i,--insecure\n"); - fprintf(stderr, " Turns off certification validation altogether.\n"); - fprintf(stderr, " -l,--cert [file path]\n"); - fprintf(stderr, " Path to a PEM encoded certificate. Optional. Will only be used for client auth\n"); - fprintf(stderr, " -k,--key [file path]\n"); - fprintf(stderr, " Path to a PEM encoded private key that matches cert. Will only be used for client auth\n"); - fprintf(stderr, " -r,--reconnect\n"); - fprintf(stderr, " Drop and re-make the connection using Session ticket. If session ticket is disabled, then re-make the connection using Session-ID \n"); - fprintf(stderr, " -T,--no-session-ticket \n"); - fprintf(stderr, " Disable session ticket for resumption.\n"); - fprintf(stderr, " --ticket-out [file path]\n"); - fprintf(stderr, " Path to a file where the session ticket can be stored.\n"); - fprintf(stderr, " --ticket-in [file path]\n"); - fprintf(stderr, " Path to session ticket file to resume connection.\n"); - fprintf(stderr, " -D,--dynamic\n"); - fprintf(stderr, " Set dynamic record resize threshold\n"); - fprintf(stderr, " -t,--timeout\n"); - fprintf(stderr, " Set dynamic record timeout threshold\n"); - fprintf(stderr, " -C,--corked-io\n"); - fprintf(stderr, " Turn on corked io\n"); - fprintf(stderr, " -B,--non-blocking\n"); - fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); - fprintf(stderr, " -L --key-log \n"); - fprintf(stderr, " Enable NSS key logging into the provided path\n"); - fprintf(stderr, " -P --psk \n" - " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" - " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" - " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); - fprintf(stderr, " -E ,--early-data \n"); - fprintf(stderr, " Sends data in file path as early data to the server. Early data will only be sent if s2nc receives a session ticket and resumes a session.\n"); - fprintf(stderr, " --renegotiation [accept|reject]\n" - " accept: Accept all server requests for a new handshake\n" - " reject: Reject all server requests for a new handshake\n"); - fprintf(stderr, " --npn \n"); - fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); - fprintf(stderr, "\n"); - fprintf(stderr, " --buffered-send \n"); - fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); - fprintf(stderr, " --prefer-low-latency\n"); - fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); - fprintf(stderr, " --prefer-throughput\n"); - fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); - /* clang-format on */ - exit(1); -} - -size_t session_state_length = 0; -uint8_t *session_state = NULL; -static int test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - GUARD_EXIT_NULL(conn); - GUARD_EXIT_NULL(ticket); - - GUARD_EXIT(s2n_session_ticket_get_data_len(ticket, &session_state_length), "Error getting ticket length "); - session_state = realloc(session_state, session_state_length); - if (session_state == NULL) { - print_s2n_error("Error getting new session state"); - exit(1); - } - GUARD_EXIT(s2n_session_ticket_get_data(ticket, session_state_length, session_state), "Error getting ticket data"); - - bool *session_ticket_recv = (bool *) ctx; - *session_ticket_recv = 1; - - return S2N_SUCCESS; -} - -struct reneg_req_ctx { - bool do_renegotiate; - s2n_renegotiate_response response; -}; - -static int reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) -{ - GUARD_EXIT_NULL(conn); - GUARD_EXIT_NULL(context); - GUARD_EXIT_NULL(response); - struct reneg_req_ctx *reneg_ctx = (struct reneg_req_ctx *) context; - - *response = reneg_ctx->response; - if (*response == S2N_RENEGOTIATE_ACCEPT) { - reneg_ctx->do_renegotiate = true; - } - return S2N_SUCCESS; -} - -static void setup_s2n_config(struct s2n_config *config, const char *cipher_prefs, s2n_status_request_type type, - struct verify_data *unsafe_verify_data, const char *host, const char *alpn_protocols, uint16_t mfl_value) -{ - if (config == NULL) { - print_s2n_error("Error getting new config"); - 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."); - - if (s2n_config_set_verify_host_callback(config, unsafe_verify_host, unsafe_verify_data) < 0) { - print_s2n_error("Error setting host name verification function."); - } - - if (type == S2N_STATUS_REQUEST_OCSP) { - if (s2n_config_set_check_stapled_ocsp_response(config, 1)) { - print_s2n_error("OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); - } - } - - unsafe_verify_data->trusted_host = host; - - if (alpn_protocols) { - /* Count the number of commas, this tells us how many protocols there - are in the list */ - const char *ptr = alpn_protocols; - int protocol_count = 1; - while (*ptr) { - if (*ptr == ',') { - protocol_count++; - } - ptr++; - } - - char **protocols = malloc(sizeof(char *) * protocol_count); - if (!protocols) { - fprintf(stderr, "Error allocating memory\n"); - exit(1); - } - - const char *next = alpn_protocols; - int idx = 0; - int length = 0; - ptr = alpn_protocols; - while (*ptr) { - if (*ptr == ',') { - protocols[idx] = malloc(length + 1); - if (!protocols[idx]) { - fprintf(stderr, "Error allocating memory\n"); - exit(1); - } - memmove(protocols[idx], next, length); - protocols[idx][length] = '\0'; - length = 0; - idx++; - ptr++; - next = ptr; - } else { - length++; - ptr++; - } - } - if (ptr != next) { - protocols[idx] = malloc(length + 1); - if (!protocols[idx]) { - fprintf(stderr, "Error allocating memory\n"); - exit(1); - } - memmove(protocols[idx], next, length); - protocols[idx][length] = '\0'; - } - - GUARD_EXIT(s2n_config_set_protocol_preferences(config, (const char *const *) protocols, protocol_count), "Failed to set protocol preferences"); - - while (protocol_count) { - protocol_count--; - free(protocols[protocol_count]); - } - free(protocols); - } - - uint8_t mfl_code = 0; - if (mfl_value > 0) { - switch (mfl_value) { - case 512: - mfl_code = S2N_TLS_MAX_FRAG_LEN_512; - break; - case 1024: - mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; - break; - case 2048: - mfl_code = S2N_TLS_MAX_FRAG_LEN_2048; - break; - case 4096: - mfl_code = S2N_TLS_MAX_FRAG_LEN_4096; - break; - default: - fprintf(stderr, "Invalid maximum fragment length value\n"); - exit(1); - } - } - - GUARD_EXIT(s2n_config_send_max_fragment_length(config, mfl_code), "Error setting maximum fragment length"); -} - -int main(int argc, char *const *argv) -{ - struct addrinfo hints, *ai_list = NULL, *ai = NULL; - int r = 0, sockfd = 0; - bool session_ticket_recv = 0; - /* Optional args */ - const char *alpn_protocols = NULL; - const char *server_name = NULL; - const char *ca_file = NULL; - const char *ca_dir = NULL; - const char *client_cert = NULL; - const char *client_key = NULL; - bool client_cert_input = false; - bool client_key_input = false; - const char *ticket_out = NULL; - char *ticket_in = NULL; - const char *serialize_out = NULL; - const char *deserialize_in = NULL; - uint16_t mfl_value = 0; - uint8_t insecure = 0; - int reconnect = 0; - uint8_t session_ticket = 1; - s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; - uint32_t dyn_rec_threshold = 0; - uint8_t dyn_rec_timeout = 0; - /* required args */ - const char *cipher_prefs = "default"; - int fips_mode = 0; - const char *host = NULL; - struct verify_data unsafe_verify_data; - const char *port = "443"; - bool echo_input = false; - const char *send_file = NULL; - int use_corked_io = 0; - uint8_t non_blocking = 0; - const char *key_log_path = NULL; - FILE *key_log_file = NULL; - char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH]; - size_t psk_list_len = 0; - char *early_data = NULL; - bool setup_reneg_cb = false; - struct reneg_req_ctx reneg_ctx = { 0 }; - bool npn = false; - uint32_t send_buffer_size = 0; - bool prefer_low_latency = false; - bool prefer_throughput = false; - - static struct option long_options[] = { - { "alpn", required_argument, 0, 'a' }, - { "ciphers", required_argument, 0, 'c' }, - { "enter-fips-mode", no_argument, NULL, 'F' }, - { "echo", no_argument, 0, 'e' }, - { "send-file", required_argument, 0, OPT_SEND_FILE }, - { "help", no_argument, 0, 'h' }, - { "name", required_argument, 0, 'n' }, - { "status", no_argument, 0, 's' }, - { "mfl", required_argument, 0, 'm' }, - { "ca-file", required_argument, 0, 'f' }, - { "ca-dir", required_argument, 0, 'd' }, - { "cert", required_argument, 0, 'l' }, - { "key", required_argument, 0, 'k' }, - { "insecure", no_argument, 0, 'i' }, - { "reconnect", no_argument, 0, 'r' }, - { "ticket-out", required_argument, 0, OPT_TICKET_OUT }, - { "ticket-in", required_argument, 0, OPT_TICKET_IN }, - { "no-session-ticket", no_argument, 0, 'T' }, - { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, - { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, - { "dynamic", required_argument, 0, 'D' }, - { "timeout", required_argument, 0, 't' }, - { "corked-io", no_argument, 0, 'C' }, - { "tls13", no_argument, 0, '3' }, - { "non-blocking", no_argument, 0, 'B' }, - { "key-log", required_argument, 0, 'L' }, - { "psk", required_argument, 0, 'P' }, - { "early-data", required_argument, 0, 'E' }, - { "renegotiation", required_argument, 0, OPT_RENEG }, - { "npn", no_argument, 0, OPT_NPN }, - { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, - { "prefer-low-latency", no_argument, NULL, OPT_PREFER_LOW_LATENCY }, - { "prefer-throughput", no_argument, NULL, OPT_PREFER_THROUGHPUT }, - { 0 }, - }; - - while (1) { - int option_index = 0; - int c = getopt_long(argc, argv, "a:c:ehn:m:sf:d:l:k:D:t:irTCBL:P:E:", long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'a': - alpn_protocols = optarg; - break; - case 'C': - use_corked_io = 1; - break; - case 'c': - cipher_prefs = optarg; - break; - case 'F': - fips_mode = 1; - break; - case 'e': - echo_input = true; - break; - case OPT_SEND_FILE: - send_file = load_file_to_cstring(optarg); - break; - case 'h': - usage(); - break; - case 'n': - server_name = optarg; - break; - case 's': - type = S2N_STATUS_REQUEST_OCSP; - break; - case 'm': - mfl_value = (uint16_t) atoi(optarg); - break; - case 'f': - ca_file = optarg; - break; - case 'd': - ca_dir = optarg; - break; - case 'l': - client_cert = load_file_to_cstring(optarg); - client_cert_input = true; - break; - case 'k': - client_key = load_file_to_cstring(optarg); - client_key_input = true; - break; - case 'i': - insecure = 1; - break; - case 'r': - reconnect = 5; - break; - case OPT_TICKET_OUT: - ticket_out = optarg; - break; - case OPT_TICKET_IN: - ticket_in = optarg; - break; - /* The serialize_out and deserialize_in options are not documented - * in the usage section as they are not intended to work correctly - * using s2nc by itself. s2nc and s2nd are processes which close - * their TCP connection upon exit. This will cause an error if one - * peer serializes and exits and the other doesn't, as serialization - * depends on a continuous TCP connection with the peer. Therefore, our - * only usage of this feature is in our integ test framework, - * which serializes and deserializes both client and server at the - * same time. Do not expect these options to work when using s2nc alone. - */ - case OPT_SERIALIZE_OUT: - serialize_out = optarg; - break; - case OPT_DESERIALIZE_IN: - deserialize_in = optarg; - break; - case 'T': - session_ticket = 0; - break; - case 't': - dyn_rec_timeout = (uint8_t) MIN(255, atoi(optarg)); - break; - case 'D': - errno = 0; - dyn_rec_threshold = strtoul(optarg, 0, 10); - if (errno == ERANGE) { - dyn_rec_threshold = 0; - } - break; - case '3': - /* Do nothing -- this argument is deprecated. */ - break; - case 'B': - non_blocking = 1; - break; - case 'L': - key_log_path = optarg; - break; - case 'P': - if (psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { - fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); - exit(1); - } - psk_optarg_list[psk_list_len++] = optarg; - break; - case 'E': - early_data = load_file_to_cstring(optarg); - GUARD_EXIT_NULL(early_data); - break; - case OPT_RENEG: - setup_reneg_cb = true; - if (strcmp(optarg, "accept") == 0) { - reneg_ctx.response = S2N_RENEGOTIATE_ACCEPT; - } else if (strcmp(optarg, "reject") == 0) { - reneg_ctx.response = S2N_RENEGOTIATE_REJECT; - } else { - fprintf(stderr, "Unrecognized option: %s\n", optarg); - exit(1); - } - break; - case OPT_NPN: - npn = true; - break; - case OPT_BUFFERED_SEND: { - intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); - if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { - fprintf(stderr, " must be a positive 32 bit value\n"); - exit(1); - } - send_buffer_size = (uint32_t) send_buffer_size_scanned_value; - break; - } - case OPT_PREFER_LOW_LATENCY: - prefer_low_latency = true; - break; - case OPT_PREFER_THROUGHPUT: - prefer_throughput = true; - break; - case '?': - default: - usage(); - break; - } - } - - if (optind < argc) { - host = argv[optind++]; - } - - /* cppcheck-suppress duplicateCondition */ - if (optind < argc) { - port = argv[optind++]; - } - - if (!host) { - usage(); - } - - if (!server_name) { - server_name = host; - } - - memset(&hints, 0, sizeof(hints)); - - 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); - } - - if (prefer_low_latency && prefer_throughput) { - fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); - exit(1); - } - - GUARD_EXIT(s2n_init(), "Error running s2n_init()"); - printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); - - if (fips_mode) { - s2n_fips_mode mode = 0; - GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); - if (mode != S2N_FIPS_MODE_ENABLED) { - fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); - exit(1); - } - printf("s2nc entered FIPS mode\n"); - } - - if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { - fprintf(stderr, "error: %s\n", gai_strerror(r)); - exit(1); - } - - do { - int connected = 0; - 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 = 1; - /* connect() succeeded */ - break; - } - - if (connected == 0) { - fprintf(stderr, "Failed to connect to %s:%s\n", host, port); - exit(1); - } - - if (non_blocking) { - int flags = fcntl(sockfd, F_GETFL, 0); - if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { - fprintf(stderr, "fcntl error: %s\n", strerror(errno)); - exit(1); - } - } - - struct s2n_config *config = s2n_config_new(); - setup_s2n_config(config, cipher_prefs, type, &unsafe_verify_data, host, alpn_protocols, mfl_value); - - if (send_buffer_size != 0) { - GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size"); - } - - if (client_cert_input != client_key_input) { - print_s2n_error("Client cert/key pair must be given."); - } - - if (client_cert_input) { - struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); - GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, client_cert, client_key), "Error getting certificate/key"); - GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); - } - - GUARD_EXIT(s2n_config_add_pem_to_trust_store(config, default_trusted_cert), - "Error adding default cert to trust store."); - if (ca_file || ca_dir) { - GUARD_EXIT(s2n_config_wipe_trust_store(config), "Error wiping trust store"); - if (s2n_config_set_verification_ca_location(config, ca_file, ca_dir) < 0) { - print_s2n_error("Error setting CA file for trust store."); - } - } else if (insecure) { - GUARD_EXIT(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); - } - - if (session_ticket) { - GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); - GUARD_EXIT(s2n_config_set_session_ticket_cb(config, test_session_ticket_cb, &session_ticket_recv), "Error setting session ticket callback"); - session_ticket_recv = 0; - } - - if (key_log_path) { - key_log_file = fopen(key_log_path, "a"); - GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); - GUARD_EXIT( - s2n_config_set_key_log_cb( - config, - key_log_callback, - (void *) key_log_file), - "Failed to set key log callback"); - } - - if (setup_reneg_cb) { - GUARD_EXIT(s2n_config_set_renegotiate_request_cb(config, reneg_req_cb, &reneg_ctx), - "Error setting renegotiation request callback"); - } - - if (npn) { - GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); - } - - if (serialize_out) { - GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), - "Error setting serialized version"); - } - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - - if (conn == NULL) { - print_s2n_error("Error getting new connection"); - exit(1); - } - - if (deserialize_in) { - GUARD_EXIT(s2n_connection_deserialize_in(conn, deserialize_in), "Failed to deserialize file"); - } - - GUARD_EXIT(s2n_connection_set_config(conn, config), "Error setting configuration"); - - GUARD_EXIT(s2n_set_server_name(conn, server_name), "Error setting server name"); - - GUARD_EXIT(s2n_connection_set_fd(conn, sockfd), "Error setting file descriptor"); - - GUARD_EXIT(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); - - if (use_corked_io) { - GUARD_EXIT(s2n_connection_use_corked_io(conn), "Error setting corked io"); - } - - /* Read in session ticket from previous session */ - if (ticket_in) { - GUARD_EXIT(get_file_size(ticket_in, &session_state_length), "Failed to read ticket-in file"); - free(session_state); - session_state = calloc(session_state_length, sizeof(uint8_t)); - GUARD_EXIT_NULL(session_state); - GUARD_EXIT(load_file_to_array(ticket_in, session_state, session_state_length), "Failed to read ticket-in file"); - } - - /* Update session state in connection if exists */ - if (session_state_length > 0) { - GUARD_EXIT(s2n_connection_set_session(conn, session_state, session_state_length), "Error setting session state in connection"); - } - - GUARD_EXIT(s2n_setup_external_psk_list(conn, psk_optarg_list, psk_list_len), "Error setting external psk list"); - - if (prefer_throughput) { - GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); - } - - if (prefer_low_latency) { - GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); - } - - if (early_data) { - if (!session_ticket) { - print_s2n_error("Early data can only be used with session tickets."); - exit(1); - } - /* Send early data if we have a received a session ticket from the server */ - if (session_state_length) { - uint32_t early_data_length = strlen(early_data); - GUARD_EXIT(early_data_send(conn, (uint8_t *) early_data, early_data_length), "Error sending early data"); - } - } - - if (!deserialize_in && negotiate(conn, sockfd) != 0) { - /* Error is printed in negotiate */ - S2N_ERROR_PRESERVE_ERRNO(); - } - - printf("Connected to %s:%s\n", host, port); - - /* Save session state from connection if reconnect is enabled. */ - if (reconnect > 0 || ticket_out) { - if (conn->actual_protocol_version >= S2N_TLS13) { - if (!session_ticket) { - print_s2n_error("s2nc can only reconnect in TLS1.3 with session tickets."); - exit(1); - } - GUARD_EXIT(echo(conn, sockfd, &session_ticket_recv), "Error calling echo"); - } else { - if (!session_ticket && s2n_connection_get_session_id_length(conn) <= 0) { - print_s2n_error("Endpoint sent empty session id so cannot resume session"); - exit(1); - } - free(session_state); - session_state_length = s2n_connection_get_session_length(conn); - session_state = calloc(session_state_length, sizeof(uint8_t)); - GUARD_EXIT_NULL(session_state); - if (s2n_connection_get_session(conn, session_state, session_state_length) != session_state_length) { - print_s2n_error("Error getting serialized session state"); - exit(1); - } - } - if (ticket_out) { - GUARD_EXIT(write_array_to_file(ticket_out, session_state, session_state_length), "Failed to write to ticket-out file"); - } - } - - if (dyn_rec_threshold > 0 && dyn_rec_timeout > 0) { - s2n_connection_set_dynamic_record_threshold(conn, dyn_rec_threshold, dyn_rec_timeout); - } - - GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); - - if (send_file != NULL) { - printf("Sending file contents:\n%s\n", send_file); - - unsigned long send_file_len = strlen(send_file); - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - send_data(conn, sockfd, send_file, send_file_len, &blocked); - } - - while (echo_input) { - fflush(stdout); - fflush(stderr); - echo(conn, sockfd, &reneg_ctx.do_renegotiate); - - if (!reneg_ctx.do_renegotiate) { - break; - } - - reneg_ctx.do_renegotiate = false; - GUARD_EXIT(renegotiate(conn, sockfd), "Renegotiation failed"); - } - - if (serialize_out) { - GUARD_EXIT(s2n_connection_serialize_out(conn, serialize_out), "Error serializing connection"); - } else { - GUARD_EXIT(wait_for_shutdown(conn, sockfd), "Error closing connection"); - } - - GUARD_EXIT(s2n_connection_free(conn), "Error freeing connection"); - - GUARD_EXIT(s2n_config_free(config), "Error freeing configuration"); - - close(sockfd); - reconnect--; - - } while (reconnect >= 0); - - if (key_log_file) { - fclose(key_log_file); - } - - GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); - - free(early_data); - free(session_state); - freeaddrinfo(ai_list); - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif + +#ifndef S2N_INTERN_LIBCRYPTO + #include + #include +#endif + +#include "api/s2n.h" +#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" + +#define OPT_TICKET_IN 1000 +#define OPT_TICKET_OUT 1001 +#define OPT_SEND_FILE 1002 +#define OPT_RENEG 1003 +#define OPT_NPN 1004 +#define OPT_PREFER_LOW_LATENCY 1005 +#define OPT_PREFER_THROUGHPUT 1006 +#define OPT_BUFFERED_SEND 1007 +#define OPT_SERIALIZE_OUT 1008 +#define OPT_DESERIALIZE_IN 1009 + +/* This should match the final cert in the s2nd default_certificate_chain */ +const char default_trusted_cert[] = + "-----BEGIN CERTIFICATE-----" + "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" + "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" + "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" + "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" + "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" + "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" + "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" + "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" + "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" + "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" + "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" + "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" + "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" + "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" + "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" + "MZ8=" + "-----END CERTIFICATE-----"; + +/* + * s2nc is an example client that uses many s2n-tls APIs. + * It is intended for testing purposes only, and should not be used in production. + */ +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"); + fprintf(stderr, "\n Options:\n\n"); + fprintf(stderr, " -a [protocols]\n"); + fprintf(stderr, " --alpn [protocols]\n"); + fprintf(stderr, " Sets the application protocols supported by this client, as a comma separated list.\n"); + fprintf(stderr, " -c [version_string]\n"); + fprintf(stderr, " --ciphers [version_string]\n"); + fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); + fprintf(stderr, " --enter-fips-mode\n"); + fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); + fprintf(stderr, " -e,--echo\n"); + fprintf(stderr, " Listen to stdin after TLS Connection is established and echo it to the Server\n"); + fprintf(stderr, " --send-file [file path]\n"); + fprintf(stderr, " Sends the contents of the provided file to the server after connecting.\n"); + fprintf(stderr, " -h,--help\n"); + fprintf(stderr, " Display this message and quit.\n"); + fprintf(stderr, " -n [server name]\n"); + fprintf(stderr, " --name [server name]\n"); + fprintf(stderr, " Sets the SNI server name header for this client. If not specified, the host value is used.\n"); + fprintf(stderr, " -s,--status\n"); + fprintf(stderr, " Request the OCSP status of the remote server certificate\n"); + fprintf(stderr, " -m,--mfl\n"); + fprintf(stderr, " Request maximum fragment length from: 512, 1024, 2048, 4096\n"); + fprintf(stderr, " -f,--ca-file [file path]\n"); + fprintf(stderr, " Location of trust store CA file (PEM format). If neither -f or -d are specified. System defaults will be used.\n"); + fprintf(stderr, " -d,--ca-dir [directory path]\n"); + fprintf(stderr, " Directory containing hashed trusted certs. If neither -f or -d are specified. System defaults will be used.\n"); + fprintf(stderr, " -i,--insecure\n"); + fprintf(stderr, " Turns off certification validation altogether.\n"); + fprintf(stderr, " -l,--cert [file path]\n"); + fprintf(stderr, " Path to a PEM encoded certificate. Optional. Will only be used for client auth\n"); + fprintf(stderr, " -k,--key [file path]\n"); + fprintf(stderr, " Path to a PEM encoded private key that matches cert. Will only be used for client auth\n"); + fprintf(stderr, " -r,--reconnect\n"); + fprintf(stderr, " Drop and re-make the connection using Session ticket. If session ticket is disabled, then re-make the connection using Session-ID \n"); + fprintf(stderr, " -T,--no-session-ticket \n"); + fprintf(stderr, " Disable session ticket for resumption.\n"); + fprintf(stderr, " --ticket-out [file path]\n"); + fprintf(stderr, " Path to a file where the session ticket can be stored.\n"); + fprintf(stderr, " --ticket-in [file path]\n"); + fprintf(stderr, " Path to session ticket file to resume connection.\n"); + fprintf(stderr, " -D,--dynamic\n"); + fprintf(stderr, " Set dynamic record resize threshold\n"); + fprintf(stderr, " -t,--timeout\n"); + fprintf(stderr, " Set dynamic record timeout threshold\n"); + fprintf(stderr, " -C,--corked-io\n"); + fprintf(stderr, " Turn on corked io\n"); + fprintf(stderr, " -B,--non-blocking\n"); + fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); + fprintf(stderr, " -L --key-log \n"); + fprintf(stderr, " Enable NSS key logging into the provided path\n"); + fprintf(stderr, " -P --psk \n" + " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" + " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" + " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); + fprintf(stderr, " -E ,--early-data \n"); + fprintf(stderr, " Sends data in file path as early data to the server. Early data will only be sent if s2nc receives a session ticket and resumes a session.\n"); + fprintf(stderr, " --renegotiation [accept|reject]\n" + " accept: Accept all server requests for a new handshake\n" + " reject: Reject all server requests for a new handshake\n"); + fprintf(stderr, " --npn \n"); + fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); + fprintf(stderr, "\n"); + fprintf(stderr, " --buffered-send \n"); + fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); + fprintf(stderr, " --prefer-low-latency\n"); + fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); + fprintf(stderr, " --prefer-throughput\n"); + fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); + /* clang-format on */ + exit(1); +} + +size_t session_state_length = 0; +uint8_t *session_state = NULL; +static int test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + GUARD_EXIT_NULL(conn); + GUARD_EXIT_NULL(ticket); + + GUARD_EXIT(s2n_session_ticket_get_data_len(ticket, &session_state_length), "Error getting ticket length "); + session_state = realloc(session_state, session_state_length); + if (session_state == NULL) { + print_s2n_error("Error getting new session state"); + exit(1); + } + GUARD_EXIT(s2n_session_ticket_get_data(ticket, session_state_length, session_state), "Error getting ticket data"); + + bool *session_ticket_recv = (bool *) ctx; + *session_ticket_recv = 1; + + return S2N_SUCCESS; +} + +struct reneg_req_ctx { + bool do_renegotiate; + s2n_renegotiate_response response; +}; + +static int reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) +{ + GUARD_EXIT_NULL(conn); + GUARD_EXIT_NULL(context); + GUARD_EXIT_NULL(response); + struct reneg_req_ctx *reneg_ctx = (struct reneg_req_ctx *) context; + + *response = reneg_ctx->response; + if (*response == S2N_RENEGOTIATE_ACCEPT) { + reneg_ctx->do_renegotiate = true; + } + return S2N_SUCCESS; +} + +static void setup_s2n_config(struct s2n_config *config, const char *cipher_prefs, s2n_status_request_type type, + struct verify_data *unsafe_verify_data, const char *host, const char *alpn_protocols, uint16_t mfl_value) +{ + if (config == NULL) { + print_s2n_error("Error getting new config"); + 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."); + + if (s2n_config_set_verify_host_callback(config, unsafe_verify_host, unsafe_verify_data) < 0) { + print_s2n_error("Error setting host name verification function."); + } + + if (type == S2N_STATUS_REQUEST_OCSP) { + if (s2n_config_set_check_stapled_ocsp_response(config, 1)) { + print_s2n_error("OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); + } + } + + unsafe_verify_data->trusted_host = host; + + if (alpn_protocols) { + /* Count the number of commas, this tells us how many protocols there + are in the list */ + const char *ptr = alpn_protocols; + int protocol_count = 1; + while (*ptr) { + if (*ptr == ',') { + protocol_count++; + } + ptr++; + } + + char **protocols = malloc(sizeof(char *) * protocol_count); + if (!protocols) { + fprintf(stderr, "Error allocating memory\n"); + exit(1); + } + + const char *next = alpn_protocols; + int idx = 0; + int length = 0; + ptr = alpn_protocols; + while (*ptr) { + if (*ptr == ',') { + protocols[idx] = malloc(length + 1); + if (!protocols[idx]) { + fprintf(stderr, "Error allocating memory\n"); + exit(1); + } + memmove(protocols[idx], next, length); + protocols[idx][length] = '\0'; + length = 0; + idx++; + ptr++; + next = ptr; + } else { + length++; + ptr++; + } + } + if (ptr != next) { + protocols[idx] = malloc(length + 1); + if (!protocols[idx]) { + fprintf(stderr, "Error allocating memory\n"); + exit(1); + } + memmove(protocols[idx], next, length); + protocols[idx][length] = '\0'; + } + + GUARD_EXIT(s2n_config_set_protocol_preferences(config, (const char *const *) protocols, protocol_count), "Failed to set protocol preferences"); + + while (protocol_count) { + protocol_count--; + free(protocols[protocol_count]); + } + free(protocols); + } + + uint8_t mfl_code = 0; + if (mfl_value > 0) { + switch (mfl_value) { + case 512: + mfl_code = S2N_TLS_MAX_FRAG_LEN_512; + break; + case 1024: + mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + break; + case 2048: + mfl_code = S2N_TLS_MAX_FRAG_LEN_2048; + break; + case 4096: + mfl_code = S2N_TLS_MAX_FRAG_LEN_4096; + break; + default: + fprintf(stderr, "Invalid maximum fragment length value\n"); + exit(1); + } + } + + GUARD_EXIT(s2n_config_send_max_fragment_length(config, mfl_code), "Error setting maximum fragment length"); +} + +int main(int argc, char *const *argv) +{ + struct addrinfo hints, *ai_list = NULL, *ai = NULL; + int r = 0, sockfd = 0; + bool session_ticket_recv = 0; + /* Optional args */ + const char *alpn_protocols = NULL; + const char *server_name = NULL; + const char *ca_file = NULL; + const char *ca_dir = NULL; + const char *client_cert = NULL; + const char *client_key = NULL; + bool client_cert_input = false; + bool client_key_input = false; + const char *ticket_out = NULL; + char *ticket_in = NULL; + const char *serialize_out = NULL; + const char *deserialize_in = NULL; + uint16_t mfl_value = 0; + uint8_t insecure = 0; + int reconnect = 0; + uint8_t session_ticket = 1; + s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; + uint32_t dyn_rec_threshold = 0; + uint8_t dyn_rec_timeout = 0; + /* required args */ + const char *cipher_prefs = "default"; + int fips_mode = 0; + const char *host = NULL; + struct verify_data unsafe_verify_data; + const char *port = "443"; + bool echo_input = false; + const char *send_file = NULL; + int use_corked_io = 0; + uint8_t non_blocking = 0; + const char *key_log_path = NULL; + FILE *key_log_file = NULL; + char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH]; + size_t psk_list_len = 0; + char *early_data = NULL; + bool setup_reneg_cb = false; + struct reneg_req_ctx reneg_ctx = { 0 }; + bool npn = false; + uint32_t send_buffer_size = 0; + bool prefer_low_latency = false; + bool prefer_throughput = false; + + static struct option long_options[] = { + { "alpn", required_argument, 0, 'a' }, + { "ciphers", required_argument, 0, 'c' }, + { "enter-fips-mode", no_argument, NULL, 'F' }, + { "echo", no_argument, 0, 'e' }, + { "send-file", required_argument, 0, OPT_SEND_FILE }, + { "help", no_argument, 0, 'h' }, + { "name", required_argument, 0, 'n' }, + { "status", no_argument, 0, 's' }, + { "mfl", required_argument, 0, 'm' }, + { "ca-file", required_argument, 0, 'f' }, + { "ca-dir", required_argument, 0, 'd' }, + { "cert", required_argument, 0, 'l' }, + { "key", required_argument, 0, 'k' }, + { "insecure", no_argument, 0, 'i' }, + { "reconnect", no_argument, 0, 'r' }, + { "ticket-out", required_argument, 0, OPT_TICKET_OUT }, + { "ticket-in", required_argument, 0, OPT_TICKET_IN }, + { "no-session-ticket", no_argument, 0, 'T' }, + { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, + { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, + { "dynamic", required_argument, 0, 'D' }, + { "timeout", required_argument, 0, 't' }, + { "corked-io", no_argument, 0, 'C' }, + { "tls13", no_argument, 0, '3' }, + { "non-blocking", no_argument, 0, 'B' }, + { "key-log", required_argument, 0, 'L' }, + { "psk", required_argument, 0, 'P' }, + { "early-data", required_argument, 0, 'E' }, + { "renegotiation", required_argument, 0, OPT_RENEG }, + { "npn", no_argument, 0, OPT_NPN }, + { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, + { "prefer-low-latency", no_argument, NULL, OPT_PREFER_LOW_LATENCY }, + { "prefer-throughput", no_argument, NULL, OPT_PREFER_THROUGHPUT }, + { 0 }, + }; + + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "a:c:ehn:m:sf:d:l:k:D:t:irTCBL:P:E:", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'a': + alpn_protocols = optarg; + break; + case 'C': + use_corked_io = 1; + break; + case 'c': + cipher_prefs = optarg; + break; + case 'F': + fips_mode = 1; + break; + case 'e': + echo_input = true; + break; + case OPT_SEND_FILE: + send_file = load_file_to_cstring(optarg); + break; + case 'h': + usage(); + break; + case 'n': + server_name = optarg; + break; + case 's': + type = S2N_STATUS_REQUEST_OCSP; + break; + case 'm': + mfl_value = (uint16_t) atoi(optarg); + break; + case 'f': + ca_file = optarg; + break; + case 'd': + ca_dir = optarg; + break; + case 'l': + client_cert = load_file_to_cstring(optarg); + client_cert_input = true; + break; + case 'k': + client_key = load_file_to_cstring(optarg); + client_key_input = true; + break; + case 'i': + insecure = 1; + break; + case 'r': + reconnect = 5; + break; + case OPT_TICKET_OUT: + ticket_out = optarg; + break; + case OPT_TICKET_IN: + ticket_in = optarg; + break; + /* The serialize_out and deserialize_in options are not documented + * in the usage section as they are not intended to work correctly + * using s2nc by itself. s2nc and s2nd are processes which close + * their TCP connection upon exit. This will cause an error if one + * peer serializes and exits and the other doesn't, as serialization + * depends on a continuous TCP connection with the peer. Therefore, our + * only usage of this feature is in our integ test framework, + * which serializes and deserializes both client and server at the + * same time. Do not expect these options to work when using s2nc alone. + */ + case OPT_SERIALIZE_OUT: + serialize_out = optarg; + break; + case OPT_DESERIALIZE_IN: + deserialize_in = optarg; + break; + case 'T': + session_ticket = 0; + break; + case 't': + dyn_rec_timeout = (uint8_t) MIN(255, atoi(optarg)); + break; + case 'D': + errno = 0; + dyn_rec_threshold = strtoul(optarg, 0, 10); + if (errno == ERANGE) { + dyn_rec_threshold = 0; + } + break; + case '3': + /* Do nothing -- this argument is deprecated. */ + break; + case 'B': + non_blocking = 1; + break; + case 'L': + key_log_path = optarg; + break; + case 'P': + if (psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { + fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); + exit(1); + } + psk_optarg_list[psk_list_len++] = optarg; + break; + case 'E': + early_data = load_file_to_cstring(optarg); + GUARD_EXIT_NULL(early_data); + break; + case OPT_RENEG: + setup_reneg_cb = true; + if (strcmp(optarg, "accept") == 0) { + reneg_ctx.response = S2N_RENEGOTIATE_ACCEPT; + } else if (strcmp(optarg, "reject") == 0) { + reneg_ctx.response = S2N_RENEGOTIATE_REJECT; + } else { + fprintf(stderr, "Unrecognized option: %s\n", optarg); + exit(1); + } + break; + case OPT_NPN: + npn = true; + break; + case OPT_BUFFERED_SEND: { + intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); + if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { + fprintf(stderr, " must be a positive 32 bit value\n"); + exit(1); + } + send_buffer_size = (uint32_t) send_buffer_size_scanned_value; + break; + } + case OPT_PREFER_LOW_LATENCY: + prefer_low_latency = true; + break; + case OPT_PREFER_THROUGHPUT: + prefer_throughput = true; + break; + case '?': + default: + usage(); + break; + } + } + + if (optind < argc) { + host = argv[optind++]; + } + + /* cppcheck-suppress duplicateCondition */ + if (optind < argc) { + port = argv[optind++]; + } + + if (!host) { + usage(); + } + + if (!server_name) { + server_name = host; + } + + memset(&hints, 0, sizeof(hints)); + + 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); + } + + if (prefer_low_latency && prefer_throughput) { + fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); + exit(1); + } + + GUARD_EXIT(s2n_init(), "Error running s2n_init()"); + printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); + + if (fips_mode) { + s2n_fips_mode mode = 0; + GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); + if (mode != S2N_FIPS_MODE_ENABLED) { + fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); + exit(1); + } + printf("s2nc entered FIPS mode\n"); + } + + if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { + fprintf(stderr, "error: %s\n", gai_strerror(r)); + exit(1); + } + + do { + int connected = 0; + 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 = 1; + /* connect() succeeded */ + break; + } + + if (connected == 0) { + fprintf(stderr, "Failed to connect to %s:%s\n", host, port); + exit(1); + } + + if (non_blocking) { + int flags = fcntl(sockfd, F_GETFL, 0); + if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { + fprintf(stderr, "fcntl error: %s\n", strerror(errno)); + exit(1); + } + } + + struct s2n_config *config = s2n_config_new(); + setup_s2n_config(config, cipher_prefs, type, &unsafe_verify_data, host, alpn_protocols, mfl_value); + + if (send_buffer_size != 0) { + GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size"); + } + + if (client_cert_input != client_key_input) { + print_s2n_error("Client cert/key pair must be given."); + } + + if (client_cert_input) { + struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); + GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, client_cert, client_key), "Error getting certificate/key"); + GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); + } + + GUARD_EXIT(s2n_config_add_pem_to_trust_store(config, default_trusted_cert), + "Error adding default cert to trust store."); + if (ca_file || ca_dir) { + GUARD_EXIT(s2n_config_wipe_trust_store(config), "Error wiping trust store"); + if (s2n_config_set_verification_ca_location(config, ca_file, ca_dir) < 0) { + print_s2n_error("Error setting CA file for trust store."); + } + } else if (insecure) { + GUARD_EXIT(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); + } + + if (session_ticket) { + GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); + GUARD_EXIT(s2n_config_set_session_ticket_cb(config, test_session_ticket_cb, &session_ticket_recv), "Error setting session ticket callback"); + session_ticket_recv = 0; + } + + if (key_log_path) { + key_log_file = fopen(key_log_path, "a"); + GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); + GUARD_EXIT( + s2n_config_set_key_log_cb( + config, + key_log_callback, + (void *) key_log_file), + "Failed to set key log callback"); + } + + if (setup_reneg_cb) { + GUARD_EXIT(s2n_config_set_renegotiate_request_cb(config, reneg_req_cb, &reneg_ctx), + "Error setting renegotiation request callback"); + } + + if (npn) { + GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); + } + + if (serialize_out) { + GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), + "Error setting serialized version"); + } + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + if (conn == NULL) { + print_s2n_error("Error getting new connection"); + exit(1); + } + + if (deserialize_in) { + GUARD_EXIT(s2n_connection_deserialize_in(conn, deserialize_in), "Failed to deserialize file"); + } + + GUARD_EXIT(s2n_connection_set_config(conn, config), "Error setting configuration"); + + GUARD_EXIT(s2n_set_server_name(conn, server_name), "Error setting server name"); + + GUARD_EXIT(s2n_connection_set_fd(conn, sockfd), "Error setting file descriptor"); + + GUARD_EXIT(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); + + if (use_corked_io) { + GUARD_EXIT(s2n_connection_use_corked_io(conn), "Error setting corked io"); + } + + /* Read in session ticket from previous session */ + if (ticket_in) { + GUARD_EXIT(get_file_size(ticket_in, &session_state_length), "Failed to read ticket-in file"); + free(session_state); + session_state = calloc(session_state_length, sizeof(uint8_t)); + GUARD_EXIT_NULL(session_state); + GUARD_EXIT(load_file_to_array(ticket_in, session_state, session_state_length), "Failed to read ticket-in file"); + } + + /* Update session state in connection if exists */ + if (session_state_length > 0) { + GUARD_EXIT(s2n_connection_set_session(conn, session_state, session_state_length), "Error setting session state in connection"); + } + + GUARD_EXIT(s2n_setup_external_psk_list(conn, psk_optarg_list, psk_list_len), "Error setting external psk list"); + + if (prefer_throughput) { + GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); + } + + if (prefer_low_latency) { + GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); + } + + if (early_data) { + if (!session_ticket) { + print_s2n_error("Early data can only be used with session tickets."); + exit(1); + } + /* Send early data if we have a received a session ticket from the server */ + if (session_state_length) { + uint32_t early_data_length = strlen(early_data); + GUARD_EXIT(early_data_send(conn, (uint8_t *) early_data, early_data_length), "Error sending early data"); + } + } + + if (!deserialize_in && negotiate(conn, sockfd) != 0) { + /* Error is printed in negotiate */ + S2N_ERROR_PRESERVE_ERRNO(); + } + + printf("Connected to %s:%s\n", host, port); + + /* Save session state from connection if reconnect is enabled. */ + if (reconnect > 0 || ticket_out) { + if (conn->actual_protocol_version >= S2N_TLS13) { + if (!session_ticket) { + print_s2n_error("s2nc can only reconnect in TLS1.3 with session tickets."); + exit(1); + } + GUARD_EXIT(echo(conn, sockfd, &session_ticket_recv), "Error calling echo"); + } else { + if (!session_ticket && s2n_connection_get_session_id_length(conn) <= 0) { + print_s2n_error("Endpoint sent empty session id so cannot resume session"); + exit(1); + } + free(session_state); + session_state_length = s2n_connection_get_session_length(conn); + session_state = calloc(session_state_length, sizeof(uint8_t)); + GUARD_EXIT_NULL(session_state); + if (s2n_connection_get_session(conn, session_state, session_state_length) != session_state_length) { + print_s2n_error("Error getting serialized session state"); + exit(1); + } + } + if (ticket_out) { + GUARD_EXIT(write_array_to_file(ticket_out, session_state, session_state_length), "Failed to write to ticket-out file"); + } + } + + if (dyn_rec_threshold > 0 && dyn_rec_timeout > 0) { + s2n_connection_set_dynamic_record_threshold(conn, dyn_rec_threshold, dyn_rec_timeout); + } + + GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); + + if (send_file != NULL) { + printf("Sending file contents:\n%s\n", send_file); + + unsigned long send_file_len = strlen(send_file); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + send_data(conn, sockfd, send_file, send_file_len, &blocked); + } + + while (echo_input) { + fflush(stdout); + fflush(stderr); + echo(conn, sockfd, &reneg_ctx.do_renegotiate); + + if (!reneg_ctx.do_renegotiate) { + break; + } + + reneg_ctx.do_renegotiate = false; + GUARD_EXIT(renegotiate(conn, sockfd), "Renegotiation failed"); + } + + if (serialize_out) { + GUARD_EXIT(s2n_connection_serialize_out(conn, serialize_out), "Error serializing connection"); + } else { + GUARD_EXIT(wait_for_shutdown(conn, sockfd), "Error closing connection"); + } + + GUARD_EXIT(s2n_connection_free(conn), "Error freeing connection"); + + GUARD_EXIT(s2n_config_free(config), "Error freeing configuration"); + + close(sockfd); + reconnect--; + + } while (reconnect >= 0); + + if (key_log_file) { + fclose(key_log_file); + } + + GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); + + free(early_data); + free(session_state); + freeaddrinfo(ai_list); + return 0; +} diff --git a/bin/s2nd.c b/bin/s2nd.c index cc1ba1ed460..d0c363d6965 100644 --- a/bin/s2nd.c +++ b/bin/s2nd.c @@ -1,712 +1,719 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#ifndef S2N_INTERN_LIBCRYPTO - #include - #include -#endif - -#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 - -/* - * s2nd is an example server that uses many s2n-tls APIs. - * It is intended for testing purposes only, and should not be used in production. - */ - -static char default_certificate_chain[] = - "-----BEGIN CERTIFICATE-----" - "MIIDHTCCAgWgAwIBAgIUPxywpg3/+VHmj8jJSvK62XC06zMwDQYJKoZIhvcNAQEL" - "BQAwHjEcMBoGA1UEAwwTczJuVGVzdEludGVybWVkaWF0ZTAgFw0yMDAxMjQwMTEw" - "MjFaGA8yMTE5MTIzMTAxMTAyMVowGDEWMBQGA1UEAwwNczJuVGVzdFNlcnZlcjCC" - "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJUbMpdROM6cjb8xgr5kgKHn" - "JVDfhbLg4pxBwWwlayb6/N60JLG9KzWAWhZBmz+Px6kr/1dL6+bL3mLuNBCQpYBS" - "Pee2n7KL9PvsMYZmnYFyn94bXbjBCRxGR+a9lcGHLlZ4C+rrLNi9pUwxf7VIRglR" - "zwHWAFg5xTX6lCmziNM4OMkq8lHkLopHDUg5yI4VTc3EEGqDIf3+0BheIHcUFbIW" - "kFOjRDdL3lMGKEj0+LErzzbhJczBlRMqSMiuYeaWgORLpRNtMeNmbR8oLJFchpF0" - "A9fIO2/Yg+nclcDDhsUBkkfcIKRySGDumKLuYM+hOHp5vQo8tcvyQ6s3U5YULQUC" - "AwEAAaNXMFUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUkVKVmfjICpx4fkvJO6YJ" - "mdoKz3owDgYDVR0PAQH/BAQDAgPoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMA0G" - "CSqGSIb3DQEBCwUAA4IBAQBXoWDI1Gi9snC4K6S7E0AoLmGEPUWzc4fd4Cbj9PRp" - "mSKpsJOYjmneIV34WqnvUXrBkkzblEb9RdszN96WuRAaZJQegRtKOWN5Iggd4sHM" - "8XEx/LeJHc08uSb2d/TnhhOPALoJl/w6M5e6yOezCEJorsOXuVBcbuEKfne7oMA1" - "GziFnVPtwiwXxsX16KilsQRylnK0bV/x1BOgYByCDcXorMndsAYjn4yG1D4l8TbC" - "kCtK1bafEVoASpOFQ8tSeOXBL7Fvw9mFFzs3/ajBTz2nBLDsnP8XH5C/vy8wNGSd" - "Tdcs7DRLYhNJxYopcMgCwyyCAtEFcHkovCSrJ6HUl/ko" - "-----END CERTIFICATE-----" - "-----BEGIN CERTIFICATE-----" - "MIIDCTCCAfGgAwIBAgIUfdybeOdDMd7cPXk6RTcEqeM3IEIwDQYJKoZIhvcNAQEL" - "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwOTUzWhgPMjEx" - "OTEyMzEwMTA5NTNaMB4xHDAaBgNVBAMME3MyblRlc3RJbnRlcm1lZGlhdGUwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2NsDkrZjYbyVeF1R9337y9OHM" - "C2xSRGB6SHrVG1bQZlPxI+E6DqDJcMB4tFLkA7AJxxRLxA7KvO9PzcHAlsqvYcMV" - "gOSAjUZ0Eiwwf6Rtgo2yByj2n1K5XDN3bpt1rROD0BIEnaU9GZd3U0QUYHBRfp0E" - "IdeWuRrlFbPpWXnBaQB/2jEfCuZzpPOiKMWt99GQ4bFBOSzpYdXLALGfb15Kr6RF" - "YoMlsyeijNeePxLeYgracu+vzJLvEzx1U7OGnlWz+VKBw/mz3gABqFfxurN5E8yb" - "4AWJ5kEUJobYcxwe+DoimPdPTWgByJlMpKjfIbnroz/oTZMiNfUCtKT3GTejAgMB" - "AAGjRTBDMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFEasSJIPBZTXyYjI" - "CN2m1Ttz3sUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAxveh" - "GKJPu7DXjoMePzlRGML2iIDT6MgKpsMnO5sNgUbJTFV3KeuASRm1SXVrVFHcQDov" - "l9P10ff0J9KOVrRCawMZZxjjtNAIrSW0G7fwmTgJMTuM5vaaGRjKy018LApcr//Q" - "Nwjh4sw9KOtNIE9krT06kli9zjsgr/EWwPCHSin8oONDgCNn1WgtrSMexsF1BSzU" - "OTq+nyn4nOPOEUthjmepG2eDkd17MNJ6GdKYnFRmC+ctSH028akERhz+EtavU4Cd" - "2eSFTKtbxOuZXyfsOwjhrufp/Ss9i57x3XotBNJ8Fv7VpxI19+Zag4DMGzd3Pisu" - "Q1VpfValnMGtVWPleg==" - "-----END CERTIFICATE-----" - "-----BEGIN CERTIFICATE-----" - "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" - "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" - "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" - "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" - "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" - "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" - "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" - "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" - "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" - "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" - "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" - "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" - "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" - "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" - "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" - "MZ8=" - "-----END CERTIFICATE-----"; - -static char default_private_key[] = - "-----BEGIN RSA PRIVATE KEY-----" - "MIIEogIBAAKCAQEAlRsyl1E4zpyNvzGCvmSAoeclUN+FsuDinEHBbCVrJvr83rQk" - "sb0rNYBaFkGbP4/HqSv/V0vr5sveYu40EJClgFI957afsov0++wxhmadgXKf3htd" - "uMEJHEZH5r2VwYcuVngL6uss2L2lTDF/tUhGCVHPAdYAWDnFNfqUKbOI0zg4ySry" - "UeQuikcNSDnIjhVNzcQQaoMh/f7QGF4gdxQVshaQU6NEN0veUwYoSPT4sSvPNuEl" - "zMGVEypIyK5h5paA5EulE20x42ZtHygskVyGkXQD18g7b9iD6dyVwMOGxQGSR9wg" - "pHJIYO6You5gz6E4enm9Cjy1y/JDqzdTlhQtBQIDAQABAoIBAGTaSJXg8jON4LJ5" - "op11DSx1U+An0B71zVEziMjFZnyvN2rLHia6dQdzEXwMVB3h+oKKp+M8DwvEyV7R" - "D5ZEwCzTc9vOwqXZ1JKxZ64oqlBsX4WzrOjSaH8fanK/uRN1g/ooqKb0+xh+7ddj" - "g6XyhKy5EPOE9Ca4rJOeMakjLmDuleQecT/DixYV6azhfaJoD70XZJWv3YzSpu/X" - "Ma+i3of0alsG/lROjNtEXE3nKzcTUgyAUoQeYRwCVpgssg/4VAUPJNDP4dVmxW8f" - "eNmjlTyXmR9S08SXkqmCHe2mBUsZY9nqcDE6ZWILZKFWIfZD9W+j2ce0FMvcc9kz" - "psxaUQECgYEAxqwsb5aQy6HBF54tdkHbUQEJMelSLNW0G1GUrcLB7eqL7qo3dUA8" - "8PDQ/dTwmmJ7aE0SK2xkQDVKXNbV4OvUNgP6tbzLWEbvmuFAEg5X1jFH2VSdwQhl" - "RDwTQw3wPZ5udy64L6gmsdDch+I7l1v4ex66RWFW+4WIs1altsLiJa0CgYEAwCGW" - "2cjtZ3kIzWgxf7DdnoUTwBM1ATBUYvx7uqVq+dbc/p8cSeMSPz3LUluaVJ2EOjEV" - "QWhx0Ih5qeitzReHRU0OHgxEgjbpJwhseD9O5POSd+fE3TtDQArOxyw4CIJKk4Z2" - "QmqzaO/LboN3Tp+/N9zfVoNZKHcCNra/uKNTH7kCgYA3QFazSdpG51s96D2Yb8RA" - "iNs3yD2UPnJyToPctxcbxWjZHPmDYDQShcZ5cSjgppbPcO+mp+RRfwCJRS4B+VPx" - "GbY1qKWcjU3BcvdQjjCbXuUuabvdnSocieCJe2zelhr+hj2u80KfnQhXufD8rRUz" - "mF4RQXrhREe6KFS5uQUPmQKBgE4rXFyvSyfWLqajxb/WDdT4/9gd+GrLZwn+/7go" - "pSWRLcjKo4/MOxhP4/FWI6xZifrDDYrXG7dkT1u5tzzCXd7sQtom05jHDoU7ACbM" - "WyT7lJQEUCxSeEIOI6MVcpbDq+PpySOsleIT7gjApEHw7LOlwZhJSHUWNmhcYhSV" - "HrTBAoGADAvBqV7JItjm2+qkXXEdPVzOunqjQdnXZjMAJ75PhHnLCCfnTRu53hT3" - "JxDETLLa/r42PlqGZ6bqSW+C+ObgYOvvySqvX8CE9o208ZwCLjHYxuYLH/86Lppr" - "ggF9KQ0xWz7Km3GXv5+bwM5bcgt1A/s6sZCimXuj3Fle3RqOTF0=" - "-----END RSA PRIVATE KEY-----"; - -#define OPT_BUFFERED_SEND 1000 -#define OPT_SERIALIZE_OUT 1001 -#define OPT_DESERIALIZE_IN 1002 - -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"); - fprintf(stderr, "\n Options:\n\n"); - fprintf(stderr, " -a [protocol]\n"); - fprintf(stderr, " --alpn [protocol]\n"); - fprintf(stderr, " Sets a single application protocol supported by this server.\n"); - fprintf(stderr, " -c [version_string]\n"); - fprintf(stderr, " --ciphers [version_string]\n"); - fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); - fprintf(stderr, " --enter-fips-mode\n"); - fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); - fprintf(stderr, " --cert\n"); - fprintf(stderr, " Path to a PEM encoded certificate [chain]. Option can be repeated to load multiple certs.\n"); - fprintf(stderr, " --key\n"); - fprintf(stderr, " Path to a PEM encoded private key that matches cert. Option can be repeated to load multiple certs.\n"); - fprintf(stderr, " -m\n"); - fprintf(stderr, " --mutualAuth\n"); - fprintf(stderr, " Request a Client Certificate. Any RSA Certificate will be accepted.\n"); - fprintf(stderr, " -n\n"); - fprintf(stderr, " --negotiate\n"); - fprintf(stderr, " Only perform tls handshake and then shutdown the connection\n"); - fprintf(stderr, " --parallelize\n"); - fprintf(stderr, " Create a new Connection handler thread for each new connection. Useful for tests with lots of connections.\n"); - fprintf(stderr, " Warning: this option isn't compatible with TLS Resumption, since each thread gets its own Session cache.\n"); - fprintf(stderr, " --prefer-low-latency\n"); - fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); - fprintf(stderr, " --prefer-throughput\n"); - fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); - fprintf(stderr, " --enable-mfl\n"); - fprintf(stderr, " Accept client's TLS maximum fragment length extension request\n"); - fprintf(stderr, " --ocsp\n"); - fprintf(stderr, " Path to a DER formatted OCSP response for stapling\n"); - fprintf(stderr, " -s\n"); - fprintf(stderr, " --self-service-blinding\n"); - fprintf(stderr, " Don't introduce 10-30 second delays on TLS Handshake errors. \n"); - fprintf(stderr, " Warning: this should only be used for testing since skipping blinding may allow timing side channels.\n"); - fprintf(stderr, " -t,--ca-file [file path]\n"); - fprintf(stderr, " Location of trust store CA file (PEM format). If neither -t or -d are specified. System defaults will be used."); - fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); - fprintf(stderr, " -d,--ca-dir [directory path]\n"); - fprintf(stderr, " Directory containing hashed trusted certs. If neither -t or -d are specified. System defaults will be used."); - fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); - fprintf(stderr, " -i,--insecure\n"); - fprintf(stderr, " Turns off certification validation altogether.\n"); - fprintf(stderr, " --stk-file\n"); - fprintf(stderr, " Location of key file used for encryption and decryption of session ticket.\n"); - fprintf(stderr, " -T,--no-session-ticket\n"); - fprintf(stderr, " Disable session ticket for resumption.\n"); - fprintf(stderr, " -C,--corked-io\n"); - fprintf(stderr, " Turn on corked io\n"); - fprintf(stderr, " --non-blocking\n"); - fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); - fprintf(stderr, " -w --https-server\n"); - fprintf(stderr, " Run s2nd in a simple https server mode.\n"); - fprintf(stderr, " -b --https-bench \n"); - fprintf(stderr, " Send number of bytes in https server mode to test throughput.\n"); - fprintf(stderr, " -L --key-log \n"); - fprintf(stderr, " Enable NSS key logging into the provided path\n"); - fprintf(stderr, " -P --psk \n" - " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" - " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" - " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); - fprintf(stderr, " -E, --max-early-data \n"); - fprintf(stderr, " Sets maximum early data allowed in session tickets. \n"); - fprintf(stderr, " -N --npn \n"); - fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); - fprintf(stderr, " -h,--help\n"); - fprintf(stderr, " Display this message and quit.\n"); - fprintf(stderr, " --buffered-send \n"); - fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); - fprintf(stderr, " -X, --max-conns \n"); - fprintf(stderr, " Sets the max number of connections s2nd will accept before shutting down.\n"); - /* clang-format on */ - exit(1); -} - -int handle_connection(int fd, struct s2n_config *config, struct conn_settings settings) -{ - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - if (!conn) { - print_s2n_error("Error getting new s2n connection"); - S2N_ERROR_PRESERVE_ERRNO(); - } - - GUARD_EXIT(s2n_setup_server_connection(conn, fd, config, settings), "Error setting up connection"); - - if (!settings.deserialize_in && negotiate(conn, fd) != S2N_SUCCESS) { - if (settings.mutual_auth) { - if (!s2n_connection_client_cert_used(conn)) { - print_s2n_error("Error: Mutual Auth was required, but not negotiated"); - } - } - - /* Error is printed in negotiate */ - S2N_ERROR_PRESERVE_ERRNO(); - } - - GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); - - if (settings.https_server) { - https(conn, settings.https_bench); - } else if (!settings.only_negotiate) { - bool stop_echo = false; - echo(conn, fd, &stop_echo); - } - - if (settings.serialize_out) { - GUARD_RETURN(s2n_connection_serialize_out(conn, settings.serialize_out), "Error serializing connection"); - } else { - GUARD_RETURN(wait_for_shutdown(conn, fd), "Error closing connection"); - } - - GUARD_RETURN(s2n_connection_wipe(conn), "Error wiping connection"); - - GUARD_RETURN(s2n_connection_free(conn), "Error freeing connection"); - - return 0; -} - -int main(int argc, char *const *argv) -{ - struct addrinfo hints, *ai = NULL; - int r = 0, sockfd = 0; - - /* required args */ - const char *host = NULL; - const char *port = NULL; - - const char *ocsp_response_file_path = NULL; - const char *session_ticket_key_file_path = NULL; - const char *cipher_prefs = "default"; - const char *alpn = NULL; - const char *key_log_path = NULL; - - /* The certificates provided by the user. If there are none provided, we will use the hardcoded default cert. - * The associated private key for each cert will be at the same index in private_keys. If the user mixes up the - * order of --cert --key for a given cert/key pair, s2n will fail to load the cert and s2nd will exit. - */ - int num_user_certificates = 0; - int num_user_private_keys = 0; - const char *certificates[MAX_CERTIFICATES] = { 0 }; - const char *private_keys[MAX_CERTIFICATES] = { 0 }; - - struct conn_settings conn_settings = { 0 }; - int fips_mode = 0; - int parallelize = 0; - int non_blocking = 0; - long int bytes = 0; - conn_settings.session_ticket = 1; - conn_settings.session_cache = 1; - conn_settings.max_conns = -1; - conn_settings.psk_list_len = 0; - int max_early_data = 0; - uint32_t send_buffer_size = 0; - bool npn = false; - - struct option long_options[] = { - { "ciphers", required_argument, NULL, 'c' }, - { "enable-mfl", no_argument, NULL, 'e' }, - { "enter-fips-mode", no_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "key", required_argument, NULL, 'k' }, - { "prefer-low-latency", no_argument, NULL, 'l' }, - { "mutualAuth", no_argument, NULL, 'm' }, - { "negotiate", no_argument, NULL, 'n' }, - { "ocsp", required_argument, NULL, 'o' }, - { "parallelize", no_argument, ¶llelize, 1 }, - { "prefer-throughput", no_argument, NULL, 'p' }, - { "cert", required_argument, NULL, 'r' }, - { "self-service-blinding", no_argument, NULL, 's' }, - { "ca-dir", required_argument, 0, 'd' }, - { "ca-file", required_argument, 0, 't' }, - { "insecure", no_argument, 0, 'i' }, - { "stk-file", required_argument, 0, 'a' }, - { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, - { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, - { "no-session-ticket", no_argument, 0, 'T' }, - { "corked-io", no_argument, 0, 'C' }, - { "max-conns", optional_argument, 0, 'X' }, - { "tls13", no_argument, 0, '3' }, - { "https-server", no_argument, 0, 'w' }, - { "https-bench", required_argument, 0, 'b' }, - { "alpn", required_argument, 0, 'A' }, - { "npn", no_argument, 0, 'N' }, - { "non-blocking", no_argument, 0, 'B' }, - { "key-log", required_argument, 0, 'L' }, - { "psk", required_argument, 0, 'P' }, - { "max-early-data", required_argument, 0, 'E' }, - { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, - /* Per getopt(3) the last element of the array has to be filled with all zeros */ - { 0 }, - }; - while (1) { - int option_index = 0; - int c = getopt_long(argc, argv, "c:hmnst:d:iTCX::wb:A:P:E:", long_options, &option_index); - if (c == -1) { - break; - } - - switch (c) { - case 0: - /* getopt_long() returns 0 if an option.flag is non-null (Eg "parallelize") */ - break; - case 'C': - conn_settings.use_corked_io = 1; - break; - case 'c': - cipher_prefs = optarg; - break; - case 'e': - conn_settings.enable_mfl = 1; - break; - case 'f': - fips_mode = 1; - break; - case 'h': - usage(); - break; - case 'k': - if (num_user_private_keys == MAX_CERTIFICATES) { - fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); - exit(1); - } - private_keys[num_user_private_keys] = load_file_to_cstring(optarg); - num_user_private_keys++; - break; - case 'l': - conn_settings.prefer_low_latency = 1; - break; - case 'm': - conn_settings.mutual_auth = 1; - break; - case 'n': - conn_settings.only_negotiate = 1; - break; - case 'o': - ocsp_response_file_path = optarg; - break; - case 'p': - conn_settings.prefer_throughput = 1; - break; - case 'r': - if (num_user_certificates == MAX_CERTIFICATES) { - fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); - exit(1); - } - certificates[num_user_certificates] = load_file_to_cstring(optarg); - num_user_certificates++; - break; - case 's': - conn_settings.self_service_blinding = 1; - break; - case 'd': - conn_settings.ca_dir = optarg; - break; - case 't': - conn_settings.ca_file = optarg; - break; - case 'i': - conn_settings.insecure = 1; - break; - case 'a': - session_ticket_key_file_path = optarg; - break; - case 'T': - conn_settings.session_ticket = 0; - break; - case '3': - /* Do nothing -- this argument is deprecated */ - break; - case 'X': - if (optarg == NULL) { - conn_settings.max_conns = 1; - } else { - conn_settings.max_conns = atoi(optarg); - } - break; - case 'w': - fprintf(stdout, "Running s2nd in simple https server mode\n"); - conn_settings.https_server = 1; - break; - case 'b': - bytes = strtoul(optarg, NULL, 10); - GUARD_EXIT(bytes, "https-bench bytes needs to be some positive long value."); - conn_settings.https_bench = bytes; - break; - case OPT_BUFFERED_SEND: { - intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); - if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { - fprintf(stderr, " must be a positive 32 bit value\n"); - exit(1); - } - send_buffer_size = (uint32_t) send_buffer_size_scanned_value; - break; - } - /* The serialize_out and deserialize_in options are not documented - * in the usage section as they are not intended to work correctly - * using s2nd by itself. s2nc and s2nd are processes which close - * their TCP connection upon exit. This will cause an error if one - * peer serializes and exits and the other doesn't, as serialization - * depends on a continuous TCP connection with the peer. Therefore, our - * only usage of this feature is in our integ test framework, - * which serializes and deserializes both client and server at the - * same time. Do not expect these options to work when using s2nd alone. - */ - case OPT_SERIALIZE_OUT: - conn_settings.serialize_out = optarg; - break; - case OPT_DESERIALIZE_IN: - conn_settings.deserialize_in = optarg; - break; - case 'A': - alpn = optarg; - break; - case 'B': - non_blocking = 1; - break; - case 'L': - key_log_path = optarg; - break; - case 'P': - if (conn_settings.psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { - fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); - exit(1); - } - conn_settings.psk_optarg_list[conn_settings.psk_list_len++] = optarg; - break; - case 'E': - max_early_data = atoi(optarg); - break; - case 'N': - npn = true; - break; - case '?': - default: - fprintf(stdout, "getopt_long returned: %d", c); - usage(); - break; - } - } - - if (conn_settings.prefer_throughput && conn_settings.prefer_low_latency) { - fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); - exit(1); - } - - if (optind < argc) { - host = argv[optind++]; - } - - /* cppcheck-suppress duplicateCondition */ - if (optind < argc) { - port = argv[optind++]; - } - - if (!host || !port) { - usage(); - } - - if (setvbuf(stdin, NULL, _IONBF, 0) < 0) { - fprintf(stderr, "Error disabling buffering for stdin\n"); - exit(1); - } - - if (setvbuf(stdout, NULL, _IONBF, 0) < 0) { - fprintf(stderr, "Error disabling buffering for stdout\n"); - exit(1); - } - - memset(&hints, 0, sizeof(hints)); - - 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); - } - - if ((r = getaddrinfo(host, port, &hints, &ai)) < 0) { - fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(r)); - exit(1); - } - - if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { - fprintf(stderr, "socket error: %s\n", strerror(errno)); - exit(1); - } - - r = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int)) < 0) { - fprintf(stderr, "setsockopt error: %s\n", strerror(errno)); - exit(1); - } - - if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { - fprintf(stderr, "bind error: %s\n", strerror(errno)); - exit(1); - } - - if (listen(sockfd, 1) == -1) { - fprintf(stderr, "listen error: %s\n", strerror(errno)); - exit(1); - } - - GUARD_EXIT(s2n_init(), "Error running s2n_init()"); - printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); - - if (fips_mode) { - s2n_fips_mode mode = 0; - GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); - if (mode != S2N_FIPS_MODE_ENABLED) { - fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); - exit(1); - } - printf("s2nd entered FIPS mode\n"); - } - - printf("Listening on %s:%s\n", host, port); - - struct s2n_config *config = s2n_config_new(); - if (!config) { - print_s2n_error("Error getting new s2n config"); - exit(1); - } - - if (num_user_certificates != num_user_private_keys) { - fprintf(stderr, "Mismatched certificate(%d) and private key(%d) count!\n", num_user_certificates, num_user_private_keys); - exit(1); - } - - int num_certificates = 0; - if (num_user_certificates == 0) { - certificates[0] = default_certificate_chain; - private_keys[0] = default_private_key; - num_certificates = 1; - } else { - num_certificates = num_user_certificates; - } - - for (int i = 0; i < num_certificates; i++) { - struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); - GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, certificates[i], private_keys[i]), "Error getting certificate/key"); - - if (ocsp_response_file_path) { - int fd = open(ocsp_response_file_path, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Error opening OCSP response file: '%s'\n", strerror(errno)); - exit(1); - } - - struct stat st = { 0 }; - if (fstat(fd, &st) < 0) { - fprintf(stderr, "Error fstat-ing OCSP response file: '%s'\n", strerror(errno)); - exit(1); - } - - uint8_t *ocsp_response = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_response, st.st_size) < 0) { - fprintf(stderr, "Error adding ocsp response: '%s'\n", s2n_strerror(s2n_errno, "EN")); - exit(1); - } - - close(fd); - } - - GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); - } - - s2n_set_common_server_config(max_early_data, config, conn_settings, cipher_prefs, session_ticket_key_file_path); - - if (parallelize) { - struct sigaction sa; - - sa.sa_handler = SIG_IGN; -#if defined(SA_NOCLDWAIT) - sa.sa_flags = SA_NOCLDWAIT; -#endif - sigemptyset(&sa.sa_mask); - sigaction(SIGCHLD, &sa, NULL); - } - - if (alpn) { - const char *protocols[] = { alpn }; - GUARD_EXIT(s2n_config_set_protocol_preferences(config, protocols, s2n_array_len(protocols)), "Failed to set alpn"); - } - - if (send_buffer_size != 0) { - GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size."); - } - - if (npn) { - GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); - } - - if (conn_settings.serialize_out) { - GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), - "Error setting serialized version"); - } - - FILE *key_log_file = NULL; - - if (key_log_path) { - key_log_file = fopen(key_log_path, "a"); - GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); - GUARD_EXIT( - s2n_config_set_key_log_cb( - config, - key_log_callback, - (void *) key_log_file), - "Failed to set key log callback"); - } - - int fd = 0; - while ((fd = accept(sockfd, ai->ai_addr, &ai->ai_addrlen)) > 0) { - if (non_blocking) { - int flags = fcntl(sockfd, F_GETFL, 0); - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { - fprintf(stderr, "fcntl error: %s\n", strerror(errno)); - exit(1); - } - } - - if (!parallelize) { - int rc = handle_connection(fd, config, conn_settings); - close(fd); - if (rc < 0) { - exit(rc); - } - - /* If max_conns was set, then exit after it is reached. Otherwise - * unlimited connections are allow, so ignore the variable. */ - if (conn_settings.max_conns > 0) { - if (conn_settings.max_conns-- == 1) { - GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); - exit(0); - } - } - } else { - /* Fork Process, one for the Acceptor (parent), and another for the Handler (child). */ - pid_t child_pid = fork(); - - if (child_pid == 0) { - /* This is the Child Handler Thread. We should handle the connection, then exit. */ - int rc = handle_connection(fd, config, conn_settings); - close(fd); - _exit(rc); - } else if (child_pid == -1) { - close(fd); - print_s2n_error("Error calling fork(). Acceptor unable to start handler."); - exit(1); - } else { - /* This is the parent Acceptor Thread, continue listening for new connections */ - close(fd); - continue; - } - } - } - - if (key_log_file) { - fclose(key_log_file); - } - - GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#include +#if !defined(_MSC_VER) +#include +#endif + +#ifndef S2N_INTERN_LIBCRYPTO + #include + #include +#endif + +#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 + +/* + * s2nd is an example server that uses many s2n-tls APIs. + * It is intended for testing purposes only, and should not be used in production. + */ + +static char default_certificate_chain[] = + "-----BEGIN CERTIFICATE-----" + "MIIDHTCCAgWgAwIBAgIUPxywpg3/+VHmj8jJSvK62XC06zMwDQYJKoZIhvcNAQEL" + "BQAwHjEcMBoGA1UEAwwTczJuVGVzdEludGVybWVkaWF0ZTAgFw0yMDAxMjQwMTEw" + "MjFaGA8yMTE5MTIzMTAxMTAyMVowGDEWMBQGA1UEAwwNczJuVGVzdFNlcnZlcjCC" + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJUbMpdROM6cjb8xgr5kgKHn" + "JVDfhbLg4pxBwWwlayb6/N60JLG9KzWAWhZBmz+Px6kr/1dL6+bL3mLuNBCQpYBS" + "Pee2n7KL9PvsMYZmnYFyn94bXbjBCRxGR+a9lcGHLlZ4C+rrLNi9pUwxf7VIRglR" + "zwHWAFg5xTX6lCmziNM4OMkq8lHkLopHDUg5yI4VTc3EEGqDIf3+0BheIHcUFbIW" + "kFOjRDdL3lMGKEj0+LErzzbhJczBlRMqSMiuYeaWgORLpRNtMeNmbR8oLJFchpF0" + "A9fIO2/Yg+nclcDDhsUBkkfcIKRySGDumKLuYM+hOHp5vQo8tcvyQ6s3U5YULQUC" + "AwEAAaNXMFUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUkVKVmfjICpx4fkvJO6YJ" + "mdoKz3owDgYDVR0PAQH/BAQDAgPoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMA0G" + "CSqGSIb3DQEBCwUAA4IBAQBXoWDI1Gi9snC4K6S7E0AoLmGEPUWzc4fd4Cbj9PRp" + "mSKpsJOYjmneIV34WqnvUXrBkkzblEb9RdszN96WuRAaZJQegRtKOWN5Iggd4sHM" + "8XEx/LeJHc08uSb2d/TnhhOPALoJl/w6M5e6yOezCEJorsOXuVBcbuEKfne7oMA1" + "GziFnVPtwiwXxsX16KilsQRylnK0bV/x1BOgYByCDcXorMndsAYjn4yG1D4l8TbC" + "kCtK1bafEVoASpOFQ8tSeOXBL7Fvw9mFFzs3/ajBTz2nBLDsnP8XH5C/vy8wNGSd" + "Tdcs7DRLYhNJxYopcMgCwyyCAtEFcHkovCSrJ6HUl/ko" + "-----END CERTIFICATE-----" + "-----BEGIN CERTIFICATE-----" + "MIIDCTCCAfGgAwIBAgIUfdybeOdDMd7cPXk6RTcEqeM3IEIwDQYJKoZIhvcNAQEL" + "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwOTUzWhgPMjEx" + "OTEyMzEwMTA5NTNaMB4xHDAaBgNVBAMME3MyblRlc3RJbnRlcm1lZGlhdGUwggEi" + "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2NsDkrZjYbyVeF1R9337y9OHM" + "C2xSRGB6SHrVG1bQZlPxI+E6DqDJcMB4tFLkA7AJxxRLxA7KvO9PzcHAlsqvYcMV" + "gOSAjUZ0Eiwwf6Rtgo2yByj2n1K5XDN3bpt1rROD0BIEnaU9GZd3U0QUYHBRfp0E" + "IdeWuRrlFbPpWXnBaQB/2jEfCuZzpPOiKMWt99GQ4bFBOSzpYdXLALGfb15Kr6RF" + "YoMlsyeijNeePxLeYgracu+vzJLvEzx1U7OGnlWz+VKBw/mz3gABqFfxurN5E8yb" + "4AWJ5kEUJobYcxwe+DoimPdPTWgByJlMpKjfIbnroz/oTZMiNfUCtKT3GTejAgMB" + "AAGjRTBDMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFEasSJIPBZTXyYjI" + "CN2m1Ttz3sUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAxveh" + "GKJPu7DXjoMePzlRGML2iIDT6MgKpsMnO5sNgUbJTFV3KeuASRm1SXVrVFHcQDov" + "l9P10ff0J9KOVrRCawMZZxjjtNAIrSW0G7fwmTgJMTuM5vaaGRjKy018LApcr//Q" + "Nwjh4sw9KOtNIE9krT06kli9zjsgr/EWwPCHSin8oONDgCNn1WgtrSMexsF1BSzU" + "OTq+nyn4nOPOEUthjmepG2eDkd17MNJ6GdKYnFRmC+ctSH028akERhz+EtavU4Cd" + "2eSFTKtbxOuZXyfsOwjhrufp/Ss9i57x3XotBNJ8Fv7VpxI19+Zag4DMGzd3Pisu" + "Q1VpfValnMGtVWPleg==" + "-----END CERTIFICATE-----" + "-----BEGIN CERTIFICATE-----" + "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" + "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" + "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" + "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" + "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" + "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" + "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" + "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" + "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" + "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" + "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" + "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" + "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" + "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" + "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" + "MZ8=" + "-----END CERTIFICATE-----"; + +static char default_private_key[] = + "-----BEGIN RSA PRIVATE KEY-----" + "MIIEogIBAAKCAQEAlRsyl1E4zpyNvzGCvmSAoeclUN+FsuDinEHBbCVrJvr83rQk" + "sb0rNYBaFkGbP4/HqSv/V0vr5sveYu40EJClgFI957afsov0++wxhmadgXKf3htd" + "uMEJHEZH5r2VwYcuVngL6uss2L2lTDF/tUhGCVHPAdYAWDnFNfqUKbOI0zg4ySry" + "UeQuikcNSDnIjhVNzcQQaoMh/f7QGF4gdxQVshaQU6NEN0veUwYoSPT4sSvPNuEl" + "zMGVEypIyK5h5paA5EulE20x42ZtHygskVyGkXQD18g7b9iD6dyVwMOGxQGSR9wg" + "pHJIYO6You5gz6E4enm9Cjy1y/JDqzdTlhQtBQIDAQABAoIBAGTaSJXg8jON4LJ5" + "op11DSx1U+An0B71zVEziMjFZnyvN2rLHia6dQdzEXwMVB3h+oKKp+M8DwvEyV7R" + "D5ZEwCzTc9vOwqXZ1JKxZ64oqlBsX4WzrOjSaH8fanK/uRN1g/ooqKb0+xh+7ddj" + "g6XyhKy5EPOE9Ca4rJOeMakjLmDuleQecT/DixYV6azhfaJoD70XZJWv3YzSpu/X" + "Ma+i3of0alsG/lROjNtEXE3nKzcTUgyAUoQeYRwCVpgssg/4VAUPJNDP4dVmxW8f" + "eNmjlTyXmR9S08SXkqmCHe2mBUsZY9nqcDE6ZWILZKFWIfZD9W+j2ce0FMvcc9kz" + "psxaUQECgYEAxqwsb5aQy6HBF54tdkHbUQEJMelSLNW0G1GUrcLB7eqL7qo3dUA8" + "8PDQ/dTwmmJ7aE0SK2xkQDVKXNbV4OvUNgP6tbzLWEbvmuFAEg5X1jFH2VSdwQhl" + "RDwTQw3wPZ5udy64L6gmsdDch+I7l1v4ex66RWFW+4WIs1altsLiJa0CgYEAwCGW" + "2cjtZ3kIzWgxf7DdnoUTwBM1ATBUYvx7uqVq+dbc/p8cSeMSPz3LUluaVJ2EOjEV" + "QWhx0Ih5qeitzReHRU0OHgxEgjbpJwhseD9O5POSd+fE3TtDQArOxyw4CIJKk4Z2" + "QmqzaO/LboN3Tp+/N9zfVoNZKHcCNra/uKNTH7kCgYA3QFazSdpG51s96D2Yb8RA" + "iNs3yD2UPnJyToPctxcbxWjZHPmDYDQShcZ5cSjgppbPcO+mp+RRfwCJRS4B+VPx" + "GbY1qKWcjU3BcvdQjjCbXuUuabvdnSocieCJe2zelhr+hj2u80KfnQhXufD8rRUz" + "mF4RQXrhREe6KFS5uQUPmQKBgE4rXFyvSyfWLqajxb/WDdT4/9gd+GrLZwn+/7go" + "pSWRLcjKo4/MOxhP4/FWI6xZifrDDYrXG7dkT1u5tzzCXd7sQtom05jHDoU7ACbM" + "WyT7lJQEUCxSeEIOI6MVcpbDq+PpySOsleIT7gjApEHw7LOlwZhJSHUWNmhcYhSV" + "HrTBAoGADAvBqV7JItjm2+qkXXEdPVzOunqjQdnXZjMAJ75PhHnLCCfnTRu53hT3" + "JxDETLLa/r42PlqGZ6bqSW+C+ObgYOvvySqvX8CE9o208ZwCLjHYxuYLH/86Lppr" + "ggF9KQ0xWz7Km3GXv5+bwM5bcgt1A/s6sZCimXuj3Fle3RqOTF0=" + "-----END RSA PRIVATE KEY-----"; + +#define OPT_BUFFERED_SEND 1000 +#define OPT_SERIALIZE_OUT 1001 +#define OPT_DESERIALIZE_IN 1002 + +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"); + fprintf(stderr, "\n Options:\n\n"); + fprintf(stderr, " -a [protocol]\n"); + fprintf(stderr, " --alpn [protocol]\n"); + fprintf(stderr, " Sets a single application protocol supported by this server.\n"); + fprintf(stderr, " -c [version_string]\n"); + fprintf(stderr, " --ciphers [version_string]\n"); + fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); + fprintf(stderr, " --enter-fips-mode\n"); + fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); + fprintf(stderr, " --cert\n"); + fprintf(stderr, " Path to a PEM encoded certificate [chain]. Option can be repeated to load multiple certs.\n"); + fprintf(stderr, " --key\n"); + fprintf(stderr, " Path to a PEM encoded private key that matches cert. Option can be repeated to load multiple certs.\n"); + fprintf(stderr, " -m\n"); + fprintf(stderr, " --mutualAuth\n"); + fprintf(stderr, " Request a Client Certificate. Any RSA Certificate will be accepted.\n"); + fprintf(stderr, " -n\n"); + fprintf(stderr, " --negotiate\n"); + fprintf(stderr, " Only perform tls handshake and then shutdown the connection\n"); + fprintf(stderr, " --parallelize\n"); + fprintf(stderr, " Create a new Connection handler thread for each new connection. Useful for tests with lots of connections.\n"); + fprintf(stderr, " Warning: this option isn't compatible with TLS Resumption, since each thread gets its own Session cache.\n"); + fprintf(stderr, " --prefer-low-latency\n"); + fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); + fprintf(stderr, " --prefer-throughput\n"); + fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); + fprintf(stderr, " --enable-mfl\n"); + fprintf(stderr, " Accept client's TLS maximum fragment length extension request\n"); + fprintf(stderr, " --ocsp\n"); + fprintf(stderr, " Path to a DER formatted OCSP response for stapling\n"); + fprintf(stderr, " -s\n"); + fprintf(stderr, " --self-service-blinding\n"); + fprintf(stderr, " Don't introduce 10-30 second delays on TLS Handshake errors. \n"); + fprintf(stderr, " Warning: this should only be used for testing since skipping blinding may allow timing side channels.\n"); + fprintf(stderr, " -t,--ca-file [file path]\n"); + fprintf(stderr, " Location of trust store CA file (PEM format). If neither -t or -d are specified. System defaults will be used."); + fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); + fprintf(stderr, " -d,--ca-dir [directory path]\n"); + fprintf(stderr, " Directory containing hashed trusted certs. If neither -t or -d are specified. System defaults will be used."); + fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); + fprintf(stderr, " -i,--insecure\n"); + fprintf(stderr, " Turns off certification validation altogether.\n"); + fprintf(stderr, " --stk-file\n"); + fprintf(stderr, " Location of key file used for encryption and decryption of session ticket.\n"); + fprintf(stderr, " -T,--no-session-ticket\n"); + fprintf(stderr, " Disable session ticket for resumption.\n"); + fprintf(stderr, " -C,--corked-io\n"); + fprintf(stderr, " Turn on corked io\n"); + fprintf(stderr, " --non-blocking\n"); + fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); + fprintf(stderr, " -w --https-server\n"); + fprintf(stderr, " Run s2nd in a simple https server mode.\n"); + fprintf(stderr, " -b --https-bench \n"); + fprintf(stderr, " Send number of bytes in https server mode to test throughput.\n"); + fprintf(stderr, " -L --key-log \n"); + fprintf(stderr, " Enable NSS key logging into the provided path\n"); + fprintf(stderr, " -P --psk \n" + " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" + " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" + " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); + fprintf(stderr, " -E, --max-early-data \n"); + fprintf(stderr, " Sets maximum early data allowed in session tickets. \n"); + fprintf(stderr, " -N --npn \n"); + fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); + fprintf(stderr, " -h,--help\n"); + fprintf(stderr, " Display this message and quit.\n"); + fprintf(stderr, " --buffered-send \n"); + fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); + fprintf(stderr, " -X, --max-conns \n"); + fprintf(stderr, " Sets the max number of connections s2nd will accept before shutting down.\n"); + /* clang-format on */ + exit(1); +} + +int handle_connection(int fd, struct s2n_config *config, struct conn_settings settings) +{ + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + if (!conn) { + print_s2n_error("Error getting new s2n connection"); + S2N_ERROR_PRESERVE_ERRNO(); + } + + GUARD_EXIT(s2n_setup_server_connection(conn, fd, config, settings), "Error setting up connection"); + + if (!settings.deserialize_in && negotiate(conn, fd) != S2N_SUCCESS) { + if (settings.mutual_auth) { + if (!s2n_connection_client_cert_used(conn)) { + print_s2n_error("Error: Mutual Auth was required, but not negotiated"); + } + } + + /* Error is printed in negotiate */ + S2N_ERROR_PRESERVE_ERRNO(); + } + + GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); + + if (settings.https_server) { + https(conn, settings.https_bench); + } else if (!settings.only_negotiate) { + bool stop_echo = false; + echo(conn, fd, &stop_echo); + } + + if (settings.serialize_out) { + GUARD_RETURN(s2n_connection_serialize_out(conn, settings.serialize_out), "Error serializing connection"); + } else { + GUARD_RETURN(wait_for_shutdown(conn, fd), "Error closing connection"); + } + + GUARD_RETURN(s2n_connection_wipe(conn), "Error wiping connection"); + + GUARD_RETURN(s2n_connection_free(conn), "Error freeing connection"); + + return 0; +} + +int main(int argc, char *const *argv) +{ + struct addrinfo hints, *ai = NULL; + int r = 0, sockfd = 0; + + /* required args */ + const char *host = NULL; + const char *port = NULL; + + const char *ocsp_response_file_path = NULL; + const char *session_ticket_key_file_path = NULL; + const char *cipher_prefs = "default"; + const char *alpn = NULL; + const char *key_log_path = NULL; + + /* The certificates provided by the user. If there are none provided, we will use the hardcoded default cert. + * The associated private key for each cert will be at the same index in private_keys. If the user mixes up the + * order of --cert --key for a given cert/key pair, s2n will fail to load the cert and s2nd will exit. + */ + int num_user_certificates = 0; + int num_user_private_keys = 0; + const char *certificates[MAX_CERTIFICATES] = { 0 }; + const char *private_keys[MAX_CERTIFICATES] = { 0 }; + + struct conn_settings conn_settings = { 0 }; + int fips_mode = 0; + int parallelize = 0; + int non_blocking = 0; + long int bytes = 0; + conn_settings.session_ticket = 1; + conn_settings.session_cache = 1; + conn_settings.max_conns = -1; + conn_settings.psk_list_len = 0; + int max_early_data = 0; + uint32_t send_buffer_size = 0; + bool npn = false; + + struct option long_options[] = { + { "ciphers", required_argument, NULL, 'c' }, + { "enable-mfl", no_argument, NULL, 'e' }, + { "enter-fips-mode", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "key", required_argument, NULL, 'k' }, + { "prefer-low-latency", no_argument, NULL, 'l' }, + { "mutualAuth", no_argument, NULL, 'm' }, + { "negotiate", no_argument, NULL, 'n' }, + { "ocsp", required_argument, NULL, 'o' }, + { "parallelize", no_argument, ¶llelize, 1 }, + { "prefer-throughput", no_argument, NULL, 'p' }, + { "cert", required_argument, NULL, 'r' }, + { "self-service-blinding", no_argument, NULL, 's' }, + { "ca-dir", required_argument, 0, 'd' }, + { "ca-file", required_argument, 0, 't' }, + { "insecure", no_argument, 0, 'i' }, + { "stk-file", required_argument, 0, 'a' }, + { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, + { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, + { "no-session-ticket", no_argument, 0, 'T' }, + { "corked-io", no_argument, 0, 'C' }, + { "max-conns", optional_argument, 0, 'X' }, + { "tls13", no_argument, 0, '3' }, + { "https-server", no_argument, 0, 'w' }, + { "https-bench", required_argument, 0, 'b' }, + { "alpn", required_argument, 0, 'A' }, + { "npn", no_argument, 0, 'N' }, + { "non-blocking", no_argument, 0, 'B' }, + { "key-log", required_argument, 0, 'L' }, + { "psk", required_argument, 0, 'P' }, + { "max-early-data", required_argument, 0, 'E' }, + { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, + /* Per getopt(3) the last element of the array has to be filled with all zeros */ + { 0 }, + }; + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "c:hmnst:d:iTCX::wb:A:P:E:", long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 0: + /* getopt_long() returns 0 if an option.flag is non-null (Eg "parallelize") */ + break; + case 'C': + conn_settings.use_corked_io = 1; + break; + case 'c': + cipher_prefs = optarg; + break; + case 'e': + conn_settings.enable_mfl = 1; + break; + case 'f': + fips_mode = 1; + break; + case 'h': + usage(); + break; + case 'k': + if (num_user_private_keys == MAX_CERTIFICATES) { + fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); + exit(1); + } + private_keys[num_user_private_keys] = load_file_to_cstring(optarg); + num_user_private_keys++; + break; + case 'l': + conn_settings.prefer_low_latency = 1; + break; + case 'm': + conn_settings.mutual_auth = 1; + break; + case 'n': + conn_settings.only_negotiate = 1; + break; + case 'o': + ocsp_response_file_path = optarg; + break; + case 'p': + conn_settings.prefer_throughput = 1; + break; + case 'r': + if (num_user_certificates == MAX_CERTIFICATES) { + fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); + exit(1); + } + certificates[num_user_certificates] = load_file_to_cstring(optarg); + num_user_certificates++; + break; + case 's': + conn_settings.self_service_blinding = 1; + break; + case 'd': + conn_settings.ca_dir = optarg; + break; + case 't': + conn_settings.ca_file = optarg; + break; + case 'i': + conn_settings.insecure = 1; + break; + case 'a': + session_ticket_key_file_path = optarg; + break; + case 'T': + conn_settings.session_ticket = 0; + break; + case '3': + /* Do nothing -- this argument is deprecated */ + break; + case 'X': + if (optarg == NULL) { + conn_settings.max_conns = 1; + } else { + conn_settings.max_conns = atoi(optarg); + } + break; + case 'w': + fprintf(stdout, "Running s2nd in simple https server mode\n"); + conn_settings.https_server = 1; + break; + case 'b': + bytes = strtoul(optarg, NULL, 10); + GUARD_EXIT(bytes, "https-bench bytes needs to be some positive long value."); + conn_settings.https_bench = bytes; + break; + case OPT_BUFFERED_SEND: { + intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); + if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { + fprintf(stderr, " must be a positive 32 bit value\n"); + exit(1); + } + send_buffer_size = (uint32_t) send_buffer_size_scanned_value; + break; + } + /* The serialize_out and deserialize_in options are not documented + * in the usage section as they are not intended to work correctly + * using s2nd by itself. s2nc and s2nd are processes which close + * their TCP connection upon exit. This will cause an error if one + * peer serializes and exits and the other doesn't, as serialization + * depends on a continuous TCP connection with the peer. Therefore, our + * only usage of this feature is in our integ test framework, + * which serializes and deserializes both client and server at the + * same time. Do not expect these options to work when using s2nd alone. + */ + case OPT_SERIALIZE_OUT: + conn_settings.serialize_out = optarg; + break; + case OPT_DESERIALIZE_IN: + conn_settings.deserialize_in = optarg; + break; + case 'A': + alpn = optarg; + break; + case 'B': + non_blocking = 1; + break; + case 'L': + key_log_path = optarg; + break; + case 'P': + if (conn_settings.psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { + fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); + exit(1); + } + conn_settings.psk_optarg_list[conn_settings.psk_list_len++] = optarg; + break; + case 'E': + max_early_data = atoi(optarg); + break; + case 'N': + npn = true; + break; + case '?': + default: + fprintf(stdout, "getopt_long returned: %d", c); + usage(); + break; + } + } + + if (conn_settings.prefer_throughput && conn_settings.prefer_low_latency) { + fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); + exit(1); + } + + if (optind < argc) { + host = argv[optind++]; + } + + /* cppcheck-suppress duplicateCondition */ + if (optind < argc) { + port = argv[optind++]; + } + + if (!host || !port) { + usage(); + } + + if (setvbuf(stdin, NULL, _IONBF, 0) < 0) { + fprintf(stderr, "Error disabling buffering for stdin\n"); + exit(1); + } + + if (setvbuf(stdout, NULL, _IONBF, 0) < 0) { + fprintf(stderr, "Error disabling buffering for stdout\n"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + + 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); + } + + if ((r = getaddrinfo(host, port, &hints, &ai)) < 0) { + fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(r)); + exit(1); + } + + if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { + fprintf(stderr, "socket error: %s\n", strerror(errno)); + exit(1); + } + + r = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int)) < 0) { + fprintf(stderr, "setsockopt error: %s\n", strerror(errno)); + exit(1); + } + + if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { + fprintf(stderr, "bind error: %s\n", strerror(errno)); + exit(1); + } + + if (listen(sockfd, 1) == -1) { + fprintf(stderr, "listen error: %s\n", strerror(errno)); + exit(1); + } + + GUARD_EXIT(s2n_init(), "Error running s2n_init()"); + printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); + + if (fips_mode) { + s2n_fips_mode mode = 0; + GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); + if (mode != S2N_FIPS_MODE_ENABLED) { + fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); + exit(1); + } + printf("s2nd entered FIPS mode\n"); + } + + printf("Listening on %s:%s\n", host, port); + + struct s2n_config *config = s2n_config_new(); + if (!config) { + print_s2n_error("Error getting new s2n config"); + exit(1); + } + + if (num_user_certificates != num_user_private_keys) { + fprintf(stderr, "Mismatched certificate(%d) and private key(%d) count!\n", num_user_certificates, num_user_private_keys); + exit(1); + } + + int num_certificates = 0; + if (num_user_certificates == 0) { + certificates[0] = default_certificate_chain; + private_keys[0] = default_private_key; + num_certificates = 1; + } else { + num_certificates = num_user_certificates; + } + + for (int i = 0; i < num_certificates; i++) { + struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); + GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, certificates[i], private_keys[i]), "Error getting certificate/key"); + + if (ocsp_response_file_path) { + int fd = open(ocsp_response_file_path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error opening OCSP response file: '%s'\n", strerror(errno)); + exit(1); + } + + struct stat st = { 0 }; + if (fstat(fd, &st) < 0) { + fprintf(stderr, "Error fstat-ing OCSP response file: '%s'\n", strerror(errno)); + exit(1); + } + + uint8_t *ocsp_response = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_response, st.st_size) < 0) { + fprintf(stderr, "Error adding ocsp response: '%s'\n", s2n_strerror(s2n_errno, "EN")); + exit(1); + } + + close(fd); + } + + GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); + } + + s2n_set_common_server_config(max_early_data, config, conn_settings, cipher_prefs, session_ticket_key_file_path); + + if (parallelize) { + struct sigaction sa; + + sa.sa_handler = SIG_IGN; +#if defined(SA_NOCLDWAIT) + sa.sa_flags = SA_NOCLDWAIT; +#endif + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, NULL); + } + + if (alpn) { + const char *protocols[] = { alpn }; + GUARD_EXIT(s2n_config_set_protocol_preferences(config, protocols, s2n_array_len(protocols)), "Failed to set alpn"); + } + + if (send_buffer_size != 0) { + GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size."); + } + + if (npn) { + GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); + } + + if (conn_settings.serialize_out) { + GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), + "Error setting serialized version"); + } + + FILE *key_log_file = NULL; + + if (key_log_path) { + key_log_file = fopen(key_log_path, "a"); + GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); + GUARD_EXIT( + s2n_config_set_key_log_cb( + config, + key_log_callback, + (void *) key_log_file), + "Failed to set key log callback"); + } + + int fd = 0; + while ((fd = accept(sockfd, ai->ai_addr, &ai->ai_addrlen)) > 0) { + if (non_blocking) { + int flags = fcntl(sockfd, F_GETFL, 0); + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + fprintf(stderr, "fcntl error: %s\n", strerror(errno)); + exit(1); + } + } + + if (!parallelize) { + int rc = handle_connection(fd, config, conn_settings); + close(fd); + if (rc < 0) { + exit(rc); + } + + /* If max_conns was set, then exit after it is reached. Otherwise + * unlimited connections are allow, so ignore the variable. */ + if (conn_settings.max_conns > 0) { + if (conn_settings.max_conns-- == 1) { + GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); + exit(0); + } + } + } else { + /* Fork Process, one for the Acceptor (parent), and another for the Handler (child). */ + pid_t child_pid = fork(); + + if (child_pid == 0) { + /* This is the Child Handler Thread. We should handle the connection, then exit. */ + int rc = handle_connection(fd, config, conn_settings); + close(fd); + _exit(rc); + } else if (child_pid == -1) { + close(fd); + print_s2n_error("Error calling fork(). Acceptor unable to start handler."); + exit(1); + } else { + /* This is the parent Acceptor Thread, continue listening for new connections */ + close(fd); + continue; + } + } + } + + if (key_log_file) { + fclose(key_log_file); + } + + GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); + + return 0; +} diff --git a/cmake/modules/Findcrypto.cmake b/cmake/modules/Findcrypto.cmake index 1ac4d9a619b..492084c14d8 100644 --- a/cmake/modules/Findcrypto.cmake +++ b/cmake/modules/Findcrypto.cmake @@ -1,113 +1,129 @@ -# - Try to find LibCrypto include dirs and libraries -# -# Usage of this module as follows: -# -# find_package(crypto) -# -# Variables used by this module, they can change the default behaviour and need -# to be set before calling find_package: -# -# Variables defined by this module: -# -# crypto_FOUND System has libcrypto, include and library dirs found -# crypto_INCLUDE_DIR The crypto include directories. -# crypto_LIBRARY The crypto library, depending on the value of BUILD_SHARED_LIBS. -# crypto_SHARED_LIBRARY The path to libcrypto.so -# crypto_STATIC_LIBRARY The path to libcrypto.a - -# the next branch exists purely for cmake compatibility with versions older than 3.15. Please do not remove it before -# we baseline on a newer version. It does not like duplicate target declarations. Work around that by checking it isn't -# defined first. -if (TARGET crypto OR TARGET AWS::crypto) - if (TARGET crypto) - set(TARGET_NAME "crypto") - else() - set(TARGET_NAME "AWS::crypto") - endif() - - get_target_property(crypto_INCLUDE_DIR ${TARGET_NAME} INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "S2N found target: ${TARGET_NAME}") - message(STATUS "crypto Include Dir: ${crypto_INCLUDE_DIR}") - set(CRYPTO_FOUND true) - set(crypto_FOUND true) -else() - find_path(crypto_INCLUDE_DIR - NAMES openssl/crypto.h - HINTS - "${CMAKE_PREFIX_PATH}" - "${CMAKE_INSTALL_PREFIX}" - PATH_SUFFIXES include - ) - - find_library(crypto_SHARED_LIBRARY - NAMES libcrypto.so libcrypto.dylib - HINTS - "${CMAKE_PREFIX_PATH}" - "${CMAKE_INSTALL_PREFIX}" - PATH_SUFFIXES build/crypto build lib64 lib - ) - - find_library(crypto_STATIC_LIBRARY - NAMES libcrypto.a - HINTS - "${CMAKE_PREFIX_PATH}" - "${CMAKE_INSTALL_PREFIX}" - PATH_SUFFIXES build/crypto build lib64 lib - ) - - if (NOT crypto_LIBRARY) - if (BUILD_SHARED_LIBS OR S2N_USE_CRYPTO_SHARED_LIBS) - if (crypto_SHARED_LIBRARY) - set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) - else() - set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) - endif() - else() - if (crypto_STATIC_LIBRARY) - set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) - else() - set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) - endif() - endif() - endif() - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(crypto DEFAULT_MSG - crypto_LIBRARY - crypto_INCLUDE_DIR - ) - - mark_as_advanced( - crypto_ROOT_DIR - crypto_INCLUDE_DIR - crypto_LIBRARY - crypto_SHARED_LIBRARY - crypto_STATIC_LIBRARY - ) - - # some versions of cmake have a super esoteric bug around capitalization differences between - # find dependency and find package, just avoid that here by checking and - # setting both. - if(CRYPTO_FOUND OR crypto_FOUND) - set(CRYPTO_FOUND true) - set(crypto_FOUND true) - - message(STATUS "LibCrypto Include Dir: ${crypto_INCLUDE_DIR}") - message(STATUS "LibCrypto Shared Lib: ${crypto_SHARED_LIBRARY}") - message(STATUS "LibCrypto Static Lib: ${crypto_STATIC_LIBRARY}") - if (NOT TARGET crypto AND - (EXISTS "${crypto_LIBRARY}") - ) - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) - add_library(AWS::crypto UNKNOWN IMPORTED) - set_target_properties(AWS::crypto PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${crypto_INCLUDE_DIR}") - set_target_properties(AWS::crypto PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${crypto_LIBRARY}") - add_dependencies(AWS::crypto Threads::Threads) - endif() - endif() - -endif() +# - Try to find LibCrypto include dirs and libraries +# +# Usage of this module as follows: +# +# find_package(crypto) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# Variables defined by this module: +# +# crypto_FOUND System has libcrypto, include and library dirs found +# crypto_INCLUDE_DIR The crypto include directories. +# crypto_LIBRARY The crypto library, depending on the value of BUILD_SHARED_LIBS. +# crypto_SHARED_LIBRARY The path to libcrypto.so +# crypto_STATIC_LIBRARY The path to libcrypto.a + +if (TARGET crypto OR TARGET AWS::crypto) + if (TARGET crypto) + set(TARGET_NAME "crypto") + else() + set(TARGET_NAME "AWS::crypto") + endif() + + get_target_property(crypto_INCLUDE_DIR ${TARGET_NAME} INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "S2N found target: ${TARGET_NAME}") + message(STATUS "crypto Include Dir: ${crypto_INCLUDE_DIR}") + set(CRYPTO_FOUND true) + set(crypto_FOUND true) +else() + find_package(OpenSSL MODULE REQUIRED) + if(OPENSSL_FOUND) + if (TARGET OpenSSL::Crypto) + set(crypto_LIBRARY OpenSSL::Crypto) + get_target_property(crypto_INCLUDE_DIR OpenSSL::Crypto INTERFACE_INCLUDE_DIRECTORIES) + # If property wasn't set, fallback to the variable + if(NOT crypto_INCLUDE_DIR) + set(crypto_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) + endif() + else() + set(crypto_LIBRARY ${OPENSSL_CRYPTO_LIBRARY}) + set(crypto_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) + endif() + set(crypto_FOUND true) + set(CRYPTO_FOUND true) + endif() + + if(NOT crypto_FOUND) + find_path(crypto_INCLUDE_DIR + NAMES openssl/crypto.h + HINTS + "${CMAKE_PREFIX_PATH}" + "${CMAKE_INSTALL_PREFIX}" + PATH_SUFFIXES include + ) + + find_library(crypto_SHARED_LIBRARY + NAMES libcrypto.so libcrypto.dylib libcrypto.dll.a crypto.lib libcrypto.lib + HINTS + "${CMAKE_PREFIX_PATH}" + "${CMAKE_INSTALL_PREFIX}" + PATH_SUFFIXES build/crypto build lib64 lib + ) + + find_library(crypto_STATIC_LIBRARY + NAMES libcrypto.a libcrypto.lib crypto.lib + HINTS + "${CMAKE_PREFIX_PATH}" + "${CMAKE_INSTALL_PREFIX}" + PATH_SUFFIXES build/crypto build lib64 lib + ) + + if (NOT crypto_LIBRARY) + if (BUILD_SHARED_LIBS OR S2N_USE_CRYPTO_SHARED_LIBS) + if (crypto_SHARED_LIBRARY) + set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) + else() + set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) + endif() + else() + if (crypto_STATIC_LIBRARY) + set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) + else() + set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) + endif() + endif() + endif() + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(crypto DEFAULT_MSG + crypto_LIBRARY + crypto_INCLUDE_DIR + ) + + mark_as_advanced( + crypto_ROOT_DIR + crypto_INCLUDE_DIR + crypto_LIBRARY + crypto_SHARED_LIBRARY + crypto_STATIC_LIBRARY + ) + + if(CRYPTO_FOUND OR crypto_FOUND) + set(CRYPTO_FOUND true) + set(crypto_FOUND true) + + message(STATUS "LibCrypto Include Dir: ${crypto_INCLUDE_DIR}") + message(STATUS "LibCrypto Shared Lib: ${crypto_SHARED_LIBRARY}") + message(STATUS "LibCrypto Static Lib: ${crypto_STATIC_LIBRARY}") + + if (NOT TARGET crypto AND NOT TARGET AWS::crypto) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + if (TARGET OpenSSL::Crypto) + add_library(AWS::crypto ALIAS OpenSSL::Crypto) + elseif(EXISTS "${crypto_LIBRARY}") + add_library(AWS::crypto UNKNOWN IMPORTED) + set_target_properties(AWS::crypto PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${crypto_INCLUDE_DIR}") + set_target_properties(AWS::crypto PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${crypto_LIBRARY}") + add_dependencies(AWS::crypto Threads::Threads) + endif() + endif() + endif() + +endif() diff --git a/crypto/s2n_certificate.c b/crypto/s2n_certificate.c index b10375f0b5c..4679c6e7fbf 100644 --- a/crypto/s2n_certificate.c +++ b/crypto/s2n_certificate.c @@ -1,875 +1,877 @@ -/* - * 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. - */ - -#ifndef _GNU_SOURCE - #define _GNU_SOURCE -#endif - -#include "crypto/s2n_certificate.h" - -#include -#include -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_openssl_x509.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_connection.h" -#include "utils/s2n_array.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -int s2n_cert_set_cert_type(struct s2n_cert *cert, s2n_pkey_type pkey_type) -{ - POSIX_ENSURE_REF(cert); - cert->pkey_type = pkey_type; - POSIX_GUARD_RESULT(s2n_pkey_setup_for_type(&cert->public_key, pkey_type)); - return 0; -} - -int s2n_create_cert_chain_from_stuffer(struct s2n_cert_chain *cert_chain_out, struct s2n_stuffer *chain_in_stuffer) -{ - DEFER_CLEANUP(struct s2n_stuffer cert_out_stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); - - struct s2n_cert **insert = &cert_chain_out->head; - uint32_t chain_size = 0; - while (s2n_stuffer_has_pem_encapsulated_block(chain_in_stuffer)) { - int result = s2n_stuffer_certificate_from_pem(chain_in_stuffer, &cert_out_stuffer); - POSIX_ENSURE(result == S2N_SUCCESS, S2N_ERR_INVALID_PEM); - - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); - POSIX_GUARD(s2n_blob_zero(&mem)); - - struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; - POSIX_GUARD(s2n_alloc(&new_node->raw, s2n_stuffer_data_available(&cert_out_stuffer))); - POSIX_GUARD(s2n_stuffer_read(&cert_out_stuffer, &new_node->raw)); - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - /* Additional 3 bytes for the length field in the protocol */ - chain_size += new_node->raw.size + 3; - new_node->next = NULL; - *insert = new_node; - insert = &new_node->next; - }; - - POSIX_ENSURE(chain_size > 0, S2N_ERR_NO_CERTIFICATE_IN_PEM); - cert_chain_out->chain_size = chain_size; - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_cert_chain_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, struct s2n_stuffer *chain_in_stuffer) -{ - return s2n_create_cert_chain_from_stuffer(cert_and_key->cert_chain, chain_in_stuffer); -} - -int s2n_cert_chain_and_key_set_cert_chain_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *cert_chain_pem, uint32_t cert_chain_len) -{ - DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); - - POSIX_GUARD(s2n_stuffer_init_ro_from_string(&chain_in_stuffer, cert_chain_pem, cert_chain_len)); - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_cert_chain(struct s2n_cert_chain_and_key *cert_and_key, const char *cert_chain_pem) -{ - DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); - - /* Turn the chain into a stuffer */ - POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_private_key_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, - struct s2n_stuffer *key_in_stuffer, struct s2n_stuffer *key_out_stuffer) -{ - struct s2n_blob key_blob = { 0 }; - - POSIX_GUARD(s2n_pkey_zero_init(cert_and_key->private_key)); - - /* Convert pem to asn1 and asn1 to the private key. Handles both PKCS#1 and PKCS#8 formats */ - int type_hint = 0; - POSIX_GUARD(s2n_stuffer_private_key_from_pem(key_in_stuffer, key_out_stuffer, &type_hint)); - key_blob.size = s2n_stuffer_data_available(key_out_stuffer); - key_blob.data = s2n_stuffer_raw_read(key_out_stuffer, key_blob.size); - POSIX_ENSURE_REF(key_blob.data); - - POSIX_GUARD_RESULT(s2n_asn1der_to_private_key(cert_and_key->private_key, &key_blob, type_hint)); - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_private_key_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *private_key_pem, uint32_t private_key_len) -{ - DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); - - /* Put the private key pem in a stuffer */ - POSIX_GUARD(s2n_stuffer_init_ro_from_string(&key_in_stuffer, private_key_pem, private_key_len)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, private_key_len)); - - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_private_key(struct s2n_cert_chain_and_key *cert_and_key, const char *private_key_pem) -{ - POSIX_ENSURE_REF(private_key_pem); - - DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); - - /* Put the private key pem in a stuffer */ - POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); - - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_free(&chain_and_key->ocsp_status)); - if (data && length) { - POSIX_GUARD(s2n_alloc(&chain_and_key->ocsp_status, length)); - POSIX_CHECKED_MEMCPY(chain_and_key->ocsp_status.data, data, length); - } - return 0; -} - -int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_free(&chain_and_key->sct_list)); - if (data && length) { - POSIX_GUARD(s2n_alloc(&chain_and_key->sct_list, length)); - POSIX_CHECKED_MEMCPY(chain_and_key->sct_list.data, data, length); - } - return 0; -} - -struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) -{ - DEFER_CLEANUP(struct s2n_blob chain_and_key_mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); - PTR_GUARD_POSIX(s2n_blob_zero(&chain_and_key_mem)); - - DEFER_CLEANUP(struct s2n_blob cert_chain_mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain))); - PTR_GUARD_POSIX(s2n_blob_zero(&cert_chain_mem)); - - DEFER_CLEANUP(struct s2n_blob pkey_mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key))); - PTR_GUARD_POSIX(s2n_blob_zero(&pkey_mem)); - - DEFER_CLEANUP(struct s2n_array *cn_names = NULL, s2n_array_free_p); - cn_names = s2n_array_new(sizeof(struct s2n_blob)); - PTR_ENSURE_REF(cn_names); - - DEFER_CLEANUP(struct s2n_array *san_names = NULL, s2n_array_free_p); - san_names = s2n_array_new(sizeof(struct s2n_blob)); - PTR_ENSURE_REF(san_names); - - struct s2n_cert_chain_and_key *chain_and_key = (struct s2n_cert_chain_and_key *) (void *) chain_and_key_mem.data; - chain_and_key->cert_chain = (struct s2n_cert_chain *) (void *) cert_chain_mem.data; - chain_and_key->private_key = (s2n_cert_private_key *) (void *) pkey_mem.data; - chain_and_key->cn_names = cn_names; - chain_and_key->san_names = san_names; - - ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key_mem); - ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain_mem); - ZERO_TO_DISABLE_DEFER_CLEANUP(pkey_mem); - ZERO_TO_DISABLE_DEFER_CLEANUP(cn_names); - ZERO_TO_DISABLE_DEFER_CLEANUP(san_names); - return chain_and_key; -} - -DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); - -int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) -{ - POSIX_ENSURE_REF(chain_and_key->san_names); - POSIX_ENSURE_REF(x509_cert); - - DEFER_CLEANUP(GENERAL_NAMES *san_names = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL), GENERAL_NAMES_free_pointer); - if (san_names == NULL) { - /* No SAN extension */ - return 0; - } - - const int num_san_names = sk_GENERAL_NAME_num(san_names); - for (int i = 0; i < num_san_names; i++) { - GENERAL_NAME *san_name = sk_GENERAL_NAME_value(san_names, i); - if (!san_name) { - continue; - } - - if (san_name->type == GEN_DNS) { - /* Decoding isn't necessary here since a DNS SAN name is ASCII(type V_ASN1_IA5STRING) */ - unsigned char *san_str = san_name->d.dNSName->data; - const size_t san_str_len = san_name->d.dNSName->length; - struct s2n_blob *san_blob = NULL; - POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->san_names, (void **) &san_blob)); - if (!san_blob) { - POSIX_BAIL(S2N_ERR_NULL_SANS); - } - - if (s2n_alloc(san_blob, san_str_len)) { - S2N_ERROR_PRESERVE_ERRNO(); - } - - POSIX_CHECKED_MEMCPY(san_blob->data, san_str, san_str_len); - san_blob->size = san_str_len; - /* normalize san_blob to lowercase */ - POSIX_GUARD(s2n_blob_char_to_lower(san_blob)); - } - } - - return 0; -} - -/* Parse CN names from the Subject of the leaf certificate. Technically there can by multiple CNs - * in the Subject but practically very few certificates in the wild will have more than one CN. - * Since the data for this certificate is coming from the application and not from an untrusted - * source, we will try our best to parse all of the CNs. - * - * A recent CAB thread proposed removing support for multiple CNs: - * https://cabforum.org/pipermail/public/2016-April/007242.html - */ - -DEFINE_POINTER_CLEANUP_FUNC(unsigned char *, OPENSSL_free); - -int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) -{ - POSIX_ENSURE_REF(chain_and_key->cn_names); - POSIX_ENSURE_REF(x509_cert); - - X509_NAME *subject = X509_get_subject_name(x509_cert); - if (!subject) { - return 0; - } - - int lastpos = -1; - while ((lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, lastpos)) >= 0) { - X509_NAME_ENTRY *name_entry = X509_NAME_get_entry(subject, lastpos); - if (!name_entry) { - continue; - } - - ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(name_entry); - if (!asn1_str) { - continue; - } - - /* We need to try and decode the CN since it may be encoded as unicode with a - * direct ASCII equivalent. Any non ASCII bytes in the string will fail later when we - * actually compare hostnames. - * - * `ASN1_STRING_to_UTF8` allocates in both the success case and in the zero return case, but - * not in the failure case (negative return value). Therefore, we use `ZERO_TO_DISABLE_DEFER_CLEANUP` - * in the failure case to prevent double-freeing `utf8_str`. For the zero and success cases, `utf8_str` - * will be freed by the `DEFER_CLEANUP`. - */ - DEFER_CLEANUP(unsigned char *utf8_str, OPENSSL_free_pointer); - const int utf8_out_len = ASN1_STRING_to_UTF8(&utf8_str, asn1_str); - if (utf8_out_len < 0) { - /* On failure, ASN1_STRING_to_UTF8 does not allocate any memory */ - ZERO_TO_DISABLE_DEFER_CLEANUP(utf8_str); - continue; - } else if (utf8_out_len == 0) { - /* We still need to free memory for this case, so let the DEFER_CLEANUP free it - * see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 and - * https://security.archlinux.org/CVE-2017-7521 - */ - } else { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->cn_names, (void **) &cn_name)); - if (cn_name == NULL) { - POSIX_BAIL(S2N_ERR_NULL_CN_NAME); - } - - if (s2n_alloc(cn_name, utf8_out_len) < 0) { - S2N_ERROR_PRESERVE_ERRNO(); - } - POSIX_CHECKED_MEMCPY(cn_name->data, utf8_str, utf8_out_len); - cn_name->size = utf8_out_len; - /* normalize cn_name to lowercase */ - POSIX_GUARD(s2n_blob_char_to_lower(cn_name)); - } - } - - return 0; -} - -static int s2n_cert_chain_and_key_set_names(struct s2n_cert_chain_and_key *chain_and_key, X509 *cert) -{ - POSIX_GUARD(s2n_cert_chain_and_key_load_sans(chain_and_key, cert)); - /* For current use cases, we *could* avoid populating the common names if any sans were loaded in - * s2n_cert_chain_and_key_load_sans. Let's unconditionally populate this field to avoid surprises - * in the future. - */ - POSIX_GUARD(s2n_cert_chain_and_key_load_cns(chain_and_key, cert)); - return 0; -} - -int s2n_cert_chain_and_key_load(struct s2n_cert_chain_and_key *chain_and_key) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(chain_and_key->cert_chain); - POSIX_ENSURE_REF(chain_and_key->cert_chain->head); - POSIX_ENSURE_REF(chain_and_key->private_key); - struct s2n_cert *head = chain_and_key->cert_chain->head; - - DEFER_CLEANUP(X509 *leaf_cert = NULL, X509_free_pointer); - POSIX_GUARD_RESULT(s2n_openssl_x509_parse(&head->raw, &leaf_cert)); - POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(leaf_cert, &head->info)); - - /* Parse the leaf cert for the public key and certificate type */ - DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - POSIX_GUARD_RESULT(s2n_pkey_from_x509(leaf_cert, &public_key, &pkey_type)); - - POSIX_ENSURE(pkey_type != S2N_PKEY_TYPE_UNKNOWN, S2N_ERR_CERT_TYPE_UNSUPPORTED); - POSIX_GUARD(s2n_cert_set_cert_type(head, pkey_type)); - - /* Validate the leaf cert's public key matches the provided private key */ - if (s2n_pkey_check_key_exists(chain_and_key->private_key) == S2N_SUCCESS) { - POSIX_GUARD(s2n_pkey_match(&public_key, chain_and_key->private_key)); - } - - /* Populate name information from the SAN/CN for the leaf certificate */ - POSIX_GUARD(s2n_cert_chain_and_key_set_names(chain_and_key, leaf_cert)); - - /* populate libcrypto nid's required for cert restrictions */ - struct s2n_cert *current = head->next; - while (current != NULL) { - DEFER_CLEANUP(X509 *parsed_cert = NULL, X509_free_pointer); - POSIX_GUARD_RESULT(s2n_openssl_x509_parse(¤t->raw, &parsed_cert)); - POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(parsed_cert, ¤t->info)); - - current = current->next; - } - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem) -{ - POSIX_ENSURE_REF(chain_and_key); - - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, chain_pem)); - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_load_public_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len) -{ - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); - POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_load_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, - uint32_t chain_pem_len, uint8_t *private_key_pem, uint32_t private_key_pem_len) -{ - POSIX_ENSURE_REF(chain_and_key); - - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_bytes(chain_and_key, private_key_pem, private_key_pem_len)); - - POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); - - return S2N_SUCCESS; -} - -S2N_CLEANUP_RESULT s2n_cert_chain_and_key_ptr_free(struct s2n_cert_chain_and_key **cert_and_key) -{ - RESULT_ENSURE_REF(cert_and_key); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(*cert_and_key)); - *cert_and_key = NULL; - return S2N_RESULT_OK; -} - -int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key) -{ - if (cert_and_key == NULL) { - return 0; - } - - /* Walk the chain and free the certs */ - if (cert_and_key->cert_chain) { - struct s2n_cert *node = cert_and_key->cert_chain->head; - while (node) { - /* Free the cert */ - POSIX_GUARD(s2n_free(&node->raw)); - /* update head so it won't point to freed memory */ - cert_and_key->cert_chain->head = node->next; - /* Free the node */ - POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); - node = cert_and_key->cert_chain->head; - } - - POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->cert_chain, sizeof(struct s2n_cert_chain))); - } - - if (cert_and_key->private_key) { - POSIX_GUARD(s2n_pkey_free(cert_and_key->private_key)); - POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->private_key, sizeof(s2n_cert_private_key))); - } - - uint32_t len = 0; - - if (cert_and_key->san_names) { - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->san_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *san_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->san_names, i, (void **) &san_name)); - POSIX_GUARD(s2n_free(san_name)); - } - POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->san_names)); - cert_and_key->san_names = NULL; - } - - if (cert_and_key->cn_names) { - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->cn_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->cn_names, i, (void **) &cn_name)); - POSIX_GUARD(s2n_free(cn_name)); - } - POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->cn_names)); - cert_and_key->cn_names = NULL; - } - - POSIX_GUARD(s2n_free(&cert_and_key->ocsp_status)); - POSIX_GUARD(s2n_free(&cert_and_key->sct_list)); - - POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key, sizeof(struct s2n_cert_chain_and_key))); - return 0; -} - -int s2n_cert_chain_free(struct s2n_cert_chain *cert_chain) -{ - /* Walk the chain and free the certs/nodes allocated prior to failure */ - if (cert_chain) { - struct s2n_cert *node = cert_chain->head; - while (node) { - /* Free the cert */ - POSIX_GUARD(s2n_free(&node->raw)); - /* update head so it won't point to freed memory */ - cert_chain->head = node->next; - /* Free the node */ - POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); - node = cert_chain->head; - } - } - - return S2N_SUCCESS; -} - -int s2n_send_cert_chain(struct s2n_connection *conn, struct s2n_stuffer *out, struct s2n_cert_chain_and_key *chain_and_key) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(chain_and_key); - struct s2n_cert_chain *chain = chain_and_key->cert_chain; - POSIX_ENSURE_REF(chain); - struct s2n_cert *cur_cert = chain->head; - POSIX_ENSURE_REF(cur_cert); - - struct s2n_stuffer_reservation cert_chain_size = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint24(out, &cert_chain_size)); - - /* Send certs and extensions (in TLS 1.3) */ - bool first_entry = true; - while (cur_cert) { - POSIX_ENSURE_REF(cur_cert); - POSIX_GUARD(s2n_stuffer_write_uint24(out, cur_cert->raw.size)); - POSIX_GUARD(s2n_stuffer_write_bytes(out, cur_cert->raw.data, cur_cert->raw.size)); - - /* According to https://tools.ietf.org/html/rfc8446#section-4.4.2, - * If an extension applies to the entire chain, it SHOULD be included in - * the first CertificateEntry. - * While the spec allow extensions to be included in other certificate - * entries, only the first matter to use here */ - if (conn->actual_protocol_version >= S2N_TLS13) { - if (first_entry) { - POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, out)); - first_entry = false; - } else { - POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, out)); - } - } - cur_cert = cur_cert->next; - } - - POSIX_GUARD(s2n_stuffer_write_vector_size(&cert_chain_size)); - - return 0; -} - -int s2n_send_empty_cert_chain(struct s2n_stuffer *out) -{ - POSIX_ENSURE_REF(out); - POSIX_GUARD(s2n_stuffer_write_uint24(out, 0)); - return 0; -} - -static int s2n_does_cert_san_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(dns_name); - - struct s2n_array *san_names = chain_and_key->san_names; - uint32_t len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(san_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *san_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(san_names, i, (void **) &san_name)); - POSIX_ENSURE_REF(san_name); - if ((dns_name->size == san_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) san_name->data, dns_name->size) == 0)) { - return 1; - } - } - - return 0; -} - -static int s2n_does_cert_cn_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(dns_name); - - struct s2n_array *cn_names = chain_and_key->cn_names; - uint32_t len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(cn_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cn_names, i, (void **) &cn_name)); - POSIX_ENSURE_REF(cn_name); - if ((dns_name->size == cn_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) cn_name->data, dns_name->size) == 0)) { - return 1; - } - } - - return 0; -} - -int s2n_cert_chain_and_key_matches_dns_name(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - uint32_t len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(chain_and_key->san_names, &len)); - if (len > 0) { - if (s2n_does_cert_san_match_hostname(chain_and_key, dns_name)) { - return 1; - } - } else { - /* Per https://tools.ietf.org/html/rfc6125#section-6.4.4 we only will - * consider the CN for matching if no valid DNS entries are provided - * in a SAN. - */ - if (s2n_does_cert_cn_match_hostname(chain_and_key, dns_name)) { - return 1; - } - } - - return 0; -} - -int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx) -{ - cert_and_key->context = ctx; - return 0; -} - -void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key) -{ - return cert_and_key->context; -} - -s2n_pkey_type s2n_cert_chain_and_key_get_pkey_type(struct s2n_cert_chain_and_key *chain_and_key) -{ - if (chain_and_key == NULL - || chain_and_key->cert_chain == NULL - || chain_and_key->cert_chain->head == NULL) { - return S2N_PKEY_TYPE_UNKNOWN; - } - return chain_and_key->cert_chain->head->pkey_type; -} - -s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *chain_and_key) -{ - PTR_ENSURE_REF(chain_and_key); - return chain_and_key->private_key; -} - -int s2n_cert_chain_get_length(const struct s2n_cert_chain_and_key *chain_and_key, uint32_t *cert_length) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(cert_length); - - struct s2n_cert *head_cert = chain_and_key->cert_chain->head; - POSIX_ENSURE_REF(head_cert); - *cert_length = 1; - struct s2n_cert *next_cert = head_cert->next; - while (next_cert != NULL) { - *cert_length += 1; - next_cert = next_cert->next; - } - - return S2N_SUCCESS; -} - -int s2n_cert_chain_get_cert(const struct s2n_cert_chain_and_key *chain_and_key, struct s2n_cert **out_cert, - const uint32_t cert_idx) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(out_cert); - - struct s2n_cert *cur_cert = chain_and_key->cert_chain->head; - POSIX_ENSURE_REF(cur_cert); - uint32_t counter = 0; - - struct s2n_cert *next_cert = cur_cert->next; - - while ((next_cert != NULL) && (counter < cert_idx)) { - cur_cert = next_cert; - next_cert = next_cert->next; - counter++; - } - - POSIX_ENSURE(counter == cert_idx, S2N_ERR_NO_CERT_FOUND); - POSIX_ENSURE(cur_cert != NULL, S2N_ERR_NO_CERT_FOUND); - *out_cert = cur_cert; - - return S2N_SUCCESS; -} - -int s2n_cert_get_der(const struct s2n_cert *cert, const uint8_t **out_cert_der, uint32_t *cert_length) -{ - POSIX_ENSURE_REF(cert); - POSIX_ENSURE_REF(out_cert_der); - POSIX_ENSURE_REF(cert_length); - - *cert_length = cert->raw.size; - *out_cert_der = cert->raw.data; - - return S2N_SUCCESS; -} - -static int s2n_asn1_obj_free(ASN1_OBJECT **data) -{ - if (*data != NULL) { - ASN1_OBJECT_free(*data); - } - return S2N_SUCCESS; -} - -static int s2n_asn1_string_free(ASN1_STRING **data) -{ - if (*data != NULL) { - ASN1_STRING_free(*data); - } - return S2N_SUCCESS; -} - -static int s2n_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) -{ - DEFER_CLEANUP(ASN1_STRING *asn1_str = NULL, s2n_asn1_string_free); - /* Note that d2i_ASN1_UTF8STRING increments *der_in to the byte following the parsed data. - * Using a temporary variable is mandatory to prevent memory free-ing errors. - * Ref to the warning section here for more information: - * https://www.openssl.org/docs/man1.1.0/man3/d2i_ASN1_UTF8STRING.html. - */ - const uint8_t *asn1_str_data = extension_data; - asn1_str = d2i_ASN1_UTF8STRING(NULL, (const unsigned char **) (void *) &asn1_str_data, extension_len); - POSIX_ENSURE(asn1_str != NULL, S2N_ERR_INVALID_X509_EXTENSION_TYPE); - /* ASN1_STRING_type() returns the type of `asn1_str`, using standard constants such as V_ASN1_OCTET_STRING. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_type.html. - */ - int type = ASN1_STRING_type(asn1_str); - POSIX_ENSURE(type == V_ASN1_UTF8STRING, S2N_ERR_INVALID_X509_EXTENSION_TYPE); - - int len = ASN1_STRING_length(asn1_str); - POSIX_ENSURE_GTE(len, 0); - if (out_data != NULL) { - POSIX_ENSURE((int64_t) *out_len >= (int64_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); - /* ASN1_STRING_data() returns an internal pointer to the data. - * Since this is an internal pointer it should not be freed or modified in any way. - * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. - */ - unsigned char *internal_data = ASN1_STRING_data(asn1_str); - POSIX_ENSURE_REF(internal_data); - POSIX_CHECKED_MEMCPY(out_data, internal_data, len); - } - *out_len = len; - return S2N_SUCCESS; -} - -int s2n_cert_get_utf8_string_from_extension_data_length(const uint8_t *extension_data, uint32_t extension_len, uint32_t *utf8_str_len) -{ - POSIX_ENSURE_REF(extension_data); - POSIX_ENSURE_GT(extension_len, 0); - POSIX_ENSURE_REF(utf8_str_len); - - POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, NULL, utf8_str_len)); - - return S2N_SUCCESS; -} - -int s2n_cert_get_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) -{ - POSIX_ENSURE_REF(extension_data); - POSIX_ENSURE_GT(extension_len, 0); - POSIX_ENSURE_REF(out_data); - POSIX_ENSURE_REF(out_len); - - POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, out_data, out_len)); - - return S2N_SUCCESS; -} - -static int s2n_parse_x509_extension(struct s2n_cert *cert, const uint8_t *oid, - uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) -{ - POSIX_ENSURE_REF(cert->raw.data); - /* Obtain the openssl x509 cert from the ASN1 DER certificate input. - * Note that d2i_X509 increments *der_in to the byte following the parsed data. - * Using a temporary variable is mandatory to prevent memory free-ing errors. - * Ref to the warning section here for more information: - * https://www.openssl.org/docs/man1.1.0/man3/d2i_X509.html. - */ - uint8_t *der_in = cert->raw.data; - DEFER_CLEANUP(X509 *x509_cert = d2i_X509(NULL, (const unsigned char **) (void *) &der_in, cert->raw.size), - X509_free_pointer); - POSIX_ENSURE_REF(x509_cert); - - /* Retrieve the number of x509 extensions present in the certificate - * X509_get_ext_count returns the number of extensions in the x509 certificate. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext_count.html. - */ - int ext_count_value = X509_get_ext_count(x509_cert); - POSIX_ENSURE_GT(ext_count_value, 0); - size_t ext_count = (size_t) ext_count_value; - - /* OBJ_txt2obj() converts the input text string into an ASN1_OBJECT structure. - * If no_name is 0 then long names and short names will be interpreted as well as numerical forms. - * If no_name is 1 only the numerical form is acceptable. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html. - */ - DEFER_CLEANUP(ASN1_OBJECT *asn1_obj_in = OBJ_txt2obj((const char *) oid, 0), s2n_asn1_obj_free); - POSIX_ENSURE_REF(asn1_obj_in); - - for (size_t loc = 0; loc < ext_count; loc++) { - ASN1_OCTET_STRING *asn1_str = NULL; - bool match_found = false; - - /* Retrieve the x509 extension at location loc. - * X509_get_ext() retrieves extension loc from x. - * The index loc can take any value from 0 to X509_get_ext_count(x) - 1. - * The returned extension is an internal pointer which must not be freed up by the application. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html. - */ - X509_EXTENSION *x509_ext = X509_get_ext(x509_cert, loc); - POSIX_ENSURE_REF(x509_ext); - - /* Retrieve the extension object/OID/extnId. - * X509_EXTENSION_get_object() returns the extension type of `x509_ext` as an ASN1_OBJECT pointer. - * The returned pointer is an internal value which must not be freed up. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_object.html. - */ - ASN1_OBJECT *asn1_obj = X509_EXTENSION_get_object(x509_ext); - POSIX_ENSURE_REF(asn1_obj); - - /* OBJ_cmp() compares two ASN1_OBJECT objects. If the two are identical 0 is returned. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_cmp.html. - */ - match_found = (0 == OBJ_cmp(asn1_obj_in, asn1_obj)); - - /* If match found, retrieve the corresponding OID value for the x509 extension */ - if (match_found) { - /* X509_EXTENSION_get_data() returns the data of extension `x509_ext`. - * The returned pointer is an internal value which must not be freed up. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_data.html. - */ - asn1_str = X509_EXTENSION_get_data(x509_ext); - POSIX_ENSURE_REF(asn1_str); - /* ASN1_STRING_length() returns the length of the content of `asn1_str`. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_length.html. - */ - int len = ASN1_STRING_length(asn1_str); - if (ext_value != NULL) { - POSIX_ENSURE_GTE(len, 0); - POSIX_ENSURE(*ext_value_len >= (uint32_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); - /* ASN1_STRING_data() returns an internal pointer to the data. - * Since this is an internal pointer it should not be freed or modified in any way. - * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. - */ - unsigned char *internal_data = ASN1_STRING_data(asn1_str); - POSIX_ENSURE_REF(internal_data); - POSIX_CHECKED_MEMCPY(ext_value, internal_data, len); - } - if (critical != NULL) { - /* Retrieve the x509 extension's critical value. - * X509_EXTENSION_get_critical() returns the criticality of extension `x509_ext`, - * it returns 1 for critical and 0 for non-critical. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_critical.html. - */ - *critical = X509_EXTENSION_get_critical(x509_ext); - } - *ext_value_len = len; - return S2N_SUCCESS; - } - } - - POSIX_BAIL(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND); -} - -int s2n_cert_get_x509_extension_value_length(struct s2n_cert *cert, const uint8_t *oid, uint32_t *ext_value_len) -{ - POSIX_ENSURE_REF(cert); - POSIX_ENSURE_REF(oid); - POSIX_ENSURE_REF(ext_value_len); - - POSIX_GUARD(s2n_parse_x509_extension(cert, oid, NULL, ext_value_len, NULL)); - - return S2N_SUCCESS; -} - -int s2n_cert_get_x509_extension_value(struct s2n_cert *cert, const uint8_t *oid, - uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) -{ - POSIX_ENSURE_REF(cert); - POSIX_ENSURE_REF(oid); - POSIX_ENSURE_REF(ext_value); - POSIX_ENSURE_REF(ext_value_len); - POSIX_ENSURE_REF(critical); - - POSIX_GUARD(s2n_parse_x509_extension(cert, oid, ext_value, ext_value_len, critical)); - - return S2N_SUCCESS; -} +/* + * 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. + */ + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + +#include "crypto/s2n_certificate.h" + +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_openssl_x509.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +int s2n_cert_set_cert_type(struct s2n_cert *cert, s2n_pkey_type pkey_type) +{ + POSIX_ENSURE_REF(cert); + cert->pkey_type = pkey_type; + POSIX_GUARD_RESULT(s2n_pkey_setup_for_type(&cert->public_key, pkey_type)); + return 0; +} + +int s2n_create_cert_chain_from_stuffer(struct s2n_cert_chain *cert_chain_out, struct s2n_stuffer *chain_in_stuffer) +{ + DEFER_CLEANUP(struct s2n_stuffer cert_out_stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); + + struct s2n_cert **insert = &cert_chain_out->head; + uint32_t chain_size = 0; + while (s2n_stuffer_has_pem_encapsulated_block(chain_in_stuffer)) { + int result = s2n_stuffer_certificate_from_pem(chain_in_stuffer, &cert_out_stuffer); + POSIX_ENSURE(result == S2N_SUCCESS, S2N_ERR_INVALID_PEM); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); + POSIX_GUARD(s2n_blob_zero(&mem)); + + struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; + POSIX_GUARD(s2n_alloc(&new_node->raw, s2n_stuffer_data_available(&cert_out_stuffer))); + POSIX_GUARD(s2n_stuffer_read(&cert_out_stuffer, &new_node->raw)); + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + /* Additional 3 bytes for the length field in the protocol */ + chain_size += new_node->raw.size + 3; + new_node->next = NULL; + *insert = new_node; + insert = &new_node->next; + }; + + POSIX_ENSURE(chain_size > 0, S2N_ERR_NO_CERTIFICATE_IN_PEM); + cert_chain_out->chain_size = chain_size; + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_cert_chain_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, struct s2n_stuffer *chain_in_stuffer) +{ + return s2n_create_cert_chain_from_stuffer(cert_and_key->cert_chain, chain_in_stuffer); +} + +int s2n_cert_chain_and_key_set_cert_chain_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *cert_chain_pem, uint32_t cert_chain_len) +{ + DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); + + POSIX_GUARD(s2n_stuffer_init_ro_from_string(&chain_in_stuffer, cert_chain_pem, cert_chain_len)); + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_cert_chain(struct s2n_cert_chain_and_key *cert_and_key, const char *cert_chain_pem) +{ + DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); + + /* Turn the chain into a stuffer */ + POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_private_key_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, + struct s2n_stuffer *key_in_stuffer, struct s2n_stuffer *key_out_stuffer) +{ + struct s2n_blob key_blob = { 0 }; + + POSIX_GUARD(s2n_pkey_zero_init(cert_and_key->private_key)); + + /* Convert pem to asn1 and asn1 to the private key. Handles both PKCS#1 and PKCS#8 formats */ + int type_hint = 0; + POSIX_GUARD(s2n_stuffer_private_key_from_pem(key_in_stuffer, key_out_stuffer, &type_hint)); + key_blob.size = s2n_stuffer_data_available(key_out_stuffer); + key_blob.data = s2n_stuffer_raw_read(key_out_stuffer, key_blob.size); + POSIX_ENSURE_REF(key_blob.data); + + POSIX_GUARD_RESULT(s2n_asn1der_to_private_key(cert_and_key->private_key, &key_blob, type_hint)); + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_private_key_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *private_key_pem, uint32_t private_key_len) +{ + DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); + + /* Put the private key pem in a stuffer */ + POSIX_GUARD(s2n_stuffer_init_ro_from_string(&key_in_stuffer, private_key_pem, private_key_len)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, private_key_len)); + + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_private_key(struct s2n_cert_chain_and_key *cert_and_key, const char *private_key_pem) +{ + POSIX_ENSURE_REF(private_key_pem); + + DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); + + /* Put the private key pem in a stuffer */ + POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); + + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_free(&chain_and_key->ocsp_status)); + if (data && length) { + POSIX_GUARD(s2n_alloc(&chain_and_key->ocsp_status, length)); + POSIX_CHECKED_MEMCPY(chain_and_key->ocsp_status.data, data, length); + } + return 0; +} + +int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_free(&chain_and_key->sct_list)); + if (data && length) { + POSIX_GUARD(s2n_alloc(&chain_and_key->sct_list, length)); + POSIX_CHECKED_MEMCPY(chain_and_key->sct_list.data, data, length); + } + return 0; +} + +struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) +{ + DEFER_CLEANUP(struct s2n_blob chain_and_key_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); + PTR_GUARD_POSIX(s2n_blob_zero(&chain_and_key_mem)); + + DEFER_CLEANUP(struct s2n_blob cert_chain_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain))); + PTR_GUARD_POSIX(s2n_blob_zero(&cert_chain_mem)); + + DEFER_CLEANUP(struct s2n_blob pkey_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key))); + PTR_GUARD_POSIX(s2n_blob_zero(&pkey_mem)); + + DEFER_CLEANUP(struct s2n_array *cn_names = NULL, s2n_array_free_p); + cn_names = s2n_array_new(sizeof(struct s2n_blob)); + PTR_ENSURE_REF(cn_names); + + DEFER_CLEANUP(struct s2n_array *san_names = NULL, s2n_array_free_p); + san_names = s2n_array_new(sizeof(struct s2n_blob)); + PTR_ENSURE_REF(san_names); + + struct s2n_cert_chain_and_key *chain_and_key = (struct s2n_cert_chain_and_key *) (void *) chain_and_key_mem.data; + chain_and_key->cert_chain = (struct s2n_cert_chain *) (void *) cert_chain_mem.data; + chain_and_key->private_key = (s2n_cert_private_key *) (void *) pkey_mem.data; + chain_and_key->cn_names = cn_names; + chain_and_key->san_names = san_names; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(pkey_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(cn_names); + ZERO_TO_DISABLE_DEFER_CLEANUP(san_names); + return chain_and_key; +} + +DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); + +int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) +{ + POSIX_ENSURE_REF(chain_and_key->san_names); + POSIX_ENSURE_REF(x509_cert); + + DEFER_CLEANUP(GENERAL_NAMES *san_names = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL), GENERAL_NAMES_free_pointer); + if (san_names == NULL) { + /* No SAN extension */ + return 0; + } + + const int num_san_names = sk_GENERAL_NAME_num(san_names); + for (int i = 0; i < num_san_names; i++) { + GENERAL_NAME *san_name = sk_GENERAL_NAME_value(san_names, i); + if (!san_name) { + continue; + } + + if (san_name->type == GEN_DNS) { + /* Decoding isn't necessary here since a DNS SAN name is ASCII(type V_ASN1_IA5STRING) */ + unsigned char *san_str = san_name->d.dNSName->data; + const size_t san_str_len = san_name->d.dNSName->length; + struct s2n_blob *san_blob = NULL; + POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->san_names, (void **) &san_blob)); + if (!san_blob) { + POSIX_BAIL(S2N_ERR_NULL_SANS); + } + + if (s2n_alloc(san_blob, san_str_len)) { + S2N_ERROR_PRESERVE_ERRNO(); + } + + POSIX_CHECKED_MEMCPY(san_blob->data, san_str, san_str_len); + san_blob->size = san_str_len; + /* normalize san_blob to lowercase */ + POSIX_GUARD(s2n_blob_char_to_lower(san_blob)); + } + } + + return 0; +} + +/* Parse CN names from the Subject of the leaf certificate. Technically there can by multiple CNs + * in the Subject but practically very few certificates in the wild will have more than one CN. + * Since the data for this certificate is coming from the application and not from an untrusted + * source, we will try our best to parse all of the CNs. + * + * A recent CAB thread proposed removing support for multiple CNs: + * https://cabforum.org/pipermail/public/2016-April/007242.html + */ + +DEFINE_POINTER_CLEANUP_FUNC(unsigned char *, OPENSSL_free); + +int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) +{ + POSIX_ENSURE_REF(chain_and_key->cn_names); + POSIX_ENSURE_REF(x509_cert); + + X509_NAME *subject = X509_get_subject_name(x509_cert); + if (!subject) { + return 0; + } + + int lastpos = -1; + while ((lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, lastpos)) >= 0) { + X509_NAME_ENTRY *name_entry = X509_NAME_get_entry(subject, lastpos); + if (!name_entry) { + continue; + } + + ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(name_entry); + if (!asn1_str) { + continue; + } + + /* We need to try and decode the CN since it may be encoded as unicode with a + * direct ASCII equivalent. Any non ASCII bytes in the string will fail later when we + * actually compare hostnames. + * + * `ASN1_STRING_to_UTF8` allocates in both the success case and in the zero return case, but + * not in the failure case (negative return value). Therefore, we use `ZERO_TO_DISABLE_DEFER_CLEANUP` + * in the failure case to prevent double-freeing `utf8_str`. For the zero and success cases, `utf8_str` + * will be freed by the `DEFER_CLEANUP`. + */ + DEFER_CLEANUP(unsigned char *utf8_str, OPENSSL_free_pointer); + const int utf8_out_len = ASN1_STRING_to_UTF8(&utf8_str, asn1_str); + if (utf8_out_len < 0) { + /* On failure, ASN1_STRING_to_UTF8 does not allocate any memory */ + ZERO_TO_DISABLE_DEFER_CLEANUP(utf8_str); + continue; + } else if (utf8_out_len == 0) { + /* We still need to free memory for this case, so let the DEFER_CLEANUP free it + * see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 and + * https://security.archlinux.org/CVE-2017-7521 + */ + } else { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->cn_names, (void **) &cn_name)); + if (cn_name == NULL) { + POSIX_BAIL(S2N_ERR_NULL_CN_NAME); + } + + if (s2n_alloc(cn_name, utf8_out_len) < 0) { + S2N_ERROR_PRESERVE_ERRNO(); + } + POSIX_CHECKED_MEMCPY(cn_name->data, utf8_str, utf8_out_len); + cn_name->size = utf8_out_len; + /* normalize cn_name to lowercase */ + POSIX_GUARD(s2n_blob_char_to_lower(cn_name)); + } + } + + return 0; +} + +static int s2n_cert_chain_and_key_set_names(struct s2n_cert_chain_and_key *chain_and_key, X509 *cert) +{ + POSIX_GUARD(s2n_cert_chain_and_key_load_sans(chain_and_key, cert)); + /* For current use cases, we *could* avoid populating the common names if any sans were loaded in + * s2n_cert_chain_and_key_load_sans. Let's unconditionally populate this field to avoid surprises + * in the future. + */ + POSIX_GUARD(s2n_cert_chain_and_key_load_cns(chain_and_key, cert)); + return 0; +} + +int s2n_cert_chain_and_key_load(struct s2n_cert_chain_and_key *chain_and_key) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(chain_and_key->cert_chain); + POSIX_ENSURE_REF(chain_and_key->cert_chain->head); + POSIX_ENSURE_REF(chain_and_key->private_key); + struct s2n_cert *head = chain_and_key->cert_chain->head; + + DEFER_CLEANUP(X509 *leaf_cert = NULL, X509_free_pointer); + POSIX_GUARD_RESULT(s2n_openssl_x509_parse(&head->raw, &leaf_cert)); + POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(leaf_cert, &head->info)); + + /* Parse the leaf cert for the public key and certificate type */ + DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + POSIX_GUARD_RESULT(s2n_pkey_from_x509(leaf_cert, &public_key, &pkey_type)); + + POSIX_ENSURE(pkey_type != S2N_PKEY_TYPE_UNKNOWN, S2N_ERR_CERT_TYPE_UNSUPPORTED); + POSIX_GUARD(s2n_cert_set_cert_type(head, pkey_type)); + + /* Validate the leaf cert's public key matches the provided private key */ + if (s2n_pkey_check_key_exists(chain_and_key->private_key) == S2N_SUCCESS) { + POSIX_GUARD(s2n_pkey_match(&public_key, chain_and_key->private_key)); + } + + /* Populate name information from the SAN/CN for the leaf certificate */ + POSIX_GUARD(s2n_cert_chain_and_key_set_names(chain_and_key, leaf_cert)); + + /* populate libcrypto nid's required for cert restrictions */ + struct s2n_cert *current = head->next; + while (current != NULL) { + DEFER_CLEANUP(X509 *parsed_cert = NULL, X509_free_pointer); + POSIX_GUARD_RESULT(s2n_openssl_x509_parse(¤t->raw, &parsed_cert)); + POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(parsed_cert, ¤t->info)); + + current = current->next; + } + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem) +{ + POSIX_ENSURE_REF(chain_and_key); + + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, chain_pem)); + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_load_public_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len) +{ + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); + POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_load_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, + uint32_t chain_pem_len, uint8_t *private_key_pem, uint32_t private_key_pem_len) +{ + POSIX_ENSURE_REF(chain_and_key); + + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_bytes(chain_and_key, private_key_pem, private_key_pem_len)); + + POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); + + return S2N_SUCCESS; +} + +S2N_CLEANUP_RESULT s2n_cert_chain_and_key_ptr_free(struct s2n_cert_chain_and_key **cert_and_key) +{ + RESULT_ENSURE_REF(cert_and_key); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(*cert_and_key)); + *cert_and_key = NULL; + return S2N_RESULT_OK; +} + +int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key) +{ + if (cert_and_key == NULL) { + return 0; + } + + /* Walk the chain and free the certs */ + if (cert_and_key->cert_chain) { + struct s2n_cert *node = cert_and_key->cert_chain->head; + while (node) { + /* Free the cert */ + POSIX_GUARD(s2n_free(&node->raw)); + /* update head so it won't point to freed memory */ + cert_and_key->cert_chain->head = node->next; + /* Free the node */ + POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); + node = cert_and_key->cert_chain->head; + } + + POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->cert_chain, sizeof(struct s2n_cert_chain))); + } + + if (cert_and_key->private_key) { + POSIX_GUARD(s2n_pkey_free(cert_and_key->private_key)); + POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->private_key, sizeof(s2n_cert_private_key))); + } + + uint32_t len = 0; + + if (cert_and_key->san_names) { + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->san_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *san_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->san_names, i, (void **) &san_name)); + POSIX_GUARD(s2n_free(san_name)); + } + POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->san_names)); + cert_and_key->san_names = NULL; + } + + if (cert_and_key->cn_names) { + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->cn_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->cn_names, i, (void **) &cn_name)); + POSIX_GUARD(s2n_free(cn_name)); + } + POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->cn_names)); + cert_and_key->cn_names = NULL; + } + + POSIX_GUARD(s2n_free(&cert_and_key->ocsp_status)); + POSIX_GUARD(s2n_free(&cert_and_key->sct_list)); + + POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key, sizeof(struct s2n_cert_chain_and_key))); + return 0; +} + +int s2n_cert_chain_free(struct s2n_cert_chain *cert_chain) +{ + /* Walk the chain and free the certs/nodes allocated prior to failure */ + if (cert_chain) { + struct s2n_cert *node = cert_chain->head; + while (node) { + /* Free the cert */ + POSIX_GUARD(s2n_free(&node->raw)); + /* update head so it won't point to freed memory */ + cert_chain->head = node->next; + /* Free the node */ + POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); + node = cert_chain->head; + } + } + + return S2N_SUCCESS; +} + +int s2n_send_cert_chain(struct s2n_connection *conn, struct s2n_stuffer *out, struct s2n_cert_chain_and_key *chain_and_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(chain_and_key); + struct s2n_cert_chain *chain = chain_and_key->cert_chain; + POSIX_ENSURE_REF(chain); + struct s2n_cert *cur_cert = chain->head; + POSIX_ENSURE_REF(cur_cert); + + struct s2n_stuffer_reservation cert_chain_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint24(out, &cert_chain_size)); + + /* Send certs and extensions (in TLS 1.3) */ + bool first_entry = true; + while (cur_cert) { + POSIX_ENSURE_REF(cur_cert); + POSIX_GUARD(s2n_stuffer_write_uint24(out, cur_cert->raw.size)); + POSIX_GUARD(s2n_stuffer_write_bytes(out, cur_cert->raw.data, cur_cert->raw.size)); + + /* According to https://tools.ietf.org/html/rfc8446#section-4.4.2, + * If an extension applies to the entire chain, it SHOULD be included in + * the first CertificateEntry. + * While the spec allow extensions to be included in other certificate + * entries, only the first matter to use here */ + if (conn->actual_protocol_version >= S2N_TLS13) { + if (first_entry) { + POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, out)); + first_entry = false; + } else { + POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, out)); + } + } + cur_cert = cur_cert->next; + } + + POSIX_GUARD(s2n_stuffer_write_vector_size(&cert_chain_size)); + + return 0; +} + +int s2n_send_empty_cert_chain(struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(out); + POSIX_GUARD(s2n_stuffer_write_uint24(out, 0)); + return 0; +} + +static int s2n_does_cert_san_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(dns_name); + + struct s2n_array *san_names = chain_and_key->san_names; + uint32_t len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(san_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *san_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(san_names, i, (void **) &san_name)); + POSIX_ENSURE_REF(san_name); + if ((dns_name->size == san_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) san_name->data, dns_name->size) == 0)) { + return 1; + } + } + + return 0; +} + +static int s2n_does_cert_cn_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(dns_name); + + struct s2n_array *cn_names = chain_and_key->cn_names; + uint32_t len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(cn_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cn_names, i, (void **) &cn_name)); + POSIX_ENSURE_REF(cn_name); + if ((dns_name->size == cn_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) cn_name->data, dns_name->size) == 0)) { + return 1; + } + } + + return 0; +} + +int s2n_cert_chain_and_key_matches_dns_name(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + uint32_t len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(chain_and_key->san_names, &len)); + if (len > 0) { + if (s2n_does_cert_san_match_hostname(chain_and_key, dns_name)) { + return 1; + } + } else { + /* Per https://tools.ietf.org/html/rfc6125#section-6.4.4 we only will + * consider the CN for matching if no valid DNS entries are provided + * in a SAN. + */ + if (s2n_does_cert_cn_match_hostname(chain_and_key, dns_name)) { + return 1; + } + } + + return 0; +} + +int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx) +{ + cert_and_key->context = ctx; + return 0; +} + +void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key) +{ + return cert_and_key->context; +} + +s2n_pkey_type s2n_cert_chain_and_key_get_pkey_type(struct s2n_cert_chain_and_key *chain_and_key) +{ + if (chain_and_key == NULL + || chain_and_key->cert_chain == NULL + || chain_and_key->cert_chain->head == NULL) { + return S2N_PKEY_TYPE_UNKNOWN; + } + return chain_and_key->cert_chain->head->pkey_type; +} + +s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *chain_and_key) +{ + PTR_ENSURE_REF(chain_and_key); + return chain_and_key->private_key; +} + +int s2n_cert_chain_get_length(const struct s2n_cert_chain_and_key *chain_and_key, uint32_t *cert_length) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(cert_length); + + struct s2n_cert *head_cert = chain_and_key->cert_chain->head; + POSIX_ENSURE_REF(head_cert); + *cert_length = 1; + struct s2n_cert *next_cert = head_cert->next; + while (next_cert != NULL) { + *cert_length += 1; + next_cert = next_cert->next; + } + + return S2N_SUCCESS; +} + +int s2n_cert_chain_get_cert(const struct s2n_cert_chain_and_key *chain_and_key, struct s2n_cert **out_cert, + const uint32_t cert_idx) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(out_cert); + + struct s2n_cert *cur_cert = chain_and_key->cert_chain->head; + POSIX_ENSURE_REF(cur_cert); + uint32_t counter = 0; + + struct s2n_cert *next_cert = cur_cert->next; + + while ((next_cert != NULL) && (counter < cert_idx)) { + cur_cert = next_cert; + next_cert = next_cert->next; + counter++; + } + + POSIX_ENSURE(counter == cert_idx, S2N_ERR_NO_CERT_FOUND); + POSIX_ENSURE(cur_cert != NULL, S2N_ERR_NO_CERT_FOUND); + *out_cert = cur_cert; + + return S2N_SUCCESS; +} + +int s2n_cert_get_der(const struct s2n_cert *cert, const uint8_t **out_cert_der, uint32_t *cert_length) +{ + POSIX_ENSURE_REF(cert); + POSIX_ENSURE_REF(out_cert_der); + POSIX_ENSURE_REF(cert_length); + + *cert_length = cert->raw.size; + *out_cert_der = cert->raw.data; + + return S2N_SUCCESS; +} + +static int s2n_asn1_obj_free(ASN1_OBJECT **data) +{ + if (*data != NULL) { + ASN1_OBJECT_free(*data); + } + return S2N_SUCCESS; +} + +static int s2n_asn1_string_free(ASN1_STRING **data) +{ + if (*data != NULL) { + ASN1_STRING_free(*data); + } + return S2N_SUCCESS; +} + +static int s2n_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) +{ + DEFER_CLEANUP(ASN1_STRING *asn1_str = NULL, s2n_asn1_string_free); + /* Note that d2i_ASN1_UTF8STRING increments *der_in to the byte following the parsed data. + * Using a temporary variable is mandatory to prevent memory free-ing errors. + * Ref to the warning section here for more information: + * https://www.openssl.org/docs/man1.1.0/man3/d2i_ASN1_UTF8STRING.html. + */ + const uint8_t *asn1_str_data = extension_data; + asn1_str = d2i_ASN1_UTF8STRING(NULL, (const unsigned char **) (void *) &asn1_str_data, extension_len); + POSIX_ENSURE(asn1_str != NULL, S2N_ERR_INVALID_X509_EXTENSION_TYPE); + /* ASN1_STRING_type() returns the type of `asn1_str`, using standard constants such as V_ASN1_OCTET_STRING. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_type.html. + */ + int type = ASN1_STRING_type(asn1_str); + POSIX_ENSURE(type == V_ASN1_UTF8STRING, S2N_ERR_INVALID_X509_EXTENSION_TYPE); + + int len = ASN1_STRING_length(asn1_str); + POSIX_ENSURE_GTE(len, 0); + if (out_data != NULL) { + POSIX_ENSURE((int64_t) *out_len >= (int64_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); + /* ASN1_STRING_data() returns an internal pointer to the data. + * Since this is an internal pointer it should not be freed or modified in any way. + * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. + */ + unsigned char *internal_data = ASN1_STRING_data(asn1_str); + POSIX_ENSURE_REF(internal_data); + POSIX_CHECKED_MEMCPY(out_data, internal_data, len); + } + *out_len = len; + return S2N_SUCCESS; +} + +int s2n_cert_get_utf8_string_from_extension_data_length(const uint8_t *extension_data, uint32_t extension_len, uint32_t *utf8_str_len) +{ + POSIX_ENSURE_REF(extension_data); + POSIX_ENSURE_GT(extension_len, 0); + POSIX_ENSURE_REF(utf8_str_len); + + POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, NULL, utf8_str_len)); + + return S2N_SUCCESS; +} + +int s2n_cert_get_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) +{ + POSIX_ENSURE_REF(extension_data); + POSIX_ENSURE_GT(extension_len, 0); + POSIX_ENSURE_REF(out_data); + POSIX_ENSURE_REF(out_len); + + POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, out_data, out_len)); + + return S2N_SUCCESS; +} + +static int s2n_parse_x509_extension(struct s2n_cert *cert, const uint8_t *oid, + uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) +{ + POSIX_ENSURE_REF(cert->raw.data); + /* Obtain the openssl x509 cert from the ASN1 DER certificate input. + * Note that d2i_X509 increments *der_in to the byte following the parsed data. + * Using a temporary variable is mandatory to prevent memory free-ing errors. + * Ref to the warning section here for more information: + * https://www.openssl.org/docs/man1.1.0/man3/d2i_X509.html. + */ + uint8_t *der_in = cert->raw.data; + DEFER_CLEANUP(X509 *x509_cert = d2i_X509(NULL, (const unsigned char **) (void *) &der_in, cert->raw.size), + X509_free_pointer); + POSIX_ENSURE_REF(x509_cert); + + /* Retrieve the number of x509 extensions present in the certificate + * X509_get_ext_count returns the number of extensions in the x509 certificate. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext_count.html. + */ + int ext_count_value = X509_get_ext_count(x509_cert); + POSIX_ENSURE_GT(ext_count_value, 0); + size_t ext_count = (size_t) ext_count_value; + + /* OBJ_txt2obj() converts the input text string into an ASN1_OBJECT structure. + * If no_name is 0 then long names and short names will be interpreted as well as numerical forms. + * If no_name is 1 only the numerical form is acceptable. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html. + */ + DEFER_CLEANUP(ASN1_OBJECT *asn1_obj_in = OBJ_txt2obj((const char *) oid, 0), s2n_asn1_obj_free); + POSIX_ENSURE_REF(asn1_obj_in); + + for (size_t loc = 0; loc < ext_count; loc++) { + ASN1_OCTET_STRING *asn1_str = NULL; + bool match_found = false; + + /* Retrieve the x509 extension at location loc. + * X509_get_ext() retrieves extension loc from x. + * The index loc can take any value from 0 to X509_get_ext_count(x) - 1. + * The returned extension is an internal pointer which must not be freed up by the application. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html. + */ + X509_EXTENSION *x509_ext = X509_get_ext(x509_cert, loc); + POSIX_ENSURE_REF(x509_ext); + + /* Retrieve the extension object/OID/extnId. + * X509_EXTENSION_get_object() returns the extension type of `x509_ext` as an ASN1_OBJECT pointer. + * The returned pointer is an internal value which must not be freed up. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_object.html. + */ + ASN1_OBJECT *asn1_obj = X509_EXTENSION_get_object(x509_ext); + POSIX_ENSURE_REF(asn1_obj); + + /* OBJ_cmp() compares two ASN1_OBJECT objects. If the two are identical 0 is returned. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_cmp.html. + */ + match_found = (0 == OBJ_cmp(asn1_obj_in, asn1_obj)); + + /* If match found, retrieve the corresponding OID value for the x509 extension */ + if (match_found) { + /* X509_EXTENSION_get_data() returns the data of extension `x509_ext`. + * The returned pointer is an internal value which must not be freed up. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_data.html. + */ + asn1_str = X509_EXTENSION_get_data(x509_ext); + POSIX_ENSURE_REF(asn1_str); + /* ASN1_STRING_length() returns the length of the content of `asn1_str`. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_length.html. + */ + int len = ASN1_STRING_length(asn1_str); + if (ext_value != NULL) { + POSIX_ENSURE_GTE(len, 0); + POSIX_ENSURE(*ext_value_len >= (uint32_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); + /* ASN1_STRING_data() returns an internal pointer to the data. + * Since this is an internal pointer it should not be freed or modified in any way. + * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. + */ + unsigned char *internal_data = ASN1_STRING_data(asn1_str); + POSIX_ENSURE_REF(internal_data); + POSIX_CHECKED_MEMCPY(ext_value, internal_data, len); + } + if (critical != NULL) { + /* Retrieve the x509 extension's critical value. + * X509_EXTENSION_get_critical() returns the criticality of extension `x509_ext`, + * it returns 1 for critical and 0 for non-critical. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_critical.html. + */ + *critical = X509_EXTENSION_get_critical(x509_ext); + } + *ext_value_len = len; + return S2N_SUCCESS; + } + } + + POSIX_BAIL(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND); +} + +int s2n_cert_get_x509_extension_value_length(struct s2n_cert *cert, const uint8_t *oid, uint32_t *ext_value_len) +{ + POSIX_ENSURE_REF(cert); + POSIX_ENSURE_REF(oid); + POSIX_ENSURE_REF(ext_value_len); + + POSIX_GUARD(s2n_parse_x509_extension(cert, oid, NULL, ext_value_len, NULL)); + + return S2N_SUCCESS; +} + +int s2n_cert_get_x509_extension_value(struct s2n_cert *cert, const uint8_t *oid, + uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) +{ + POSIX_ENSURE_REF(cert); + POSIX_ENSURE_REF(oid); + POSIX_ENSURE_REF(ext_value); + POSIX_ENSURE_REF(ext_value_len); + POSIX_ENSURE_REF(critical); + + POSIX_GUARD(s2n_parse_x509_extension(cert, oid, ext_value, ext_value_len, critical)); + + return S2N_SUCCESS; +} diff --git a/crypto/s2n_dhe.c b/crypto/s2n_dhe.c index af365f8510f..ba400890e8e 100644 --- a/crypto/s2n_dhe.c +++ b/crypto/s2n_dhe.c @@ -1,383 +1,384 @@ -/* - * 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 "crypto/s2n_dhe.h" - -#include -#include -#include -#include - -#include "crypto/s2n_openssl.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -#define S2N_MIN_DH_PRIME_SIZE_BYTES (2048 / 8) - -/* Caller is not responsible for freeing values returned by these accessors - * Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html - */ -static const BIGNUM *s2n_get_Ys_dh_param(struct s2n_dh_params *dh_params) -{ - const BIGNUM *Ys = NULL; - -/* DH made opaque in Openssl 1.1.0 */ -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - DH_get0_key(dh_params->dh, &Ys, NULL); -#else - Ys = dh_params->dh->pub_key; -#endif - - return Ys; -} - -static const BIGNUM *s2n_get_p_dh_param(struct s2n_dh_params *dh_params) -{ - const BIGNUM *p = NULL; -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - DH_get0_pqg(dh_params->dh, &p, NULL, NULL); -#else - p = dh_params->dh->p; -#endif - - return p; -} - -static const BIGNUM *s2n_get_g_dh_param(struct s2n_dh_params *dh_params) -{ - const BIGNUM *g = NULL; -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - DH_get0_pqg(dh_params->dh, NULL, NULL, &g); -#else - g = dh_params->dh->g; -#endif - - return g; -} - -static int s2n_check_p_g_dh_params(struct s2n_dh_params *dh_params) -{ - POSIX_ENSURE_REF(dh_params); - POSIX_ENSURE_REF(dh_params->dh); - - const BIGNUM *p = s2n_get_p_dh_param(dh_params); - const BIGNUM *g = s2n_get_g_dh_param(dh_params); - - POSIX_ENSURE_REF(g); - POSIX_ENSURE_REF(p); - - S2N_ERROR_IF(DH_size(dh_params->dh) < S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_PARAMS_CREATE); - S2N_ERROR_IF(BN_is_zero(g), S2N_ERR_DH_PARAMS_CREATE); - S2N_ERROR_IF(BN_is_zero(p), S2N_ERR_DH_PARAMS_CREATE); - - return S2N_SUCCESS; -} - -static int s2n_check_pub_key_dh_params(struct s2n_dh_params *dh_params) -{ - const BIGNUM *pub_key = s2n_get_Ys_dh_param(dh_params); - POSIX_ENSURE_REF(pub_key); - - /* - * https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5 - * - * The following algorithm MAY be used to validate a received public - * key y. - * - * 1. Verify that y lies within the interval [2,p-1]. - * If it does not, the key is invalid. - * - * This check is optional per the RFC, but applied here as - * defense-in-depth to reject degenerate public key values. - */ - S2N_ERROR_IF(BN_is_zero(pub_key), S2N_ERR_DH_PARAMS_CREATE); - S2N_ERROR_IF(BN_is_one(pub_key), S2N_ERR_DH_PARAMS_CREATE); - - const BIGNUM *p = s2n_get_p_dh_param(dh_params); - POSIX_ENSURE_REF(p); - - BIGNUM *p_minus_one = BN_dup(p); - POSIX_ENSURE_REF(p_minus_one); - if (!BN_sub_word(p_minus_one, 1)) { - BN_free(p_minus_one); - POSIX_BAIL(S2N_ERR_DH_PARAMS_CREATE); - } - int cmp = BN_cmp(pub_key, p_minus_one); - BN_free(p_minus_one); - S2N_ERROR_IF(cmp > 0, S2N_ERR_DH_PARAMS_CREATE); - - return S2N_SUCCESS; -} - -static int s2n_set_p_g_Ys_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *p, struct s2n_blob *g, - struct s2n_blob *Ys) -{ - POSIX_ENSURE(p->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); - POSIX_ENSURE(g->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); - POSIX_ENSURE(Ys->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); - BIGNUM *bn_p = BN_bin2bn((const unsigned char *) p->data, p->size, NULL); - BIGNUM *bn_g = BN_bin2bn((const unsigned char *) g->data, g->size, NULL); - BIGNUM *bn_Ys = BN_bin2bn((const unsigned char *) Ys->data, Ys->size, NULL); - -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - /* Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html: - * values that have been passed in should not be freed directly after this function has been called - */ - POSIX_GUARD_OSSL(DH_set0_pqg(dh_params->dh, bn_p, NULL, bn_g), S2N_ERR_DH_PARAMS_CREATE); - - /* Same as DH_set0_pqg */ - POSIX_GUARD_OSSL(DH_set0_key(dh_params->dh, bn_Ys, NULL), S2N_ERR_DH_PARAMS_CREATE); -#else - dh_params->dh->p = bn_p; - dh_params->dh->g = bn_g; - dh_params->dh->pub_key = bn_Ys; -#endif - - return S2N_SUCCESS; -} - -int s2n_check_all_dh_params(struct s2n_dh_params *dh_params) -{ - POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); - POSIX_GUARD(s2n_check_pub_key_dh_params(dh_params)); - - return S2N_SUCCESS; -} - -int s2n_pkcs3_to_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *pkcs3) -{ - POSIX_ENSURE_REF(dh_params); - POSIX_PRECONDITION(s2n_blob_validate(pkcs3)); - DEFER_CLEANUP(struct s2n_dh_params temp_dh_params = { 0 }, s2n_dh_params_free); - - uint8_t *original_ptr = pkcs3->data; - temp_dh_params.dh = d2i_DHparams(NULL, (const unsigned char **) (void *) &pkcs3->data, pkcs3->size); - - POSIX_GUARD(s2n_check_p_g_dh_params(&temp_dh_params)); - - if (pkcs3->data) { - POSIX_ENSURE_GTE(pkcs3->data, original_ptr); - POSIX_ENSURE((uint32_t) (pkcs3->data - original_ptr) == pkcs3->size, S2N_ERR_INVALID_PKCS3); - } - - pkcs3->data = original_ptr; - - /* Require at least 2048 bits for the DH size */ - POSIX_ENSURE(DH_size(temp_dh_params.dh) >= S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_TOO_SMALL); - - /* Check the generator and prime */ - POSIX_GUARD(s2n_dh_params_check(&temp_dh_params)); - - dh_params->dh = temp_dh_params.dh; - - ZERO_TO_DISABLE_DEFER_CLEANUP(temp_dh_params); - - return S2N_SUCCESS; -} - -int s2n_dh_p_g_Ys_to_dh_params(struct s2n_dh_params *server_dh_params, struct s2n_blob *p, struct s2n_blob *g, - struct s2n_blob *Ys) -{ - POSIX_ENSURE_REF(server_dh_params); - POSIX_PRECONDITION(s2n_blob_validate(p)); - POSIX_PRECONDITION(s2n_blob_validate(g)); - POSIX_PRECONDITION(s2n_blob_validate(Ys)); - - server_dh_params->dh = DH_new(); - POSIX_ENSURE(server_dh_params->dh != NULL, S2N_ERR_DH_PARAMS_CREATE); - - POSIX_GUARD(s2n_set_p_g_Ys_dh_params(server_dh_params, p, g, Ys)); - POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); - - return S2N_SUCCESS; -} - -int s2n_dh_params_to_p_g_Ys(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *out, struct s2n_blob *output) -{ - POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); - POSIX_PRECONDITION(s2n_stuffer_validate(out)); - POSIX_PRECONDITION(s2n_blob_validate(output)); - - const BIGNUM *bn_p = s2n_get_p_dh_param(server_dh_params); - const BIGNUM *bn_g = s2n_get_g_dh_param(server_dh_params); - const BIGNUM *bn_Ys = s2n_get_Ys_dh_param(server_dh_params); - - uint16_t p_size = BN_num_bytes(bn_p); - uint16_t g_size = BN_num_bytes(bn_g); - uint16_t Ys_size = BN_num_bytes(bn_Ys); - uint8_t *p = NULL; - uint8_t *g = NULL; - uint8_t *Ys = NULL; - - output->data = s2n_stuffer_raw_write(out, 0); - POSIX_ENSURE_REF(output->data); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, p_size)); - p = s2n_stuffer_raw_write(out, p_size); - POSIX_ENSURE_REF(p); - POSIX_ENSURE(BN_bn2bin(bn_p, p) == p_size, S2N_ERR_DH_SERIALIZING); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, g_size)); - g = s2n_stuffer_raw_write(out, g_size); - POSIX_ENSURE_REF(g); - POSIX_ENSURE(BN_bn2bin(bn_g, g) == g_size, S2N_ERR_DH_SERIALIZING); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, Ys_size)); - Ys = s2n_stuffer_raw_write(out, Ys_size); - POSIX_ENSURE_REF(Ys); - POSIX_ENSURE(BN_bn2bin(bn_Ys, Ys) == Ys_size, S2N_ERR_DH_SERIALIZING); - - output->size = p_size + 2 + g_size + 2 + Ys_size + 2; - - return S2N_SUCCESS; -} - -int s2n_dh_compute_shared_secret_as_client(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_out, - struct s2n_blob *shared_key) -{ - struct s2n_dh_params client_params = { 0 }; - uint8_t *client_pub_key = NULL; - uint16_t client_pub_key_size = 0; - int shared_key_size = 0; - - POSIX_GUARD(s2n_dh_params_check(server_dh_params)); - POSIX_GUARD(s2n_dh_params_copy(server_dh_params, &client_params)); - POSIX_GUARD(s2n_dh_generate_ephemeral_key(&client_params)); - POSIX_GUARD(s2n_alloc(shared_key, DH_size(server_dh_params->dh))); - - const BIGNUM *client_pub_key_bn = s2n_get_Ys_dh_param(&client_params); - POSIX_ENSURE_REF(client_pub_key_bn); - client_pub_key_size = BN_num_bytes(client_pub_key_bn); - POSIX_GUARD(s2n_stuffer_write_uint16(Yc_out, client_pub_key_size)); - client_pub_key = s2n_stuffer_raw_write(Yc_out, client_pub_key_size); - if (client_pub_key == NULL) { - POSIX_GUARD(s2n_free(shared_key)); - POSIX_GUARD(s2n_dh_params_free(&client_params)); - POSIX_BAIL(S2N_ERR_DH_WRITING_PUBLIC_KEY); - } - - if (BN_bn2bin(client_pub_key_bn, client_pub_key) != client_pub_key_size) { - POSIX_GUARD(s2n_free(shared_key)); - POSIX_GUARD(s2n_dh_params_free(&client_params)); - POSIX_BAIL(S2N_ERR_DH_COPYING_PUBLIC_KEY); - } - - /* server_dh_params already validated */ - const BIGNUM *server_pub_key_bn = s2n_get_Ys_dh_param(server_dh_params); - shared_key_size = DH_compute_key(shared_key->data, server_pub_key_bn, client_params.dh); - if (shared_key_size < 0) { - POSIX_GUARD(s2n_free(shared_key)); - POSIX_GUARD(s2n_dh_params_free(&client_params)); - POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); - } - - shared_key->size = shared_key_size; - - POSIX_GUARD(s2n_dh_params_free(&client_params)); - - return S2N_SUCCESS; -} - -int s2n_dh_compute_shared_secret_as_server(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_in, - struct s2n_blob *shared_key) -{ - uint16_t Yc_length = 0; - struct s2n_blob Yc = { 0 }; - int shared_key_size = 0; - BIGNUM *pub_key = NULL; - - POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); - int server_dh_params_size = DH_size(server_dh_params->dh); - POSIX_ENSURE(server_dh_params_size <= INT32_MAX, S2N_ERR_INTEGER_OVERFLOW); - - /* - * As defined in https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.2, - * the client's DH public value (Yc) is sent as a variable-length opaque value. - * Validate that Yc_length does not exceed the DH group size to prevent - * unnecessary computation and memory allocation on oversized keys. - * - * According to https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5, - * the valid range of Yc is [2, p-1]. When encoding a BIGNUM to bytes, - * leading zeros are often stripped, in which case Yc_length might be - * less than server_dh_params_size. - */ - POSIX_GUARD(s2n_stuffer_read_uint16(Yc_in, &Yc_length)); - POSIX_ENSURE(Yc_length > 0, S2N_ERR_DH_SHARED_SECRET); - POSIX_ENSURE((int) Yc_length <= server_dh_params_size, S2N_ERR_DH_SHARED_SECRET); - - Yc.size = Yc_length; - Yc.data = s2n_stuffer_raw_read(Yc_in, Yc.size); - POSIX_ENSURE_REF(Yc.data); - - pub_key = BN_bin2bn((const unsigned char *) Yc.data, Yc.size, NULL); - POSIX_ENSURE_REF(pub_key); - POSIX_GUARD(s2n_alloc(shared_key, server_dh_params_size)); - - shared_key_size = DH_compute_key(shared_key->data, pub_key, server_dh_params->dh); - if (shared_key_size <= 0) { - BN_free(pub_key); - POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); - } - - shared_key->size = shared_key_size; - - BN_free(pub_key); - - return S2N_SUCCESS; -} - -int s2n_dh_params_check(struct s2n_dh_params *dh_params) -{ - POSIX_ENSURE_REF(dh_params); - POSIX_ENSURE_REF(dh_params->dh); - int codes = 0; - - POSIX_GUARD_OSSL(DH_check(dh_params->dh, &codes), S2N_ERR_DH_PARAMETER_CHECK); - POSIX_ENSURE(codes == 0, S2N_ERR_DH_PARAMETER_CHECK); - - return S2N_SUCCESS; -} - -int s2n_dh_params_copy(struct s2n_dh_params *from, struct s2n_dh_params *to) -{ - POSIX_GUARD(s2n_check_p_g_dh_params(from)); - POSIX_ENSURE_REF(to); - - to->dh = DHparams_dup(from->dh); - POSIX_ENSURE(to->dh != NULL, S2N_ERR_DH_COPYING_PARAMETERS); - - return S2N_SUCCESS; -} - -int s2n_dh_generate_ephemeral_key(struct s2n_dh_params *dh_params) -{ - POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); - - POSIX_GUARD_OSSL(DH_generate_key(dh_params->dh), S2N_ERR_DH_GENERATING_PARAMETERS); - - return S2N_SUCCESS; -} - -int s2n_dh_params_free(struct s2n_dh_params *dh_params) -{ - POSIX_ENSURE_REF(dh_params); - DH_free(dh_params->dh); - dh_params->dh = NULL; - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_dhe.h" + +#include +#include +#include +#include + +#include "crypto/s2n_openssl.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +#define S2N_MIN_DH_PRIME_SIZE_BYTES (2048 / 8) + +/* Caller is not responsible for freeing values returned by these accessors + * Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html + */ +static const BIGNUM *s2n_get_Ys_dh_param(struct s2n_dh_params *dh_params) +{ + const BIGNUM *Ys = NULL; + +/* DH made opaque in Openssl 1.1.0 */ +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + DH_get0_key(dh_params->dh, &Ys, NULL); +#else + Ys = dh_params->dh->pub_key; +#endif + + return Ys; +} + +static const BIGNUM *s2n_get_p_dh_param(struct s2n_dh_params *dh_params) +{ + const BIGNUM *p = NULL; +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + DH_get0_pqg(dh_params->dh, &p, NULL, NULL); +#else + p = dh_params->dh->p; +#endif + + return p; +} + +static const BIGNUM *s2n_get_g_dh_param(struct s2n_dh_params *dh_params) +{ + const BIGNUM *g = NULL; +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + DH_get0_pqg(dh_params->dh, NULL, NULL, &g); +#else + g = dh_params->dh->g; +#endif + + return g; +} + +static int s2n_check_p_g_dh_params(struct s2n_dh_params *dh_params) +{ + POSIX_ENSURE_REF(dh_params); + POSIX_ENSURE_REF(dh_params->dh); + + const BIGNUM *p = s2n_get_p_dh_param(dh_params); + const BIGNUM *g = s2n_get_g_dh_param(dh_params); + + POSIX_ENSURE_REF(g); + POSIX_ENSURE_REF(p); + + S2N_ERROR_IF(DH_size(dh_params->dh) < S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_PARAMS_CREATE); + S2N_ERROR_IF(BN_is_zero(g), S2N_ERR_DH_PARAMS_CREATE); + S2N_ERROR_IF(BN_is_zero(p), S2N_ERR_DH_PARAMS_CREATE); + + return S2N_SUCCESS; +} + +static int s2n_check_pub_key_dh_params(struct s2n_dh_params *dh_params) +{ + const BIGNUM *pub_key = s2n_get_Ys_dh_param(dh_params); + POSIX_ENSURE_REF(pub_key); + + /* + * https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5 + * + * The following algorithm MAY be used to validate a received public + * key y. + * + * 1. Verify that y lies within the interval [2,p-1]. + * If it does not, the key is invalid. + * + * This check is optional per the RFC, but applied here as + * defense-in-depth to reject degenerate public key values. + */ + S2N_ERROR_IF(BN_is_zero(pub_key), S2N_ERR_DH_PARAMS_CREATE); + S2N_ERROR_IF(BN_is_one(pub_key), S2N_ERR_DH_PARAMS_CREATE); + + const BIGNUM *p = s2n_get_p_dh_param(dh_params); + POSIX_ENSURE_REF(p); + + BIGNUM *p_minus_one = BN_dup(p); + POSIX_ENSURE_REF(p_minus_one); + if (!BN_sub_word(p_minus_one, 1)) { + BN_free(p_minus_one); + POSIX_BAIL(S2N_ERR_DH_PARAMS_CREATE); + } + int cmp = BN_cmp(pub_key, p_minus_one); + BN_free(p_minus_one); + S2N_ERROR_IF(cmp > 0, S2N_ERR_DH_PARAMS_CREATE); + + return S2N_SUCCESS; +} + +static int s2n_set_p_g_Ys_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *p, struct s2n_blob *g, + struct s2n_blob *Ys) +{ + POSIX_ENSURE(p->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); + POSIX_ENSURE(g->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); + POSIX_ENSURE(Ys->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); + BIGNUM *bn_p = BN_bin2bn((const unsigned char *) p->data, p->size, NULL); + BIGNUM *bn_g = BN_bin2bn((const unsigned char *) g->data, g->size, NULL); + BIGNUM *bn_Ys = BN_bin2bn((const unsigned char *) Ys->data, Ys->size, NULL); + +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + /* Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html: + * values that have been passed in should not be freed directly after this function has been called + */ + POSIX_GUARD_OSSL(DH_set0_pqg(dh_params->dh, bn_p, NULL, bn_g), S2N_ERR_DH_PARAMS_CREATE); + + /* Same as DH_set0_pqg */ + POSIX_GUARD_OSSL(DH_set0_key(dh_params->dh, bn_Ys, NULL), S2N_ERR_DH_PARAMS_CREATE); +#else + dh_params->dh->p = bn_p; + dh_params->dh->g = bn_g; + dh_params->dh->pub_key = bn_Ys; +#endif + + return S2N_SUCCESS; +} + +int s2n_check_all_dh_params(struct s2n_dh_params *dh_params) +{ + POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); + POSIX_GUARD(s2n_check_pub_key_dh_params(dh_params)); + + return S2N_SUCCESS; +} + +int s2n_pkcs3_to_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *pkcs3) +{ + POSIX_ENSURE_REF(dh_params); + POSIX_PRECONDITION(s2n_blob_validate(pkcs3)); + DEFER_CLEANUP(struct s2n_dh_params temp_dh_params = { 0 }, s2n_dh_params_free); + + uint8_t *original_ptr = pkcs3->data; + temp_dh_params.dh = d2i_DHparams(NULL, (const unsigned char **) (void *) &pkcs3->data, pkcs3->size); + + POSIX_GUARD(s2n_check_p_g_dh_params(&temp_dh_params)); + + if (pkcs3->data) { + POSIX_ENSURE_GTE(pkcs3->data, original_ptr); + POSIX_ENSURE((uint32_t) (pkcs3->data - original_ptr) == pkcs3->size, S2N_ERR_INVALID_PKCS3); + } + + pkcs3->data = original_ptr; + + /* Require at least 2048 bits for the DH size */ + POSIX_ENSURE(DH_size(temp_dh_params.dh) >= S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_TOO_SMALL); + + /* Check the generator and prime */ + POSIX_GUARD(s2n_dh_params_check(&temp_dh_params)); + + dh_params->dh = temp_dh_params.dh; + + ZERO_TO_DISABLE_DEFER_CLEANUP(temp_dh_params); + + return S2N_SUCCESS; +} + +int s2n_dh_p_g_Ys_to_dh_params(struct s2n_dh_params *server_dh_params, struct s2n_blob *p, struct s2n_blob *g, + struct s2n_blob *Ys) +{ + POSIX_ENSURE_REF(server_dh_params); + POSIX_PRECONDITION(s2n_blob_validate(p)); + POSIX_PRECONDITION(s2n_blob_validate(g)); + POSIX_PRECONDITION(s2n_blob_validate(Ys)); + + server_dh_params->dh = DH_new(); + POSIX_ENSURE(server_dh_params->dh != NULL, S2N_ERR_DH_PARAMS_CREATE); + + POSIX_GUARD(s2n_set_p_g_Ys_dh_params(server_dh_params, p, g, Ys)); + POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); + + return S2N_SUCCESS; +} + +int s2n_dh_params_to_p_g_Ys(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *out, struct s2n_blob *output) +{ + POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); + POSIX_PRECONDITION(s2n_stuffer_validate(out)); + POSIX_PRECONDITION(s2n_blob_validate(output)); + + const BIGNUM *bn_p = s2n_get_p_dh_param(server_dh_params); + const BIGNUM *bn_g = s2n_get_g_dh_param(server_dh_params); + const BIGNUM *bn_Ys = s2n_get_Ys_dh_param(server_dh_params); + + uint16_t p_size = BN_num_bytes(bn_p); + uint16_t g_size = BN_num_bytes(bn_g); + uint16_t Ys_size = BN_num_bytes(bn_Ys); + uint8_t *p = NULL; + uint8_t *g = NULL; + uint8_t *Ys = NULL; + + output->data = s2n_stuffer_raw_write(out, 0); + POSIX_ENSURE_REF(output->data); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, p_size)); + p = s2n_stuffer_raw_write(out, p_size); + POSIX_ENSURE_REF(p); + POSIX_ENSURE(BN_bn2bin(bn_p, p) == p_size, S2N_ERR_DH_SERIALIZING); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, g_size)); + g = s2n_stuffer_raw_write(out, g_size); + POSIX_ENSURE_REF(g); + POSIX_ENSURE(BN_bn2bin(bn_g, g) == g_size, S2N_ERR_DH_SERIALIZING); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, Ys_size)); + Ys = s2n_stuffer_raw_write(out, Ys_size); + POSIX_ENSURE_REF(Ys); + POSIX_ENSURE(BN_bn2bin(bn_Ys, Ys) == Ys_size, S2N_ERR_DH_SERIALIZING); + + output->size = p_size + 2 + g_size + 2 + Ys_size + 2; + + return S2N_SUCCESS; +} + +int s2n_dh_compute_shared_secret_as_client(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_out, + struct s2n_blob *shared_key) +{ + struct s2n_dh_params client_params = { 0 }; + uint8_t *client_pub_key = NULL; + uint16_t client_pub_key_size = 0; + int shared_key_size = 0; + + POSIX_GUARD(s2n_dh_params_check(server_dh_params)); + POSIX_GUARD(s2n_dh_params_copy(server_dh_params, &client_params)); + POSIX_GUARD(s2n_dh_generate_ephemeral_key(&client_params)); + POSIX_GUARD(s2n_alloc(shared_key, DH_size(server_dh_params->dh))); + + const BIGNUM *client_pub_key_bn = s2n_get_Ys_dh_param(&client_params); + POSIX_ENSURE_REF(client_pub_key_bn); + client_pub_key_size = BN_num_bytes(client_pub_key_bn); + POSIX_GUARD(s2n_stuffer_write_uint16(Yc_out, client_pub_key_size)); + client_pub_key = s2n_stuffer_raw_write(Yc_out, client_pub_key_size); + if (client_pub_key == NULL) { + POSIX_GUARD(s2n_free(shared_key)); + POSIX_GUARD(s2n_dh_params_free(&client_params)); + POSIX_BAIL(S2N_ERR_DH_WRITING_PUBLIC_KEY); + } + + if (BN_bn2bin(client_pub_key_bn, client_pub_key) != client_pub_key_size) { + POSIX_GUARD(s2n_free(shared_key)); + POSIX_GUARD(s2n_dh_params_free(&client_params)); + POSIX_BAIL(S2N_ERR_DH_COPYING_PUBLIC_KEY); + } + + /* server_dh_params already validated */ + const BIGNUM *server_pub_key_bn = s2n_get_Ys_dh_param(server_dh_params); + shared_key_size = DH_compute_key(shared_key->data, server_pub_key_bn, client_params.dh); + if (shared_key_size < 0) { + POSIX_GUARD(s2n_free(shared_key)); + POSIX_GUARD(s2n_dh_params_free(&client_params)); + POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); + } + + shared_key->size = shared_key_size; + + POSIX_GUARD(s2n_dh_params_free(&client_params)); + + return S2N_SUCCESS; +} + +int s2n_dh_compute_shared_secret_as_server(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_in, + struct s2n_blob *shared_key) +{ + uint16_t Yc_length = 0; + struct s2n_blob Yc = { 0 }; + int shared_key_size = 0; + BIGNUM *pub_key = NULL; + + POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); + int server_dh_params_size = DH_size(server_dh_params->dh); + POSIX_ENSURE(server_dh_params_size <= INT32_MAX, S2N_ERR_INTEGER_OVERFLOW); + + /* + * As defined in https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.2, + * the client's DH public value (Yc) is sent as a variable-length opaque value. + * Validate that Yc_length does not exceed the DH group size to prevent + * unnecessary computation and memory allocation on oversized keys. + * + * According to https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5, + * the valid range of Yc is [2, p-1]. When encoding a BIGNUM to bytes, + * leading zeros are often stripped, in which case Yc_length might be + * less than server_dh_params_size. + */ + POSIX_GUARD(s2n_stuffer_read_uint16(Yc_in, &Yc_length)); + POSIX_ENSURE(Yc_length > 0, S2N_ERR_DH_SHARED_SECRET); + POSIX_ENSURE((int) Yc_length <= server_dh_params_size, S2N_ERR_DH_SHARED_SECRET); + + Yc.size = Yc_length; + Yc.data = s2n_stuffer_raw_read(Yc_in, Yc.size); + POSIX_ENSURE_REF(Yc.data); + + pub_key = BN_bin2bn((const unsigned char *) Yc.data, Yc.size, NULL); + POSIX_ENSURE_REF(pub_key); + POSIX_GUARD(s2n_alloc(shared_key, server_dh_params_size)); + + shared_key_size = DH_compute_key(shared_key->data, pub_key, server_dh_params->dh); + if (shared_key_size <= 0) { + BN_free(pub_key); + POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); + } + + shared_key->size = shared_key_size; + + BN_free(pub_key); + + return S2N_SUCCESS; +} + +int s2n_dh_params_check(struct s2n_dh_params *dh_params) +{ + POSIX_ENSURE_REF(dh_params); + POSIX_ENSURE_REF(dh_params->dh); + int codes = 0; + + POSIX_GUARD_OSSL(DH_check(dh_params->dh, &codes), S2N_ERR_DH_PARAMETER_CHECK); + POSIX_ENSURE(codes == 0, S2N_ERR_DH_PARAMETER_CHECK); + + return S2N_SUCCESS; +} + +int s2n_dh_params_copy(struct s2n_dh_params *from, struct s2n_dh_params *to) +{ + POSIX_GUARD(s2n_check_p_g_dh_params(from)); + POSIX_ENSURE_REF(to); + + to->dh = DHparams_dup(from->dh); + POSIX_ENSURE(to->dh != NULL, S2N_ERR_DH_COPYING_PARAMETERS); + + return S2N_SUCCESS; +} + +int s2n_dh_generate_ephemeral_key(struct s2n_dh_params *dh_params) +{ + POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); + + POSIX_GUARD_OSSL(DH_generate_key(dh_params->dh), S2N_ERR_DH_GENERATING_PARAMETERS); + + return S2N_SUCCESS; +} + +int s2n_dh_params_free(struct s2n_dh_params *dh_params) +{ + POSIX_ENSURE_REF(dh_params); + DH_free(dh_params->dh); + dh_params->dh = NULL; + + return S2N_SUCCESS; +} diff --git a/crypto/s2n_hkdf.c b/crypto/s2n_hkdf.c index b70a9826b57..06273c1413c 100644 --- a/crypto/s2n_hkdf.c +++ b/crypto/s2n_hkdf.c @@ -1,394 +1,395 @@ -/* - * 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 "crypto/s2n_hkdf.h" - -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hmac.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF - #include -#endif - -#define MAX_DIGEST_SIZE 64 /* Current highest is SHA512 */ -#define MAX_HKDF_ROUNDS 255 - -/* Reference: RFC 5869 */ - -struct s2n_hkdf_impl { - int (*hkdf)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output); - int (*hkdf_extract)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key); - int (*hkdf_expand)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, - const struct s2n_blob *info, struct s2n_blob *output); -}; - -static int s2n_custom_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - uint8_t hmac_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); - POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); - pseudo_rand_key->size = hmac_size; - - POSIX_GUARD(s2n_hmac_init(hmac, alg, salt->data, salt->size)); - POSIX_GUARD(s2n_hmac_update(hmac, key->data, key->size)); - POSIX_GUARD(s2n_hmac_digest(hmac, pseudo_rand_key->data, pseudo_rand_key->size)); - - POSIX_GUARD(s2n_hmac_reset(hmac)); - - return S2N_SUCCESS; -} - -static int s2n_custom_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - uint8_t prev[MAX_DIGEST_SIZE] = { 0 }; - - uint32_t done_len = 0; - uint8_t hash_len = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &hash_len)); - POSIX_ENSURE_GT(hash_len, 0); - uint32_t total_rounds = output->size / hash_len; - if (output->size % hash_len) { - total_rounds++; - } - - POSIX_ENSURE(total_rounds > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - POSIX_ENSURE(total_rounds <= MAX_HKDF_ROUNDS, S2N_ERR_HKDF_OUTPUT_SIZE); - - for (uint32_t curr_round = 1; curr_round <= total_rounds; curr_round++) { - uint32_t cat_len = 0; - POSIX_GUARD(s2n_hmac_init(hmac, alg, pseudo_rand_key->data, pseudo_rand_key->size)); - if (curr_round != 1) { - POSIX_GUARD(s2n_hmac_update(hmac, prev, hash_len)); - } - POSIX_GUARD(s2n_hmac_update(hmac, info->data, info->size)); - uint8_t curr_round_byte = curr_round; - POSIX_GUARD(s2n_hmac_update(hmac, &curr_round_byte, 1)); - POSIX_GUARD(s2n_hmac_digest(hmac, prev, hash_len)); - - cat_len = hash_len; - if (done_len + hash_len > output->size) { - cat_len = output->size - done_len; - } - - POSIX_CHECKED_MEMCPY(output->data + done_len, prev, cat_len); - - done_len += cat_len; - - POSIX_GUARD(s2n_hmac_reset(hmac)); - } - - return S2N_SUCCESS; -} - -static int s2n_custom_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - uint8_t prk_pad[MAX_DIGEST_SIZE] = { 0 }; - struct s2n_blob pseudo_rand_key = { 0 }; - POSIX_GUARD(s2n_blob_init(&pseudo_rand_key, prk_pad, sizeof(prk_pad))); - - POSIX_GUARD(s2n_custom_hkdf_extract(hmac, alg, salt, key, &pseudo_rand_key)); - POSIX_GUARD(s2n_custom_hkdf_expand(hmac, alg, &pseudo_rand_key, info, output)); - - return S2N_SUCCESS; -} - -const struct s2n_hkdf_impl s2n_custom_hkdf_impl = { - .hkdf = &s2n_custom_hkdf, - .hkdf_extract = &s2n_custom_hkdf_extract, - .hkdf_expand = &s2n_custom_hkdf_expand, -}; - -#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF -static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - const EVP_MD *digest = NULL; - POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); - - /* The out_len argument of HKDF_extract is set to the number of bytes written to out_key, and - * is not used to ensure that out_key is large enough to contain the PRK. Ensure that the PRK - * output will fit in the blob. - */ - uint8_t hmac_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); - POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); - - size_t bytes_written = 0; - POSIX_GUARD_OSSL(HKDF_extract(pseudo_rand_key->data, &bytes_written, digest, key->data, key->size, - salt->data, salt->size), - S2N_ERR_HKDF); - - /* HKDF_extract updates the out_len argument based on the digest size. Update the blob's size based on this. */ - POSIX_ENSURE_LTE(bytes_written, pseudo_rand_key->size); - pseudo_rand_key->size = bytes_written; - - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - - const EVP_MD *digest = NULL; - POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); - - POSIX_GUARD_OSSL(HKDF_expand(output->data, output->size, digest, pseudo_rand_key->data, pseudo_rand_key->size, - info->data, info->size), - S2N_ERR_HKDF); - - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - - const EVP_MD *digest = NULL; - POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); - - POSIX_GUARD_OSSL(HKDF(output->data, output->size, digest, key->data, key->size, salt->data, salt->size, - info->data, info->size), - S2N_ERR_HKDF); - - return S2N_SUCCESS; -} - -bool s2n_libcrypto_supports_hkdf() -{ - return true; -} - -#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0) - - #include "crypto/s2n_kdf.h" - -static S2N_RESULT s2n_hkdf_kdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *salt, const struct s2n_blob *key, const struct s2n_blob *info, - struct s2n_blob *output, int mode) -{ - /* As an optimization, we should be able to fetch and cache this EVP_KDF* - * once when s2n_init is called. - */ - DEFER_CLEANUP(EVP_KDF *hkdf_impl = EVP_KDF_fetch(NULL, "HKDF", NULL), - EVP_KDF_free_pointer); - RESULT_ENSURE(hkdf_impl, S2N_ERR_PRF_INVALID_ALGORITHM); - - DEFER_CLEANUP(EVP_KDF_CTX *hkdf_ctx = EVP_KDF_CTX_new(hkdf_impl), - EVP_KDF_CTX_free_pointer); - RESULT_ENSURE_REF(hkdf_ctx); - - const EVP_MD *digest = NULL; - RESULT_GUARD(s2n_hmac_md_from_alg(alg, &digest)); - RESULT_ENSURE_REF(digest); - const char *digest_name = EVP_MD_get0_name(digest); - RESULT_ENSURE_REF(digest_name); - - OSSL_PARAM params[] = { - S2N_OSSL_PARAM_INT(OSSL_KDF_PARAM_MODE, mode), - S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_KEY, key), - S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_INFO, info), - S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SALT, salt), - /* Casting away the const is safe because providers are forbidden from - * modifying any OSSL_PARAM value other than return_size. - * Even the examples in the Openssl documentation cast const strings to - * non-const void pointers when setting up OSSL_PARAMs. - */ - S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name), - OSSL_PARAM_END, - }; - - /* From the HKDF docs (https://docs.openssl.org/3.1/man7/EVP_KDF-HKDF/): - * > When using EVP_KDF_HKDF_MODE_EXTRACT_ONLY the keylen parameter must equal - * > the size of the intermediate fixed-length pseudorandom key otherwise an - * > error will occur. - */ - if (mode == EVP_KDF_HKDF_MODE_EXTRACT_ONLY) { - RESULT_GUARD_OSSL(EVP_KDF_CTX_set_params(hkdf_ctx, params), S2N_ERR_HKDF); - size_t key_size = EVP_KDF_CTX_get_kdf_size(hkdf_ctx); - RESULT_ENSURE(key_size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - RESULT_ENSURE(key_size <= output->size, S2N_ERR_HKDF_OUTPUT_SIZE); - output->size = key_size; - } - - RESULT_GUARD_OSSL(EVP_KDF_derive(hkdf_ctx, output->data, output->size, params), - S2N_ERR_HKDF); - return S2N_RESULT_OK; -} - -static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *salt, const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - struct s2n_blob empty_info = { 0 }; - POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, &empty_info, pseudo_rand_key, - EVP_KDF_HKDF_MODE_EXTRACT_ONLY)); - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - struct s2n_blob empty_salt = { 0 }; - POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, &empty_salt, pseudo_rand_key, info, output, - EVP_KDF_HKDF_MODE_EXPAND_ONLY)); - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, info, output, - EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND)); - return S2N_SUCCESS; -} - -bool s2n_libcrypto_supports_hkdf() -{ - return true; -} - -#else - -static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -bool s2n_libcrypto_supports_hkdf() -{ - return false; -} - -#endif /* S2N_LIBCRYPTO_SUPPORTS_HKDF */ - -const struct s2n_hkdf_impl s2n_libcrypto_hkdf_impl = { - .hkdf = &s2n_libcrypto_hkdf, - .hkdf_extract = &s2n_libcrypto_hkdf_extract, - .hkdf_expand = &s2n_libcrypto_hkdf_expand, -}; - -static const struct s2n_hkdf_impl *s2n_get_hkdf_implementation() -{ - /* By default, s2n-tls uses a custom HKDF implementation. When operating in FIPS mode, the - * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. - */ - if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_hkdf()) { - return &s2n_libcrypto_hkdf_impl; - } - - return &s2n_custom_hkdf_impl; -} - -int s2n_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - POSIX_ENSURE_REF(hmac); - POSIX_ENSURE_REF(salt); - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(pseudo_rand_key); - - const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); - POSIX_ENSURE_REF(hkdf_implementation); - - POSIX_GUARD(hkdf_implementation->hkdf_extract(hmac, alg, salt, key, pseudo_rand_key)); - - return S2N_SUCCESS; -} - -static int s2n_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, - const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(hmac); - POSIX_ENSURE_REF(pseudo_rand_key); - POSIX_ENSURE_REF(info); - POSIX_ENSURE_REF(output); - - const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); - POSIX_ENSURE_REF(hkdf_implementation); - - POSIX_GUARD(hkdf_implementation->hkdf_expand(hmac, alg, pseudo_rand_key, info, output)); - - return S2N_SUCCESS; -} - -int s2n_hkdf_expand_label(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *secret, const struct s2n_blob *label, - const struct s2n_blob *context, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(label); - POSIX_ENSURE_REF(context); - POSIX_ENSURE_REF(output); - - /* Per RFC8446: 7.1, a HKDF label is a 2 byte length field, and two 1...255 byte arrays with a one byte length field each. */ - uint8_t hkdf_label_buf[2 + 256 + 256]; - struct s2n_blob hkdf_label_blob = { 0 }; - struct s2n_stuffer hkdf_label = { 0 }; - - POSIX_ENSURE_LTE(label->size, S2N_MAX_HKDF_EXPAND_LABEL_LENGTH); - - POSIX_GUARD(s2n_blob_init(&hkdf_label_blob, hkdf_label_buf, sizeof(hkdf_label_buf))); - POSIX_GUARD(s2n_stuffer_init(&hkdf_label, &hkdf_label_blob)); - POSIX_GUARD(s2n_stuffer_write_uint16(&hkdf_label, output->size)); - POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, label->size + sizeof("tls13 ") - 1)); - POSIX_GUARD(s2n_stuffer_write_str(&hkdf_label, "tls13 ")); - POSIX_GUARD(s2n_stuffer_write(&hkdf_label, label)); - POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, context->size)); - POSIX_GUARD(s2n_stuffer_write(&hkdf_label, context)); - - hkdf_label_blob.size = s2n_stuffer_data_available(&hkdf_label); - POSIX_GUARD(s2n_hkdf_expand(hmac, alg, secret, &hkdf_label_blob, output)); - - return S2N_SUCCESS; -} - -int s2n_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(hmac); - POSIX_ENSURE_REF(salt); - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(info); - POSIX_ENSURE_REF(output); - - const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); - POSIX_ENSURE_REF(hkdf_implementation); - - POSIX_GUARD(hkdf_implementation->hkdf(hmac, alg, salt, key, info, output)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hkdf.h" + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hmac.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF + #include +#endif + +#define MAX_DIGEST_SIZE 64 /* Current highest is SHA512 */ +#define MAX_HKDF_ROUNDS 255 + +/* Reference: RFC 5869 */ + +struct s2n_hkdf_impl { + int (*hkdf)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output); + int (*hkdf_extract)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key); + int (*hkdf_expand)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, + const struct s2n_blob *info, struct s2n_blob *output); +}; + +static int s2n_custom_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + uint8_t hmac_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); + POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); + pseudo_rand_key->size = hmac_size; + + POSIX_GUARD(s2n_hmac_init(hmac, alg, salt->data, salt->size)); + POSIX_GUARD(s2n_hmac_update(hmac, key->data, key->size)); + POSIX_GUARD(s2n_hmac_digest(hmac, pseudo_rand_key->data, pseudo_rand_key->size)); + + POSIX_GUARD(s2n_hmac_reset(hmac)); + + return S2N_SUCCESS; +} + +static int s2n_custom_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + uint8_t prev[MAX_DIGEST_SIZE] = { 0 }; + + uint32_t done_len = 0; + uint8_t hash_len = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &hash_len)); + POSIX_ENSURE_GT(hash_len, 0); + uint32_t total_rounds = output->size / hash_len; + if (output->size % hash_len) { + total_rounds++; + } + + POSIX_ENSURE(total_rounds > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + POSIX_ENSURE(total_rounds <= MAX_HKDF_ROUNDS, S2N_ERR_HKDF_OUTPUT_SIZE); + + for (uint32_t curr_round = 1; curr_round <= total_rounds; curr_round++) { + uint32_t cat_len = 0; + POSIX_GUARD(s2n_hmac_init(hmac, alg, pseudo_rand_key->data, pseudo_rand_key->size)); + if (curr_round != 1) { + POSIX_GUARD(s2n_hmac_update(hmac, prev, hash_len)); + } + POSIX_GUARD(s2n_hmac_update(hmac, info->data, info->size)); + uint8_t curr_round_byte = curr_round; + POSIX_GUARD(s2n_hmac_update(hmac, &curr_round_byte, 1)); + POSIX_GUARD(s2n_hmac_digest(hmac, prev, hash_len)); + + cat_len = hash_len; + if (done_len + hash_len > output->size) { + cat_len = output->size - done_len; + } + + POSIX_CHECKED_MEMCPY(output->data + done_len, prev, cat_len); + + done_len += cat_len; + + POSIX_GUARD(s2n_hmac_reset(hmac)); + } + + return S2N_SUCCESS; +} + +static int s2n_custom_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + uint8_t prk_pad[MAX_DIGEST_SIZE] = { 0 }; + struct s2n_blob pseudo_rand_key = { 0 }; + POSIX_GUARD(s2n_blob_init(&pseudo_rand_key, prk_pad, sizeof(prk_pad))); + + POSIX_GUARD(s2n_custom_hkdf_extract(hmac, alg, salt, key, &pseudo_rand_key)); + POSIX_GUARD(s2n_custom_hkdf_expand(hmac, alg, &pseudo_rand_key, info, output)); + + return S2N_SUCCESS; +} + +const struct s2n_hkdf_impl s2n_custom_hkdf_impl = { + .hkdf = &s2n_custom_hkdf, + .hkdf_extract = &s2n_custom_hkdf_extract, + .hkdf_expand = &s2n_custom_hkdf_expand, +}; + +#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF +static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + const EVP_MD *digest = NULL; + POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); + + /* The out_len argument of HKDF_extract is set to the number of bytes written to out_key, and + * is not used to ensure that out_key is large enough to contain the PRK. Ensure that the PRK + * output will fit in the blob. + */ + uint8_t hmac_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); + POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); + + size_t bytes_written = 0; + POSIX_GUARD_OSSL(HKDF_extract(pseudo_rand_key->data, &bytes_written, digest, key->data, key->size, + salt->data, salt->size), + S2N_ERR_HKDF); + + /* HKDF_extract updates the out_len argument based on the digest size. Update the blob's size based on this. */ + POSIX_ENSURE_LTE(bytes_written, pseudo_rand_key->size); + pseudo_rand_key->size = bytes_written; + + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + + const EVP_MD *digest = NULL; + POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); + + POSIX_GUARD_OSSL(HKDF_expand(output->data, output->size, digest, pseudo_rand_key->data, pseudo_rand_key->size, + info->data, info->size), + S2N_ERR_HKDF); + + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + + const EVP_MD *digest = NULL; + POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); + + POSIX_GUARD_OSSL(HKDF(output->data, output->size, digest, key->data, key->size, salt->data, salt->size, + info->data, info->size), + S2N_ERR_HKDF); + + return S2N_SUCCESS; +} + +bool s2n_libcrypto_supports_hkdf() +{ + return true; +} + +#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0) + + #include "crypto/s2n_kdf.h" + +static S2N_RESULT s2n_hkdf_kdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *salt, const struct s2n_blob *key, const struct s2n_blob *info, + struct s2n_blob *output, int mode) +{ + /* As an optimization, we should be able to fetch and cache this EVP_KDF* + * once when s2n_init is called. + */ + DEFER_CLEANUP(EVP_KDF *hkdf_impl = EVP_KDF_fetch(NULL, "HKDF", NULL), + EVP_KDF_free_pointer); + RESULT_ENSURE(hkdf_impl, S2N_ERR_PRF_INVALID_ALGORITHM); + + DEFER_CLEANUP(EVP_KDF_CTX *hkdf_ctx = EVP_KDF_CTX_new(hkdf_impl), + EVP_KDF_CTX_free_pointer); + RESULT_ENSURE_REF(hkdf_ctx); + + const EVP_MD *digest = NULL; + RESULT_GUARD(s2n_hmac_md_from_alg(alg, &digest)); + RESULT_ENSURE_REF(digest); + const char *digest_name = EVP_MD_get0_name(digest); + RESULT_ENSURE_REF(digest_name); + + OSSL_PARAM params[] = { + S2N_OSSL_PARAM_INT(OSSL_KDF_PARAM_MODE, mode), + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_KEY, key), + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_INFO, info), + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SALT, salt), + /* Casting away the const is safe because providers are forbidden from + * modifying any OSSL_PARAM value other than return_size. + * Even the examples in the Openssl documentation cast const strings to + * non-const void pointers when setting up OSSL_PARAMs. + */ + S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name), + OSSL_PARAM_END, + }; + + /* From the HKDF docs (https://docs.openssl.org/3.1/man7/EVP_KDF-HKDF/): + * > When using EVP_KDF_HKDF_MODE_EXTRACT_ONLY the keylen parameter must equal + * > the size of the intermediate fixed-length pseudorandom key otherwise an + * > error will occur. + */ + if (mode == EVP_KDF_HKDF_MODE_EXTRACT_ONLY) { + RESULT_GUARD_OSSL(EVP_KDF_CTX_set_params(hkdf_ctx, params), S2N_ERR_HKDF); + size_t key_size = EVP_KDF_CTX_get_kdf_size(hkdf_ctx); + RESULT_ENSURE(key_size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + RESULT_ENSURE(key_size <= output->size, S2N_ERR_HKDF_OUTPUT_SIZE); + output->size = key_size; + } + + RESULT_GUARD_OSSL(EVP_KDF_derive(hkdf_ctx, output->data, output->size, params), + S2N_ERR_HKDF); + return S2N_RESULT_OK; +} + +static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *salt, const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + struct s2n_blob empty_info = { 0 }; + POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, &empty_info, pseudo_rand_key, + EVP_KDF_HKDF_MODE_EXTRACT_ONLY)); + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + struct s2n_blob empty_salt = { 0 }; + POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, &empty_salt, pseudo_rand_key, info, output, + EVP_KDF_HKDF_MODE_EXPAND_ONLY)); + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, info, output, + EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND)); + return S2N_SUCCESS; +} + +bool s2n_libcrypto_supports_hkdf() +{ + return true; +} + +#else + +static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +bool s2n_libcrypto_supports_hkdf() +{ + return false; +} + +#endif /* S2N_LIBCRYPTO_SUPPORTS_HKDF */ + +const struct s2n_hkdf_impl s2n_libcrypto_hkdf_impl = { + .hkdf = &s2n_libcrypto_hkdf, + .hkdf_extract = &s2n_libcrypto_hkdf_extract, + .hkdf_expand = &s2n_libcrypto_hkdf_expand, +}; + +static const struct s2n_hkdf_impl *s2n_get_hkdf_implementation() +{ + /* By default, s2n-tls uses a custom HKDF implementation. When operating in FIPS mode, the + * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. + */ + if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_hkdf()) { + return &s2n_libcrypto_hkdf_impl; + } + + return &s2n_custom_hkdf_impl; +} + +int s2n_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + POSIX_ENSURE_REF(hmac); + POSIX_ENSURE_REF(salt); + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(pseudo_rand_key); + + const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); + POSIX_ENSURE_REF(hkdf_implementation); + + POSIX_GUARD(hkdf_implementation->hkdf_extract(hmac, alg, salt, key, pseudo_rand_key)); + + return S2N_SUCCESS; +} + +static int s2n_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, + const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(hmac); + POSIX_ENSURE_REF(pseudo_rand_key); + POSIX_ENSURE_REF(info); + POSIX_ENSURE_REF(output); + + const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); + POSIX_ENSURE_REF(hkdf_implementation); + + POSIX_GUARD(hkdf_implementation->hkdf_expand(hmac, alg, pseudo_rand_key, info, output)); + + return S2N_SUCCESS; +} + +int s2n_hkdf_expand_label(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *secret, const struct s2n_blob *label, + const struct s2n_blob *context, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(label); + POSIX_ENSURE_REF(context); + POSIX_ENSURE_REF(output); + + /* Per RFC8446: 7.1, a HKDF label is a 2 byte length field, and two 1...255 byte arrays with a one byte length field each. */ + uint8_t hkdf_label_buf[2 + 256 + 256]; + struct s2n_blob hkdf_label_blob = { 0 }; + struct s2n_stuffer hkdf_label = { 0 }; + + POSIX_ENSURE_LTE(label->size, S2N_MAX_HKDF_EXPAND_LABEL_LENGTH); + + POSIX_GUARD(s2n_blob_init(&hkdf_label_blob, hkdf_label_buf, sizeof(hkdf_label_buf))); + POSIX_GUARD(s2n_stuffer_init(&hkdf_label, &hkdf_label_blob)); + POSIX_GUARD(s2n_stuffer_write_uint16(&hkdf_label, output->size)); + POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, label->size + sizeof("tls13 ") - 1)); + POSIX_GUARD(s2n_stuffer_write_str(&hkdf_label, "tls13 ")); + POSIX_GUARD(s2n_stuffer_write(&hkdf_label, label)); + POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, context->size)); + POSIX_GUARD(s2n_stuffer_write(&hkdf_label, context)); + + hkdf_label_blob.size = s2n_stuffer_data_available(&hkdf_label); + POSIX_GUARD(s2n_hkdf_expand(hmac, alg, secret, &hkdf_label_blob, output)); + + return S2N_SUCCESS; +} + +int s2n_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(hmac); + POSIX_ENSURE_REF(salt); + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(info); + POSIX_ENSURE_REF(output); + + const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); + POSIX_ENSURE_REF(hkdf_implementation); + + POSIX_GUARD(hkdf_implementation->hkdf(hmac, alg, salt, key, info, output)); + + return S2N_SUCCESS; +} diff --git a/crypto/s2n_locking.c b/crypto/s2n_locking.c index 3f48b4dc376..42e8379acb2 100644 --- a/crypto/s2n_locking.c +++ b/crypto/s2n_locking.c @@ -1,118 +1,120 @@ -/* - * 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 "crypto/s2n_locking.h" - -#include -#include - -#include "crypto/s2n_openssl.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* Writing multithreaded applications using Openssl-1.0.2 - * requires calling CRYPTO_set_locking_callback. - * If the callback is not set, locks are no-ops and unexpected - * behavior may occur, particularly for RSA and X509. - * - * In the past s2n-tls relied on customers setting the callback - * themselves, but that seems unnecessary since other parts of - * the library (like fork detection) already rely on the pthreads library. - * - * For more information: - * https://www.openssl.org/blog/blog/2017/02/21/threads/ - * https://www.openssl.org/docs/man1.0.2/man3/threads.html - */ - -#define S2N_MUTEXES(mem) ((pthread_mutex_t *) (void *) (mem).data) - -/* While the locking-related APIs "exist" in later versions of - * Openssl, they tend to be placeholders or hardcoded values like: - * #define CRYPTO_get_locking_callback() (NULL) - * So the code will compile with strange warnings / errors like - * loop conditions always being false. - */ -#if !(S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) - -static struct s2n_blob mutexes_mem = { 0 }; -static size_t mutexes_count = 0; - -static void s2n_locking_cb(int mode, int n, char *file, int line) -{ - pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); - if (!mutexes_mem.data || n < 0 || (size_t) n >= mutexes_count) { - return; - } - - if (mode & CRYPTO_LOCK) { - pthread_mutex_lock(&(mutexes[n])); - } else { - pthread_mutex_unlock(&(mutexes[n])); - } -} - -S2N_RESULT s2n_locking_init(void) -{ - if (CRYPTO_get_locking_callback() != NULL) { - return S2N_RESULT_OK; - } - - int num_locks = CRYPTO_num_locks(); - RESULT_ENSURE_GTE(num_locks, 0); - - RESULT_GUARD_POSIX(s2n_realloc(&mutexes_mem, num_locks * sizeof(pthread_mutex_t))); - - pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); - mutexes_count = 0; - for (size_t i = 0; i < (size_t) num_locks; i++) { - RESULT_ENSURE_EQ(pthread_mutex_init(&(mutexes[i]), NULL), 0); - mutexes_count++; - } - - CRYPTO_set_locking_callback((void (*)()) s2n_locking_cb); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_locking_cleanup(void) -{ - if (CRYPTO_get_locking_callback() == (void (*)()) s2n_locking_cb) { - CRYPTO_set_locking_callback(NULL); - } - - pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); - if (mutexes) { - while (mutexes_count > 0) { - RESULT_ENSURE_EQ(pthread_mutex_destroy(&(mutexes[mutexes_count - 1])), 0); - mutexes_count--; - } - RESULT_GUARD_POSIX(s2n_free(&mutexes_mem)); - } - - return S2N_RESULT_OK; -} - -#else - -S2N_RESULT s2n_locking_init(void) -{ - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_locking_cleanup(void) -{ - return S2N_RESULT_OK; -} - -#endif +/* + * 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 "crypto/s2n_locking.h" + +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "crypto/s2n_openssl.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* Writing multithreaded applications using Openssl-1.0.2 + * requires calling CRYPTO_set_locking_callback. + * If the callback is not set, locks are no-ops and unexpected + * behavior may occur, particularly for RSA and X509. + * + * In the past s2n-tls relied on customers setting the callback + * themselves, but that seems unnecessary since other parts of + * the library (like fork detection) already rely on the pthreads library. + * + * For more information: + * https://www.openssl.org/blog/blog/2017/02/21/threads/ + * https://www.openssl.org/docs/man1.0.2/man3/threads.html + */ + +#define S2N_MUTEXES(mem) ((pthread_mutex_t *) (void *) (mem).data) + +/* While the locking-related APIs "exist" in later versions of + * Openssl, they tend to be placeholders or hardcoded values like: + * #define CRYPTO_get_locking_callback() (NULL) + * So the code will compile with strange warnings / errors like + * loop conditions always being false. + */ +#if !(S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) + +static struct s2n_blob mutexes_mem = { 0 }; +static size_t mutexes_count = 0; + +static void s2n_locking_cb(int mode, int n, char *file, int line) +{ + pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); + if (!mutexes_mem.data || n < 0 || (size_t) n >= mutexes_count) { + return; + } + + if (mode & CRYPTO_LOCK) { + pthread_mutex_lock(&(mutexes[n])); + } else { + pthread_mutex_unlock(&(mutexes[n])); + } +} + +S2N_RESULT s2n_locking_init(void) +{ + if (CRYPTO_get_locking_callback() != NULL) { + return S2N_RESULT_OK; + } + + int num_locks = CRYPTO_num_locks(); + RESULT_ENSURE_GTE(num_locks, 0); + + RESULT_GUARD_POSIX(s2n_realloc(&mutexes_mem, num_locks * sizeof(pthread_mutex_t))); + + pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); + mutexes_count = 0; + for (size_t i = 0; i < (size_t) num_locks; i++) { + RESULT_ENSURE_EQ(pthread_mutex_init(&(mutexes[i]), NULL), 0); + mutexes_count++; + } + + CRYPTO_set_locking_callback((void (*)()) s2n_locking_cb); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_locking_cleanup(void) +{ + if (CRYPTO_get_locking_callback() == (void (*)()) s2n_locking_cb) { + CRYPTO_set_locking_callback(NULL); + } + + pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); + if (mutexes) { + while (mutexes_count > 0) { + RESULT_ENSURE_EQ(pthread_mutex_destroy(&(mutexes[mutexes_count - 1])), 0); + mutexes_count--; + } + RESULT_GUARD_POSIX(s2n_free(&mutexes_mem)); + } + + return S2N_RESULT_OK; +} + +#else + +S2N_RESULT s2n_locking_init(void) +{ + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_locking_cleanup(void) +{ + return S2N_RESULT_OK; +} + +#endif diff --git a/crypto/s2n_openssl_x509.c b/crypto/s2n_openssl_x509.c index caf5205ec1c..17b76c93fb6 100644 --- a/crypto/s2n_openssl_x509.c +++ b/crypto/s2n_openssl_x509.c @@ -1,149 +1,150 @@ -/* - * 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 "crypto/s2n_openssl_x509.h" - -#include "api/s2n.h" - -DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY *, EVP_PKEY_free); -DEFINE_POINTER_CLEANUP_FUNC(EC_KEY *, EC_KEY_free); - -S2N_CLEANUP_RESULT s2n_openssl_x509_stack_pop_free(STACK_OF(X509) **cert_chain) -{ - RESULT_ENSURE_REF(*cert_chain); - sk_X509_pop_free(*cert_chain, X509_free); - *cert_chain = NULL; - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_openssl_asn1_time_free_pointer(ASN1_GENERALIZEDTIME **time_ptr) -{ - /* The ANS1_*TIME structs are just typedef wrappers around ASN1_STRING - * - * The ASN1_TIME, ASN1_UTCTIME and ASN1_GENERALIZEDTIME structures are - * represented as an ASN1_STRING internally and can be freed up using - * ASN1_STRING_free(). - * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_tm.html - */ - RESULT_ENSURE_REF(*time_ptr); - ASN1_STRING_free((ASN1_STRING *) *time_ptr); - *time_ptr = NULL; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_parse_impl(struct s2n_blob *asn1der, X509 **cert_out, uint32_t *parsed_length) -{ - RESULT_ENSURE_REF(asn1der); - RESULT_ENSURE_REF(asn1der->data); - RESULT_ENSURE_REF(cert_out); - RESULT_ENSURE_REF(parsed_length); - - uint8_t *cert_to_parse = asn1der->data; - *cert_out = d2i_X509(NULL, (const unsigned char **) (void *) &cert_to_parse, asn1der->size); - RESULT_ENSURE(*cert_out != NULL, S2N_ERR_DECODE_CERTIFICATE); - - /* If cert parsing is successful, d2i_X509 increments *cert_to_parse to the byte following the parsed data */ - *parsed_length = cert_to_parse - asn1der->data; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_parse_without_length_validation(struct s2n_blob *asn1der, X509 **cert_out) -{ - RESULT_ENSURE_REF(asn1der); - RESULT_ENSURE_REF(cert_out); - - uint32_t parsed_len = 0; - RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_parse(struct s2n_blob *asn1der, X509 **cert_out) -{ - RESULT_ENSURE_REF(asn1der); - RESULT_ENSURE_REF(cert_out); - - uint32_t parsed_len = 0; - RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); - - /* Some TLS clients in the wild send extra trailing bytes after the Certificate. - * Allow this in s2n for backwards compatibility with existing clients. */ - uint32_t trailing_bytes = asn1der->size - parsed_len; - RESULT_ENSURE(trailing_bytes <= S2N_MAX_ALLOWED_CERT_TRAILING_BYTES, S2N_ERR_DECODE_CERTIFICATE); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_get_cert_info(X509 *cert, struct s2n_cert_info *info) -{ - RESULT_ENSURE_REF(cert); - RESULT_ENSURE_REF(info); - - X509_NAME *issuer_name = X509_get_issuer_name(cert); - RESULT_ENSURE_REF(issuer_name); - - X509_NAME *subject_name = X509_get_subject_name(cert); - RESULT_ENSURE_REF(subject_name); - - if (X509_NAME_cmp(issuer_name, subject_name) == 0) { - info->self_signed = true; - } else { - info->self_signed = false; - } - -#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x02070000f) - RESULT_ENSURE_REF(cert->sig_alg); - info->signature_nid = OBJ_obj2nid(cert->sig_alg->algorithm); -#else - info->signature_nid = X509_get_signature_nid(cert); -#endif - - /* There is no method to directly retrieve the signature digest from the X509* - * that is available in all libcryptos, so instead we use find_sigid_algs. For - * a signature with NID_ecdsa_with_SHA256 this will return NID_SHA256 - * - * signature_digest_nid may not always be set. ML-DSA does not have an associated digest. - */ - int find_result = OBJ_find_sigid_algs(info->signature_nid, &info->signature_digest_nid, NULL); - if (find_result != 1) { - /* OBJ_find_sigid_algs may fail for ML-DSA-44 and ML-DSA-87, depending on the - * version of AWS-LC. See https://github.com/aws/aws-lc/issues/2347. - * - * In order to handle this bug, we interpret failures from OBJ_find_sigid_algs - * as signature_digest_nid==0, which is equivalent to an undefined digest. - */ - info->signature_digest_nid = 0; - } - - DEFER_CLEANUP(EVP_PKEY *pubkey = X509_get_pubkey(cert), EVP_PKEY_free_pointer); - RESULT_ENSURE(pubkey != NULL, S2N_ERR_DECODE_CERTIFICATE); - - info->public_key_bits = EVP_PKEY_bits(pubkey); - RESULT_ENSURE(info->public_key_bits > 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); - - if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { - DEFER_CLEANUP(EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pubkey), EC_KEY_free_pointer); - RESULT_ENSURE_REF(ec_key); - const EC_GROUP *ec_group = EC_KEY_get0_group(ec_key); - RESULT_ENSURE_REF(ec_group); - info->public_key_nid = EC_GROUP_get_curve_name(ec_group); - } else { - info->public_key_nid = EVP_PKEY_id(pubkey); - } - RESULT_ENSURE(info->public_key_nid != NID_undef, S2N_ERR_CERT_TYPE_UNSUPPORTED); - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_openssl_x509.h" + +#include "api/s2n.h" + +DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY *, EVP_PKEY_free); +DEFINE_POINTER_CLEANUP_FUNC(EC_KEY *, EC_KEY_free); + +S2N_CLEANUP_RESULT s2n_openssl_x509_stack_pop_free(STACK_OF(X509) **cert_chain) +{ + RESULT_ENSURE_REF(*cert_chain); + sk_X509_pop_free(*cert_chain, X509_free); + *cert_chain = NULL; + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_openssl_asn1_time_free_pointer(ASN1_GENERALIZEDTIME **time_ptr) +{ + /* The ANS1_*TIME structs are just typedef wrappers around ASN1_STRING + * + * The ASN1_TIME, ASN1_UTCTIME and ASN1_GENERALIZEDTIME structures are + * represented as an ASN1_STRING internally and can be freed up using + * ASN1_STRING_free(). + * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_tm.html + */ + RESULT_ENSURE_REF(*time_ptr); + ASN1_STRING_free((ASN1_STRING *) *time_ptr); + *time_ptr = NULL; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_parse_impl(struct s2n_blob *asn1der, X509 **cert_out, uint32_t *parsed_length) +{ + RESULT_ENSURE_REF(asn1der); + RESULT_ENSURE_REF(asn1der->data); + RESULT_ENSURE_REF(cert_out); + RESULT_ENSURE_REF(parsed_length); + + uint8_t *cert_to_parse = asn1der->data; + *cert_out = d2i_X509(NULL, (const unsigned char **) (void *) &cert_to_parse, asn1der->size); + RESULT_ENSURE(*cert_out != NULL, S2N_ERR_DECODE_CERTIFICATE); + + /* If cert parsing is successful, d2i_X509 increments *cert_to_parse to the byte following the parsed data */ + *parsed_length = cert_to_parse - asn1der->data; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_parse_without_length_validation(struct s2n_blob *asn1der, X509 **cert_out) +{ + RESULT_ENSURE_REF(asn1der); + RESULT_ENSURE_REF(cert_out); + + uint32_t parsed_len = 0; + RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_parse(struct s2n_blob *asn1der, X509 **cert_out) +{ + RESULT_ENSURE_REF(asn1der); + RESULT_ENSURE_REF(cert_out); + + uint32_t parsed_len = 0; + RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); + + /* Some TLS clients in the wild send extra trailing bytes after the Certificate. + * Allow this in s2n for backwards compatibility with existing clients. */ + uint32_t trailing_bytes = asn1der->size - parsed_len; + RESULT_ENSURE(trailing_bytes <= S2N_MAX_ALLOWED_CERT_TRAILING_BYTES, S2N_ERR_DECODE_CERTIFICATE); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_get_cert_info(X509 *cert, struct s2n_cert_info *info) +{ + RESULT_ENSURE_REF(cert); + RESULT_ENSURE_REF(info); + + X509_NAME *issuer_name = X509_get_issuer_name(cert); + RESULT_ENSURE_REF(issuer_name); + + X509_NAME *subject_name = X509_get_subject_name(cert); + RESULT_ENSURE_REF(subject_name); + + if (X509_NAME_cmp(issuer_name, subject_name) == 0) { + info->self_signed = true; + } else { + info->self_signed = false; + } + +#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x02070000f) + RESULT_ENSURE_REF(cert->sig_alg); + info->signature_nid = OBJ_obj2nid(cert->sig_alg->algorithm); +#else + info->signature_nid = X509_get_signature_nid(cert); +#endif + + /* There is no method to directly retrieve the signature digest from the X509* + * that is available in all libcryptos, so instead we use find_sigid_algs. For + * a signature with NID_ecdsa_with_SHA256 this will return NID_SHA256 + * + * signature_digest_nid may not always be set. ML-DSA does not have an associated digest. + */ + int find_result = OBJ_find_sigid_algs(info->signature_nid, &info->signature_digest_nid, NULL); + if (find_result != 1) { + /* OBJ_find_sigid_algs may fail for ML-DSA-44 and ML-DSA-87, depending on the + * version of AWS-LC. See https://github.com/aws/aws-lc/issues/2347. + * + * In order to handle this bug, we interpret failures from OBJ_find_sigid_algs + * as signature_digest_nid==0, which is equivalent to an undefined digest. + */ + info->signature_digest_nid = 0; + } + + DEFER_CLEANUP(EVP_PKEY *pubkey = X509_get_pubkey(cert), EVP_PKEY_free_pointer); + RESULT_ENSURE(pubkey != NULL, S2N_ERR_DECODE_CERTIFICATE); + + info->public_key_bits = EVP_PKEY_bits(pubkey); + RESULT_ENSURE(info->public_key_bits > 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); + + if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { + DEFER_CLEANUP(EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pubkey), EC_KEY_free_pointer); + RESULT_ENSURE_REF(ec_key); + const EC_GROUP *ec_group = EC_KEY_get0_group(ec_key); + RESULT_ENSURE_REF(ec_group); + info->public_key_nid = EC_GROUP_get_curve_name(ec_group); + } else { + info->public_key_nid = EVP_PKEY_id(pubkey); + } + RESULT_ENSURE(info->public_key_nid != NID_undef, S2N_ERR_CERT_TYPE_UNSUPPORTED); + + return S2N_RESULT_OK; +} diff --git a/crypto/s2n_pkey_evp.c b/crypto/s2n_pkey_evp.c index 7775f87659b..13a3ec8846c 100644 --- a/crypto/s2n_pkey_evp.c +++ b/crypto/s2n_pkey_evp.c @@ -1,373 +1,374 @@ -/* - * 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 "crypto/s2n_pkey_evp.h" - -#include -#include - -#include "crypto/s2n_evp.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_pkey.h" -#include "crypto/s2n_rsa_pss.h" -#include "error/s2n_errno.h" -#include "tls/s2n_signature_algorithms.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free); - -static S2N_RESULT s2n_evp_md_ctx_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pctx) -{ -#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX - EVP_MD_CTX_set_pkey_ctx(ctx, pctx); - return S2N_RESULT_OK; -#else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -#endif -} - -static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx) -{ -#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) - RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST), S2N_ERR_PKEY_CTX_INIT); - return S2N_RESULT_OK; -#else - RESULT_BAIL(S2N_ERR_RSA_PSS_NOT_SUPPORTED); -#endif -} - -static S2N_RESULT s2n_pkey_evp_validate_sig_alg(const struct s2n_pkey *key, s2n_signature_algorithm sig_alg) -{ - RESULT_ENSURE_REF(key); - - /* Ensure that the signature algorithm type matches the key type. */ - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - RESULT_GUARD(s2n_pkey_get_type(key->pkey, &pkey_type)); - s2n_pkey_type sig_alg_type = S2N_PKEY_TYPE_UNKNOWN; - RESULT_GUARD(s2n_signature_algorithm_get_pkey_type(sig_alg, &sig_alg_type)); - RESULT_ENSURE(pkey_type == sig_alg_type, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - - return S2N_RESULT_OK; -} - -static EVP_PKEY_CTX *s2n_evp_pkey_ctx_new(EVP_PKEY *pkey, s2n_hash_algorithm hash_alg) -{ - PTR_ENSURE_REF(pkey); - switch (hash_alg) { -#if S2N_LIBCRYPTO_SUPPORTS_PROVIDERS - /* For openssl-3.0, pkey methods will do an implicit fetch for the signing - * algorithm, which includes the hash algorithm. If using a legacy hash - * algorithm, specify the non-fips version. - */ - case S2N_HASH_MD5: - case S2N_HASH_MD5_SHA1: - case S2N_HASH_SHA1: - return EVP_PKEY_CTX_new_from_pkey(NULL, pkey, "-fips"); -#endif - default: - return EVP_PKEY_CTX_new(pkey, NULL); - } -} - -/* Our "digest-and-sign" EVP signing logic is intended to support FIPS 140-3. - * FIPS 140-3 does not allow signing or verifying externally calculated digests - * for RSA and ECDSA verify. - * See https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures, - * and note that "component" tests only exist for ECDSA sign. - * - * In order to avoid signing externally calculated digests, we naively would - * need access to the full message to be signed at the time of signing. That's - * a problem for TLS1.2, where the client cert verify message requires signing - * every handshake message sent or received before the client cert verify message. - * To avoid storing every single handshake message in its entirety, we instead - * keep a running hash of the messages in an EVP hash state. Then, instead of - * digesting that hash state, we pass it unmodified to EVP_DigestSignFinal. - * That would normally not be allowed, since the hash state was initialized without - * a key using EVP_DigestInit instead of with a key using EVP_DigestSignInit. - * We make it work by using the EVP_MD_CTX_set_pkey_ctx method to attach a key - * to an existing hash state. - * - * All that means that "digest-and-sign" requires two things: - * - A single EVP hash state to sign. So we must not use a custom MD5_SHA1 hash, - * which doesn't produce a single hash state. - * - EVP_MD_CTX_set_pkey_ctx to exist and to behave as expected. Existence - * alone is not sufficient: the method exists in openssl-3.0-fips, but - * it cannot be used to setup a hash state for EVP_DigestSignFinal. - * - * Currently only awslc-fips meets both these requirements. New libcryptos - * should be assumed not to meet these requirements until proven otherwise. - */ -static int s2n_pkey_evp_digest_and_sign(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - /* Custom MD5_SHA1 involves combining separate MD5 and SHA1 hashes. - * That involves two hash states instead of the single hash state this - * method requires. - */ - POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); - - /* Not all implementations of EVP_MD_CTX_set_pkey_ctx behave as required - * by this method. Using EVP_MD_CTX_set_pkey_ctx to convert a hash initialized - * with EVP_DigestInit to one that can be finalized with EVP_DigestSignFinal - * is not entirely standard. - * - * However, this behavior is known to work with awslc-fips. - */ - POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); - - EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; - POSIX_ENSURE_REF(ctx); - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); - - size_t signature_size = signature->size; - POSIX_GUARD_OSSL(EVP_DigestSignFinal(ctx, signature->data, &signature_size), S2N_ERR_SIGN); - POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); - signature->size = signature_size; - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); - - return S2N_SUCCESS; -} - -/* See s2n_evp_digest_and_sign for more information */ -static bool s2n_pkey_evp_digest_and_sign_is_required(s2n_signature_algorithm sig_alg) -{ - if (sig_alg == S2N_SIGNATURE_MLDSA) { - /* The FIPS restrictions do not apply to ML-DSA */ - return false; - } - return s2n_libcrypto_is_awslc_fips(); -} - -/* "digest-then-sign" means that we calculate the digest for a hash state, - * then sign the digest bytes. That is not allowed by FIPS 140-3, but is allowed - * in all other cases. - */ -static int s2n_pkey_evp_digest_then_sign(EVP_PKEY_CTX *pctx, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - uint8_t digest_length = 0; - POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); - POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); - - uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); - - size_t signature_size = signature->size; - POSIX_GUARD_OSSL(EVP_PKEY_sign(pctx, signature->data, &signature_size, - digest_out, digest_length), - S2N_ERR_SIGN); - POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); - signature->size = signature_size; - - return S2N_SUCCESS; -} - -int s2n_pkey_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(priv); - POSIX_ENSURE_REF(hash_state); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(priv->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_sign_init(pctx), S2N_ERR_PKEY_CTX_INIT); - - if (sig_alg != S2N_SIGNATURE_MLDSA) { - POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); - } - - if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); - POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); - } - - if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { - POSIX_GUARD(s2n_pkey_evp_digest_and_sign(pctx, sig_alg, hash_state, signature)); - } else { - POSIX_GUARD(s2n_pkey_evp_digest_then_sign(pctx, hash_state, signature)); - } - - return S2N_SUCCESS; -} - -/* See s2n_evp_digest_and_sign for more information */ -static int s2n_pkey_evp_digest_and_verify(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - /* See digest-and-sign requirements */ - POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); - POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); - - EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; - POSIX_ENSURE_REF(ctx); - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); - - POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE); - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); - - return S2N_SUCCESS; -} - -/* See s2n_evp_digest_then_sign for more information */ -static int s2n_pkey_evp_digest_then_verify(EVP_PKEY_CTX *pctx, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - uint8_t digest_length = 0; - POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); - POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); - - uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); - - POSIX_GUARD_OSSL(EVP_PKEY_verify(pctx, signature->data, signature->size, - digest_out, digest_length), - S2N_ERR_VERIFY_SIGNATURE); - return S2N_SUCCESS; -} - -int s2n_pkey_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pub); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - POSIX_GUARD_RESULT(s2n_pkey_evp_validate_sig_alg(pub, sig_alg)); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(pub->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_verify_init(pctx), S2N_ERR_PKEY_CTX_INIT); - - if (sig_alg != S2N_SIGNATURE_MLDSA) { - POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); - } - - if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); - POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); - } - - if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { - POSIX_GUARD(s2n_pkey_evp_digest_and_verify(pctx, sig_alg, hash_state, signature)); - } else { - POSIX_GUARD(s2n_pkey_evp_digest_then_verify(pctx, hash_state, signature)); - } - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_pkey_evp_size(const struct s2n_pkey *pkey, uint32_t *size_out) -{ - RESULT_ENSURE_REF(pkey); - RESULT_ENSURE_REF(pkey->pkey); - RESULT_ENSURE_REF(size_out); - - const int size = EVP_PKEY_size(pkey->pkey); - RESULT_ENSURE_GT(size, 0); - *size_out = size; - - return S2N_RESULT_OK; -} - -int s2n_pkey_evp_encrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(in); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(key->pkey); - - s2n_pkey_type type = 0; - POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); - POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_encrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING), S2N_ERR_PKEY_CTX_INIT); - - size_t out_size = out->size; - POSIX_GUARD_OSSL(EVP_PKEY_encrypt(pctx, out->data, &out_size, in->data, in->size), S2N_ERR_ENCRYPT); - POSIX_ENSURE(out_size == out->size, S2N_ERR_SIZE_MISMATCH); - - return S2N_SUCCESS; -} - -int s2n_pkey_evp_decrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(in); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(key->pkey); - - s2n_pkey_type type = 0; - POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); - POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); - - uint32_t expected_size = 0; - POSIX_GUARD_RESULT(s2n_pkey_size(key, &expected_size)); - - /* RSA decryption requires more output memory than the size of the final decrypted message */ - struct s2n_blob buffer = { 0 }; - uint8_t buffer_bytes[4096] = { 0 }; - POSIX_GUARD(s2n_blob_init(&buffer, buffer_bytes, sizeof(buffer_bytes))); - POSIX_ENSURE(out->size <= buffer.size, S2N_ERR_NOMEM); - POSIX_ENSURE(expected_size <= buffer.size, S2N_ERR_NOMEM); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_decrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); - /* The padding is actually RSA_PKCS1_PADDING, but we'll handle the padding later */ - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING), S2N_ERR_PKEY_CTX_INIT); - - size_t out_size = buffer.size; - POSIX_GUARD_OSSL(EVP_PKEY_decrypt(pctx, buffer.data, &out_size, in->data, in->size), S2N_ERR_DECRYPT); - POSIX_ENSURE(out_size == expected_size, S2N_ERR_SIZE_MISMATCH); - - /* Handle padding in constant time to avoid Bleichenbacher oracles. - * If the padding is wrong, we return random output rather than failing. - * That ensures that padding failures are treated the same as wrong outputs. - */ - POSIX_GUARD_RESULT(s2n_get_public_random_data(out)); - s2n_constant_time_pkcs1_unpad_or_dont(out->data, buffer.data, out_size, out->size); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_pkey_evp_init(struct s2n_pkey *pkey) -{ - RESULT_ENSURE_REF(pkey); - pkey->size = &s2n_pkey_evp_size; - pkey->sign = &s2n_pkey_evp_sign; - pkey->verify = &s2n_pkey_evp_verify; - pkey->encrypt = s2n_pkey_evp_encrypt; - pkey->decrypt = s2n_pkey_evp_decrypt; - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_pkey_evp.h" + +#include +#include + +#include "crypto/s2n_evp.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_pkey.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "tls/s2n_signature_algorithms.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free); + +static S2N_RESULT s2n_evp_md_ctx_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pctx) +{ +#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + EVP_MD_CTX_set_pkey_ctx(ctx, pctx); + return S2N_RESULT_OK; +#else + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +#endif +} + +static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx) +{ +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) + RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST), S2N_ERR_PKEY_CTX_INIT); + return S2N_RESULT_OK; +#else + RESULT_BAIL(S2N_ERR_RSA_PSS_NOT_SUPPORTED); +#endif +} + +static S2N_RESULT s2n_pkey_evp_validate_sig_alg(const struct s2n_pkey *key, s2n_signature_algorithm sig_alg) +{ + RESULT_ENSURE_REF(key); + + /* Ensure that the signature algorithm type matches the key type. */ + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + RESULT_GUARD(s2n_pkey_get_type(key->pkey, &pkey_type)); + s2n_pkey_type sig_alg_type = S2N_PKEY_TYPE_UNKNOWN; + RESULT_GUARD(s2n_signature_algorithm_get_pkey_type(sig_alg, &sig_alg_type)); + RESULT_ENSURE(pkey_type == sig_alg_type, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + + return S2N_RESULT_OK; +} + +static EVP_PKEY_CTX *s2n_evp_pkey_ctx_new(EVP_PKEY *pkey, s2n_hash_algorithm hash_alg) +{ + PTR_ENSURE_REF(pkey); + switch (hash_alg) { +#if S2N_LIBCRYPTO_SUPPORTS_PROVIDERS + /* For openssl-3.0, pkey methods will do an implicit fetch for the signing + * algorithm, which includes the hash algorithm. If using a legacy hash + * algorithm, specify the non-fips version. + */ + case S2N_HASH_MD5: + case S2N_HASH_MD5_SHA1: + case S2N_HASH_SHA1: + return EVP_PKEY_CTX_new_from_pkey(NULL, pkey, "-fips"); +#endif + default: + return EVP_PKEY_CTX_new(pkey, NULL); + } +} + +/* Our "digest-and-sign" EVP signing logic is intended to support FIPS 140-3. + * FIPS 140-3 does not allow signing or verifying externally calculated digests + * for RSA and ECDSA verify. + * See https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures, + * and note that "component" tests only exist for ECDSA sign. + * + * In order to avoid signing externally calculated digests, we naively would + * need access to the full message to be signed at the time of signing. That's + * a problem for TLS1.2, where the client cert verify message requires signing + * every handshake message sent or received before the client cert verify message. + * To avoid storing every single handshake message in its entirety, we instead + * keep a running hash of the messages in an EVP hash state. Then, instead of + * digesting that hash state, we pass it unmodified to EVP_DigestSignFinal. + * That would normally not be allowed, since the hash state was initialized without + * a key using EVP_DigestInit instead of with a key using EVP_DigestSignInit. + * We make it work by using the EVP_MD_CTX_set_pkey_ctx method to attach a key + * to an existing hash state. + * + * All that means that "digest-and-sign" requires two things: + * - A single EVP hash state to sign. So we must not use a custom MD5_SHA1 hash, + * which doesn't produce a single hash state. + * - EVP_MD_CTX_set_pkey_ctx to exist and to behave as expected. Existence + * alone is not sufficient: the method exists in openssl-3.0-fips, but + * it cannot be used to setup a hash state for EVP_DigestSignFinal. + * + * Currently only awslc-fips meets both these requirements. New libcryptos + * should be assumed not to meet these requirements until proven otherwise. + */ +static int s2n_pkey_evp_digest_and_sign(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + /* Custom MD5_SHA1 involves combining separate MD5 and SHA1 hashes. + * That involves two hash states instead of the single hash state this + * method requires. + */ + POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); + + /* Not all implementations of EVP_MD_CTX_set_pkey_ctx behave as required + * by this method. Using EVP_MD_CTX_set_pkey_ctx to convert a hash initialized + * with EVP_DigestInit to one that can be finalized with EVP_DigestSignFinal + * is not entirely standard. + * + * However, this behavior is known to work with awslc-fips. + */ + POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); + + EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; + POSIX_ENSURE_REF(ctx); + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); + + size_t signature_size = signature->size; + POSIX_GUARD_OSSL(EVP_DigestSignFinal(ctx, signature->data, &signature_size), S2N_ERR_SIGN); + POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); + signature->size = signature_size; + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); + + return S2N_SUCCESS; +} + +/* See s2n_evp_digest_and_sign for more information */ +static bool s2n_pkey_evp_digest_and_sign_is_required(s2n_signature_algorithm sig_alg) +{ + if (sig_alg == S2N_SIGNATURE_MLDSA) { + /* The FIPS restrictions do not apply to ML-DSA */ + return false; + } + return s2n_libcrypto_is_awslc_fips(); +} + +/* "digest-then-sign" means that we calculate the digest for a hash state, + * then sign the digest bytes. That is not allowed by FIPS 140-3, but is allowed + * in all other cases. + */ +static int s2n_pkey_evp_digest_then_sign(EVP_PKEY_CTX *pctx, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + uint8_t digest_length = 0; + POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); + POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); + + uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); + + size_t signature_size = signature->size; + POSIX_GUARD_OSSL(EVP_PKEY_sign(pctx, signature->data, &signature_size, + digest_out, digest_length), + S2N_ERR_SIGN); + POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); + signature->size = signature_size; + + return S2N_SUCCESS; +} + +int s2n_pkey_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(priv); + POSIX_ENSURE_REF(hash_state); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(priv->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_sign_init(pctx), S2N_ERR_PKEY_CTX_INIT); + + if (sig_alg != S2N_SIGNATURE_MLDSA) { + POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); + } + + if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); + POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); + } + + if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { + POSIX_GUARD(s2n_pkey_evp_digest_and_sign(pctx, sig_alg, hash_state, signature)); + } else { + POSIX_GUARD(s2n_pkey_evp_digest_then_sign(pctx, hash_state, signature)); + } + + return S2N_SUCCESS; +} + +/* See s2n_evp_digest_and_sign for more information */ +static int s2n_pkey_evp_digest_and_verify(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + /* See digest-and-sign requirements */ + POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); + POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); + + EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; + POSIX_ENSURE_REF(ctx); + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); + + POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE); + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); + + return S2N_SUCCESS; +} + +/* See s2n_evp_digest_then_sign for more information */ +static int s2n_pkey_evp_digest_then_verify(EVP_PKEY_CTX *pctx, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + uint8_t digest_length = 0; + POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); + POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); + + uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); + + POSIX_GUARD_OSSL(EVP_PKEY_verify(pctx, signature->data, signature->size, + digest_out, digest_length), + S2N_ERR_VERIFY_SIGNATURE); + return S2N_SUCCESS; +} + +int s2n_pkey_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pub); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + POSIX_GUARD_RESULT(s2n_pkey_evp_validate_sig_alg(pub, sig_alg)); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(pub->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_verify_init(pctx), S2N_ERR_PKEY_CTX_INIT); + + if (sig_alg != S2N_SIGNATURE_MLDSA) { + POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); + } + + if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); + POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); + } + + if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { + POSIX_GUARD(s2n_pkey_evp_digest_and_verify(pctx, sig_alg, hash_state, signature)); + } else { + POSIX_GUARD(s2n_pkey_evp_digest_then_verify(pctx, hash_state, signature)); + } + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_pkey_evp_size(const struct s2n_pkey *pkey, uint32_t *size_out) +{ + RESULT_ENSURE_REF(pkey); + RESULT_ENSURE_REF(pkey->pkey); + RESULT_ENSURE_REF(size_out); + + const int size = EVP_PKEY_size(pkey->pkey); + RESULT_ENSURE_GT(size, 0); + *size_out = size; + + return S2N_RESULT_OK; +} + +int s2n_pkey_evp_encrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(in); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(key->pkey); + + s2n_pkey_type type = 0; + POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); + POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_encrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING), S2N_ERR_PKEY_CTX_INIT); + + size_t out_size = out->size; + POSIX_GUARD_OSSL(EVP_PKEY_encrypt(pctx, out->data, &out_size, in->data, in->size), S2N_ERR_ENCRYPT); + POSIX_ENSURE(out_size == out->size, S2N_ERR_SIZE_MISMATCH); + + return S2N_SUCCESS; +} + +int s2n_pkey_evp_decrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(in); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(key->pkey); + + s2n_pkey_type type = 0; + POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); + POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); + + uint32_t expected_size = 0; + POSIX_GUARD_RESULT(s2n_pkey_size(key, &expected_size)); + + /* RSA decryption requires more output memory than the size of the final decrypted message */ + struct s2n_blob buffer = { 0 }; + uint8_t buffer_bytes[4096] = { 0 }; + POSIX_GUARD(s2n_blob_init(&buffer, buffer_bytes, sizeof(buffer_bytes))); + POSIX_ENSURE(out->size <= buffer.size, S2N_ERR_NOMEM); + POSIX_ENSURE(expected_size <= buffer.size, S2N_ERR_NOMEM); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_decrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); + /* The padding is actually RSA_PKCS1_PADDING, but we'll handle the padding later */ + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING), S2N_ERR_PKEY_CTX_INIT); + + size_t out_size = buffer.size; + POSIX_GUARD_OSSL(EVP_PKEY_decrypt(pctx, buffer.data, &out_size, in->data, in->size), S2N_ERR_DECRYPT); + POSIX_ENSURE(out_size == expected_size, S2N_ERR_SIZE_MISMATCH); + + /* Handle padding in constant time to avoid Bleichenbacher oracles. + * If the padding is wrong, we return random output rather than failing. + * That ensures that padding failures are treated the same as wrong outputs. + */ + POSIX_GUARD_RESULT(s2n_get_public_random_data(out)); + s2n_constant_time_pkcs1_unpad_or_dont(out->data, buffer.data, out_size, out->size); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_pkey_evp_init(struct s2n_pkey *pkey) +{ + RESULT_ENSURE_REF(pkey); + pkey->size = &s2n_pkey_evp_size; + pkey->sign = &s2n_pkey_evp_sign; + pkey->verify = &s2n_pkey_evp_verify; + pkey->encrypt = s2n_pkey_evp_encrypt; + pkey->decrypt = s2n_pkey_evp_decrypt; + return S2N_RESULT_OK; +} diff --git a/error/s2n_errno.c b/error/s2n_errno.c index a442e7b4b61..b000bc8eeb3 100644 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -1,514 +1,516 @@ -/* - * 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 "error/s2n_errno.h" - -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "utils/s2n_map.h" -#include "utils/s2n_safety.h" - -#ifdef S2N_STACKTRACE - #include -#endif - -__thread int s2n_errno; -__thread struct s2n_debug_info _s2n_debug_info = { .debug_str = "", .source = "" }; - -/** - * Returns the address of the thread-local `s2n_errno` variable - */ -int *s2n_errno_location() -{ - return &s2n_errno; -} - -static const char *no_such_language = "Language is not supported for error translation"; -static const char *no_such_error = "Internal s2n error"; - -/* - * Define error entries with descriptions in this macro once - * to generate code in next 2 following functions. - */ -/* clang-format off */ -#define ERR_ENTRIES(ERR_ENTRY) \ - ERR_ENTRY(S2N_ERR_OK, "no error") \ - ERR_ENTRY(S2N_ERR_IO, "underlying I/O operation failed, check system errno") \ - ERR_ENTRY(S2N_ERR_CLOSED, "connection is closed") \ - ERR_ENTRY(S2N_ERR_IO_BLOCKED, "underlying I/O operation would block") \ - ERR_ENTRY(S2N_ERR_ASYNC_BLOCKED, "blocked on external async function invocation") \ - ERR_ENTRY(S2N_ERR_ALERT, "TLS alert received") \ - ERR_ENTRY(S2N_ERR_ENCRYPT, "error encrypting data") \ - ERR_ENTRY(S2N_ERR_DECRYPT, "error decrypting data") \ - ERR_ENTRY(S2N_ERR_BAD_MESSAGE, "Bad message encountered") \ - ERR_ENTRY(S2N_ERR_KEY_INIT, "error initializing encryption key") \ - ERR_ENTRY(S2N_ERR_KEY_DESTROY, "error destroying encryption key") \ - ERR_ENTRY(S2N_ERR_DH_SERIALIZING, "error serializing Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_DH_SHARED_SECRET, "error computing Diffie-Hellman shared secret") \ - ERR_ENTRY(S2N_ERR_DH_WRITING_PUBLIC_KEY, "error writing Diffie-Hellman public key") \ - ERR_ENTRY(S2N_ERR_DH_FAILED_SIGNING, "error signing Diffie-Hellman values") \ - ERR_ENTRY(S2N_ERR_DH_COPYING_PARAMETERS, "error copying Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_DH_GENERATING_PARAMETERS, "error generating Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_CIPHER_NOT_SUPPORTED, "Cipher is not supported") \ - ERR_ENTRY(S2N_ERR_NO_APPLICATION_PROTOCOL, "No supported application protocol to negotiate") \ - ERR_ENTRY(S2N_ERR_FALLBACK_DETECTED, "TLS fallback detected") \ - ERR_ENTRY(S2N_ERR_HASH_DIGEST_FAILED, "failed to create hash digest") \ - ERR_ENTRY(S2N_ERR_HASH_INIT_FAILED, "error initializing hash") \ - ERR_ENTRY(S2N_ERR_HASH_UPDATE_FAILED, "error updating hash") \ - ERR_ENTRY(S2N_ERR_HASH_COPY_FAILED, "error copying hash") \ - ERR_ENTRY(S2N_ERR_HASH_WIPE_FAILED, "error wiping hash") \ - ERR_ENTRY(S2N_ERR_HASH_NOT_READY, "hash not in a valid state for the attempted operation") \ - ERR_ENTRY(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED, "error allowing MD5 to be used when in FIPS mode") \ - ERR_ENTRY(S2N_ERR_DECODE_CERTIFICATE, "error decoding certificate") \ - ERR_ENTRY(S2N_ERR_DECODE_PRIVATE_KEY, "error decoding private key") \ - ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHM, "Invalid signature algorithm") \ - ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_SCHEME, "Invalid signature scheme") \ - ERR_ENTRY(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, "Unable to negotiate a supported signature scheme") \ - ERR_ENTRY(S2N_ERR_CBC_VERIFY, "Failed CBC verification") \ - ERR_ENTRY(S2N_ERR_DH_COPYING_PUBLIC_KEY, "error copying Diffie-Hellman public key") \ - ERR_ENTRY(S2N_ERR_SIGN, "error signing data") \ - ERR_ENTRY(S2N_ERR_VERIFY_SIGNATURE, "error verifying signature") \ - ERR_ENTRY(S2N_ERR_ECDHE_GEN_KEY, "Failed to generate an ECDHE key") \ - ERR_ENTRY(S2N_ERR_ECDHE_SHARED_SECRET, "Error computing ECDHE shared secret") \ - ERR_ENTRY(S2N_ERR_ECDHE_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDHE handshake") \ - ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY, "Failed to validate the peer's point on the elliptic curve") \ - ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS, "Failed to validate the peer's point on the elliptic curve, per FIPS requirements") \ - ERR_ENTRY(S2N_ERR_ECDSA_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDSA SignatureScheme handshake") \ - ERR_ENTRY(S2N_ERR_ECDHE_SERIALIZING, "Error serializing ECDHE public") \ - ERR_ENTRY(S2N_ERR_KEM_UNSUPPORTED_PARAMS, "Unsupported KEM params was presented during a handshake that uses a KEM") \ - ERR_ENTRY(S2N_ERR_SHUTDOWN_RECORD_TYPE, "Non alert record received during s2n_shutdown()") \ - ERR_ENTRY(S2N_ERR_SHUTDOWN_CLOSED, "Peer closed before sending their close_notify") \ - ERR_ENTRY(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO, "renegotiation_info should be empty") \ - ERR_ENTRY(S2N_ERR_RECORD_LIMIT, "TLS record limit reached") \ - ERR_ENTRY(S2N_ERR_CERT_UNTRUSTED, "Certificate is untrusted") \ - ERR_ENTRY(S2N_ERR_CERT_INVALID_HOSTNAME, "Certificate is not valid for the supplied hostname") \ - ERR_ENTRY(S2N_ERR_CERT_REVOKED, "Certificate has been revoked by the CA") \ - ERR_ENTRY(S2N_ERR_CERT_NOT_YET_VALID, "Certificate is not yet valid") \ - ERR_ENTRY(S2N_ERR_CERT_EXPIRED, "Certificate has expired") \ - ERR_ENTRY(S2N_ERR_CERT_TYPE_UNSUPPORTED, "Certificate Type is unsupported") \ - ERR_ENTRY(S2N_ERR_CERT_INVALID, "Certificate is invalid") \ - ERR_ENTRY(S2N_ERR_CERT_INTENT_INVALID, "Certificate intent is invalid for the current context.") \ - ERR_ENTRY(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, "The maximum certificate chain depth has been exceeded") \ - ERR_ENTRY(S2N_ERR_CERT_REJECTED, "Certificate failed custom application validation") \ - ERR_ENTRY(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical certificate extension") \ - ERR_ENTRY(S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, "The certificate key or signature is not allowed by the security policy") \ - ERR_ENTRY(S2N_ERR_CRL_LOOKUP_FAILED, "No CRL could be found for the corresponding certificate") \ - ERR_ENTRY(S2N_ERR_CRL_SIGNATURE, "The signature of the CRL is invalid") \ - ERR_ENTRY(S2N_ERR_CRL_ISSUER, "Unable to get the CRL issuer certificate") \ - ERR_ENTRY(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical CRL extension") \ - ERR_ENTRY(S2N_ERR_CRL_INVALID_THIS_UPDATE, "The CRL contains an invalid thisUpdate field") \ - ERR_ENTRY(S2N_ERR_CRL_INVALID_NEXT_UPDATE, "The CRL contains an invalid nextUpdate field") \ - ERR_ENTRY(S2N_ERR_CRL_NOT_YET_VALID, "The CRL is not yet valid") \ - ERR_ENTRY(S2N_ERR_CRL_EXPIRED, "The CRL has expired") \ - ERR_ENTRY(S2N_ERR_INVALID_MAX_FRAG_LEN, "invalid Maximum Fragmentation Length encountered") \ - ERR_ENTRY(S2N_ERR_MAX_FRAG_LEN_MISMATCH, "Negotiated Maximum Fragmentation Length from server does not match the requested length by client") \ - ERR_ENTRY(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED, "TLS protocol version is not supported by configuration") \ - ERR_ENTRY(S2N_ERR_BAD_KEY_SHARE, "Bad key share received") \ - ERR_ENTRY(S2N_ERR_CANCELLED, "handshake was cancelled") \ - ERR_ENTRY(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED, "Protocol downgrade detected by client") \ - ERR_ENTRY(S2N_ERR_MADVISE, "error calling madvise") \ - ERR_ENTRY(S2N_ERR_ALLOC, "error allocating memory") \ - ERR_ENTRY(S2N_ERR_MLOCK, "error calling mlock (Did you run prlimit?)") \ - ERR_ENTRY(S2N_ERR_MUNLOCK, "error calling munlock") \ - ERR_ENTRY(S2N_ERR_FSTAT, "error calling fstat") \ - ERR_ENTRY(S2N_ERR_OPEN, "error calling open") \ - ERR_ENTRY(S2N_ERR_MMAP, "error calling mmap") \ - ERR_ENTRY(S2N_ERR_ATEXIT, "error calling atexit") \ - ERR_ENTRY(S2N_ERR_NOMEM, "no memory") \ - ERR_ENTRY(S2N_ERR_NULL, "NULL pointer encountered") \ - ERR_ENTRY(S2N_ERR_SAFETY, "a safety check failed") \ - ERR_ENTRY(S2N_ERR_INITIALIZED, "s2n is initialized") \ - ERR_ENTRY(S2N_ERR_NOT_INITIALIZED, "s2n not initialized") \ - ERR_ENTRY(S2N_ERR_RANDOM_UNINITIALIZED, "s2n entropy not initialized") \ - ERR_ENTRY(S2N_ERR_OPEN_RANDOM, "error opening urandom") \ - ERR_ENTRY(S2N_ERR_RESIZE_STATIC_STUFFER, "cannot resize a static stuffer") \ - ERR_ENTRY(S2N_ERR_RESIZE_TAINTED_STUFFER, "cannot resize a tainted stuffer") \ - ERR_ENTRY(S2N_ERR_STUFFER_OUT_OF_DATA, "stuffer is out of data") \ - ERR_ENTRY(S2N_ERR_STUFFER_IS_FULL, "stuffer is full") \ - ERR_ENTRY(S2N_ERR_STUFFER_NOT_FOUND, "stuffer expected bytes were not found") \ - ERR_ENTRY(S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA, "stuffer has unprocessed data") \ - ERR_ENTRY(S2N_ERR_HASH_INVALID_ALGORITHM, "invalid hash algorithm") \ - ERR_ENTRY(S2N_ERR_PRF_INVALID_ALGORITHM, "invalid prf hash algorithm") \ - ERR_ENTRY(S2N_ERR_PRF_INVALID_SEED, "invalid prf seeds provided") \ - ERR_ENTRY(S2N_ERR_PRF_DERIVE, "error deriving a secret from the PRF") \ - ERR_ENTRY(S2N_ERR_P_HASH_INVALID_ALGORITHM, "invalid p_hash algorithm") \ - ERR_ENTRY(S2N_ERR_P_HASH_INIT_FAILED, "error initializing p_hash") \ - ERR_ENTRY(S2N_ERR_P_HASH_UPDATE_FAILED, "error updating p_hash") \ - ERR_ENTRY(S2N_ERR_P_HASH_FINAL_FAILED, "error creating p_hash digest") \ - ERR_ENTRY(S2N_ERR_P_HASH_WIPE_FAILED, "error wiping p_hash") \ - ERR_ENTRY(S2N_ERR_HMAC_INVALID_ALGORITHM, "invalid HMAC algorithm") \ - ERR_ENTRY(S2N_ERR_HKDF_OUTPUT_SIZE, "invalid HKDF output size") \ - ERR_ENTRY(S2N_ERR_HKDF, "error generating HKDF output") \ - ERR_ENTRY(S2N_ERR_ALERT_PRESENT, "TLS alert is already pending") \ - ERR_ENTRY(S2N_ERR_HANDSHAKE_STATE, "Invalid handshake state encountered") \ - ERR_ENTRY(S2N_ERR_SHUTDOWN_PAUSED, "s2n_shutdown() called while paused") \ - ERR_ENTRY(S2N_ERR_SIZE_MISMATCH, "size mismatch") \ - ERR_ENTRY(S2N_ERR_RANDOM, "Error generating random data") \ - ERR_ENTRY(S2N_ERR_KEY_CHECK, "Invalid key") \ - ERR_ENTRY(S2N_ERR_CIPHER_TYPE, "Unknown cipher type used") \ - ERR_ENTRY(S2N_ERR_MAP_DUPLICATE, "Duplicate map key inserted") \ - ERR_ENTRY(S2N_ERR_MAP_IMMUTABLE, "Attempt to update an immutable map") \ - ERR_ENTRY(S2N_ERR_MAP_MUTABLE, "Attempt to lookup a mutable map") \ - ERR_ENTRY(S2N_ERR_MAP_INVALID_MAP_SIZE, "Attempt to create a map with 0 capacity") \ - ERR_ENTRY(S2N_ERR_INITIAL_HMAC, "error calling EVP_CIPHER_CTX_ctrl for composite cbc cipher") \ - ERR_ENTRY(S2N_ERR_INVALID_NONCE_TYPE, "Invalid AEAD nonce type") \ - ERR_ENTRY(S2N_ERR_UNIMPLEMENTED, "Unimplemented feature") \ - ERR_ENTRY(S2N_ERR_HANDSHAKE_UNREACHABLE, "Unreachable handshake state machine handler invoked") \ - ERR_ENTRY(S2N_ERR_READ, "error calling read") \ - ERR_ENTRY(S2N_ERR_WRITE, "error calling write") \ - ERR_ENTRY(S2N_ERR_BAD_FD, "Invalid file descriptor") \ - ERR_ENTRY(S2N_ERR_RDRAND_FAILED, "Error executing rdrand instruction") \ - ERR_ENTRY(S2N_ERR_FAILED_CACHE_RETRIEVAL, "Failed cache retrieval") \ - ERR_ENTRY(S2N_ERR_X509_TRUST_STORE, "Error initializing trust store") \ - ERR_ENTRY(S2N_ERR_UNKNOWN_PROTOCOL_VERSION, "Error determining client protocol version") \ - ERR_ENTRY(S2N_ERR_NULL_CN_NAME, "Error parsing CN names") \ - ERR_ENTRY(S2N_ERR_NULL_SANS, "Error parsing SANS") \ - ERR_ENTRY(S2N_ERR_CLIENT_HELLO_VERSION, "Could not get client hello version") \ - ERR_ENTRY(S2N_ERR_CLIENT_PROTOCOL_VERSION, "Could not get client protocol version") \ - ERR_ENTRY(S2N_ERR_SERVER_PROTOCOL_VERSION, "Could not get server protocol version") \ - ERR_ENTRY(S2N_ERR_ACTUAL_PROTOCOL_VERSION, "Could not get actual protocol version") \ - ERR_ENTRY(S2N_ERR_POLLING_FROM_SOCKET, "Error polling from socket") \ - ERR_ENTRY(S2N_ERR_RECV_STUFFER_FROM_CONN, "Error receiving stuffer from connection") \ - ERR_ENTRY(S2N_ERR_SEND_STUFFER_TO_CONN, "Error sending stuffer to connection") \ - ERR_ENTRY(S2N_ERR_PRECONDITION_VIOLATION, "Precondition violation") \ - ERR_ENTRY(S2N_ERR_POSTCONDITION_VIOLATION, "Postcondition violation") \ - ERR_ENTRY(S2N_ERR_INTEGER_OVERFLOW, "Integer overflow violation") \ - ERR_ENTRY(S2N_ERR_ARRAY_INDEX_OOB, "Array index out of bounds") \ - ERR_ENTRY(S2N_ERR_FREE_STATIC_BLOB, "Cannot free a static blob") \ - ERR_ENTRY(S2N_ERR_RESIZE_STATIC_BLOB, "Cannot resize a static blob") \ - ERR_ENTRY(S2N_ERR_RECORD_LENGTH_TOO_LARGE, "Record length exceeds protocol version maximum") \ - ERR_ENTRY(S2N_ERR_SET_DUPLICATE_VALUE, "Set already contains the provided value") \ - ERR_ENTRY(S2N_ERR_ASYNC_CALLBACK_FAILED, "Callback associated with async private keys function has failed") \ - ERR_ENTRY(S2N_ERR_ASYNC_MORE_THAN_ONE, "Only one asynchronous operation can be in-progress at the same time") \ - ERR_ENTRY(S2N_ERR_NO_ALERT, "No Alert present") \ - ERR_ENTRY(S2N_ERR_SERVER_MODE, "Operation not allowed in server mode") \ - ERR_ENTRY(S2N_ERR_CLIENT_MODE, "Operation not allowed in client mode") \ - ERR_ENTRY(S2N_ERR_CLIENT_MODE_DISABLED, "client connections not allowed") \ - ERR_ENTRY(S2N_ERR_TOO_MANY_CERTIFICATES, "only 1 certificate is supported in client mode") \ - ERR_ENTRY(S2N_ERR_TOO_MANY_SIGNATURE_SCHEMES, "Max supported length of SignatureAlgorithms/SignatureSchemes list is 128") \ - ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_FIPS_MODE, "Client Auth is not supported when in FIPS mode") \ - ERR_ENTRY(S2N_ERR_INVALID_BASE64, "invalid base64 encountered") \ - ERR_ENTRY(S2N_ERR_INVALID_HEX, "invalid HEX encountered") \ - ERR_ENTRY(S2N_ERR_INVALID_PEM, "invalid PEM encountered") \ - ERR_ENTRY(S2N_ERR_DH_PARAMS_CREATE, "error creating Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_DH_TOO_SMALL, "Diffie-Hellman parameters are too small") \ - ERR_ENTRY(S2N_ERR_DH_PARAMETER_CHECK, "Diffie-Hellman parameter check failed") \ - ERR_ENTRY(S2N_ERR_INVALID_PKCS3, "invalid PKCS3 encountered") \ - ERR_ENTRY(S2N_ERR_NO_CERTIFICATE_IN_PEM, "No certificate in PEM") \ - ERR_ENTRY(S2N_ERR_SERVER_NAME_TOO_LONG, "server name is too long") \ - ERR_ENTRY(S2N_ERR_NUM_DEFAULT_CERTIFICATES, "exceeded max default certificates or provided no default") \ - ERR_ENTRY(S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE, "setting multiple default certificates per auth type is not allowed") \ - ERR_ENTRY(S2N_ERR_INVALID_CIPHER_PREFERENCES, "Invalid Cipher Preferences version") \ - ERR_ENTRY(S2N_ERR_INVALID_APPLICATION_PROTOCOL, "The supplied application protocol name is invalid") \ - ERR_ENTRY(S2N_ERR_KEY_MISMATCH, "public and private key do not match") \ - ERR_ENTRY(S2N_ERR_SEND_SIZE, "Retried s2n_send() size is invalid") \ - ERR_ENTRY(S2N_ERR_CORK_SET_ON_UNMANAGED, "Attempt to set connection cork management on unmanaged IO") \ - ERR_ENTRY(S2N_ERR_UNRECOGNIZED_EXTENSION, "TLS extension not recognized") \ - ERR_ENTRY(S2N_ERR_EXTENSION_NOT_RECEIVED, "The TLS extension was not received") \ - ERR_ENTRY(S2N_ERR_INVALID_SCT_LIST, "SCT list is invalid") \ - ERR_ENTRY(S2N_ERR_INVALID_OCSP_RESPONSE, "OCSP response is invalid") \ - ERR_ENTRY(S2N_ERR_UPDATING_EXTENSION, "Updating extension data failed") \ - ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE, "Serialized session state is not in valid format") \ - ERR_ENTRY(S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG, "Serialized session state is too long") \ - ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_LONG, "Session id is too long") \ - ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_SESSION_RESUMPTION_MODE, "Client Auth is not supported in session resumption mode") \ - ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_LENGTH, "Session ticket key length cannot be zero") \ - ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH, "Session ticket key name should be unique and the name length cannot be zero") \ - ERR_ENTRY(S2N_ERR_TICKET_KEY_NOT_UNIQUE, "Cannot add session ticket key because it was added before") \ - ERR_ENTRY(S2N_ERR_TICKET_KEY_LIMIT, "Limit reached for unexpired session ticket keys") \ - ERR_ENTRY(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY, "No key in encrypt-decrypt state is available to encrypt session ticket") \ - ERR_ENTRY(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED, "Failed to select a key from keys in encrypt-decrypt state") \ - ERR_ENTRY(S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND, "Key used in already assigned session ticket not found for decryption") \ - ERR_ENTRY(S2N_ERR_SENDING_NST, "Error in session ticket status encountered before sending NST") \ - ERR_ENTRY(S2N_ERR_INVALID_DYNAMIC_THRESHOLD, "invalid dynamic record threshold") \ - ERR_ENTRY(S2N_ERR_INVALID_ARGUMENT, "invalid argument provided into a function call") \ - ERR_ENTRY(S2N_ERR_NOT_IN_UNIT_TEST, "Illegal configuration, can only be used during unit tests") \ - ERR_ENTRY(S2N_ERR_NOT_IN_TEST, "Illegal configuration, can only be used during unit or integration tests") \ - ERR_ENTRY(S2N_ERR_UNSUPPORTED_CPU, "Unsupported CPU architecture") \ - ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_SHORT, "Session id is too short") \ - ERR_ENTRY(S2N_ERR_CONNECTION_CACHING_DISALLOWED, "This connection is not allowed to be cached") \ - ERR_ENTRY(S2N_ERR_SESSION_TICKET_NOT_SUPPORTED, "Session ticket not supported for this connection") \ - ERR_ENTRY(S2N_ERR_OCSP_NOT_SUPPORTED, "OCSP stapling was requested, but is not supported") \ - ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES, "Invalid signature algorithms preferences version") \ - ERR_ENTRY(S2N_ERR_RSA_PSS_NOT_SUPPORTED, "RSA-PSS signing not supported by underlying libcrypto implementation") \ - ERR_ENTRY(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE, "Inner plaintext size exceeds limit") \ - ERR_ENTRY(S2N_ERR_INVALID_ECC_PREFERENCES, "Invalid ecc curves preferences version") \ - ERR_ENTRY(S2N_ERR_RECORD_STUFFER_SIZE, "Record stuffer out of space") \ - ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL, "Fragment length is too small") \ - ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE, "Fragment length is too large") \ - ERR_ENTRY(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING, "Record stuffer needs to be drained first") \ - ERR_ENTRY(S2N_ERR_UNSUPPORTED_EXTENSION, "Illegal use of a known, supported extension") \ - ERR_ENTRY(S2N_ERR_MISSING_EXTENSION, "Mandatory extension not received") \ - ERR_ENTRY(S2N_ERR_DUPLICATE_EXTENSION, "Extension block contains two or more extensions of the same type") \ - ERR_ENTRY(S2N_ERR_DEPRECATED_SECURITY_POLICY, "Deprecated security policy. Please choose a different security policy.") \ - ERR_ENTRY(S2N_ERR_INVALID_SECURITY_POLICY, "Invalid security policy") \ - ERR_ENTRY(S2N_ERR_INVALID_KEM_PREFERENCES, "Invalid kem preferences version") \ - ERR_ENTRY(S2N_ERR_INVALID_PARSED_EXTENSIONS, "Invalid parsed extension data") \ - ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_PERFORMED, "Async operation was already performed, cannot perform it again") \ - ERR_ENTRY(S2N_ERR_ASYNC_NOT_PERFORMED, "Async operation is not performed, cannot apply its result") \ - ERR_ENTRY(S2N_ERR_ASYNC_WRONG_CONNECTION, "Async private key operation can only be consumed by connection which initiated it") \ - ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_APPLIED, "Async operation was already applied to connection, cannot apply it again") \ - ERR_ENTRY(S2N_ERR_INVALID_HELLO_RETRY, "Invalid hello retry request") \ - ERR_ENTRY(S2N_ERR_INVALID_STATE, "Invalid state, this is the result of invalid use of an API. Check the API documentation for the function that raised this error for more info") \ - ERR_ENTRY(S2N_ERR_UNSUPPORTED_WITH_QUIC, "Functionality not supported when running with QUIC support enabled") \ - ERR_ENTRY(S2N_ERR_PQ_CRYPTO, "An error occurred in a post-quantum crypto function") \ - ERR_ENTRY(S2N_ERR_DUPLICATE_PSK_IDENTITIES, "The list of pre-shared keys provided contains duplicate psk identities") \ - ERR_ENTRY(S2N_ERR_OFFERED_PSKS_TOO_LONG, "The total pre-shared key data is too long to send over the wire") \ - ERR_ENTRY(S2N_ERR_INVALID_SESSION_TICKET, "Session ticket data is not valid") \ - ERR_ENTRY(S2N_ERR_ZERO_LIFETIME_TICKET, "Calculated session lifetime is zero") \ - ERR_ENTRY(S2N_ERR_REENTRANCY, "Original execution must complete before method can be called again") \ - ERR_ENTRY(S2N_ERR_INVALID_CERT_STATE, "Certificate validation entered an invalid state and is not able to continue") \ - ERR_ENTRY(S2N_ERR_INVALID_SUPPORTED_GROUP_STATE, "SupportedGroup preference decision entered invalid state and selected both KEM and EC Curve") \ - ERR_ENTRY(S2N_ERR_INVALID_EARLY_DATA_STATE, "Early data in invalid state") \ - ERR_ENTRY(S2N_ERR_EARLY_DATA_NOT_ALLOWED, "Early data is not allowed by the connection") \ - ERR_ENTRY(S2N_ERR_NO_CERT_FOUND, "Certificate not found") \ - ERR_ENTRY(S2N_ERR_NO_PRIVATE_KEY, "Certificate found, but no corresponding private key") \ - ERR_ENTRY(S2N_ERR_CERT_NOT_VALIDATED, "Certificate not validated") \ - ERR_ENTRY(S2N_ERR_MAX_EARLY_DATA_SIZE, "Maximum early data bytes exceeded") \ - ERR_ENTRY(S2N_ERR_EARLY_DATA_BLOCKED, "Blocked on early data") \ - ERR_ENTRY(S2N_ERR_PSK_MODE, "Mixing resumption and external PSKs is not supported") \ - ERR_ENTRY(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND, "X509 extension value not found") \ - ERR_ENTRY(S2N_ERR_INVALID_X509_EXTENSION_TYPE, "Invalid X509 extension type") \ - ERR_ENTRY(S2N_ERR_INSUFFICIENT_MEM_SIZE, "The provided buffer size is not large enough to contain the output data. Try increasing the allocation size.") \ - ERR_ENTRY(S2N_ERR_KEYING_MATERIAL_EXPIRED, "The lifetime of the connection keying material has exceeded the limit. Perform a new full handshake.") \ - ERR_ENTRY(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, "Unable to decrypt rejected early data") \ - ERR_ENTRY(S2N_ERR_PKEY_CTX_INIT, "Unable to initialize the libcrypto pkey context") \ - ERR_ENTRY(S2N_ERR_SECRET_SCHEDULE_STATE, "Correct inputs to secret calculation not available") \ - ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NUMBER_MISMATCH, "The libcrypto major version number seen at compile-time is different from the major version number seen at run-time") \ - ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NAME_MISMATCH, "The libcrypto major version name seen at compile-time is different from the major version name seen at run-time") \ - ERR_ENTRY(S2N_ERR_OSSL_PROVIDER, "Failed to load or unload an openssl provider") \ - ERR_ENTRY(S2N_ERR_CERT_OWNERSHIP, "The ownership of the certificate chain is incompatible with the operation") \ - ERR_ENTRY(S2N_ERR_INTERNAL_LIBCRYPTO_ERROR, "An internal error has occurred in the libcrypto API") \ - ERR_ENTRY(S2N_ERR_NO_RENEGOTIATION, "Only secure, server-initiated renegotiation is supported") \ - ERR_ENTRY(S2N_ERR_APP_DATA_BLOCKED, "Blocked on application data during handshake") \ - ERR_ENTRY(S2N_ERR_KTLS_MANAGED_IO, "kTLS cannot be enabled while custom I/O is configured for the connection") \ - ERR_ENTRY(S2N_ERR_HANDSHAKE_NOT_COMPLETE, "Operation is only allowed after the handshake is complete") \ - ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, "kTLS is unsupported on this platform") \ - ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \ - ERR_ENTRY(S2N_ERR_KTLS_ENABLE, "An error occurred when attempting to enable kTLS on socket. Ensure the 'tls' kernel module is enabled.") \ - ERR_ENTRY(S2N_ERR_KTLS_BAD_CMSG, "Error handling cmsghdr.") \ - ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \ - ERR_ENTRY(S2N_ERR_TEST_ASSERTION, "Test assertion failed") \ - ERR_ENTRY(S2N_ERR_KTLS_RENEG, "kTLS does not support secure renegotiation") \ - ERR_ENTRY(S2N_ERR_KTLS_KEYUPDATE, "Received KeyUpdate from peer, but kernel does not support updating tls keys") \ - ERR_ENTRY(S2N_ERR_KTLS_KEY_LIMIT, "Reached key encryption limit, but kernel does not support updating tls keys") \ - ERR_ENTRY(S2N_ERR_KTLS_SOCKOPT, "A call to sockopt failed when attempting to update the keys in the kernel") \ - ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client forbids mutual authentication, but server requested a cert") \ - ERR_ENTRY(S2N_ERR_MISSING_CERT_REQUEST, "Client requires mutual authentication, but server did not request a cert") \ - ERR_ENTRY(S2N_ERR_MISSING_CLIENT_CERT, "Server requires client certificate") \ - ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ - ERR_ENTRY(S2N_ERR_TOO_MANY_CAS, "Too many certificate authorities in trust store"); \ - ERR_ENTRY(S2N_ERR_BAD_HEX, "Could not parse malformed hex string"); \ - ERR_ENTRY(S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK, "Config set to NULL before client hello callback. This should not be possible outside of tests."); \ - ERR_ENTRY(S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO, "The invoked s2n-tls API is not supported by the libcrypto"); \ - ERR_ENTRY(S2N_ERR_FIPS_MODE_UNSUPPORTED, "FIPS mode is not supported for the libcrypto"); \ - /* clang-format on */ - -#define ERR_STR_CASE(ERR, str) \ - case ERR: \ - return str; -#define ERR_NAME_CASE(ERR, str) \ - case ERR: \ - return #ERR; - -const char *s2n_strerror(int error, const char *lang) -{ - if (lang == NULL) { - lang = "EN"; - } - - if (strcasecmp(lang, "EN")) { - return no_such_language; - } - - s2n_error err = error; - switch (err) { - ERR_ENTRIES(ERR_STR_CASE) - - /* Skip block ends */ - case S2N_ERR_T_OK_END: - case S2N_ERR_T_IO_END: - case S2N_ERR_T_CLOSED_END: - case S2N_ERR_T_BLOCKED_END: - case S2N_ERR_T_ALERT_END: - case S2N_ERR_T_PROTO_END: - case S2N_ERR_T_INTERNAL_END: - case S2N_ERR_T_USAGE_END: - break; - - /* No default to make compiler fail on missing values */ - } - - return no_such_error; -} - -const char *s2n_strerror_name(int error) -{ - s2n_error err = error; - switch (err) { - ERR_ENTRIES(ERR_NAME_CASE) - - /* Skip block ends */ - case S2N_ERR_T_OK_END: - case S2N_ERR_T_IO_END: - case S2N_ERR_T_CLOSED_END: - case S2N_ERR_T_BLOCKED_END: - case S2N_ERR_T_ALERT_END: - case S2N_ERR_T_PROTO_END: - case S2N_ERR_T_INTERNAL_END: - case S2N_ERR_T_USAGE_END: - break; - - /* No default to make compiler fail on missing values */ - } - - return no_such_error; -} - -const char *s2n_strerror_debug(int error, const char *lang) -{ - if (lang == NULL) { - lang = "EN"; - } - - if (strcasecmp(lang, "EN")) { - return no_such_language; - } - - /* No error, just return the no error string */ - if (error == S2N_ERR_OK) { - return s2n_strerror(error, lang); - } - - return _s2n_debug_info.debug_str; -} - -const char *s2n_strerror_source(int error) -{ - /* No error, just return the no error string */ - if (error == S2N_ERR_OK) { - return s2n_strerror(error, "EN"); - } - - return _s2n_debug_info.source; -} - -int s2n_error_get_type(int error) -{ - return (error >> S2N_ERR_NUM_VALUE_BITS); -} - -/* https://www.gnu.org/software/libc/manual/html_node/Backtraces.html */ -static bool s_s2n_stack_traces_enabled = false; - -bool s2n_stack_traces_enabled() -{ - return s_s2n_stack_traces_enabled; -} - -int s2n_stack_traces_enabled_set(bool newval) -{ - s_s2n_stack_traces_enabled = newval; - return S2N_SUCCESS; -} - -void s2n_debug_info_reset(void) -{ - _s2n_debug_info.debug_str = ""; - _s2n_debug_info.source = ""; -} - -#ifdef S2N_STACKTRACE - - #define MAX_BACKTRACE_DEPTH 20 -__thread struct s2n_stacktrace tl_stacktrace = { 0 }; - -int s2n_free_stacktrace(void) -{ - if (tl_stacktrace.trace != NULL) { - free(tl_stacktrace.trace); - struct s2n_stacktrace zero_stacktrace = { 0 }; - tl_stacktrace = zero_stacktrace; - } - return S2N_SUCCESS; -} - -int s2n_calculate_stacktrace(void) -{ - if (!s_s2n_stack_traces_enabled) { - return S2N_SUCCESS; - } - - int old_errno = errno; - POSIX_GUARD(s2n_free_stacktrace()); - void *array[MAX_BACKTRACE_DEPTH]; - tl_stacktrace.trace_size = backtrace(array, MAX_BACKTRACE_DEPTH); - tl_stacktrace.trace = backtrace_symbols(array, tl_stacktrace.trace_size); - errno = old_errno; - return S2N_SUCCESS; -} - -int s2n_get_stacktrace(struct s2n_stacktrace *trace) -{ - *trace = tl_stacktrace; - return S2N_SUCCESS; -} - -int s2n_print_stacktrace(FILE *fptr) -{ - if (!s_s2n_stack_traces_enabled) { - fprintf(fptr, "%s\n%s\n", - "NOTE: Some details are omitted, run with S2N_PRINT_STACKTRACE=1 for a verbose backtrace.", - "See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide"); - return S2N_SUCCESS; - } - - fprintf(fptr, "\nStacktrace is:\n"); - for (int i = 0; i < tl_stacktrace.trace_size; ++i) { - fprintf(fptr, "%s\n", tl_stacktrace.trace[i]); - } - return S2N_SUCCESS; -} - -#else /* !S2N_STACKTRACE */ -int s2n_free_stacktrace(void) -{ - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_calculate_stacktrace(void) -{ - if (!s_s2n_stack_traces_enabled) { - return S2N_SUCCESS; - } - - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_get_stacktrace(struct s2n_stacktrace *trace) -{ - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_print_stacktrace(FILE *fptr) -{ - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} -#endif /* S2N_STACKTRACE */ +/* + * 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 "error/s2n_errno.h" + +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "utils/s2n_map.h" +#include "utils/s2n_safety.h" + +#ifdef S2N_STACKTRACE + #include +#endif + +__thread int s2n_errno; +__thread struct s2n_debug_info _s2n_debug_info = { .debug_str = "", .source = "" }; + +/** + * Returns the address of the thread-local `s2n_errno` variable + */ +int *s2n_errno_location() +{ + return &s2n_errno; +} + +static const char *no_such_language = "Language is not supported for error translation"; +static const char *no_such_error = "Internal s2n error"; + +/* + * Define error entries with descriptions in this macro once + * to generate code in next 2 following functions. + */ +/* clang-format off */ +#define ERR_ENTRIES(ERR_ENTRY) \ + ERR_ENTRY(S2N_ERR_OK, "no error") \ + ERR_ENTRY(S2N_ERR_IO, "underlying I/O operation failed, check system errno") \ + ERR_ENTRY(S2N_ERR_CLOSED, "connection is closed") \ + ERR_ENTRY(S2N_ERR_IO_BLOCKED, "underlying I/O operation would block") \ + ERR_ENTRY(S2N_ERR_ASYNC_BLOCKED, "blocked on external async function invocation") \ + ERR_ENTRY(S2N_ERR_ALERT, "TLS alert received") \ + ERR_ENTRY(S2N_ERR_ENCRYPT, "error encrypting data") \ + ERR_ENTRY(S2N_ERR_DECRYPT, "error decrypting data") \ + ERR_ENTRY(S2N_ERR_BAD_MESSAGE, "Bad message encountered") \ + ERR_ENTRY(S2N_ERR_KEY_INIT, "error initializing encryption key") \ + ERR_ENTRY(S2N_ERR_KEY_DESTROY, "error destroying encryption key") \ + ERR_ENTRY(S2N_ERR_DH_SERIALIZING, "error serializing Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_DH_SHARED_SECRET, "error computing Diffie-Hellman shared secret") \ + ERR_ENTRY(S2N_ERR_DH_WRITING_PUBLIC_KEY, "error writing Diffie-Hellman public key") \ + ERR_ENTRY(S2N_ERR_DH_FAILED_SIGNING, "error signing Diffie-Hellman values") \ + ERR_ENTRY(S2N_ERR_DH_COPYING_PARAMETERS, "error copying Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_DH_GENERATING_PARAMETERS, "error generating Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_CIPHER_NOT_SUPPORTED, "Cipher is not supported") \ + ERR_ENTRY(S2N_ERR_NO_APPLICATION_PROTOCOL, "No supported application protocol to negotiate") \ + ERR_ENTRY(S2N_ERR_FALLBACK_DETECTED, "TLS fallback detected") \ + ERR_ENTRY(S2N_ERR_HASH_DIGEST_FAILED, "failed to create hash digest") \ + ERR_ENTRY(S2N_ERR_HASH_INIT_FAILED, "error initializing hash") \ + ERR_ENTRY(S2N_ERR_HASH_UPDATE_FAILED, "error updating hash") \ + ERR_ENTRY(S2N_ERR_HASH_COPY_FAILED, "error copying hash") \ + ERR_ENTRY(S2N_ERR_HASH_WIPE_FAILED, "error wiping hash") \ + ERR_ENTRY(S2N_ERR_HASH_NOT_READY, "hash not in a valid state for the attempted operation") \ + ERR_ENTRY(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED, "error allowing MD5 to be used when in FIPS mode") \ + ERR_ENTRY(S2N_ERR_DECODE_CERTIFICATE, "error decoding certificate") \ + ERR_ENTRY(S2N_ERR_DECODE_PRIVATE_KEY, "error decoding private key") \ + ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHM, "Invalid signature algorithm") \ + ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_SCHEME, "Invalid signature scheme") \ + ERR_ENTRY(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, "Unable to negotiate a supported signature scheme") \ + ERR_ENTRY(S2N_ERR_CBC_VERIFY, "Failed CBC verification") \ + ERR_ENTRY(S2N_ERR_DH_COPYING_PUBLIC_KEY, "error copying Diffie-Hellman public key") \ + ERR_ENTRY(S2N_ERR_SIGN, "error signing data") \ + ERR_ENTRY(S2N_ERR_VERIFY_SIGNATURE, "error verifying signature") \ + ERR_ENTRY(S2N_ERR_ECDHE_GEN_KEY, "Failed to generate an ECDHE key") \ + ERR_ENTRY(S2N_ERR_ECDHE_SHARED_SECRET, "Error computing ECDHE shared secret") \ + ERR_ENTRY(S2N_ERR_ECDHE_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDHE handshake") \ + ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY, "Failed to validate the peer's point on the elliptic curve") \ + ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS, "Failed to validate the peer's point on the elliptic curve, per FIPS requirements") \ + ERR_ENTRY(S2N_ERR_ECDSA_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDSA SignatureScheme handshake") \ + ERR_ENTRY(S2N_ERR_ECDHE_SERIALIZING, "Error serializing ECDHE public") \ + ERR_ENTRY(S2N_ERR_KEM_UNSUPPORTED_PARAMS, "Unsupported KEM params was presented during a handshake that uses a KEM") \ + ERR_ENTRY(S2N_ERR_SHUTDOWN_RECORD_TYPE, "Non alert record received during s2n_shutdown()") \ + ERR_ENTRY(S2N_ERR_SHUTDOWN_CLOSED, "Peer closed before sending their close_notify") \ + ERR_ENTRY(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO, "renegotiation_info should be empty") \ + ERR_ENTRY(S2N_ERR_RECORD_LIMIT, "TLS record limit reached") \ + ERR_ENTRY(S2N_ERR_CERT_UNTRUSTED, "Certificate is untrusted") \ + ERR_ENTRY(S2N_ERR_CERT_INVALID_HOSTNAME, "Certificate is not valid for the supplied hostname") \ + ERR_ENTRY(S2N_ERR_CERT_REVOKED, "Certificate has been revoked by the CA") \ + ERR_ENTRY(S2N_ERR_CERT_NOT_YET_VALID, "Certificate is not yet valid") \ + ERR_ENTRY(S2N_ERR_CERT_EXPIRED, "Certificate has expired") \ + ERR_ENTRY(S2N_ERR_CERT_TYPE_UNSUPPORTED, "Certificate Type is unsupported") \ + ERR_ENTRY(S2N_ERR_CERT_INVALID, "Certificate is invalid") \ + ERR_ENTRY(S2N_ERR_CERT_INTENT_INVALID, "Certificate intent is invalid for the current context.") \ + ERR_ENTRY(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, "The maximum certificate chain depth has been exceeded") \ + ERR_ENTRY(S2N_ERR_CERT_REJECTED, "Certificate failed custom application validation") \ + ERR_ENTRY(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical certificate extension") \ + ERR_ENTRY(S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, "The certificate key or signature is not allowed by the security policy") \ + ERR_ENTRY(S2N_ERR_CRL_LOOKUP_FAILED, "No CRL could be found for the corresponding certificate") \ + ERR_ENTRY(S2N_ERR_CRL_SIGNATURE, "The signature of the CRL is invalid") \ + ERR_ENTRY(S2N_ERR_CRL_ISSUER, "Unable to get the CRL issuer certificate") \ + ERR_ENTRY(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical CRL extension") \ + ERR_ENTRY(S2N_ERR_CRL_INVALID_THIS_UPDATE, "The CRL contains an invalid thisUpdate field") \ + ERR_ENTRY(S2N_ERR_CRL_INVALID_NEXT_UPDATE, "The CRL contains an invalid nextUpdate field") \ + ERR_ENTRY(S2N_ERR_CRL_NOT_YET_VALID, "The CRL is not yet valid") \ + ERR_ENTRY(S2N_ERR_CRL_EXPIRED, "The CRL has expired") \ + ERR_ENTRY(S2N_ERR_INVALID_MAX_FRAG_LEN, "invalid Maximum Fragmentation Length encountered") \ + ERR_ENTRY(S2N_ERR_MAX_FRAG_LEN_MISMATCH, "Negotiated Maximum Fragmentation Length from server does not match the requested length by client") \ + ERR_ENTRY(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED, "TLS protocol version is not supported by configuration") \ + ERR_ENTRY(S2N_ERR_BAD_KEY_SHARE, "Bad key share received") \ + ERR_ENTRY(S2N_ERR_CANCELLED, "handshake was cancelled") \ + ERR_ENTRY(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED, "Protocol downgrade detected by client") \ + ERR_ENTRY(S2N_ERR_MADVISE, "error calling madvise") \ + ERR_ENTRY(S2N_ERR_ALLOC, "error allocating memory") \ + ERR_ENTRY(S2N_ERR_MLOCK, "error calling mlock (Did you run prlimit?)") \ + ERR_ENTRY(S2N_ERR_MUNLOCK, "error calling munlock") \ + ERR_ENTRY(S2N_ERR_FSTAT, "error calling fstat") \ + ERR_ENTRY(S2N_ERR_OPEN, "error calling open") \ + ERR_ENTRY(S2N_ERR_MMAP, "error calling mmap") \ + ERR_ENTRY(S2N_ERR_ATEXIT, "error calling atexit") \ + ERR_ENTRY(S2N_ERR_NOMEM, "no memory") \ + ERR_ENTRY(S2N_ERR_NULL, "NULL pointer encountered") \ + ERR_ENTRY(S2N_ERR_SAFETY, "a safety check failed") \ + ERR_ENTRY(S2N_ERR_INITIALIZED, "s2n is initialized") \ + ERR_ENTRY(S2N_ERR_NOT_INITIALIZED, "s2n not initialized") \ + ERR_ENTRY(S2N_ERR_RANDOM_UNINITIALIZED, "s2n entropy not initialized") \ + ERR_ENTRY(S2N_ERR_OPEN_RANDOM, "error opening urandom") \ + ERR_ENTRY(S2N_ERR_RESIZE_STATIC_STUFFER, "cannot resize a static stuffer") \ + ERR_ENTRY(S2N_ERR_RESIZE_TAINTED_STUFFER, "cannot resize a tainted stuffer") \ + ERR_ENTRY(S2N_ERR_STUFFER_OUT_OF_DATA, "stuffer is out of data") \ + ERR_ENTRY(S2N_ERR_STUFFER_IS_FULL, "stuffer is full") \ + ERR_ENTRY(S2N_ERR_STUFFER_NOT_FOUND, "stuffer expected bytes were not found") \ + ERR_ENTRY(S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA, "stuffer has unprocessed data") \ + ERR_ENTRY(S2N_ERR_HASH_INVALID_ALGORITHM, "invalid hash algorithm") \ + ERR_ENTRY(S2N_ERR_PRF_INVALID_ALGORITHM, "invalid prf hash algorithm") \ + ERR_ENTRY(S2N_ERR_PRF_INVALID_SEED, "invalid prf seeds provided") \ + ERR_ENTRY(S2N_ERR_PRF_DERIVE, "error deriving a secret from the PRF") \ + ERR_ENTRY(S2N_ERR_P_HASH_INVALID_ALGORITHM, "invalid p_hash algorithm") \ + ERR_ENTRY(S2N_ERR_P_HASH_INIT_FAILED, "error initializing p_hash") \ + ERR_ENTRY(S2N_ERR_P_HASH_UPDATE_FAILED, "error updating p_hash") \ + ERR_ENTRY(S2N_ERR_P_HASH_FINAL_FAILED, "error creating p_hash digest") \ + ERR_ENTRY(S2N_ERR_P_HASH_WIPE_FAILED, "error wiping p_hash") \ + ERR_ENTRY(S2N_ERR_HMAC_INVALID_ALGORITHM, "invalid HMAC algorithm") \ + ERR_ENTRY(S2N_ERR_HKDF_OUTPUT_SIZE, "invalid HKDF output size") \ + ERR_ENTRY(S2N_ERR_HKDF, "error generating HKDF output") \ + ERR_ENTRY(S2N_ERR_ALERT_PRESENT, "TLS alert is already pending") \ + ERR_ENTRY(S2N_ERR_HANDSHAKE_STATE, "Invalid handshake state encountered") \ + ERR_ENTRY(S2N_ERR_SHUTDOWN_PAUSED, "s2n_shutdown() called while paused") \ + ERR_ENTRY(S2N_ERR_SIZE_MISMATCH, "size mismatch") \ + ERR_ENTRY(S2N_ERR_RANDOM, "Error generating random data") \ + ERR_ENTRY(S2N_ERR_KEY_CHECK, "Invalid key") \ + ERR_ENTRY(S2N_ERR_CIPHER_TYPE, "Unknown cipher type used") \ + ERR_ENTRY(S2N_ERR_MAP_DUPLICATE, "Duplicate map key inserted") \ + ERR_ENTRY(S2N_ERR_MAP_IMMUTABLE, "Attempt to update an immutable map") \ + ERR_ENTRY(S2N_ERR_MAP_MUTABLE, "Attempt to lookup a mutable map") \ + ERR_ENTRY(S2N_ERR_MAP_INVALID_MAP_SIZE, "Attempt to create a map with 0 capacity") \ + ERR_ENTRY(S2N_ERR_INITIAL_HMAC, "error calling EVP_CIPHER_CTX_ctrl for composite cbc cipher") \ + ERR_ENTRY(S2N_ERR_INVALID_NONCE_TYPE, "Invalid AEAD nonce type") \ + ERR_ENTRY(S2N_ERR_UNIMPLEMENTED, "Unimplemented feature") \ + ERR_ENTRY(S2N_ERR_HANDSHAKE_UNREACHABLE, "Unreachable handshake state machine handler invoked") \ + ERR_ENTRY(S2N_ERR_READ, "error calling read") \ + ERR_ENTRY(S2N_ERR_WRITE, "error calling write") \ + ERR_ENTRY(S2N_ERR_BAD_FD, "Invalid file descriptor") \ + ERR_ENTRY(S2N_ERR_RDRAND_FAILED, "Error executing rdrand instruction") \ + ERR_ENTRY(S2N_ERR_FAILED_CACHE_RETRIEVAL, "Failed cache retrieval") \ + ERR_ENTRY(S2N_ERR_X509_TRUST_STORE, "Error initializing trust store") \ + ERR_ENTRY(S2N_ERR_UNKNOWN_PROTOCOL_VERSION, "Error determining client protocol version") \ + ERR_ENTRY(S2N_ERR_NULL_CN_NAME, "Error parsing CN names") \ + ERR_ENTRY(S2N_ERR_NULL_SANS, "Error parsing SANS") \ + ERR_ENTRY(S2N_ERR_CLIENT_HELLO_VERSION, "Could not get client hello version") \ + ERR_ENTRY(S2N_ERR_CLIENT_PROTOCOL_VERSION, "Could not get client protocol version") \ + ERR_ENTRY(S2N_ERR_SERVER_PROTOCOL_VERSION, "Could not get server protocol version") \ + ERR_ENTRY(S2N_ERR_ACTUAL_PROTOCOL_VERSION, "Could not get actual protocol version") \ + ERR_ENTRY(S2N_ERR_POLLING_FROM_SOCKET, "Error polling from socket") \ + ERR_ENTRY(S2N_ERR_RECV_STUFFER_FROM_CONN, "Error receiving stuffer from connection") \ + ERR_ENTRY(S2N_ERR_SEND_STUFFER_TO_CONN, "Error sending stuffer to connection") \ + ERR_ENTRY(S2N_ERR_PRECONDITION_VIOLATION, "Precondition violation") \ + ERR_ENTRY(S2N_ERR_POSTCONDITION_VIOLATION, "Postcondition violation") \ + ERR_ENTRY(S2N_ERR_INTEGER_OVERFLOW, "Integer overflow violation") \ + ERR_ENTRY(S2N_ERR_ARRAY_INDEX_OOB, "Array index out of bounds") \ + ERR_ENTRY(S2N_ERR_FREE_STATIC_BLOB, "Cannot free a static blob") \ + ERR_ENTRY(S2N_ERR_RESIZE_STATIC_BLOB, "Cannot resize a static blob") \ + ERR_ENTRY(S2N_ERR_RECORD_LENGTH_TOO_LARGE, "Record length exceeds protocol version maximum") \ + ERR_ENTRY(S2N_ERR_SET_DUPLICATE_VALUE, "Set already contains the provided value") \ + ERR_ENTRY(S2N_ERR_ASYNC_CALLBACK_FAILED, "Callback associated with async private keys function has failed") \ + ERR_ENTRY(S2N_ERR_ASYNC_MORE_THAN_ONE, "Only one asynchronous operation can be in-progress at the same time") \ + ERR_ENTRY(S2N_ERR_NO_ALERT, "No Alert present") \ + ERR_ENTRY(S2N_ERR_SERVER_MODE, "Operation not allowed in server mode") \ + ERR_ENTRY(S2N_ERR_CLIENT_MODE, "Operation not allowed in client mode") \ + ERR_ENTRY(S2N_ERR_CLIENT_MODE_DISABLED, "client connections not allowed") \ + ERR_ENTRY(S2N_ERR_TOO_MANY_CERTIFICATES, "only 1 certificate is supported in client mode") \ + ERR_ENTRY(S2N_ERR_TOO_MANY_SIGNATURE_SCHEMES, "Max supported length of SignatureAlgorithms/SignatureSchemes list is 128") \ + ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_FIPS_MODE, "Client Auth is not supported when in FIPS mode") \ + ERR_ENTRY(S2N_ERR_INVALID_BASE64, "invalid base64 encountered") \ + ERR_ENTRY(S2N_ERR_INVALID_HEX, "invalid HEX encountered") \ + ERR_ENTRY(S2N_ERR_INVALID_PEM, "invalid PEM encountered") \ + ERR_ENTRY(S2N_ERR_DH_PARAMS_CREATE, "error creating Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_DH_TOO_SMALL, "Diffie-Hellman parameters are too small") \ + ERR_ENTRY(S2N_ERR_DH_PARAMETER_CHECK, "Diffie-Hellman parameter check failed") \ + ERR_ENTRY(S2N_ERR_INVALID_PKCS3, "invalid PKCS3 encountered") \ + ERR_ENTRY(S2N_ERR_NO_CERTIFICATE_IN_PEM, "No certificate in PEM") \ + ERR_ENTRY(S2N_ERR_SERVER_NAME_TOO_LONG, "server name is too long") \ + ERR_ENTRY(S2N_ERR_NUM_DEFAULT_CERTIFICATES, "exceeded max default certificates or provided no default") \ + ERR_ENTRY(S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE, "setting multiple default certificates per auth type is not allowed") \ + ERR_ENTRY(S2N_ERR_INVALID_CIPHER_PREFERENCES, "Invalid Cipher Preferences version") \ + ERR_ENTRY(S2N_ERR_INVALID_APPLICATION_PROTOCOL, "The supplied application protocol name is invalid") \ + ERR_ENTRY(S2N_ERR_KEY_MISMATCH, "public and private key do not match") \ + ERR_ENTRY(S2N_ERR_SEND_SIZE, "Retried s2n_send() size is invalid") \ + ERR_ENTRY(S2N_ERR_CORK_SET_ON_UNMANAGED, "Attempt to set connection cork management on unmanaged IO") \ + ERR_ENTRY(S2N_ERR_UNRECOGNIZED_EXTENSION, "TLS extension not recognized") \ + ERR_ENTRY(S2N_ERR_EXTENSION_NOT_RECEIVED, "The TLS extension was not received") \ + ERR_ENTRY(S2N_ERR_INVALID_SCT_LIST, "SCT list is invalid") \ + ERR_ENTRY(S2N_ERR_INVALID_OCSP_RESPONSE, "OCSP response is invalid") \ + ERR_ENTRY(S2N_ERR_UPDATING_EXTENSION, "Updating extension data failed") \ + ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE, "Serialized session state is not in valid format") \ + ERR_ENTRY(S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG, "Serialized session state is too long") \ + ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_LONG, "Session id is too long") \ + ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_SESSION_RESUMPTION_MODE, "Client Auth is not supported in session resumption mode") \ + ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_LENGTH, "Session ticket key length cannot be zero") \ + ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH, "Session ticket key name should be unique and the name length cannot be zero") \ + ERR_ENTRY(S2N_ERR_TICKET_KEY_NOT_UNIQUE, "Cannot add session ticket key because it was added before") \ + ERR_ENTRY(S2N_ERR_TICKET_KEY_LIMIT, "Limit reached for unexpired session ticket keys") \ + ERR_ENTRY(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY, "No key in encrypt-decrypt state is available to encrypt session ticket") \ + ERR_ENTRY(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED, "Failed to select a key from keys in encrypt-decrypt state") \ + ERR_ENTRY(S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND, "Key used in already assigned session ticket not found for decryption") \ + ERR_ENTRY(S2N_ERR_SENDING_NST, "Error in session ticket status encountered before sending NST") \ + ERR_ENTRY(S2N_ERR_INVALID_DYNAMIC_THRESHOLD, "invalid dynamic record threshold") \ + ERR_ENTRY(S2N_ERR_INVALID_ARGUMENT, "invalid argument provided into a function call") \ + ERR_ENTRY(S2N_ERR_NOT_IN_UNIT_TEST, "Illegal configuration, can only be used during unit tests") \ + ERR_ENTRY(S2N_ERR_NOT_IN_TEST, "Illegal configuration, can only be used during unit or integration tests") \ + ERR_ENTRY(S2N_ERR_UNSUPPORTED_CPU, "Unsupported CPU architecture") \ + ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_SHORT, "Session id is too short") \ + ERR_ENTRY(S2N_ERR_CONNECTION_CACHING_DISALLOWED, "This connection is not allowed to be cached") \ + ERR_ENTRY(S2N_ERR_SESSION_TICKET_NOT_SUPPORTED, "Session ticket not supported for this connection") \ + ERR_ENTRY(S2N_ERR_OCSP_NOT_SUPPORTED, "OCSP stapling was requested, but is not supported") \ + ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES, "Invalid signature algorithms preferences version") \ + ERR_ENTRY(S2N_ERR_RSA_PSS_NOT_SUPPORTED, "RSA-PSS signing not supported by underlying libcrypto implementation") \ + ERR_ENTRY(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE, "Inner plaintext size exceeds limit") \ + ERR_ENTRY(S2N_ERR_INVALID_ECC_PREFERENCES, "Invalid ecc curves preferences version") \ + ERR_ENTRY(S2N_ERR_RECORD_STUFFER_SIZE, "Record stuffer out of space") \ + ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL, "Fragment length is too small") \ + ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE, "Fragment length is too large") \ + ERR_ENTRY(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING, "Record stuffer needs to be drained first") \ + ERR_ENTRY(S2N_ERR_UNSUPPORTED_EXTENSION, "Illegal use of a known, supported extension") \ + ERR_ENTRY(S2N_ERR_MISSING_EXTENSION, "Mandatory extension not received") \ + ERR_ENTRY(S2N_ERR_DUPLICATE_EXTENSION, "Extension block contains two or more extensions of the same type") \ + ERR_ENTRY(S2N_ERR_DEPRECATED_SECURITY_POLICY, "Deprecated security policy. Please choose a different security policy.") \ + ERR_ENTRY(S2N_ERR_INVALID_SECURITY_POLICY, "Invalid security policy") \ + ERR_ENTRY(S2N_ERR_INVALID_KEM_PREFERENCES, "Invalid kem preferences version") \ + ERR_ENTRY(S2N_ERR_INVALID_PARSED_EXTENSIONS, "Invalid parsed extension data") \ + ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_PERFORMED, "Async operation was already performed, cannot perform it again") \ + ERR_ENTRY(S2N_ERR_ASYNC_NOT_PERFORMED, "Async operation is not performed, cannot apply its result") \ + ERR_ENTRY(S2N_ERR_ASYNC_WRONG_CONNECTION, "Async private key operation can only be consumed by connection which initiated it") \ + ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_APPLIED, "Async operation was already applied to connection, cannot apply it again") \ + ERR_ENTRY(S2N_ERR_INVALID_HELLO_RETRY, "Invalid hello retry request") \ + ERR_ENTRY(S2N_ERR_INVALID_STATE, "Invalid state, this is the result of invalid use of an API. Check the API documentation for the function that raised this error for more info") \ + ERR_ENTRY(S2N_ERR_UNSUPPORTED_WITH_QUIC, "Functionality not supported when running with QUIC support enabled") \ + ERR_ENTRY(S2N_ERR_PQ_CRYPTO, "An error occurred in a post-quantum crypto function") \ + ERR_ENTRY(S2N_ERR_DUPLICATE_PSK_IDENTITIES, "The list of pre-shared keys provided contains duplicate psk identities") \ + ERR_ENTRY(S2N_ERR_OFFERED_PSKS_TOO_LONG, "The total pre-shared key data is too long to send over the wire") \ + ERR_ENTRY(S2N_ERR_INVALID_SESSION_TICKET, "Session ticket data is not valid") \ + ERR_ENTRY(S2N_ERR_ZERO_LIFETIME_TICKET, "Calculated session lifetime is zero") \ + ERR_ENTRY(S2N_ERR_REENTRANCY, "Original execution must complete before method can be called again") \ + ERR_ENTRY(S2N_ERR_INVALID_CERT_STATE, "Certificate validation entered an invalid state and is not able to continue") \ + ERR_ENTRY(S2N_ERR_INVALID_SUPPORTED_GROUP_STATE, "SupportedGroup preference decision entered invalid state and selected both KEM and EC Curve") \ + ERR_ENTRY(S2N_ERR_INVALID_EARLY_DATA_STATE, "Early data in invalid state") \ + ERR_ENTRY(S2N_ERR_EARLY_DATA_NOT_ALLOWED, "Early data is not allowed by the connection") \ + ERR_ENTRY(S2N_ERR_NO_CERT_FOUND, "Certificate not found") \ + ERR_ENTRY(S2N_ERR_NO_PRIVATE_KEY, "Certificate found, but no corresponding private key") \ + ERR_ENTRY(S2N_ERR_CERT_NOT_VALIDATED, "Certificate not validated") \ + ERR_ENTRY(S2N_ERR_MAX_EARLY_DATA_SIZE, "Maximum early data bytes exceeded") \ + ERR_ENTRY(S2N_ERR_EARLY_DATA_BLOCKED, "Blocked on early data") \ + ERR_ENTRY(S2N_ERR_PSK_MODE, "Mixing resumption and external PSKs is not supported") \ + ERR_ENTRY(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND, "X509 extension value not found") \ + ERR_ENTRY(S2N_ERR_INVALID_X509_EXTENSION_TYPE, "Invalid X509 extension type") \ + ERR_ENTRY(S2N_ERR_INSUFFICIENT_MEM_SIZE, "The provided buffer size is not large enough to contain the output data. Try increasing the allocation size.") \ + ERR_ENTRY(S2N_ERR_KEYING_MATERIAL_EXPIRED, "The lifetime of the connection keying material has exceeded the limit. Perform a new full handshake.") \ + ERR_ENTRY(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, "Unable to decrypt rejected early data") \ + ERR_ENTRY(S2N_ERR_PKEY_CTX_INIT, "Unable to initialize the libcrypto pkey context") \ + ERR_ENTRY(S2N_ERR_SECRET_SCHEDULE_STATE, "Correct inputs to secret calculation not available") \ + ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NUMBER_MISMATCH, "The libcrypto major version number seen at compile-time is different from the major version number seen at run-time") \ + ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NAME_MISMATCH, "The libcrypto major version name seen at compile-time is different from the major version name seen at run-time") \ + ERR_ENTRY(S2N_ERR_OSSL_PROVIDER, "Failed to load or unload an openssl provider") \ + ERR_ENTRY(S2N_ERR_CERT_OWNERSHIP, "The ownership of the certificate chain is incompatible with the operation") \ + ERR_ENTRY(S2N_ERR_INTERNAL_LIBCRYPTO_ERROR, "An internal error has occurred in the libcrypto API") \ + ERR_ENTRY(S2N_ERR_NO_RENEGOTIATION, "Only secure, server-initiated renegotiation is supported") \ + ERR_ENTRY(S2N_ERR_APP_DATA_BLOCKED, "Blocked on application data during handshake") \ + ERR_ENTRY(S2N_ERR_KTLS_MANAGED_IO, "kTLS cannot be enabled while custom I/O is configured for the connection") \ + ERR_ENTRY(S2N_ERR_HANDSHAKE_NOT_COMPLETE, "Operation is only allowed after the handshake is complete") \ + ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, "kTLS is unsupported on this platform") \ + ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \ + ERR_ENTRY(S2N_ERR_KTLS_ENABLE, "An error occurred when attempting to enable kTLS on socket. Ensure the 'tls' kernel module is enabled.") \ + ERR_ENTRY(S2N_ERR_KTLS_BAD_CMSG, "Error handling cmsghdr.") \ + ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \ + ERR_ENTRY(S2N_ERR_TEST_ASSERTION, "Test assertion failed") \ + ERR_ENTRY(S2N_ERR_KTLS_RENEG, "kTLS does not support secure renegotiation") \ + ERR_ENTRY(S2N_ERR_KTLS_KEYUPDATE, "Received KeyUpdate from peer, but kernel does not support updating tls keys") \ + ERR_ENTRY(S2N_ERR_KTLS_KEY_LIMIT, "Reached key encryption limit, but kernel does not support updating tls keys") \ + ERR_ENTRY(S2N_ERR_KTLS_SOCKOPT, "A call to sockopt failed when attempting to update the keys in the kernel") \ + ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client forbids mutual authentication, but server requested a cert") \ + ERR_ENTRY(S2N_ERR_MISSING_CERT_REQUEST, "Client requires mutual authentication, but server did not request a cert") \ + ERR_ENTRY(S2N_ERR_MISSING_CLIENT_CERT, "Server requires client certificate") \ + ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ + ERR_ENTRY(S2N_ERR_TOO_MANY_CAS, "Too many certificate authorities in trust store"); \ + ERR_ENTRY(S2N_ERR_BAD_HEX, "Could not parse malformed hex string"); \ + ERR_ENTRY(S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK, "Config set to NULL before client hello callback. This should not be possible outside of tests."); \ + ERR_ENTRY(S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO, "The invoked s2n-tls API is not supported by the libcrypto"); \ + ERR_ENTRY(S2N_ERR_FIPS_MODE_UNSUPPORTED, "FIPS mode is not supported for the libcrypto"); \ + /* clang-format on */ + +#define ERR_STR_CASE(ERR, str) \ + case ERR: \ + return str; +#define ERR_NAME_CASE(ERR, str) \ + case ERR: \ + return #ERR; + +const char *s2n_strerror(int error, const char *lang) +{ + if (lang == NULL) { + lang = "EN"; + } + + if (strcasecmp(lang, "EN")) { + return no_such_language; + } + + s2n_error err = error; + switch (err) { + ERR_ENTRIES(ERR_STR_CASE) + + /* Skip block ends */ + case S2N_ERR_T_OK_END: + case S2N_ERR_T_IO_END: + case S2N_ERR_T_CLOSED_END: + case S2N_ERR_T_BLOCKED_END: + case S2N_ERR_T_ALERT_END: + case S2N_ERR_T_PROTO_END: + case S2N_ERR_T_INTERNAL_END: + case S2N_ERR_T_USAGE_END: + break; + + /* No default to make compiler fail on missing values */ + } + + return no_such_error; +} + +const char *s2n_strerror_name(int error) +{ + s2n_error err = error; + switch (err) { + ERR_ENTRIES(ERR_NAME_CASE) + + /* Skip block ends */ + case S2N_ERR_T_OK_END: + case S2N_ERR_T_IO_END: + case S2N_ERR_T_CLOSED_END: + case S2N_ERR_T_BLOCKED_END: + case S2N_ERR_T_ALERT_END: + case S2N_ERR_T_PROTO_END: + case S2N_ERR_T_INTERNAL_END: + case S2N_ERR_T_USAGE_END: + break; + + /* No default to make compiler fail on missing values */ + } + + return no_such_error; +} + +const char *s2n_strerror_debug(int error, const char *lang) +{ + if (lang == NULL) { + lang = "EN"; + } + + if (strcasecmp(lang, "EN")) { + return no_such_language; + } + + /* No error, just return the no error string */ + if (error == S2N_ERR_OK) { + return s2n_strerror(error, lang); + } + + return _s2n_debug_info.debug_str; +} + +const char *s2n_strerror_source(int error) +{ + /* No error, just return the no error string */ + if (error == S2N_ERR_OK) { + return s2n_strerror(error, "EN"); + } + + return _s2n_debug_info.source; +} + +int s2n_error_get_type(int error) +{ + return (error >> S2N_ERR_NUM_VALUE_BITS); +} + +/* https://www.gnu.org/software/libc/manual/html_node/Backtraces.html */ +static bool s_s2n_stack_traces_enabled = false; + +bool s2n_stack_traces_enabled() +{ + return s_s2n_stack_traces_enabled; +} + +int s2n_stack_traces_enabled_set(bool newval) +{ + s_s2n_stack_traces_enabled = newval; + return S2N_SUCCESS; +} + +void s2n_debug_info_reset(void) +{ + _s2n_debug_info.debug_str = ""; + _s2n_debug_info.source = ""; +} + +#ifdef S2N_STACKTRACE + + #define MAX_BACKTRACE_DEPTH 20 +__thread struct s2n_stacktrace tl_stacktrace = { 0 }; + +int s2n_free_stacktrace(void) +{ + if (tl_stacktrace.trace != NULL) { + free(tl_stacktrace.trace); + struct s2n_stacktrace zero_stacktrace = { 0 }; + tl_stacktrace = zero_stacktrace; + } + return S2N_SUCCESS; +} + +int s2n_calculate_stacktrace(void) +{ + if (!s_s2n_stack_traces_enabled) { + return S2N_SUCCESS; + } + + int old_errno = errno; + POSIX_GUARD(s2n_free_stacktrace()); + void *array[MAX_BACKTRACE_DEPTH]; + tl_stacktrace.trace_size = backtrace(array, MAX_BACKTRACE_DEPTH); + tl_stacktrace.trace = backtrace_symbols(array, tl_stacktrace.trace_size); + errno = old_errno; + return S2N_SUCCESS; +} + +int s2n_get_stacktrace(struct s2n_stacktrace *trace) +{ + *trace = tl_stacktrace; + return S2N_SUCCESS; +} + +int s2n_print_stacktrace(FILE *fptr) +{ + if (!s_s2n_stack_traces_enabled) { + fprintf(fptr, "%s\n%s\n", + "NOTE: Some details are omitted, run with S2N_PRINT_STACKTRACE=1 for a verbose backtrace.", + "See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide"); + return S2N_SUCCESS; + } + + fprintf(fptr, "\nStacktrace is:\n"); + for (int i = 0; i < tl_stacktrace.trace_size; ++i) { + fprintf(fptr, "%s\n", tl_stacktrace.trace[i]); + } + return S2N_SUCCESS; +} + +#else /* !S2N_STACKTRACE */ +int s2n_free_stacktrace(void) +{ + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_calculate_stacktrace(void) +{ + if (!s_s2n_stack_traces_enabled) { + return S2N_SUCCESS; + } + + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_get_stacktrace(struct s2n_stacktrace *trace) +{ + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_print_stacktrace(FILE *fptr) +{ + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} +#endif /* S2N_STACKTRACE */ diff --git a/stuffer/s2n_stuffer.c b/stuffer/s2n_stuffer.c index f4ac02bbb8d..f9f3ff178d9 100644 --- a/stuffer/s2n_stuffer.c +++ b/stuffer/s2n_stuffer.c @@ -1,469 +1,470 @@ -/* - * 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 "stuffer/s2n_stuffer.h" - -#include "error/s2n_errno.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer) -{ - /** - * Note that we do not assert any properties on the tainted field, - * as any boolean value in that field is valid. - */ - RESULT_ENSURE_REF(stuffer); - RESULT_GUARD(s2n_blob_validate(&stuffer->blob)); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(stuffer->growable, stuffer->alloced), S2N_ERR_SAFETY); - - /* <= is valid because we can have a fully written/read stuffer */ - RESULT_DEBUG_ENSURE(stuffer->high_water_mark <= stuffer->blob.size, S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(stuffer->write_cursor <= stuffer->high_water_mark, S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(stuffer->read_cursor <= stuffer->write_cursor, S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_stuffer_reservation_validate(const struct s2n_stuffer_reservation *reservation) -{ - /** - * Note that we need two dereferences here to decrease proof complexity - * for CBMC (see https://github.com/awslabs/s2n/issues/2290). We can roll back - * this change once CBMC can handle common subexpression elimination. - */ - RESULT_ENSURE_REF(reservation); - const struct s2n_stuffer_reservation reserve_obj = *reservation; - RESULT_GUARD(s2n_stuffer_validate(reserve_obj.stuffer)); - const struct s2n_stuffer stuffer_obj = *(reserve_obj.stuffer); - - /* Verify that write_cursor + length can be represented as a uint32_t without overflow */ - RESULT_ENSURE_LTE(reserve_obj.write_cursor, UINT32_MAX - reserve_obj.length); - /* The entire reservation must fit between the stuffer read and write cursors */ - RESULT_ENSURE_LTE(reserve_obj.write_cursor + reserve_obj.length, stuffer_obj.write_cursor); - RESULT_ENSURE_GTE(reserve_obj.write_cursor, stuffer_obj.read_cursor); - - return S2N_RESULT_OK; -} - -/** - * Initialize a stuffer to reference some data `in`. - * - * `stuffer` will not own the data, and the caller is responsible for ensuring that - * the data pointed to by `in` outlives `stuffer`. - */ -int s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in) -{ - POSIX_ENSURE_MUT(stuffer); - POSIX_PRECONDITION(s2n_blob_validate(in)); - stuffer->blob = *in; - stuffer->read_cursor = 0; - stuffer->write_cursor = 0; - stuffer->high_water_mark = 0; - stuffer->alloced = 0; - stuffer->growable = 0; - stuffer->tainted = 0; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in) -{ - POSIX_ENSURE_REF(in); - POSIX_GUARD(s2n_stuffer_init(stuffer, in)); - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, in->size)); - return S2N_SUCCESS; -} - -int s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_ENSURE_REF(stuffer); - *stuffer = (struct s2n_stuffer){ 0 }; - POSIX_GUARD(s2n_alloc(&stuffer->blob, size)); - POSIX_GUARD(s2n_stuffer_init(stuffer, &stuffer->blob)); - - stuffer->alloced = 1; - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_GUARD(s2n_stuffer_alloc(stuffer, size)); - - stuffer->growable = 1; - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_free(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (stuffer->alloced) { - POSIX_GUARD(s2n_free(&stuffer->blob)); - } - *stuffer = (struct s2n_stuffer){ 0 }; - return S2N_SUCCESS; -} - -int s2n_stuffer_free_without_wipe(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (stuffer->alloced) { - POSIX_GUARD(s2n_free_without_wipe(&stuffer->blob)); - } - *stuffer = (struct s2n_stuffer){ 0 }; - return S2N_SUCCESS; -} - -int s2n_stuffer_resize(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); - POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); - - if (size == stuffer->blob.size) { - return S2N_SUCCESS; - } - - if (size == 0) { - s2n_stuffer_wipe(stuffer); - return s2n_free(&stuffer->blob); - } - - if (size < stuffer->blob.size) { - POSIX_CHECKED_MEMSET(stuffer->blob.data + size, S2N_WIPE_PATTERN, (stuffer->blob.size - size)); - if (stuffer->read_cursor > size) { - stuffer->read_cursor = size; - } - if (stuffer->write_cursor > size) { - stuffer->write_cursor = size; - } - if (stuffer->high_water_mark > size) { - stuffer->high_water_mark = size; - } - stuffer->blob.size = size; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; - } - - POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_resize_if_empty(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (stuffer->blob.data == NULL) { - POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); - POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); - POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); - } - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -/** - * Reset read and write progress. - * - * This sets the read and write cursors to zero. - */ -int s2n_stuffer_rewrite(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - stuffer->write_cursor = 0; - stuffer->read_cursor = 0; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_rewind_read(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE(stuffer->read_cursor >= size, S2N_ERR_STUFFER_OUT_OF_DATA); - stuffer->read_cursor -= size; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -/** - * Reset read progress. - * - * This sets the read cursor to zero. - */ -int s2n_stuffer_reread(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - stuffer->read_cursor = 0; - return S2N_SUCCESS; -} - -int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - uint32_t wipe_size = S2N_MIN(size, stuffer->write_cursor); - - stuffer->write_cursor -= wipe_size; - stuffer->read_cursor = S2N_MIN(stuffer->read_cursor, stuffer->write_cursor); - POSIX_CHECKED_MEMSET(stuffer->blob.data + stuffer->write_cursor, S2N_WIPE_PATTERN, wipe_size); - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer) -{ - return stuffer && (stuffer->read_cursor == stuffer->write_cursor) && !stuffer->tainted; -} - -int s2n_stuffer_wipe(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (!s2n_stuffer_is_wiped(stuffer)) { - POSIX_CHECKED_MEMSET(stuffer->blob.data, S2N_WIPE_PATTERN, stuffer->high_water_mark); - } - - stuffer->tainted = 0; - stuffer->write_cursor = 0; - stuffer->read_cursor = 0; - stuffer->high_water_mark = 0; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_skip_read(struct s2n_stuffer *stuffer, uint32_t n) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE(s2n_stuffer_data_available(stuffer) >= n, S2N_ERR_STUFFER_OUT_OF_DATA); - - stuffer->read_cursor += n; - return S2N_SUCCESS; -} - -void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len) -{ - PTR_GUARD_POSIX(s2n_stuffer_skip_read(stuffer, data_len)); - - stuffer->tainted = 1; - - return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - data_len) : NULL; -} - -int s2n_stuffer_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(out); - - return s2n_stuffer_read_bytes(stuffer, out->data, out->size); -} - -int s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) -{ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, out->size)); - - void *ptr = (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - out->size) : NULL; - POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, out->size), S2N_ERR_NULL); - - POSIX_CHECKED_MEMCPY(out->data, ptr, out->size); - POSIX_CHECKED_MEMSET(ptr, 0, out->size); - - return S2N_SUCCESS; -} - -int s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) -{ - POSIX_ENSURE_REF(data); - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); - POSIX_ENSURE_REF(stuffer->blob.data); - void *ptr = stuffer->blob.data + stuffer->read_cursor - size; - - POSIX_CHECKED_MEMCPY(data, ptr, size); - - return S2N_SUCCESS; -} - -int s2n_stuffer_erase_and_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) -{ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); - POSIX_ENSURE_REF(stuffer->blob.data); - void *ptr = stuffer->blob.data + stuffer->read_cursor - size; - - POSIX_CHECKED_MEMCPY(data, ptr, size); - POSIX_CHECKED_MEMSET(ptr, 0, size); - - return S2N_SUCCESS; -} - -int s2n_stuffer_skip_write(struct s2n_stuffer *stuffer, const uint32_t n) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, n)); - stuffer->write_cursor += n; - stuffer->high_water_mark = S2N_MAX(stuffer->write_cursor, stuffer->high_water_mark); - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -void *s2n_stuffer_raw_write(struct s2n_stuffer *stuffer, const uint32_t data_len) -{ - PTR_GUARD_POSIX(s2n_stuffer_skip_write(stuffer, data_len)); - - stuffer->tainted = 1; - - return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->write_cursor - data_len) : NULL; -} - -int s2n_stuffer_write(struct s2n_stuffer *stuffer, const struct s2n_blob *in) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_PRECONDITION(s2n_blob_validate(in)); - return s2n_stuffer_write_bytes(stuffer, in->data, in->size); -} - -int s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *data, const uint32_t size) -{ - if (size == 0) { - return S2N_SUCCESS; - } - - POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, size)); - - void *ptr = stuffer->blob.data + stuffer->write_cursor - size; - POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); - - if (ptr == data) { - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; - } - - POSIX_CHECKED_MEMCPY(ptr, data, size); - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_writev_bytes(struct s2n_stuffer *stuffer, const struct iovec *iov, size_t iov_count, uint32_t offs, - uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE_REF(iov); - void *ptr = s2n_stuffer_raw_write(stuffer, size); - POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); - - size_t size_left = size, to_skip = offs; - for (size_t i = 0; i < iov_count; i++) { - if (to_skip >= iov[i].iov_len) { - to_skip -= iov[i].iov_len; - continue; - } - size_t iov_len_op = iov[i].iov_len - to_skip; - POSIX_ENSURE_LTE(iov_len_op, UINT32_MAX); - uint32_t iov_len = (uint32_t) iov_len_op; - uint32_t iov_size_to_take = S2N_MIN(size_left, iov_len); - POSIX_ENSURE_REF(iov[i].iov_base); - POSIX_ENSURE_LT(to_skip, iov[i].iov_len); - POSIX_CHECKED_MEMCPY(ptr, ((uint8_t *) (iov[i].iov_base)) + to_skip, iov_size_to_take); - size_left -= iov_size_to_take; - if (size_left == 0) { - break; - } - ptr = (void *) ((uint8_t *) ptr + iov_size_to_take); - to_skip = 0; - } - - return S2N_SUCCESS; -} - -static int s2n_stuffer_copy_impl(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) -{ - POSIX_GUARD(s2n_stuffer_skip_read(from, len)); - POSIX_GUARD(s2n_stuffer_skip_write(to, len)); - - uint8_t *from_ptr = (from->blob.data) ? (from->blob.data + from->read_cursor - len) : NULL; - uint8_t *to_ptr = (to->blob.data) ? (to->blob.data + to->write_cursor - len) : NULL; - - POSIX_CHECKED_MEMCPY(to_ptr, from_ptr, len); - - return S2N_SUCCESS; -} - -int s2n_stuffer_reserve_space(struct s2n_stuffer *stuffer, uint32_t n) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (s2n_stuffer_space_remaining(stuffer) < n) { - POSIX_ENSURE(stuffer->growable, S2N_ERR_STUFFER_IS_FULL); - /* Always grow a stuffer by at least 1k */ - const uint32_t growth = S2N_MAX(n - s2n_stuffer_space_remaining(stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES); - uint32_t new_size = 0; - POSIX_GUARD(s2n_add_overflow(stuffer->blob.size, growth, &new_size)); - POSIX_GUARD(s2n_stuffer_resize(stuffer, new_size)); - } - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -/* Copies "len" bytes from "from" to "to". - * If the copy cannot succeed (i.e. there are either not enough bytes available, or there is not enough space to write them - * restore the old value of the stuffer */ -int s2n_stuffer_copy(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) -{ - const uint32_t orig_read_cursor = from->read_cursor; - const uint32_t orig_write_cursor = to->write_cursor; - - if (s2n_stuffer_copy_impl(from, to, len) < 0) { - from->read_cursor = orig_read_cursor; - to->write_cursor = orig_write_cursor; - S2N_ERROR_PRESERVE_ERRNO(); - } - - return S2N_SUCCESS; -} - -int s2n_stuffer_extract_blob(struct s2n_stuffer *stuffer, struct s2n_blob *out) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE_REF(out); - POSIX_GUARD(s2n_realloc(out, s2n_stuffer_data_available(stuffer))); - - if (s2n_stuffer_data_available(stuffer) > 0) { - POSIX_CHECKED_MEMCPY(out->data, stuffer->blob.data + stuffer->read_cursor, s2n_stuffer_data_available(stuffer)); - } - - POSIX_POSTCONDITION(s2n_blob_validate(out)); - return S2N_SUCCESS; -} - -int s2n_stuffer_shift(struct s2n_stuffer *stuffer) -{ - POSIX_ENSURE_REF(stuffer); - struct s2n_stuffer copy = *stuffer; - POSIX_GUARD(s2n_stuffer_rewrite(©)); - - uint8_t *data = stuffer->blob.data; - /* Adding 0 to a NULL value is undefined behavior */ - if (stuffer->read_cursor != 0) { - data += stuffer->read_cursor; - } - - uint32_t data_size = s2n_stuffer_data_available(stuffer); - POSIX_GUARD(s2n_stuffer_write_bytes(©, data, data_size)); - *stuffer = copy; - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "stuffer/s2n_stuffer.h" + +#include "error/s2n_errno.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer) +{ + /** + * Note that we do not assert any properties on the tainted field, + * as any boolean value in that field is valid. + */ + RESULT_ENSURE_REF(stuffer); + RESULT_GUARD(s2n_blob_validate(&stuffer->blob)); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(stuffer->growable, stuffer->alloced), S2N_ERR_SAFETY); + + /* <= is valid because we can have a fully written/read stuffer */ + RESULT_DEBUG_ENSURE(stuffer->high_water_mark <= stuffer->blob.size, S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(stuffer->write_cursor <= stuffer->high_water_mark, S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(stuffer->read_cursor <= stuffer->write_cursor, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_stuffer_reservation_validate(const struct s2n_stuffer_reservation *reservation) +{ + /** + * Note that we need two dereferences here to decrease proof complexity + * for CBMC (see https://github.com/awslabs/s2n/issues/2290). We can roll back + * this change once CBMC can handle common subexpression elimination. + */ + RESULT_ENSURE_REF(reservation); + const struct s2n_stuffer_reservation reserve_obj = *reservation; + RESULT_GUARD(s2n_stuffer_validate(reserve_obj.stuffer)); + const struct s2n_stuffer stuffer_obj = *(reserve_obj.stuffer); + + /* Verify that write_cursor + length can be represented as a uint32_t without overflow */ + RESULT_ENSURE_LTE(reserve_obj.write_cursor, UINT32_MAX - reserve_obj.length); + /* The entire reservation must fit between the stuffer read and write cursors */ + RESULT_ENSURE_LTE(reserve_obj.write_cursor + reserve_obj.length, stuffer_obj.write_cursor); + RESULT_ENSURE_GTE(reserve_obj.write_cursor, stuffer_obj.read_cursor); + + return S2N_RESULT_OK; +} + +/** + * Initialize a stuffer to reference some data `in`. + * + * `stuffer` will not own the data, and the caller is responsible for ensuring that + * the data pointed to by `in` outlives `stuffer`. + */ +int s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in) +{ + POSIX_ENSURE_MUT(stuffer); + POSIX_PRECONDITION(s2n_blob_validate(in)); + stuffer->blob = *in; + stuffer->read_cursor = 0; + stuffer->write_cursor = 0; + stuffer->high_water_mark = 0; + stuffer->alloced = 0; + stuffer->growable = 0; + stuffer->tainted = 0; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in) +{ + POSIX_ENSURE_REF(in); + POSIX_GUARD(s2n_stuffer_init(stuffer, in)); + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, in->size)); + return S2N_SUCCESS; +} + +int s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_ENSURE_REF(stuffer); + *stuffer = (struct s2n_stuffer){ 0 }; + POSIX_GUARD(s2n_alloc(&stuffer->blob, size)); + POSIX_GUARD(s2n_stuffer_init(stuffer, &stuffer->blob)); + + stuffer->alloced = 1; + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_GUARD(s2n_stuffer_alloc(stuffer, size)); + + stuffer->growable = 1; + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_free(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (stuffer->alloced) { + POSIX_GUARD(s2n_free(&stuffer->blob)); + } + *stuffer = (struct s2n_stuffer){ 0 }; + return S2N_SUCCESS; +} + +int s2n_stuffer_free_without_wipe(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (stuffer->alloced) { + POSIX_GUARD(s2n_free_without_wipe(&stuffer->blob)); + } + *stuffer = (struct s2n_stuffer){ 0 }; + return S2N_SUCCESS; +} + +int s2n_stuffer_resize(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); + POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); + + if (size == stuffer->blob.size) { + return S2N_SUCCESS; + } + + if (size == 0) { + s2n_stuffer_wipe(stuffer); + return s2n_free(&stuffer->blob); + } + + if (size < stuffer->blob.size) { + POSIX_CHECKED_MEMSET(stuffer->blob.data + size, S2N_WIPE_PATTERN, (stuffer->blob.size - size)); + if (stuffer->read_cursor > size) { + stuffer->read_cursor = size; + } + if (stuffer->write_cursor > size) { + stuffer->write_cursor = size; + } + if (stuffer->high_water_mark > size) { + stuffer->high_water_mark = size; + } + stuffer->blob.size = size; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; + } + + POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_resize_if_empty(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (stuffer->blob.data == NULL) { + POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); + POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); + POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); + } + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +/** + * Reset read and write progress. + * + * This sets the read and write cursors to zero. + */ +int s2n_stuffer_rewrite(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + stuffer->write_cursor = 0; + stuffer->read_cursor = 0; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_rewind_read(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE(stuffer->read_cursor >= size, S2N_ERR_STUFFER_OUT_OF_DATA); + stuffer->read_cursor -= size; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +/** + * Reset read progress. + * + * This sets the read cursor to zero. + */ +int s2n_stuffer_reread(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + stuffer->read_cursor = 0; + return S2N_SUCCESS; +} + +int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + uint32_t wipe_size = S2N_MIN(size, stuffer->write_cursor); + + stuffer->write_cursor -= wipe_size; + stuffer->read_cursor = S2N_MIN(stuffer->read_cursor, stuffer->write_cursor); + POSIX_CHECKED_MEMSET(stuffer->blob.data + stuffer->write_cursor, S2N_WIPE_PATTERN, wipe_size); + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer) +{ + return stuffer && (stuffer->read_cursor == stuffer->write_cursor) && !stuffer->tainted; +} + +int s2n_stuffer_wipe(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (!s2n_stuffer_is_wiped(stuffer)) { + POSIX_CHECKED_MEMSET(stuffer->blob.data, S2N_WIPE_PATTERN, stuffer->high_water_mark); + } + + stuffer->tainted = 0; + stuffer->write_cursor = 0; + stuffer->read_cursor = 0; + stuffer->high_water_mark = 0; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_skip_read(struct s2n_stuffer *stuffer, uint32_t n) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE(s2n_stuffer_data_available(stuffer) >= n, S2N_ERR_STUFFER_OUT_OF_DATA); + + stuffer->read_cursor += n; + return S2N_SUCCESS; +} + +void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len) +{ + PTR_GUARD_POSIX(s2n_stuffer_skip_read(stuffer, data_len)); + + stuffer->tainted = 1; + + return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - data_len) : NULL; +} + +int s2n_stuffer_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(out); + + return s2n_stuffer_read_bytes(stuffer, out->data, out->size); +} + +int s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) +{ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, out->size)); + + void *ptr = (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - out->size) : NULL; + POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, out->size), S2N_ERR_NULL); + + POSIX_CHECKED_MEMCPY(out->data, ptr, out->size); + POSIX_CHECKED_MEMSET(ptr, 0, out->size); + + return S2N_SUCCESS; +} + +int s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) +{ + POSIX_ENSURE_REF(data); + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); + POSIX_ENSURE_REF(stuffer->blob.data); + void *ptr = stuffer->blob.data + stuffer->read_cursor - size; + + POSIX_CHECKED_MEMCPY(data, ptr, size); + + return S2N_SUCCESS; +} + +int s2n_stuffer_erase_and_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) +{ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); + POSIX_ENSURE_REF(stuffer->blob.data); + void *ptr = stuffer->blob.data + stuffer->read_cursor - size; + + POSIX_CHECKED_MEMCPY(data, ptr, size); + POSIX_CHECKED_MEMSET(ptr, 0, size); + + return S2N_SUCCESS; +} + +int s2n_stuffer_skip_write(struct s2n_stuffer *stuffer, const uint32_t n) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, n)); + stuffer->write_cursor += n; + stuffer->high_water_mark = S2N_MAX(stuffer->write_cursor, stuffer->high_water_mark); + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +void *s2n_stuffer_raw_write(struct s2n_stuffer *stuffer, const uint32_t data_len) +{ + PTR_GUARD_POSIX(s2n_stuffer_skip_write(stuffer, data_len)); + + stuffer->tainted = 1; + + return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->write_cursor - data_len) : NULL; +} + +int s2n_stuffer_write(struct s2n_stuffer *stuffer, const struct s2n_blob *in) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_PRECONDITION(s2n_blob_validate(in)); + return s2n_stuffer_write_bytes(stuffer, in->data, in->size); +} + +int s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *data, const uint32_t size) +{ + if (size == 0) { + return S2N_SUCCESS; + } + + POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, size)); + + void *ptr = stuffer->blob.data + stuffer->write_cursor - size; + POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); + + if (ptr == data) { + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; + } + + POSIX_CHECKED_MEMCPY(ptr, data, size); + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_writev_bytes(struct s2n_stuffer *stuffer, const struct iovec *iov, size_t iov_count, uint32_t offs, + uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE_REF(iov); + void *ptr = s2n_stuffer_raw_write(stuffer, size); + POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); + + size_t size_left = size, to_skip = offs; + for (size_t i = 0; i < iov_count; i++) { + if (to_skip >= iov[i].iov_len) { + to_skip -= iov[i].iov_len; + continue; + } + size_t iov_len_op = iov[i].iov_len - to_skip; + POSIX_ENSURE_LTE(iov_len_op, UINT32_MAX); + uint32_t iov_len = (uint32_t) iov_len_op; + uint32_t iov_size_to_take = S2N_MIN(size_left, iov_len); + POSIX_ENSURE_REF(iov[i].iov_base); + POSIX_ENSURE_LT(to_skip, iov[i].iov_len); + POSIX_CHECKED_MEMCPY(ptr, ((uint8_t *) (iov[i].iov_base)) + to_skip, iov_size_to_take); + size_left -= iov_size_to_take; + if (size_left == 0) { + break; + } + ptr = (void *) ((uint8_t *) ptr + iov_size_to_take); + to_skip = 0; + } + + return S2N_SUCCESS; +} + +static int s2n_stuffer_copy_impl(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) +{ + POSIX_GUARD(s2n_stuffer_skip_read(from, len)); + POSIX_GUARD(s2n_stuffer_skip_write(to, len)); + + uint8_t *from_ptr = (from->blob.data) ? (from->blob.data + from->read_cursor - len) : NULL; + uint8_t *to_ptr = (to->blob.data) ? (to->blob.data + to->write_cursor - len) : NULL; + + POSIX_CHECKED_MEMCPY(to_ptr, from_ptr, len); + + return S2N_SUCCESS; +} + +int s2n_stuffer_reserve_space(struct s2n_stuffer *stuffer, uint32_t n) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (s2n_stuffer_space_remaining(stuffer) < n) { + POSIX_ENSURE(stuffer->growable, S2N_ERR_STUFFER_IS_FULL); + /* Always grow a stuffer by at least 1k */ + const uint32_t growth = S2N_MAX(n - s2n_stuffer_space_remaining(stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES); + uint32_t new_size = 0; + POSIX_GUARD(s2n_add_overflow(stuffer->blob.size, growth, &new_size)); + POSIX_GUARD(s2n_stuffer_resize(stuffer, new_size)); + } + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +/* Copies "len" bytes from "from" to "to". + * If the copy cannot succeed (i.e. there are either not enough bytes available, or there is not enough space to write them + * restore the old value of the stuffer */ +int s2n_stuffer_copy(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) +{ + const uint32_t orig_read_cursor = from->read_cursor; + const uint32_t orig_write_cursor = to->write_cursor; + + if (s2n_stuffer_copy_impl(from, to, len) < 0) { + from->read_cursor = orig_read_cursor; + to->write_cursor = orig_write_cursor; + S2N_ERROR_PRESERVE_ERRNO(); + } + + return S2N_SUCCESS; +} + +int s2n_stuffer_extract_blob(struct s2n_stuffer *stuffer, struct s2n_blob *out) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE_REF(out); + POSIX_GUARD(s2n_realloc(out, s2n_stuffer_data_available(stuffer))); + + if (s2n_stuffer_data_available(stuffer) > 0) { + POSIX_CHECKED_MEMCPY(out->data, stuffer->blob.data + stuffer->read_cursor, s2n_stuffer_data_available(stuffer)); + } + + POSIX_POSTCONDITION(s2n_blob_validate(out)); + return S2N_SUCCESS; +} + +int s2n_stuffer_shift(struct s2n_stuffer *stuffer) +{ + POSIX_ENSURE_REF(stuffer); + struct s2n_stuffer copy = *stuffer; + POSIX_GUARD(s2n_stuffer_rewrite(©)); + + uint8_t *data = stuffer->blob.data; + /* Adding 0 to a NULL value is undefined behavior */ + if (stuffer->read_cursor != 0) { + data += stuffer->read_cursor; + } + + uint32_t data_size = s2n_stuffer_data_available(stuffer); + POSIX_GUARD(s2n_stuffer_write_bytes(©, data, data_size)); + *stuffer = copy; + return S2N_SUCCESS; +} diff --git a/stuffer/s2n_stuffer_file.c b/stuffer/s2n_stuffer_file.c index 73033f1db1c..1d68f932289 100644 --- a/stuffer/s2n_stuffer_file.c +++ b/stuffer/s2n_stuffer_file.c @@ -21,6 +21,13 @@ #include #endif + +#if defined(_MSC_VER) +#include +#define read _read +#define write _write +#endif + #include "error/s2n_errno.h" #include "stuffer/s2n_stuffer.h" #include "utils/s2n_io.h" diff --git a/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c b/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c index 3bd23bddd6d..201bf551b02 100644 --- a/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c +++ b/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c @@ -1,61 +1,62 @@ -/* - * 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 "utils/s2n_array.h" -#include "utils/s2n_result.h" - -#include - -#include - -void s2n_array_insert_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_array *array = cbmc_allocate_s2n_array(); - __CPROVER_assume(s2n_result_is_ok(s2n_array_validate(array))); - __CPROVER_assume(s2n_array_is_bounded(array, MAX_ARRAY_LEN, MAX_ARRAY_ELEMENT_SIZE)); - uint32_t idx; - void **element = malloc(sizeof(void *)); - - nondet_s2n_mem_init(); - - struct s2n_array old_array = *array; - struct store_byte_from_buffer old_byte; - save_byte_from_array(array->mem.data, array->len, &old_byte); - - /* Operation under verification. */ - if (s2n_result_is_ok(s2n_array_insert(array, idx, element))) { - /* - * In the case s2n_array_insert is successful, we can ensure the array isn't empty - * and index is within bounds. - */ - assert(array->mem.data != NULL); - assert(array->len == (old_array.len + 1)); - assert(idx < array->len); - assert(*element == (array->mem.data + (array->element_size * idx))); - assert(s2n_result_is_ok(s2n_array_validate(array))); - if (old_array.len != 0 && idx == old_array.len) { - assert_byte_from_blob_matches(&array->mem, &old_byte); - } - - /* Verify the array capacity increases by the correct amount. */ - uint32_t old_capacity = old_array.mem.size / old_array.element_size; - if (old_array.len >= old_capacity) { - uint32_t expected_new_capacity = S2N_MAX(S2N_INITIAL_ARRAY_SIZE, old_capacity * 2) * old_array.element_size; - uint32_t new_capacity = array->mem.size; - assert(new_capacity == expected_new_capacity); - } - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "utils/s2n_array.h" +#include "utils/s2n_result.h" + +#include + +#include + +void s2n_array_insert_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_array *array = cbmc_allocate_s2n_array(); + __CPROVER_assume(s2n_result_is_ok(s2n_array_validate(array))); + __CPROVER_assume(s2n_array_is_bounded(array, MAX_ARRAY_LEN, MAX_ARRAY_ELEMENT_SIZE)); + uint32_t idx; + void **element = malloc(sizeof(void *)); + + nondet_s2n_mem_init(); + + struct s2n_array old_array = *array; + struct store_byte_from_buffer old_byte; + save_byte_from_array(array->mem.data, array->len, &old_byte); + + /* Operation under verification. */ + if (s2n_result_is_ok(s2n_array_insert(array, idx, element))) { + /* + * In the case s2n_array_insert is successful, we can ensure the array isn't empty + * and index is within bounds. + */ + assert(array->mem.data != NULL); + assert(array->len == (old_array.len + 1)); + assert(idx < array->len); + assert(*element == (array->mem.data + (array->element_size * idx))); + assert(s2n_result_is_ok(s2n_array_validate(array))); + if (old_array.len != 0 && idx == old_array.len) { + assert_byte_from_blob_matches(&array->mem, &old_byte); + } + + /* Verify the array capacity increases by the correct amount. */ + uint32_t old_capacity = old_array.mem.size / old_array.element_size; + if (old_array.len >= old_capacity) { + uint32_t expected_new_capacity = S2N_MAX(S2N_INITIAL_ARRAY_SIZE, old_capacity * 2) * old_array.element_size; + uint32_t new_capacity = array->mem.size; + assert(new_capacity == expected_new_capacity); + } + } +} diff --git a/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c b/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c index d29c3150e54..d2ad475d4c3 100644 --- a/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c +++ b/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c @@ -1,32 +1,33 @@ -/* - * 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 "crypto/s2n_hash.h" - -#include - -void s2n_hash_digest_size_harness() -{ - /* Non-deterministic inputs. */ - s2n_hash_algorithm alg; - uint8_t * out = malloc(sizeof(*out)); - - /* Operation under verification. */ - if (s2n_hash_digest_size(alg, out) == S2N_SUCCESS) { - assert(*out <= S2N_MAX_DIGEST_LEN); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hash.h" + +#include + +void s2n_hash_digest_size_harness() +{ + /* Non-deterministic inputs. */ + s2n_hash_algorithm alg; + uint8_t * out = malloc(sizeof(*out)); + + /* Operation under verification. */ + if (s2n_hash_digest_size(alg, out) == S2N_SUCCESS) { + assert(*out <= S2N_MAX_DIGEST_LEN); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c b/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c index ebf1369c0ee..e7081bed0c3 100644 --- a/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c @@ -1,53 +1,54 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_raw_write_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t data_len; - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare. */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - void *retval = s2n_stuffer_raw_write(stuffer, data_len); - - if (retval != NULL) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); - assert(retval == stuffer->blob.data + old_stuffer.write_cursor); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); - assert(stuffer->tainted == 1); - if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_raw_write_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t data_len; + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare. */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + void *retval = s2n_stuffer_raw_write(stuffer, data_len); + + if (retval != NULL) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); + assert(retval == stuffer->blob.data + old_stuffer.write_cursor); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); + assert(stuffer->tainted == 1); + if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c index 8e7046f0023..0cbd43dcb29 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c @@ -1,65 +1,66 @@ -/* - * 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" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -int s2n_stuffer_reserve(struct s2n_stuffer *, struct s2n_stuffer_reservation *, const uint8_t); - -void s2n_stuffer_reserve_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); - const uint8_t length; - - /* Non-deterministically set initialized (in s2n_mem) to true. */ - if (nondet_bool()) { s2n_mem_init(); } - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve(stuffer, reservation, length) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + length); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + length, old_stuffer.high_water_mark)); - assert(reservation->length == length); - if (old_stuffer.blob.size > 0 && reservation->length > 0) { - size_t idx; - __CPROVER_assume(idx >= reservation->write_cursor - && idx < (reservation->write_cursor + reservation->length)); - assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - } - assert(stuffer == reservation->stuffer); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); - } else { - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +int s2n_stuffer_reserve(struct s2n_stuffer *, struct s2n_stuffer_reservation *, const uint8_t); + +void s2n_stuffer_reserve_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); + const uint8_t length; + + /* Non-deterministically set initialized (in s2n_mem) to true. */ + if (nondet_bool()) { s2n_mem_init(); } + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve(stuffer, reservation, length) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + length); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + length, old_stuffer.high_water_mark)); + assert(reservation->length == length); + if (old_stuffer.blob.size > 0 && reservation->length > 0) { + size_t idx; + __CPROVER_assume(idx >= reservation->write_cursor + && idx < (reservation->write_cursor + reservation->length)); + assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + } + assert(stuffer == reservation->stuffer); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); + } else { + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c index 128de7d5354..0f0f3f21d67 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c @@ -1,63 +1,64 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_reserve_space_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t size; - - nondet_s2n_mem_init(); - - /* Save previous state. */ - struct s2n_stuffer old_stuffer = *stuffer; - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve_space(stuffer, size) == S2N_SUCCESS) { - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - if (s2n_stuffer_space_remaining(&old_stuffer) < size) { - /* Always grow a stuffer by at least 1k */ - assert(stuffer->blob.size - == (S2N_MAX(size - s2n_stuffer_space_remaining(&old_stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES) - + old_stuffer.blob.size)); - assert(stuffer->blob.allocated >= size); - } else { - assert_stuffer_equivalence(stuffer, &old_stuffer, &old_byte_from_stuffer); - } - } else { - /* - * s2n_realloc could fail, so we can onyl guarantee equivalence of - * data pointer, but not the elements in it. - */ - assert(stuffer->blob.data == old_stuffer.blob.data); - assert(stuffer->blob.size == old_stuffer.blob.size); - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_reserve_space_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t size; + + nondet_s2n_mem_init(); + + /* Save previous state. */ + struct s2n_stuffer old_stuffer = *stuffer; + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve_space(stuffer, size) == S2N_SUCCESS) { + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + if (s2n_stuffer_space_remaining(&old_stuffer) < size) { + /* Always grow a stuffer by at least 1k */ + assert(stuffer->blob.size + == (S2N_MAX(size - s2n_stuffer_space_remaining(&old_stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES) + + old_stuffer.blob.size)); + assert(stuffer->blob.allocated >= size); + } else { + assert_stuffer_equivalence(stuffer, &old_stuffer, &old_byte_from_stuffer); + } + } else { + /* + * s2n_realloc could fail, so we can onyl guarantee equivalence of + * data pointer, but not the elements in it. + */ + assert(stuffer->blob.data == old_stuffer.blob.data); + assert(stuffer->blob.size == old_stuffer.blob.size); + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c index f4473ab6eef..2f8ba9fb311 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c @@ -1,61 +1,62 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_reserve_uint16_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve_uint16(stuffer, reservation) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + sizeof(uint16_t)); - assert(stuffer->high_water_mark - == S2N_MAX(old_stuffer.write_cursor + sizeof(uint16_t), old_stuffer.high_water_mark)); - assert(reservation->length == sizeof(uint16_t)); - if (old_stuffer.blob.size > 0) { - size_t idx; - __CPROVER_assume(idx >= reservation->write_cursor - && idx < (reservation->write_cursor + reservation->length)); - assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - } - assert(stuffer == reservation->stuffer); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); - } else { - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_reserve_uint16_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve_uint16(stuffer, reservation) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + sizeof(uint16_t)); + assert(stuffer->high_water_mark + == S2N_MAX(old_stuffer.write_cursor + sizeof(uint16_t), old_stuffer.high_water_mark)); + assert(reservation->length == sizeof(uint16_t)); + if (old_stuffer.blob.size > 0) { + size_t idx; + __CPROVER_assume(idx >= reservation->write_cursor + && idx < (reservation->write_cursor + reservation->length)); + assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + } + assert(stuffer == reservation->stuffer); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); + } else { + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c index 5538dda5aaf..f99764c5c5f 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c @@ -1,60 +1,61 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_reserve_uint24_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve_uint24(stuffer, reservation) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + SIZEOF_UINT24); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + SIZEOF_UINT24, old_stuffer.high_water_mark)); - assert(reservation->length == SIZEOF_UINT24); - if (old_stuffer.blob.size > 0) { - size_t idx; - __CPROVER_assume(idx >= reservation->write_cursor - && idx < (reservation->write_cursor + reservation->length)); - assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - } - assert(stuffer == reservation->stuffer); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); - } else { - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_reserve_uint24_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve_uint24(stuffer, reservation) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + SIZEOF_UINT24); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + SIZEOF_UINT24, old_stuffer.high_water_mark)); + assert(reservation->length == SIZEOF_UINT24); + if (old_stuffer.blob.size > 0) { + size_t idx; + __CPROVER_assume(idx >= reservation->write_cursor + && idx < (reservation->write_cursor + reservation->length)); + assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + } + assert(stuffer == reservation->stuffer); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); + } else { + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c b/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c index 3c9f732144e..2a21526b568 100644 --- a/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c @@ -1,48 +1,49 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_skip_write_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t data_len; - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_skip_write(stuffer, data_len) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); - if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_skip_write_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t data_len; + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_skip_write(stuffer, data_len) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); + if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c b/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c index ebca6bf697f..fcf0c38285e 100644 --- a/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c @@ -1,66 +1,67 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_wipe_n_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - uint32_t n; - - /* Assume preconditions. */ - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - - /* Save previous state. */ - struct s2n_stuffer old_stuffer = *stuffer; - - /* Save byte from untouched portion to compare after the wipe */ - uint32_t expect_wiped = S2N_MIN(n, old_stuffer.write_cursor); - uint32_t expected_write_cursor = old_stuffer.write_cursor - expect_wiped; - struct store_byte_from_buffer old_byte; - save_byte_from_array(old_stuffer.blob.data, expected_write_cursor, &old_byte); - - /* Given a valid stuffer, wipe_n always succeeds */ - assert(s2n_stuffer_wipe_n(stuffer, n) == S2N_SUCCESS); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - - /* The basic stuffer fields should NOT be updated */ - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - assert(stuffer->tainted == old_stuffer.tainted); - assert(stuffer->blob == old_stuffer.blob); - assert(stuffer->blob.data == old_stuffer.blob.data); - - /* The read and write cursors should be updated */ - assert(S2N_IMPLIES(expect_wiped < n, stuffer->write_cursor == 0)); - assert(S2N_IMPLIES(expect_wiped < n, stuffer->read_cursor == 0)); - assert(stuffer->write_cursor == expected_write_cursor); - assert(stuffer->read_cursor == S2N_MIN(old_stuffer.read_cursor, stuffer->write_cursor)); - - /* Any data before the new write cursor should NOT be updated */ - if (expected_write_cursor > 0) { - assert_byte_from_buffer_matches(stuffer->blob.data, &old_byte); - } - - /* Everything after the new write cursor should be wiped */ - if (expect_wiped > 0) { - assert_all_bytes_are(stuffer->blob.data + stuffer->write_cursor, - S2N_WIPE_PATTERN, expect_wiped); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_wipe_n_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + uint32_t n; + + /* Assume preconditions. */ + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + + /* Save previous state. */ + struct s2n_stuffer old_stuffer = *stuffer; + + /* Save byte from untouched portion to compare after the wipe */ + uint32_t expect_wiped = S2N_MIN(n, old_stuffer.write_cursor); + uint32_t expected_write_cursor = old_stuffer.write_cursor - expect_wiped; + struct store_byte_from_buffer old_byte; + save_byte_from_array(old_stuffer.blob.data, expected_write_cursor, &old_byte); + + /* Given a valid stuffer, wipe_n always succeeds */ + assert(s2n_stuffer_wipe_n(stuffer, n) == S2N_SUCCESS); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + + /* The basic stuffer fields should NOT be updated */ + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + assert(stuffer->tainted == old_stuffer.tainted); + assert(stuffer->blob == old_stuffer.blob); + assert(stuffer->blob.data == old_stuffer.blob.data); + + /* The read and write cursors should be updated */ + assert(S2N_IMPLIES(expect_wiped < n, stuffer->write_cursor == 0)); + assert(S2N_IMPLIES(expect_wiped < n, stuffer->read_cursor == 0)); + assert(stuffer->write_cursor == expected_write_cursor); + assert(stuffer->read_cursor == S2N_MIN(old_stuffer.read_cursor, stuffer->write_cursor)); + + /* Any data before the new write cursor should NOT be updated */ + if (expected_write_cursor > 0) { + assert_byte_from_buffer_matches(stuffer->blob.data, &old_byte); + } + + /* Everything after the new write cursor should be wiped */ + if (expect_wiped > 0) { + assert_all_bytes_are(stuffer->blob.data + stuffer->write_cursor, + S2N_WIPE_PATTERN, expect_wiped); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c b/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c index e64daf162c0..2a3c224de49 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c @@ -1,63 +1,64 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_write_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_blob *blob = cbmc_allocate_s2n_blob(); - __CPROVER_assume(s2n_result_is_ok(s2n_blob_validate(blob))); - uint32_t idx; - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - - /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ - __CPROVER_assume(idx < stuffer->blob.size); - if (__CPROVER_overflow_plus(old_stuffer.write_cursor, blob->size)) { - __CPROVER_assume(idx < old_stuffer.write_cursor); - } else { - __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + blob->size); - } - uint8_t untouched_byte = stuffer->blob.data[ idx ]; - - /* Store a byte from the blob to compare. */ - struct s2n_blob old_blob = *blob; - struct store_byte_from_buffer old_byte_from_blob; - save_byte_from_blob(blob, &old_byte_from_blob); - - /* Operation under verification. */ - if (s2n_stuffer_write(stuffer, blob) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + blob->size); - assert(stuffer->blob.data[ idx ] == untouched_byte); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + blob->size, old_stuffer.high_water_mark)); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert_blob_equivalence(blob, &old_blob, &old_byte_from_blob); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_blob *blob = cbmc_allocate_s2n_blob(); + __CPROVER_assume(s2n_result_is_ok(s2n_blob_validate(blob))); + uint32_t idx; + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + + /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ + __CPROVER_assume(idx < stuffer->blob.size); + if (__CPROVER_overflow_plus(old_stuffer.write_cursor, blob->size)) { + __CPROVER_assume(idx < old_stuffer.write_cursor); + } else { + __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + blob->size); + } + uint8_t untouched_byte = stuffer->blob.data[ idx ]; + + /* Store a byte from the blob to compare. */ + struct s2n_blob old_blob = *blob; + struct store_byte_from_buffer old_byte_from_blob; + save_byte_from_blob(blob, &old_byte_from_blob); + + /* Operation under verification. */ + if (s2n_stuffer_write(stuffer, blob) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + blob->size); + assert(stuffer->blob.data[ idx ] == untouched_byte); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + blob->size, old_stuffer.high_water_mark)); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert_blob_equivalence(blob, &old_blob, &old_byte_from_blob); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c index d2671bbaf71..b5eecfc8edb 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c @@ -1,57 +1,58 @@ -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_write_bytes_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t idx; - uint32_t size; - uint8_t *data = malloc(size); - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - - /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ - __CPROVER_assume(idx < stuffer->blob.size); - if (__CPROVER_overflow_plus(old_stuffer.write_cursor, size)) { - __CPROVER_assume(idx < old_stuffer.write_cursor); - } else { - __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + size); - } - uint8_t untouched_byte = stuffer->blob.data[ idx ]; - - /* Operation under verification. */ - if (s2n_stuffer_write_bytes(stuffer, data, size) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + size); - assert(stuffer->blob.data[ idx ] == untouched_byte); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + size, old_stuffer.high_water_mark)); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } - assert(stuffer->read_cursor == old_stuffer.read_cursor); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_bytes_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t idx; + uint32_t size; + uint8_t *data = malloc(size); + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + + /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ + __CPROVER_assume(idx < stuffer->blob.size); + if (__CPROVER_overflow_plus(old_stuffer.write_cursor, size)) { + __CPROVER_assume(idx < old_stuffer.write_cursor); + } else { + __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + size); + } + uint8_t untouched_byte = stuffer->blob.data[ idx ]; + + /* Operation under verification. */ + if (s2n_stuffer_write_bytes(stuffer, data, size) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + size); + assert(stuffer->blob.data[ idx ] == untouched_byte); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + size, old_stuffer.high_water_mark)); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } + assert(stuffer->read_cursor == old_stuffer.read_cursor); +} 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 index 5c364427b1b..1c1211dca50 100644 --- 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 @@ -1,74 +1,75 @@ -/* - * 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 = S2N_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); -} +#include "utils/s2n_prelude.h" +/* + * 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 = S2N_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/s2n_stuffer_write_uint16_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/s2n_stuffer_write_uint16_hex_harness.c index d95c7681bb9..af2ed47444c 100644 --- 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 @@ -1,63 +1,64 @@ -/* - * 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 = S2N_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); -} +#include "utils/s2n_prelude.h" +/* + * 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 = S2N_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/s2n_stuffer_write_uint8_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/s2n_stuffer_write_uint8_hex_harness.c index 7c398714ca3..3deeb341fa6 100644 --- 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 @@ -1,63 +1,64 @@ -/* - * 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 = S2N_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); -} +#include "utils/s2n_prelude.h" +/* + * 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 = S2N_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/fuzz/s2n_cert_req_recv_test.c b/tests/fuzz/s2n_cert_req_recv_test.c index a4fd27598bb..80e3410e74a 100644 --- a/tests/fuzz/s2n_cert_req_recv_test.c +++ b/tests/fuzz/s2n_cert_req_recv_test.c @@ -1,100 +1,101 @@ -/* - * 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. - */ - -/* Target Functions: s2n_cert_req_recv s2n_recv_client_cert_preferences - s2n_cert_type_to_pkey_type s2n_recv_supported_sig_scheme_list - s2n_choose_sig_scheme_from_peer_preference_list - s2n_set_cert_chain_as_client */ - -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static char *cert_chain, *private_key; -struct s2n_cert_chain_and_key *default_cert; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - /* Initialize test chain and key */ - cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(cert_chain); - private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(private_key); - default_cert = s2n_cert_chain_and_key_new(); - POSIX_ENSURE_REF(default_cert); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - - return S2N_SUCCESS; -} - -static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least one byte of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 1); - - /* Setup */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - POSIX_ENSURE_REF(client_conn); - struct s2n_config *client_config = s2n_config_new(); - POSIX_ENSURE_REF(client_config); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); - POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); - POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); - - /* Pull a byte off the libfuzzer input and use it to set parameters */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&client_conn->handshake.io, &randval)); - client_conn->actual_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_cert_req_recv(client_conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(client_conn)); - POSIX_GUARD(s2n_config_free(client_config)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(default_cert); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_cert_req_recv s2n_recv_client_cert_preferences + s2n_cert_type_to_pkey_type s2n_recv_supported_sig_scheme_list + s2n_choose_sig_scheme_from_peer_preference_list + s2n_set_cert_chain_as_client */ + +#include +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static char *cert_chain, *private_key; +struct s2n_cert_chain_and_key *default_cert; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + /* Initialize test chain and key */ + cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(cert_chain); + private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(private_key); + default_cert = s2n_cert_chain_and_key_new(); + POSIX_ENSURE_REF(default_cert); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + + return S2N_SUCCESS; +} + +static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least one byte of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 1); + + /* Setup */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + POSIX_ENSURE_REF(client_conn); + struct s2n_config *client_config = s2n_config_new(); + POSIX_ENSURE_REF(client_config); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); + + /* Pull a byte off the libfuzzer input and use it to set parameters */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&client_conn->handshake.io, &randval)); + client_conn->actual_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_cert_req_recv(client_conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_config_free(client_config)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(default_cert); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/fuzz/s2n_certificate_extensions_parse_test.c b/tests/fuzz/s2n_certificate_extensions_parse_test.c index c6a8401666b..2b278f63662 100644 --- a/tests/fuzz/s2n_certificate_extensions_parse_test.c +++ b/tests/fuzz/s2n_certificate_extensions_parse_test.c @@ -1,115 +1,116 @@ -/* - * 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. - */ - -/* Target Functions: s2n_certificate_extensions_parse - s2n_recv_server_sct_list s2n_server_certificate_status_recv - s2n_x509_validator_validate_cert_stapled_ocsp_response */ - -#include - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls13.h" - -struct host_verify_data { - const char *name; - uint8_t found_name; - uint8_t callback_invoked; -}; - -static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return 1; -} - -/* This test is for TLS versions 1.3 and up only */ -static const uint8_t TLS_VERSIONS[] = {S2N_TLS13}; - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least one byte of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 1); - - /* Setup */ - struct s2n_stuffer fuzz_stuffer = {0}; - POSIX_GUARD(s2n_stuffer_alloc(&fuzz_stuffer, len)); - POSIX_GUARD(s2n_stuffer_write_bytes(&fuzz_stuffer, buf, len)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "20240503")); - - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - POSIX_ENSURE_REF(client_conn); - POSIX_GUARD(s2n_connection_set_config(client_conn, config)); - - /* Pull a byte off the libfuzzer input and use it to set parameters */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&fuzz_stuffer, &randval)); - client_conn->x509_validator.skip_cert_validation = (randval >> 7) % 2; - - /* Set connection to TLS 1.2 to temporary work around cert validation setup */ - client_conn->actual_protocol_version = S2N_TLS12; - - /* Set cert chain and trust store for verification of OCSP response */ - if ((randval >> 6) % 2 && OPENSSL_VERSION_NUMBER >= 0x10101000L) { - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - POSIX_GUARD(s2n_connection_set_verify_host_callback(client_conn, verify_host_accept_everything, &verify_data)); - char cert_chain[S2N_MAX_TEST_PEM_SIZE]; - POSIX_GUARD(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_x509_trust_store_add_pem(client_conn->x509_validator.trust_store, cert_chain)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(client_conn, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - POSIX_GUARD(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type; - - POSIX_GUARD_RESULT(s2n_x509_validator_validate_cert_chain(&client_conn->x509_validator, client_conn, chain_data, chain_len, &pkey_type, &public_key_out)); - POSIX_GUARD(s2n_pkey_free(&public_key_out)); - } - - client_conn->actual_protocol_version = TLS_VERSIONS[(randval & 0x07) % s2n_array_len(TLS_VERSIONS)]; - client_conn->client_protocol_version = TLS_VERSIONS[((randval >> 3) & 0x07) % s2n_array_len(TLS_VERSIONS)]; - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_extension_list_recv(S2N_EXTENSION_LIST_CERTIFICATE, client_conn, &fuzz_stuffer); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(client_conn)); - POSIX_GUARD(s2n_stuffer_free(&fuzz_stuffer)); - - return S2N_SUCCESS; -} - -S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_certificate_extensions_parse + s2n_recv_server_sct_list s2n_server_certificate_status_recv + s2n_x509_validator_validate_cert_stapled_ocsp_response */ + +#include + +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls13.h" + +struct host_verify_data { + const char *name; + uint8_t found_name; + uint8_t callback_invoked; +}; + +static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 1; +} + +/* This test is for TLS versions 1.3 and up only */ +static const uint8_t TLS_VERSIONS[] = {S2N_TLS13}; + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least one byte of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 1); + + /* Setup */ + struct s2n_stuffer fuzz_stuffer = {0}; + POSIX_GUARD(s2n_stuffer_alloc(&fuzz_stuffer, len)); + POSIX_GUARD(s2n_stuffer_write_bytes(&fuzz_stuffer, buf, len)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "20240503")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + POSIX_ENSURE_REF(client_conn); + POSIX_GUARD(s2n_connection_set_config(client_conn, config)); + + /* Pull a byte off the libfuzzer input and use it to set parameters */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&fuzz_stuffer, &randval)); + client_conn->x509_validator.skip_cert_validation = (randval >> 7) % 2; + + /* Set connection to TLS 1.2 to temporary work around cert validation setup */ + client_conn->actual_protocol_version = S2N_TLS12; + + /* Set cert chain and trust store for verification of OCSP response */ + if ((randval >> 6) % 2 && OPENSSL_VERSION_NUMBER >= 0x10101000L) { + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + POSIX_GUARD(s2n_connection_set_verify_host_callback(client_conn, verify_host_accept_everything, &verify_data)); + char cert_chain[S2N_MAX_TEST_PEM_SIZE]; + POSIX_GUARD(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_x509_trust_store_add_pem(client_conn->x509_validator.trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(client_conn, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + POSIX_GUARD(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type; + + POSIX_GUARD_RESULT(s2n_x509_validator_validate_cert_chain(&client_conn->x509_validator, client_conn, chain_data, chain_len, &pkey_type, &public_key_out)); + POSIX_GUARD(s2n_pkey_free(&public_key_out)); + } + + client_conn->actual_protocol_version = TLS_VERSIONS[(randval & 0x07) % s2n_array_len(TLS_VERSIONS)]; + client_conn->client_protocol_version = TLS_VERSIONS[((randval >> 3) & 0x07) % s2n_array_len(TLS_VERSIONS)]; + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_extension_list_recv(S2N_EXTENSION_LIST_CERTIFICATE, client_conn, &fuzz_stuffer); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_stuffer_free(&fuzz_stuffer)); + + return S2N_SUCCESS; +} + +S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) diff --git a/tests/fuzz/s2n_client_key_recv_fuzz_test.c b/tests/fuzz/s2n_client_key_recv_fuzz_test.c index 3176a6a1429..ff0925a73f0 100644 --- a/tests/fuzz/s2n_client_key_recv_fuzz_test.c +++ b/tests/fuzz/s2n_client_key_recv_fuzz_test.c @@ -1,148 +1,149 @@ -/* - * 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. - */ - -/* Target Functions: s2n_client_key_recv s2n_kex_client_key_recv calculate_keys - s2n_hybrid_client_action s2n_kex_tls_prf s2n_prf_key_expansion - s2n_rsa_client_key_recv s2n_dhe_client_key_recv - s2n_ecdhe_client_key_recv s2n_kem_client_key_recv */ - -#include - -#include -#include - -#include "tls/s2n_kem.h" -#include "tls/s2n_client_key_exchange.h" -#include "tls/s2n_kex.h" -#include "tls/s2n_security_policies.h" - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; - -/* Connection setup variables */ -uint8_t *cert_chain_pem = NULL; -uint8_t *private_key_pem = NULL; -char *dhparams_pem = NULL; -uint32_t cert_chain_len = 0; -uint32_t private_key_len = 0; -struct s2n_config *config; -struct s2n_cert_chain_and_key *chain_and_key; -struct s2n_cert_chain_and_key *cert; -struct s2n_cipher_suite **test_suites; -int num_suites; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - test_suites = cipher_preferences_test_all.suites; - num_suites = cipher_preferences_test_all.count; - - /* One time Diffie-Hellman negotiation to speed along fuzz tests*/ - cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE); - private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE); - dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE); - - POSIX_ENSURE_REF(cert_chain_pem); - POSIX_ENSURE_REF(private_key_pem); - POSIX_ENSURE_REF(dhparams_pem); - - s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE); - s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE); - s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE); - - config = s2n_config_new(); - chain_and_key = s2n_cert_chain_and_key_new(); - - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(chain_and_key); - - s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len); - s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key); - s2n_config_add_dhparams(config, dhparams_pem); - - cert = s2n_config_get_single_default_cert(config); - POSIX_ENSURE_REF(cert); - - return S2N_SUCCESS; -} - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least two bytes of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 2); - - /* Setup */ - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - POSIX_ENSURE_REF(server_conn); - POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->handshake.io, buf, len)); - - /* Read bytes from the libfuzzer input and use them to set parameters */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); - server_conn->server_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; - - POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); - server_conn->secure->cipher_suite = test_suites[randval % num_suites]; - - /* Skip incompatible TLS 1.3 cipher suites */ - if (server_conn->secure->cipher_suite->key_exchange_alg == NULL) { - POSIX_GUARD(s2n_connection_free(server_conn)); - return S2N_SUCCESS; - } - - server_conn->handshake_params.our_chain_and_key = cert; - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(server_conn, &ecc_preferences)); - POSIX_ENSURE_REF(ecc_preferences); - - if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_ecdhe_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.server_ecc_evp_params); - } - - if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_kem_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { - server_conn->kex_params.kem_params.kem = &s2n_mlkem_768; - } - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_client_key_recv(server_conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(server_conn)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - s2n_config_free(config); - s2n_cert_chain_and_key_free(chain_and_key); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_client_key_recv s2n_kex_client_key_recv calculate_keys + s2n_hybrid_client_action s2n_kex_tls_prf s2n_prf_key_expansion + s2n_rsa_client_key_recv s2n_dhe_client_key_recv + s2n_ecdhe_client_key_recv s2n_kem_client_key_recv */ + +#include + +#include +#include + +#include "tls/s2n_kem.h" +#include "tls/s2n_client_key_exchange.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_security_policies.h" + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; + +/* Connection setup variables */ +uint8_t *cert_chain_pem = NULL; +uint8_t *private_key_pem = NULL; +char *dhparams_pem = NULL; +uint32_t cert_chain_len = 0; +uint32_t private_key_len = 0; +struct s2n_config *config; +struct s2n_cert_chain_and_key *chain_and_key; +struct s2n_cert_chain_and_key *cert; +struct s2n_cipher_suite **test_suites; +int num_suites; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + test_suites = cipher_preferences_test_all.suites; + num_suites = cipher_preferences_test_all.count; + + /* One time Diffie-Hellman negotiation to speed along fuzz tests*/ + cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE); + private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE); + dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE); + + POSIX_ENSURE_REF(cert_chain_pem); + POSIX_ENSURE_REF(private_key_pem); + POSIX_ENSURE_REF(dhparams_pem); + + s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE); + s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE); + s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE); + + config = s2n_config_new(); + chain_and_key = s2n_cert_chain_and_key_new(); + + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(chain_and_key); + + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len); + s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key); + s2n_config_add_dhparams(config, dhparams_pem); + + cert = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(cert); + + return S2N_SUCCESS; +} + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least two bytes of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 2); + + /* Setup */ + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + POSIX_ENSURE_REF(server_conn); + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->handshake.io, buf, len)); + + /* Read bytes from the libfuzzer input and use them to set parameters */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); + server_conn->server_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; + + POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); + server_conn->secure->cipher_suite = test_suites[randval % num_suites]; + + /* Skip incompatible TLS 1.3 cipher suites */ + if (server_conn->secure->cipher_suite->key_exchange_alg == NULL) { + POSIX_GUARD(s2n_connection_free(server_conn)); + return S2N_SUCCESS; + } + + server_conn->handshake_params.our_chain_and_key = cert; + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(server_conn, &ecc_preferences)); + POSIX_ENSURE_REF(ecc_preferences); + + if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_ecdhe_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.server_ecc_evp_params); + } + + if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_kem_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { + server_conn->kex_params.kem_params.kem = &s2n_mlkem_768; + } + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_client_key_recv(server_conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(server_conn)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + s2n_config_free(config); + s2n_cert_chain_and_key_free(chain_and_key); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/fuzz/s2n_select_server_cert_test.c b/tests/fuzz/s2n_select_server_cert_test.c index 5287201f894..8cb825b7930 100644 --- a/tests/fuzz/s2n_select_server_cert_test.c +++ b/tests/fuzz/s2n_select_server_cert_test.c @@ -1,279 +1,280 @@ -/* - * 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. - */ - -/* Target Functions: s2n_conn_find_name_matching_certs s2n_config_add_cert_chain_and_key_to_store - s2n_server_received_server_name s2n_find_cert_matches s2n_create_wildcard_hostname */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" - -#define MAX_TOKENS 1024 -#define MAX_CERTIFICATES 256 - -/* - * Tokenize the input fuzz buffer based on NULL bytes into output array and return the number of tokens. - * Avoiding extra heap allocation here to increase fuzz test rate. - */ -size_t find_strings(const uint8_t *buf, size_t len, const char **output_strings, size_t max_strings) -{ - size_t num_strings = 0; - int cursor = 0; - while(1) { - if (cursor >= len || num_strings == max_strings) { - return num_strings; - } - const char *cur_str = (const char *) (buf + cursor); - const char *next_null = (const char *) memchr((const void *) cur_str, '\0', (len - cursor)); - if (next_null == NULL) { - return num_strings; - } - - /* We found a null byte. Move the cursor beyond it. */ - cursor = (((const uint8_t *) next_null - buf) + 1); - if (cursor >= len) { - return num_strings; - } - output_strings[num_strings] = cur_str; - num_strings++; - } -} - -GENERAL_NAME *string_to_general_name(const char *str) -{ - ASN1_IA5STRING *asn1_name_str = ASN1_IA5STRING_new(); - if (!asn1_name_str) { - return NULL; - } - - if (!ASN1_STRING_set(asn1_name_str, str, strlen(str))) { - ASN1_IA5STRING_free(asn1_name_str); - return NULL; - } - - GENERAL_NAME *san_name = GENERAL_NAME_new(); - if (!san_name) { - ASN1_IA5STRING_free(asn1_name_str); - return NULL; - } - - GENERAL_NAME_set0_value(san_name, GEN_DNS, asn1_name_str); - return san_name; -} - -static int set_x509_sans(X509* x509_cert, const char **names, size_t num_sans) -{ - GENERAL_NAMES* san_names = sk_GENERAL_NAME_new_null(); - if (!san_names) { - return -1; - } - - for (int i = 0; i < num_sans; i++) { - GENERAL_NAME *san_name = string_to_general_name(names[i]); - if (!san_name) { - continue; - } - sk_GENERAL_NAME_push(san_names, san_name); - } - - - if (X509_add1_ext_i2d(x509_cert, NID_subject_alt_name, san_names, 0, X509V3_ADD_REPLACE) <= 0) { - GENERAL_NAMES_free(san_names); - return -1; - } - - GENERAL_NAMES_free(san_names); - return S2N_SUCCESS; -} - -static int set_x509_cns(X509 *x509_cert, const char **cns, size_t num_cns) -{ - X509_NAME *x509_name = X509_NAME_new(); - if (!x509_name) { - return -1; - } - - for (int i = 0; i < num_cns; i++) { - X509_NAME_add_entry_by_NID(x509_name, NID_commonName, MBSTRING_ASC, (unsigned char *)(uintptr_t) cns[i], -1, -1, 1); - } - - X509_set_subject_name(x509_cert, x509_name); - X509_NAME_free(x509_name); - return S2N_SUCCESS; -} - -static struct s2n_cert_chain_and_key *create_cert(const char **names, int num_names) -{ - struct s2n_cert_chain_and_key *cert = s2n_cert_chain_and_key_new(); - X509 *x509_cert = X509_new(); - - if (!x509_cert || !cert) { - goto cert_cleanup; - } - - /* Figure out if this cert should use SANs or CNs. s2n will only use one or the other - * for name matching. - */ - const uint8_t is_san = names[0][0] & 0x1; - if (is_san) { - if (set_x509_sans(x509_cert, names, num_names) < 0) { - goto cert_cleanup; - } - if (s2n_cert_chain_and_key_load_sans(cert, x509_cert) < 0) { - goto cert_cleanup; - } - } else { - if (set_x509_cns(x509_cert, names, num_names) < 0) { - goto cert_cleanup; - } - if (s2n_cert_chain_and_key_load_cns(cert, x509_cert) < 0) { - goto cert_cleanup; - } - } - X509_free(x509_cert); - x509_cert = NULL; - - /* Figure out if this should be an RSA or ECDSA certificate */ - s2n_pkey_type pkey_type = (names[0][0] & 0x2) ? S2N_PKEY_TYPE_RSA: S2N_PKEY_TYPE_ECDSA; - struct s2n_cert *head = calloc(1, sizeof(struct s2n_cert)); - if (!head) { - goto cert_cleanup; - } - - head->pkey_type = pkey_type; - cert->cert_chain->head = head; - return cert; - -cert_cleanup: - if (cert) { s2n_cert_chain_and_key_free(cert); } - if (x509_cert) { X509_free(x509_cert); } - return NULL; -} - -/* - * Try to create some number of certificates with SANs/CNs provided by a list of input C strings. Return the number of - * certificates created. - */ -static size_t create_certs(const char **strings, unsigned int num_strings, struct s2n_cert_chain_and_key **out_certs, unsigned int num_certs) -{ - if (num_certs == 0) { - return S2N_SUCCESS; - } - - /* We want the fuzz input to give us at least 1 domain name string for each cert */ - if (num_strings <= num_certs) { - return S2N_SUCCESS; - } - - const int num_names_per_cert = num_strings / num_certs; - size_t num_certs_added = 0; - for (int i = 0; i < num_certs; i++) { - struct s2n_cert_chain_and_key *cert = create_cert((&strings[i*num_names_per_cert]), num_names_per_cert); - if (!cert) { - continue; - } - out_certs[num_certs_added] = cert; - num_certs_added++; - } - - return num_certs_added; -} - - -/* - * This fuzz test uses the fuzz input to: - * - Generate the data to populate in the SAN of a certificate - * - Generate the data to populate the SNI TLS extension - * - Fuzz the certificate matching function in s2n: s2n_cert_chain_and_key_matches_name - */ -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - struct s2n_config *config = s2n_config_new(); - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - struct s2n_cert_chain_and_key *certs[MAX_CERTIFICATES] = { NULL }; - - if (!config || !conn || len == 0) { - goto cleanup; - } - - /* Create an array of strings based on fuzz input. To populate the cert SANs,CN and - * input hostname. - */ - const char *strings[MAX_TOKENS] = { NULL }; - size_t num_strings = find_strings(buf, len, strings, MAX_TOKENS); - if (num_strings == 0) { - goto cleanup; - } - - if (s2n_connection_set_config(conn, config) < 0) { - goto cleanup; - } - - const int max_certs_to_create = buf[0] % MAX_CERTIFICATES; - const int num_certs = create_certs(strings, num_strings, certs, max_certs_to_create); - if (num_certs == 0) { - goto cleanup; - } - - /* Add all of the certs created by fuzz input to store. */ - for (int i = 0; i < num_certs; i++) { - if (s2n_config_add_cert_chain_and_key_to_store(config, certs[i]) < 0) { - goto cleanup; - } - } - - for (int i = 0; i < num_strings; i++) { - strncpy(conn->server_name, strings[i], S2N_MAX_SERVER_NAME); - /* Not checking the return value as we aren't using this fuzz test for matching correctness. */ - s2n_conn_find_name_matching_certs(conn); - } -cleanup: - for (int i = 0; i < MAX_CERTIFICATES; i++) { - if (certs[i]) { - free(certs[i]->cert_chain->head); - certs[i]->cert_chain->head = NULL; - s2n_cert_chain_and_key_free(certs[i]); - } - } - if (conn != NULL) { s2n_connection_free(conn); } - if (config != NULL) { s2n_config_free(config); } - - return S2N_SUCCESS; -} - -S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_conn_find_name_matching_certs s2n_config_add_cert_chain_and_key_to_store + s2n_server_received_server_name s2n_find_cert_matches s2n_create_wildcard_hostname */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" + +#define MAX_TOKENS 1024 +#define MAX_CERTIFICATES 256 + +/* + * Tokenize the input fuzz buffer based on NULL bytes into output array and return the number of tokens. + * Avoiding extra heap allocation here to increase fuzz test rate. + */ +size_t find_strings(const uint8_t *buf, size_t len, const char **output_strings, size_t max_strings) +{ + size_t num_strings = 0; + int cursor = 0; + while(1) { + if (cursor >= len || num_strings == max_strings) { + return num_strings; + } + const char *cur_str = (const char *) (buf + cursor); + const char *next_null = (const char *) memchr((const void *) cur_str, '\0', (len - cursor)); + if (next_null == NULL) { + return num_strings; + } + + /* We found a null byte. Move the cursor beyond it. */ + cursor = (((const uint8_t *) next_null - buf) + 1); + if (cursor >= len) { + return num_strings; + } + output_strings[num_strings] = cur_str; + num_strings++; + } +} + +GENERAL_NAME *string_to_general_name(const char *str) +{ + ASN1_IA5STRING *asn1_name_str = ASN1_IA5STRING_new(); + if (!asn1_name_str) { + return NULL; + } + + if (!ASN1_STRING_set(asn1_name_str, str, strlen(str))) { + ASN1_IA5STRING_free(asn1_name_str); + return NULL; + } + + GENERAL_NAME *san_name = GENERAL_NAME_new(); + if (!san_name) { + ASN1_IA5STRING_free(asn1_name_str); + return NULL; + } + + GENERAL_NAME_set0_value(san_name, GEN_DNS, asn1_name_str); + return san_name; +} + +static int set_x509_sans(X509* x509_cert, const char **names, size_t num_sans) +{ + GENERAL_NAMES* san_names = sk_GENERAL_NAME_new_null(); + if (!san_names) { + return -1; + } + + for (int i = 0; i < num_sans; i++) { + GENERAL_NAME *san_name = string_to_general_name(names[i]); + if (!san_name) { + continue; + } + sk_GENERAL_NAME_push(san_names, san_name); + } + + + if (X509_add1_ext_i2d(x509_cert, NID_subject_alt_name, san_names, 0, X509V3_ADD_REPLACE) <= 0) { + GENERAL_NAMES_free(san_names); + return -1; + } + + GENERAL_NAMES_free(san_names); + return S2N_SUCCESS; +} + +static int set_x509_cns(X509 *x509_cert, const char **cns, size_t num_cns) +{ + X509_NAME *x509_name = X509_NAME_new(); + if (!x509_name) { + return -1; + } + + for (int i = 0; i < num_cns; i++) { + X509_NAME_add_entry_by_NID(x509_name, NID_commonName, MBSTRING_ASC, (unsigned char *)(uintptr_t) cns[i], -1, -1, 1); + } + + X509_set_subject_name(x509_cert, x509_name); + X509_NAME_free(x509_name); + return S2N_SUCCESS; +} + +static struct s2n_cert_chain_and_key *create_cert(const char **names, int num_names) +{ + struct s2n_cert_chain_and_key *cert = s2n_cert_chain_and_key_new(); + X509 *x509_cert = X509_new(); + + if (!x509_cert || !cert) { + goto cert_cleanup; + } + + /* Figure out if this cert should use SANs or CNs. s2n will only use one or the other + * for name matching. + */ + const uint8_t is_san = names[0][0] & 0x1; + if (is_san) { + if (set_x509_sans(x509_cert, names, num_names) < 0) { + goto cert_cleanup; + } + if (s2n_cert_chain_and_key_load_sans(cert, x509_cert) < 0) { + goto cert_cleanup; + } + } else { + if (set_x509_cns(x509_cert, names, num_names) < 0) { + goto cert_cleanup; + } + if (s2n_cert_chain_and_key_load_cns(cert, x509_cert) < 0) { + goto cert_cleanup; + } + } + X509_free(x509_cert); + x509_cert = NULL; + + /* Figure out if this should be an RSA or ECDSA certificate */ + s2n_pkey_type pkey_type = (names[0][0] & 0x2) ? S2N_PKEY_TYPE_RSA: S2N_PKEY_TYPE_ECDSA; + struct s2n_cert *head = calloc(1, sizeof(struct s2n_cert)); + if (!head) { + goto cert_cleanup; + } + + head->pkey_type = pkey_type; + cert->cert_chain->head = head; + return cert; + +cert_cleanup: + if (cert) { s2n_cert_chain_and_key_free(cert); } + if (x509_cert) { X509_free(x509_cert); } + return NULL; +} + +/* + * Try to create some number of certificates with SANs/CNs provided by a list of input C strings. Return the number of + * certificates created. + */ +static size_t create_certs(const char **strings, unsigned int num_strings, struct s2n_cert_chain_and_key **out_certs, unsigned int num_certs) +{ + if (num_certs == 0) { + return S2N_SUCCESS; + } + + /* We want the fuzz input to give us at least 1 domain name string for each cert */ + if (num_strings <= num_certs) { + return S2N_SUCCESS; + } + + const int num_names_per_cert = num_strings / num_certs; + size_t num_certs_added = 0; + for (int i = 0; i < num_certs; i++) { + struct s2n_cert_chain_and_key *cert = create_cert((&strings[i*num_names_per_cert]), num_names_per_cert); + if (!cert) { + continue; + } + out_certs[num_certs_added] = cert; + num_certs_added++; + } + + return num_certs_added; +} + + +/* + * This fuzz test uses the fuzz input to: + * - Generate the data to populate in the SAN of a certificate + * - Generate the data to populate the SNI TLS extension + * - Fuzz the certificate matching function in s2n: s2n_cert_chain_and_key_matches_name + */ +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + struct s2n_cert_chain_and_key *certs[MAX_CERTIFICATES] = { NULL }; + + if (!config || !conn || len == 0) { + goto cleanup; + } + + /* Create an array of strings based on fuzz input. To populate the cert SANs,CN and + * input hostname. + */ + const char *strings[MAX_TOKENS] = { NULL }; + size_t num_strings = find_strings(buf, len, strings, MAX_TOKENS); + if (num_strings == 0) { + goto cleanup; + } + + if (s2n_connection_set_config(conn, config) < 0) { + goto cleanup; + } + + const int max_certs_to_create = buf[0] % MAX_CERTIFICATES; + const int num_certs = create_certs(strings, num_strings, certs, max_certs_to_create); + if (num_certs == 0) { + goto cleanup; + } + + /* Add all of the certs created by fuzz input to store. */ + for (int i = 0; i < num_certs; i++) { + if (s2n_config_add_cert_chain_and_key_to_store(config, certs[i]) < 0) { + goto cleanup; + } + } + + for (int i = 0; i < num_strings; i++) { + strncpy(conn->server_name, strings[i], S2N_MAX_SERVER_NAME); + /* Not checking the return value as we aren't using this fuzz test for matching correctness. */ + s2n_conn_find_name_matching_certs(conn); + } +cleanup: + for (int i = 0; i < MAX_CERTIFICATES; i++) { + if (certs[i]) { + free(certs[i]->cert_chain->head); + certs[i]->cert_chain->head = NULL; + s2n_cert_chain_and_key_free(certs[i]); + } + } + if (conn != NULL) { s2n_connection_free(conn); } + if (config != NULL) { s2n_config_free(config); } + + return S2N_SUCCESS; +} + +S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) diff --git a/tests/fuzz/s2n_tls13_cert_req_recv_test.c b/tests/fuzz/s2n_tls13_cert_req_recv_test.c index dec288ee1d7..796976ea3b7 100644 --- a/tests/fuzz/s2n_tls13_cert_req_recv_test.c +++ b/tests/fuzz/s2n_tls13_cert_req_recv_test.c @@ -1,81 +1,82 @@ -/* - * 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. - */ - -/* Target Functions: s2n_tls13_cert_req_recv s2n_extension_list_recv s2n_extension_process - s2n_extension_list_process s2n_extension_parse s2n_extension_list_parse */ - -#include - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static char *cert_chain, *private_key; -struct s2n_cert_chain_and_key *default_cert; -struct s2n_config *client_config; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - /* Initialize test chain and key */ - cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(cert_chain); - private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(private_key); - default_cert = s2n_cert_chain_and_key_new(); - POSIX_ENSURE_REF(default_cert); - client_config = s2n_config_new(); - POSIX_ENSURE_REF(client_config); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); - return S2N_SUCCESS; -} - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* Setup */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_SERVER); - POSIX_ENSURE_REF(client_conn); - POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); - POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_tls13_cert_req_recv(client_conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(client_conn)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - s2n_config_free(client_config); - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(default_cert); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_tls13_cert_req_recv s2n_extension_list_recv s2n_extension_process + s2n_extension_list_process s2n_extension_parse s2n_extension_list_parse */ + +#include + +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static char *cert_chain, *private_key; +struct s2n_cert_chain_and_key *default_cert; +struct s2n_config *client_config; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + /* Initialize test chain and key */ + cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(cert_chain); + private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(private_key); + default_cert = s2n_cert_chain_and_key_new(); + POSIX_ENSURE_REF(default_cert); + client_config = s2n_config_new(); + POSIX_ENSURE_REF(client_config); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + return S2N_SUCCESS; +} + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* Setup */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_SERVER); + POSIX_ENSURE_REF(client_conn); + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_tls13_cert_req_recv(client_conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(client_conn)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + s2n_config_free(client_config); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(default_cert); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/fuzz/s2n_tls13_cert_verify_recv_test.c b/tests/fuzz/s2n_tls13_cert_verify_recv_test.c index 8231ebd0be5..544ef6fda65 100644 --- a/tests/fuzz/s2n_tls13_cert_verify_recv_test.c +++ b/tests/fuzz/s2n_tls13_cert_verify_recv_test.c @@ -1,95 +1,96 @@ -/* - * 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. - */ - -/* Target Functions: s2n_tls13_cert_verify_recv s2n_signature_algorithm_recv - s2n_tls13_cert_read_and_verify_signature */ - -#include - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -uint8_t *cert_chain = NULL; -uint8_t *private_key = NULL; -uint32_t cert_chain_len = 0; -uint32_t private_key_len = 0; -struct s2n_cert_chain_and_key *default_cert; -struct s2n_config *conn_config; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - /* Initialize test chain and key */ - cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(cert_chain); - private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(private_key); - default_cert = s2n_cert_chain_and_key_new(); - POSIX_ENSURE_REF(default_cert); - conn_config = s2n_config_new(); - POSIX_ENSURE_REF(conn_config); - POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(conn_config, default_cert)); - - return S2N_SUCCESS; -} - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least one byte of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 1); - - /* Setup */ - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_set_config(conn, conn_config)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, buf, len)); - - /* Pull a byte off the libfuzzer input and use it to set the connection mode */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&conn->handshake.io, &randval)); - if (randval % 2) { - conn->mode = S2N_CLIENT; - } - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_tls13_cert_verify_recv(conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(conn)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - s2n_config_free(conn_config); - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(default_cert); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_tls13_cert_verify_recv s2n_signature_algorithm_recv + s2n_tls13_cert_read_and_verify_signature */ + +#include + +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +uint8_t *cert_chain = NULL; +uint8_t *private_key = NULL; +uint32_t cert_chain_len = 0; +uint32_t private_key_len = 0; +struct s2n_cert_chain_and_key *default_cert; +struct s2n_config *conn_config; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + /* Initialize test chain and key */ + cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(cert_chain); + private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(private_key); + default_cert = s2n_cert_chain_and_key_new(); + POSIX_ENSURE_REF(default_cert); + conn_config = s2n_config_new(); + POSIX_ENSURE_REF(conn_config); + POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(conn_config, default_cert)); + + return S2N_SUCCESS; +} + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least one byte of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 1); + + /* Setup */ + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_set_config(conn, conn_config)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, buf, len)); + + /* Pull a byte off the libfuzzer input and use it to set the connection mode */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&conn->handshake.io, &randval)); + if (randval % 2) { + conn->mode = S2N_CLIENT; + } + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_tls13_cert_verify_recv(conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(conn)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + s2n_config_free(conn_config); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(default_cert); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/testlib/s2n_key_schedule_testlib.c b/tests/testlib/s2n_key_schedule_testlib.c index 273f8b8b3cc..5268953965c 100644 --- a/tests/testlib/s2n_key_schedule_testlib.c +++ b/tests/testlib/s2n_key_schedule_testlib.c @@ -1,78 +1,79 @@ -/* - * 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 "testlib/s2n_testlib.h" - -S2N_RESULT s2n_connection_set_test_transcript_hash(struct s2n_connection *conn, - message_type_t message_type, const struct s2n_blob *digest) -{ - RESULT_GUARD(s2n_connection_set_test_message_type(conn, message_type)); - RESULT_CHECKED_MEMCPY(conn->handshake.hashes->transcript_hash_digest, - digest->data, digest->size); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_test_early_secret(struct s2n_connection *conn, - const struct s2n_blob *early_secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(early_secret); - RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, - early_secret->data, early_secret->size); - conn->secrets.extract_secret_type = S2N_EARLY_SECRET; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_test_handshake_secret(struct s2n_connection *conn, - const struct s2n_blob *handshake_secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(handshake_secret); - RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, - handshake_secret->data, handshake_secret->size); - conn->secrets.extract_secret_type = S2N_HANDSHAKE_SECRET; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_test_master_secret(struct s2n_connection *conn, - const struct s2n_blob *master_secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(master_secret); - RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, - master_secret->data, master_secret->size); - conn->secrets.extract_secret_type = S2N_MASTER_SECRET; - return S2N_RESULT_OK; -} - -/* This function will iterate over all rows and columns of the handshake state - * machine until it finds a valid (handshake_type, handshake_number) such that - * the active message is `expected_message_type`. If callers need to depend on a - * specific `message_number` or `handshake_type` this function should not be - * used. - */ -S2N_RESULT s2n_connection_set_test_message_type(struct s2n_connection *conn, message_type_t expected_message_type) -{ - for (uint32_t handshake = 0; handshake < S2N_HANDSHAKES_COUNT; handshake++) { - for (int message = 0; message < S2N_MAX_HANDSHAKE_LENGTH; message++) { - conn->handshake.handshake_type = handshake; - conn->handshake.message_number = message; - if (s2n_conn_get_current_message_type(conn) == expected_message_type) { - return S2N_RESULT_OK; - } - } - } - RESULT_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); -} +#include "utils/s2n_prelude.h" +/* + * 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 "testlib/s2n_testlib.h" + +S2N_RESULT s2n_connection_set_test_transcript_hash(struct s2n_connection *conn, + message_type_t message_type, const struct s2n_blob *digest) +{ + RESULT_GUARD(s2n_connection_set_test_message_type(conn, message_type)); + RESULT_CHECKED_MEMCPY(conn->handshake.hashes->transcript_hash_digest, + digest->data, digest->size); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_test_early_secret(struct s2n_connection *conn, + const struct s2n_blob *early_secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(early_secret); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, + early_secret->data, early_secret->size); + conn->secrets.extract_secret_type = S2N_EARLY_SECRET; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_test_handshake_secret(struct s2n_connection *conn, + const struct s2n_blob *handshake_secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(handshake_secret); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, + handshake_secret->data, handshake_secret->size); + conn->secrets.extract_secret_type = S2N_HANDSHAKE_SECRET; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_test_master_secret(struct s2n_connection *conn, + const struct s2n_blob *master_secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(master_secret); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, + master_secret->data, master_secret->size); + conn->secrets.extract_secret_type = S2N_MASTER_SECRET; + return S2N_RESULT_OK; +} + +/* This function will iterate over all rows and columns of the handshake state + * machine until it finds a valid (handshake_type, handshake_number) such that + * the active message is `expected_message_type`. If callers need to depend on a + * specific `message_number` or `handshake_type` this function should not be + * used. + */ +S2N_RESULT s2n_connection_set_test_message_type(struct s2n_connection *conn, message_type_t expected_message_type) +{ + for (uint32_t handshake = 0; handshake < S2N_HANDSHAKES_COUNT; handshake++) { + for (int message = 0; message < S2N_MAX_HANDSHAKE_LENGTH; message++) { + conn->handshake.handshake_type = handshake; + conn->handshake.message_number = message; + if (s2n_conn_get_current_message_type(conn) == expected_message_type) { + return S2N_RESULT_OK; + } + } + } + RESULT_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); +} diff --git a/tests/testlib/s2n_ktls_test_utils.c b/tests/testlib/s2n_ktls_test_utils.c index 18fa9080155..4a1d26c2df9 100644 --- a/tests/testlib/s2n_ktls_test_utils.c +++ b/tests/testlib/s2n_ktls_test_utils.c @@ -1,249 +1,250 @@ -/* - * 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 "testlib/s2n_ktls_test_utils.h" - -S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, - int cmsg_type, uint8_t record_type); -S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type); - -/* Since it is possible to read partial data, we need a way to update the length - * of the previous record for the mock stuffer IO implementation. */ -static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_stuffer *io_ctx, - uint16_t remaining_len) -{ - RESULT_ENSURE_REF(io_ctx); - RESULT_ENSURE(remaining_len > 0, S2N_ERR_IO); - - /* rewind so we can read the last header with the updated len */ - RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(&io_ctx->ancillary_buffer, S2N_TEST_KTLS_MOCK_HEADER_SIZE)); - - /* get position for the last header's length */ - uint32_t rewrite_len_ptr = io_ctx->ancillary_buffer.read_cursor + S2N_TEST_KTLS_MOCK_HEADER_TAG_SIZE; - /* create a new stuffer pointing to len data and rewrite it */ - struct s2n_stuffer rewrite_len_stuffer = io_ctx->ancillary_buffer; - RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&rewrite_len_stuffer)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&rewrite_len_stuffer, rewrite_len_ptr)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&rewrite_len_stuffer, remaining_len)); - - return S2N_RESULT_OK; -} - -ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *msg) -{ - errno = EINVAL; - POSIX_ENSURE_REF(msg); - - struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; - POSIX_ENSURE_REF(io_ctx); - struct s2n_stuffer *data_buffer = &io_ctx->data_buffer; - io_ctx->sendmsg_invoked_count++; - - uint8_t record_type = 0; - struct msghdr msg_to_parse = *msg; - POSIX_GUARD_RESULT(s2n_ktls_get_control_data(&msg_to_parse, S2N_TLS_SET_RECORD_TYPE, &record_type)); - - size_t total_len = 0; - for (size_t count = 0; count < msg->msg_iovlen; count++) { - POSIX_ENSURE_REF(msg->msg_iov); - uint8_t *buf = msg->msg_iov[count].iov_base; - size_t len = msg->msg_iov[count].iov_len; - - if (s2n_stuffer_write_bytes(data_buffer, buf, len) != S2N_SUCCESS) { - size_t partial_len = S2N_MIN(len, s2n_stuffer_space_remaining(data_buffer)); - POSIX_GUARD(s2n_stuffer_write_bytes(data_buffer, buf, partial_len)); - total_len += partial_len; - if (total_len) { - break; - } - errno = EAGAIN; - return -1; - } - total_len += len; - } - if (total_len) { - /* write record_type and len after some data was written successfully */ - POSIX_GUARD(s2n_stuffer_write_uint8(&io_ctx->ancillary_buffer, record_type)); - POSIX_GUARD(s2n_stuffer_write_uint16(&io_ctx->ancillary_buffer, total_len)); - } - - return total_len; -} - -/* In userspace TLS, s2n first reads the header to determine the length of next record - * and then reads the entire record into conn->in. In kTLS it is not possible to know - * the length of the next record. Instead the socket returns the minimum of - * bytes-requested and data-available, reading multiple consecutive records if they - * are of the same type. */ -ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg) -{ - errno = EINVAL; - POSIX_ENSURE_REF(msg); - POSIX_ENSURE_REF(msg->msg_iov); - - struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; - POSIX_ENSURE_REF(io_ctx); - io_ctx->recvmsg_invoked_count++; - - uint8_t *buf = msg->msg_iov->iov_base; - POSIX_ENSURE_REF(buf); - - /* There is no data available so return blocked */ - if (!s2n_stuffer_data_available(&io_ctx->ancillary_buffer)) { - errno = EAGAIN; - return -1; - } - - /* s2n only receives using msg_iovlen of 1 */ - POSIX_ENSURE_EQ(msg->msg_iovlen, 1); - size_t size = msg->msg_iov->iov_len; - - uint8_t record_type = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&io_ctx->ancillary_buffer, &record_type)); - POSIX_GUARD_RESULT(s2n_ktls_set_control_data(msg, msg->msg_control, msg->msg_controllen, - S2N_TLS_GET_RECORD_TYPE, record_type)); - - ssize_t bytes_read = 0; - while (bytes_read < size) { - uint16_t n_avail = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(&io_ctx->ancillary_buffer, &n_avail)); - - size_t n_read = S2N_MIN(size - bytes_read, n_avail); - POSIX_ENSURE_GT(n_read, 0); - POSIX_GUARD(s2n_stuffer_read_bytes(&io_ctx->data_buffer, buf + bytes_read, n_read)); - - bytes_read += n_read; - - /* handle partially read records */ - ssize_t remaining_len = n_avail - n_read; - if (remaining_len) { - POSIX_GUARD_RESULT(s2n_test_ktls_update_prev_header_len(io_ctx, remaining_len)); - break; - } - - /* attempt to read multiple records (must be of the same type) */ - uint8_t next_record_type = 0; - int ret = s2n_stuffer_peek_char(&io_ctx->ancillary_buffer, (char *) &next_record_type); - bool no_more_records = ret != S2N_SUCCESS; - if (no_more_records) { - break; - } - - bool next_record_different_type = next_record_type != record_type; - if (next_record_different_type) { - break; - } - - POSIX_GUARD(s2n_stuffer_skip_read(&io_ctx->ancillary_buffer, sizeof(record_type))); - } - - return bytes_read; -} - -S2N_RESULT s2n_test_init_ktls_io_stuffer_send(struct s2n_connection *conn, - struct s2n_test_ktls_io_stuffer *io) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(io); - - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->data_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->ancillary_buffer, 0)); - RESULT_GUARD(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_io_stuffer, io)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server, - struct s2n_connection *client, struct s2n_test_ktls_io_stuffer_pair *io_pair) -{ - RESULT_ENSURE_REF(server); - RESULT_ENSURE_REF(client); - RESULT_ENSURE_REF(io_pair); - /* setup stuffer IO */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.data_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.ancillary_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.data_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.ancillary_buffer, 0)); - - RESULT_GUARD(s2n_ktls_set_sendmsg_cb(server, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->client_in)); - RESULT_GUARD(s2n_ktls_set_recvmsg_cb(server, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->server_in)); - RESULT_GUARD(s2n_ktls_set_sendmsg_cb(client, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->server_in)); - RESULT_GUARD(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->client_in)); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_free(struct s2n_test_ktls_io_stuffer *io) -{ - RESULT_ENSURE_REF(io); - RESULT_GUARD_POSIX(s2n_stuffer_free(&io->data_buffer)); - RESULT_GUARD_POSIX(s2n_stuffer_free(&io->ancillary_buffer)); - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_pair_free(struct s2n_test_ktls_io_stuffer_pair *pair) -{ - RESULT_ENSURE_REF(pair); - - RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->client_in)); - RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->server_in)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_validate_data(struct s2n_test_ktls_io_stuffer *ktls_io, - const uint8_t *expected_data, uint16_t expected_len) -{ - RESULT_ENSURE_REF(ktls_io); - RESULT_ENSURE_REF(expected_data); - - struct s2n_stuffer validate_data_stuffer = ktls_io->data_buffer; - RESULT_ENSURE_EQ(s2n_stuffer_data_available(&validate_data_stuffer), expected_len); - uint8_t *data_ptr = s2n_stuffer_raw_read(&validate_data_stuffer, expected_len); - RESULT_ENSURE_REF(data_ptr); - RESULT_ENSURE_EQ(memcmp(data_ptr, expected_data, expected_len), 0); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_validate_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, - uint8_t expected_record_type, uint16_t expected_len) -{ - RESULT_ENSURE_REF(ktls_io); - - struct s2n_stuffer validate_ancillary_stuffer = ktls_io->ancillary_buffer; - uint8_t tag = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&validate_ancillary_stuffer, &tag)); - RESULT_ENSURE_EQ(tag, expected_record_type); - uint16_t len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&validate_ancillary_stuffer, &len)); - RESULT_ENSURE_EQ(len, expected_len); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_records_in_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, - uint16_t expected_records) -{ - RESULT_ENSURE_REF(ktls_io); - - size_t size = s2n_stuffer_data_available(&ktls_io->ancillary_buffer); - size_t records = size / S2N_TEST_KTLS_MOCK_HEADER_SIZE; - size_t extra = size % S2N_TEST_KTLS_MOCK_HEADER_SIZE; - - RESULT_ENSURE_EQ(records, expected_records); - RESULT_ENSURE_EQ(extra, 0); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "testlib/s2n_ktls_test_utils.h" + +S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, + int cmsg_type, uint8_t record_type); +S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type); + +/* Since it is possible to read partial data, we need a way to update the length + * of the previous record for the mock stuffer IO implementation. */ +static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_stuffer *io_ctx, + uint16_t remaining_len) +{ + RESULT_ENSURE_REF(io_ctx); + RESULT_ENSURE(remaining_len > 0, S2N_ERR_IO); + + /* rewind so we can read the last header with the updated len */ + RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(&io_ctx->ancillary_buffer, S2N_TEST_KTLS_MOCK_HEADER_SIZE)); + + /* get position for the last header's length */ + uint32_t rewrite_len_ptr = io_ctx->ancillary_buffer.read_cursor + S2N_TEST_KTLS_MOCK_HEADER_TAG_SIZE; + /* create a new stuffer pointing to len data and rewrite it */ + struct s2n_stuffer rewrite_len_stuffer = io_ctx->ancillary_buffer; + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&rewrite_len_stuffer)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&rewrite_len_stuffer, rewrite_len_ptr)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&rewrite_len_stuffer, remaining_len)); + + return S2N_RESULT_OK; +} + +ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *msg) +{ + errno = EINVAL; + POSIX_ENSURE_REF(msg); + + struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; + POSIX_ENSURE_REF(io_ctx); + struct s2n_stuffer *data_buffer = &io_ctx->data_buffer; + io_ctx->sendmsg_invoked_count++; + + uint8_t record_type = 0; + struct msghdr msg_to_parse = *msg; + POSIX_GUARD_RESULT(s2n_ktls_get_control_data(&msg_to_parse, S2N_TLS_SET_RECORD_TYPE, &record_type)); + + size_t total_len = 0; + for (size_t count = 0; count < msg->msg_iovlen; count++) { + POSIX_ENSURE_REF(msg->msg_iov); + uint8_t *buf = msg->msg_iov[count].iov_base; + size_t len = msg->msg_iov[count].iov_len; + + if (s2n_stuffer_write_bytes(data_buffer, buf, len) != S2N_SUCCESS) { + size_t partial_len = S2N_MIN(len, s2n_stuffer_space_remaining(data_buffer)); + POSIX_GUARD(s2n_stuffer_write_bytes(data_buffer, buf, partial_len)); + total_len += partial_len; + if (total_len) { + break; + } + errno = EAGAIN; + return -1; + } + total_len += len; + } + if (total_len) { + /* write record_type and len after some data was written successfully */ + POSIX_GUARD(s2n_stuffer_write_uint8(&io_ctx->ancillary_buffer, record_type)); + POSIX_GUARD(s2n_stuffer_write_uint16(&io_ctx->ancillary_buffer, total_len)); + } + + return total_len; +} + +/* In userspace TLS, s2n first reads the header to determine the length of next record + * and then reads the entire record into conn->in. In kTLS it is not possible to know + * the length of the next record. Instead the socket returns the minimum of + * bytes-requested and data-available, reading multiple consecutive records if they + * are of the same type. */ +ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg) +{ + errno = EINVAL; + POSIX_ENSURE_REF(msg); + POSIX_ENSURE_REF(msg->msg_iov); + + struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; + POSIX_ENSURE_REF(io_ctx); + io_ctx->recvmsg_invoked_count++; + + uint8_t *buf = msg->msg_iov->iov_base; + POSIX_ENSURE_REF(buf); + + /* There is no data available so return blocked */ + if (!s2n_stuffer_data_available(&io_ctx->ancillary_buffer)) { + errno = EAGAIN; + return -1; + } + + /* s2n only receives using msg_iovlen of 1 */ + POSIX_ENSURE_EQ(msg->msg_iovlen, 1); + size_t size = msg->msg_iov->iov_len; + + uint8_t record_type = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&io_ctx->ancillary_buffer, &record_type)); + POSIX_GUARD_RESULT(s2n_ktls_set_control_data(msg, msg->msg_control, msg->msg_controllen, + S2N_TLS_GET_RECORD_TYPE, record_type)); + + ssize_t bytes_read = 0; + while (bytes_read < size) { + uint16_t n_avail = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(&io_ctx->ancillary_buffer, &n_avail)); + + size_t n_read = S2N_MIN(size - bytes_read, n_avail); + POSIX_ENSURE_GT(n_read, 0); + POSIX_GUARD(s2n_stuffer_read_bytes(&io_ctx->data_buffer, buf + bytes_read, n_read)); + + bytes_read += n_read; + + /* handle partially read records */ + ssize_t remaining_len = n_avail - n_read; + if (remaining_len) { + POSIX_GUARD_RESULT(s2n_test_ktls_update_prev_header_len(io_ctx, remaining_len)); + break; + } + + /* attempt to read multiple records (must be of the same type) */ + uint8_t next_record_type = 0; + int ret = s2n_stuffer_peek_char(&io_ctx->ancillary_buffer, (char *) &next_record_type); + bool no_more_records = ret != S2N_SUCCESS; + if (no_more_records) { + break; + } + + bool next_record_different_type = next_record_type != record_type; + if (next_record_different_type) { + break; + } + + POSIX_GUARD(s2n_stuffer_skip_read(&io_ctx->ancillary_buffer, sizeof(record_type))); + } + + return bytes_read; +} + +S2N_RESULT s2n_test_init_ktls_io_stuffer_send(struct s2n_connection *conn, + struct s2n_test_ktls_io_stuffer *io) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(io); + + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->data_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->ancillary_buffer, 0)); + RESULT_GUARD(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_io_stuffer, io)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server, + struct s2n_connection *client, struct s2n_test_ktls_io_stuffer_pair *io_pair) +{ + RESULT_ENSURE_REF(server); + RESULT_ENSURE_REF(client); + RESULT_ENSURE_REF(io_pair); + /* setup stuffer IO */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.data_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.ancillary_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.data_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.ancillary_buffer, 0)); + + RESULT_GUARD(s2n_ktls_set_sendmsg_cb(server, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->client_in)); + RESULT_GUARD(s2n_ktls_set_recvmsg_cb(server, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->server_in)); + RESULT_GUARD(s2n_ktls_set_sendmsg_cb(client, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->server_in)); + RESULT_GUARD(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->client_in)); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_free(struct s2n_test_ktls_io_stuffer *io) +{ + RESULT_ENSURE_REF(io); + RESULT_GUARD_POSIX(s2n_stuffer_free(&io->data_buffer)); + RESULT_GUARD_POSIX(s2n_stuffer_free(&io->ancillary_buffer)); + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_pair_free(struct s2n_test_ktls_io_stuffer_pair *pair) +{ + RESULT_ENSURE_REF(pair); + + RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->client_in)); + RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->server_in)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_validate_data(struct s2n_test_ktls_io_stuffer *ktls_io, + const uint8_t *expected_data, uint16_t expected_len) +{ + RESULT_ENSURE_REF(ktls_io); + RESULT_ENSURE_REF(expected_data); + + struct s2n_stuffer validate_data_stuffer = ktls_io->data_buffer; + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&validate_data_stuffer), expected_len); + uint8_t *data_ptr = s2n_stuffer_raw_read(&validate_data_stuffer, expected_len); + RESULT_ENSURE_REF(data_ptr); + RESULT_ENSURE_EQ(memcmp(data_ptr, expected_data, expected_len), 0); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_validate_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, + uint8_t expected_record_type, uint16_t expected_len) +{ + RESULT_ENSURE_REF(ktls_io); + + struct s2n_stuffer validate_ancillary_stuffer = ktls_io->ancillary_buffer; + uint8_t tag = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&validate_ancillary_stuffer, &tag)); + RESULT_ENSURE_EQ(tag, expected_record_type); + uint16_t len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&validate_ancillary_stuffer, &len)); + RESULT_ENSURE_EQ(len, expected_len); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_records_in_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, + uint16_t expected_records) +{ + RESULT_ENSURE_REF(ktls_io); + + size_t size = s2n_stuffer_data_available(&ktls_io->ancillary_buffer); + size_t records = size / S2N_TEST_KTLS_MOCK_HEADER_SIZE; + size_t extra = size % S2N_TEST_KTLS_MOCK_HEADER_SIZE; + + RESULT_ENSURE_EQ(records, expected_records); + RESULT_ENSURE_EQ(extra, 0); + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_test_certs.c b/tests/testlib/s2n_test_certs.c index 1d97261968a..6d651f01ddb 100644 --- a/tests/testlib/s2n_test_certs.c +++ b/tests/testlib/s2n_test_certs.c @@ -1,265 +1,266 @@ -/* - * 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 "crypto/s2n_libcrypto.h" -#include "crypto/s2n_mldsa.h" -#include "crypto/s2n_rsa_pss.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -#define S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED 5 - -int s2n_test_cert_chain_and_key_new(struct s2n_cert_chain_and_key **chain_and_key, - const char *cert_chain_file, const char *private_key_file) -{ - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - - POSIX_GUARD(s2n_read_test_pem(cert_chain_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(private_key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - POSIX_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_test_cert_permutation_load_server_chain_from_name( - struct s2n_cert_chain_and_key **chain_and_key, const char *name) -{ - char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/permutations/%s/server-chain.pem", name); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/permutations/%s/server-key.pem", name); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - RESULT_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); - - return S2N_RESULT_OK; -} - -int s2n_test_cert_permutation_load_server_chain(struct s2n_cert_chain_and_key **chain_and_key, - const char *type, const char *signature, const char *size, const char *digest) -{ - char name_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - snprintf(name_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "%s_%s_%s_%s", type, signature, size, digest); - POSIX_GUARD_RESULT(s2n_test_cert_permutation_load_server_chain_from_name(chain_and_key, name_buffer)); - return S2N_SUCCESS; -} - -int s2n_test_cert_permutation_get_ca_path(char *output, const char *type, const char *signature, - const char *size, const char *digest) -{ - sprintf(output, "../pems/permutations/%s_%s_%s_%s/ca-cert.pem", type, signature, size, digest); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_test_cert_permutation_get_server_chain_path(char *output, const char *type, - const char *signature, const char *size, const char *digest) -{ - sprintf(output, "../pems/permutations/%s_%s_%s_%s/server-chain.pem", type, signature, size, - digest); - return S2N_RESULT_OK; -} - -int s2n_read_test_pem(const char *pem_path, char *pem_out, long int max_size) -{ - uint32_t pem_len = 0; - - POSIX_GUARD(s2n_read_test_pem_and_len(pem_path, (uint8_t *) pem_out, &pem_len, max_size - 1)); - pem_out[pem_len] = '\0'; - - return 0; -} - -int s2n_read_test_pem_and_len(const char *pem_path, uint8_t *pem_out, uint32_t *pem_len, long int max_size) -{ - FILE *pem_file = fopen(pem_path, "rb"); - if (!pem_file) { - POSIX_BAIL(S2N_ERR_NULL); - } - - /* Make sure we can fit the pem into the output buffer */ - fseek(pem_file, 0, SEEK_END); - const long int pem_file_size = ftell(pem_file); - /* one extra for the null byte */ - rewind(pem_file); - - if (max_size < (pem_file_size)) { - POSIX_BAIL(S2N_ERR_NOMEM); - } - - if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < pem_file_size) { - POSIX_BAIL(S2N_ERR_IO); - } - - *pem_len = pem_file_size; - fclose(pem_file); - - return 0; -} - -S2N_RESULT s2n_test_cert_chain_data_from_pem(struct s2n_connection *conn, const char *pem_path, - struct s2n_stuffer *cert_chain_stuffer) -{ - RESULT_ENSURE_REF(cert_chain_stuffer); - - uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t cert_chain_pem_len = 0; - RESULT_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, cert_chain_pem, &cert_chain_pem_len, S2N_MAX_TEST_PEM_SIZE)); - - RESULT_GUARD(s2n_test_cert_chain_data_from_pem_data(conn, cert_chain_pem, cert_chain_pem_len, cert_chain_stuffer)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_cert_chain_data_from_pem_data(struct s2n_connection *conn, uint8_t *pem_data, uint32_t pem_data_len, - struct s2n_stuffer *cert_chain_stuffer) -{ - DEFER_CLEANUP(struct s2n_stuffer certificate_message_stuffer = { 0 }, s2n_stuffer_free); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&certificate_message_stuffer, 4096)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, pem_data, pem_data_len)); - - RESULT_GUARD_POSIX(s2n_send_cert_chain(conn, &certificate_message_stuffer, chain_and_key)); - - /* Skip the cert chain length */ - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&certificate_message_stuffer, 3)); - - uint32_t cert_chain_len = s2n_stuffer_data_available(&certificate_message_stuffer); - RESULT_GUARD_POSIX(s2n_stuffer_alloc(cert_chain_stuffer, cert_chain_len)); - RESULT_GUARD_POSIX(s2n_stuffer_copy(&certificate_message_stuffer, cert_chain_stuffer, cert_chain_len)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_cert_chains_init(struct s2n_test_cert_chain_list *chains) -{ - /* Load all permutations */ - { - DIR *root = opendir("../pems/permutations"); - RESULT_ENSURE_REF(root); - struct dirent *dir = NULL; - while ((dir = readdir(root)) != NULL) { - /* Skip ., .., the README, etc. - * This is pretty hacky: we can switch to stat later if necessary. -- */ - if (strchr(dir->d_name, '_') == NULL) { - continue; - } - if (!s2n_is_rsa_pss_certs_supported() && strstr(dir->d_name, "pss")) { - chains->skipped++; - continue; - } - if (s2n_libcrypto_is_openssl_fips() && strstr(dir->d_name, "rsae_pkcs_1024_sha1")) { - chains->skipped++; - continue; - } - - RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); - struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; - - RESULT_GUARD(s2n_test_cert_permutation_load_server_chain_from_name( - &entry->chain, dir->d_name)); - chains->count++; - } - closedir(root); - } - - /* Load all MLDSA test certs. - * MLDSA is not yet included in permutations due to difficulties generating MLDSA certs. - * We instead continue to test with the example certs from the RFC. - */ - const char *mldsa_names[] = { "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" }; - if (s2n_mldsa_is_supported()) { - for (size_t i = 0; i < s2n_array_len(mldsa_names); i++) { - RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); - struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; - - char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/mldsa/%s.crt", mldsa_names[i]); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/mldsa/%s-seed.priv", mldsa_names[i]); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - entry->chain = s2n_cert_chain_and_key_new(); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem( - entry->chain, cert_chain_pem, private_key_pem)); - - chains->count++; - } - } else { - chains->skipped += s2n_array_len(mldsa_names); - } - - /* Sanity check result */ - RESULT_ENSURE_LTE(chains->skipped, S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED); - RESULT_ENSURE_EQ(chains->skipped + chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); - - return S2N_RESULT_OK; -} - -/* - * Sets "supported" to a specific value for all chains of a given pkey_type. - * "supported" could indicate an index in an array of policies (as in s2n_security_policies_test), - * or could indicate a specific policy version, or could simply indicate true/false. - * The meaning of the value depends on the structure of the test. - */ -S2N_RESULT s2n_test_cert_chains_set_supported(struct s2n_test_cert_chain_list *chains, - s2n_pkey_type target_type, uint64_t supported) -{ - for (size_t i = 0; i < chains->count; i++) { - struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; - s2n_pkey_type entry_type = entry->chain->cert_chain->head->pkey_type; - if (entry_type == target_type) { - entry->supported = supported; - } - } - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_test_cert_chains_free(struct s2n_test_cert_chain_list *chains) -{ - for (size_t i = 0; i < chains->count; i++) { - struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(entry->chain)); - entry->supported = 0; - } - chains->count = 0; - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_libcrypto.h" +#include "crypto/s2n_mldsa.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +#define S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED 5 + +int s2n_test_cert_chain_and_key_new(struct s2n_cert_chain_and_key **chain_and_key, + const char *cert_chain_file, const char *private_key_file) +{ + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + + POSIX_GUARD(s2n_read_test_pem(cert_chain_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(private_key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + POSIX_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_test_cert_permutation_load_server_chain_from_name( + struct s2n_cert_chain_and_key **chain_and_key, const char *name) +{ + char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/permutations/%s/server-chain.pem", name); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/permutations/%s/server-key.pem", name); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + RESULT_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); + + return S2N_RESULT_OK; +} + +int s2n_test_cert_permutation_load_server_chain(struct s2n_cert_chain_and_key **chain_and_key, + const char *type, const char *signature, const char *size, const char *digest) +{ + char name_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + snprintf(name_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "%s_%s_%s_%s", type, signature, size, digest); + POSIX_GUARD_RESULT(s2n_test_cert_permutation_load_server_chain_from_name(chain_and_key, name_buffer)); + return S2N_SUCCESS; +} + +int s2n_test_cert_permutation_get_ca_path(char *output, const char *type, const char *signature, + const char *size, const char *digest) +{ + sprintf(output, "../pems/permutations/%s_%s_%s_%s/ca-cert.pem", type, signature, size, digest); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_test_cert_permutation_get_server_chain_path(char *output, const char *type, + const char *signature, const char *size, const char *digest) +{ + sprintf(output, "../pems/permutations/%s_%s_%s_%s/server-chain.pem", type, signature, size, + digest); + return S2N_RESULT_OK; +} + +int s2n_read_test_pem(const char *pem_path, char *pem_out, long int max_size) +{ + uint32_t pem_len = 0; + + POSIX_GUARD(s2n_read_test_pem_and_len(pem_path, (uint8_t *) pem_out, &pem_len, max_size - 1)); + pem_out[pem_len] = '\0'; + + return 0; +} + +int s2n_read_test_pem_and_len(const char *pem_path, uint8_t *pem_out, uint32_t *pem_len, long int max_size) +{ + FILE *pem_file = fopen(pem_path, "rb"); + if (!pem_file) { + POSIX_BAIL(S2N_ERR_NULL); + } + + /* Make sure we can fit the pem into the output buffer */ + fseek(pem_file, 0, SEEK_END); + const long int pem_file_size = ftell(pem_file); + /* one extra for the null byte */ + rewind(pem_file); + + if (max_size < (pem_file_size)) { + POSIX_BAIL(S2N_ERR_NOMEM); + } + + if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < pem_file_size) { + POSIX_BAIL(S2N_ERR_IO); + } + + *pem_len = pem_file_size; + fclose(pem_file); + + return 0; +} + +S2N_RESULT s2n_test_cert_chain_data_from_pem(struct s2n_connection *conn, const char *pem_path, + struct s2n_stuffer *cert_chain_stuffer) +{ + RESULT_ENSURE_REF(cert_chain_stuffer); + + uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t cert_chain_pem_len = 0; + RESULT_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, cert_chain_pem, &cert_chain_pem_len, S2N_MAX_TEST_PEM_SIZE)); + + RESULT_GUARD(s2n_test_cert_chain_data_from_pem_data(conn, cert_chain_pem, cert_chain_pem_len, cert_chain_stuffer)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_cert_chain_data_from_pem_data(struct s2n_connection *conn, uint8_t *pem_data, uint32_t pem_data_len, + struct s2n_stuffer *cert_chain_stuffer) +{ + DEFER_CLEANUP(struct s2n_stuffer certificate_message_stuffer = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&certificate_message_stuffer, 4096)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, pem_data, pem_data_len)); + + RESULT_GUARD_POSIX(s2n_send_cert_chain(conn, &certificate_message_stuffer, chain_and_key)); + + /* Skip the cert chain length */ + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&certificate_message_stuffer, 3)); + + uint32_t cert_chain_len = s2n_stuffer_data_available(&certificate_message_stuffer); + RESULT_GUARD_POSIX(s2n_stuffer_alloc(cert_chain_stuffer, cert_chain_len)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(&certificate_message_stuffer, cert_chain_stuffer, cert_chain_len)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_cert_chains_init(struct s2n_test_cert_chain_list *chains) +{ + /* Load all permutations */ + { + DIR *root = opendir("../pems/permutations"); + RESULT_ENSURE_REF(root); + struct dirent *dir = NULL; + while ((dir = readdir(root)) != NULL) { + /* Skip ., .., the README, etc. + * This is pretty hacky: we can switch to stat later if necessary. +- */ + if (strchr(dir->d_name, '_') == NULL) { + continue; + } + if (!s2n_is_rsa_pss_certs_supported() && strstr(dir->d_name, "pss")) { + chains->skipped++; + continue; + } + if (s2n_libcrypto_is_openssl_fips() && strstr(dir->d_name, "rsae_pkcs_1024_sha1")) { + chains->skipped++; + continue; + } + + RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); + struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; + + RESULT_GUARD(s2n_test_cert_permutation_load_server_chain_from_name( + &entry->chain, dir->d_name)); + chains->count++; + } + closedir(root); + } + + /* Load all MLDSA test certs. + * MLDSA is not yet included in permutations due to difficulties generating MLDSA certs. + * We instead continue to test with the example certs from the RFC. + */ + const char *mldsa_names[] = { "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" }; + if (s2n_mldsa_is_supported()) { + for (size_t i = 0; i < s2n_array_len(mldsa_names); i++) { + RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); + struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; + + char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/mldsa/%s.crt", mldsa_names[i]); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/mldsa/%s-seed.priv", mldsa_names[i]); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + entry->chain = s2n_cert_chain_and_key_new(); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem( + entry->chain, cert_chain_pem, private_key_pem)); + + chains->count++; + } + } else { + chains->skipped += s2n_array_len(mldsa_names); + } + + /* Sanity check result */ + RESULT_ENSURE_LTE(chains->skipped, S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED); + RESULT_ENSURE_EQ(chains->skipped + chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); + + return S2N_RESULT_OK; +} + +/* + * Sets "supported" to a specific value for all chains of a given pkey_type. + * "supported" could indicate an index in an array of policies (as in s2n_security_policies_test), + * or could indicate a specific policy version, or could simply indicate true/false. + * The meaning of the value depends on the structure of the test. + */ +S2N_RESULT s2n_test_cert_chains_set_supported(struct s2n_test_cert_chain_list *chains, + s2n_pkey_type target_type, uint64_t supported) +{ + for (size_t i = 0; i < chains->count; i++) { + struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; + s2n_pkey_type entry_type = entry->chain->cert_chain->head->pkey_type; + if (entry_type == target_type) { + entry->supported = supported; + } + } + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_test_cert_chains_free(struct s2n_test_cert_chain_list *chains) +{ + for (size_t i = 0; i < chains->count; i++) { + struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(entry->chain)); + entry->supported = 0; + } + chains->count = 0; + return S2N_RESULT_OK; +} diff --git a/tests/unit/s2n_async_pkey_test.c b/tests/unit/s2n_async_pkey_test.c index c2cefd6dc6d..ae9c2a2e16c 100644 --- a/tests/unit/s2n_async_pkey_test.c +++ b/tests/unit/s2n_async_pkey_test.c @@ -1,725 +1,726 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_async_pkey.h" - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "utils/s2n_safety.h" - -struct s2n_async_pkey_op *pkey_op = NULL; - -uint8_t test_digest_data[] = "I hashed this"; -const uint32_t test_digest_size = sizeof(test_digest_data); -const uint8_t test_signature_data[] = "I signed this"; -const uint32_t test_signature_size = sizeof(test_signature_data); -uint8_t test_encrypted_data[] = "I encrypted this"; -const uint32_t test_encrypted_size = sizeof(test_encrypted_data); -uint8_t test_decrypted_data[] = "I decrypted this"; -const uint32_t test_decrypted_size = sizeof(test_decrypted_data); - -uint8_t offload_callback_count = 0; - -typedef int(async_handler)(struct s2n_connection *conn); - -/* Declaring a flag to check if sign operation is called at least once for all cipher_suites - * while performing handshake through handler (async_handler_sign_with_different_pkey_and_apply) */ -static bool async_handler_sign_operation_called = false; - -static int async_handler_fail(struct s2n_connection *conn) -{ - FAIL_MSG("async_handler_fail should never get invoked"); - return S2N_FAILURE; -} - -static int async_handler_wipe_connection_and_apply(struct s2n_connection *conn) -{ - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); - EXPECT_NOT_NULL(chain_and_key); - - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Wipe connection */ - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* Test that we can perform pkey operation, even if original connection was wiped */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); - - /* Test that pkey op can't be applied to wiped connection */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_ASYNC_WRONG_CONNECTION); - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - return S2N_FAILURE; -} - -static int async_handler_sign_with_different_pkey_and_apply(struct s2n_connection *conn) -{ - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); - EXPECT_NOT_NULL(chain_and_key); - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Test that we can perform pkey operation */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); - - /* Get type for pkey_op */ - s2n_async_pkey_op_type type = { 0 }; - EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(pkey_op, &type)); - - /* Test apply with different certificate chain only for sign operation */ - if (type == S2N_ASYNC_SIGN) { - /* Create new chain and key, and modify current server conn */ - struct s2n_cert_chain_and_key *chain_and_key_2 = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key_2, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - /* Change server conn cert data */ - EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); - conn->handshake_params.our_chain_and_key = chain_and_key_2; - - /* Test that async sign operation will fail as signature was performed over different private key */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_VERIFY_SIGNATURE); - - /* Set pkey_op's validation mode to S2N_ASYNC_PKEY_VALIDATION_FAST and test that async sign apply will pass now */ - EXPECT_SUCCESS(s2n_async_pkey_op_set_validation_mode(pkey_op, S2N_ASYNC_PKEY_VALIDATION_FAST)); - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - - /* Set chain_and_key back to original value and free new chain_and_key */ - conn->handshake_params.our_chain_and_key = chain_and_key; - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key_2)); - - /* Update async_handler_sign_operation_called flag to true */ - async_handler_sign_operation_called = true; - } else { - /* Test decrypt operation passes */ - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - } - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - return S2N_SUCCESS; -} - -static int async_handler_free_pkey_op(struct s2n_connection *conn) -{ - static int function_entered = 0; - - /* Return failure on the second entrance into function so that we drop from try_handshake */ - if (function_entered++ % 2 == 1) { - return S2N_FAILURE; - } - - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - /* Return success so that try_handshake calls s2n_negotiate again */ - return S2N_SUCCESS; -} - -static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn, async_handler handler) -{ - s2n_blocked_status server_blocked; - s2n_blocked_status client_blocked; - - int tries = 0; - do { - int client_rc = s2n_negotiate(client_conn, &client_blocked); - if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return S2N_FAILURE; - } - - int server_rc = s2n_negotiate(server_conn, &server_blocked); - if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return S2N_FAILURE; - } - - if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { - POSIX_GUARD(handler(server_conn)); - } - - EXPECT_NOT_EQUAL(++tries, 5); - } while (client_blocked || server_blocked); - - POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - return S2N_SUCCESS; -} - -int async_pkey_apply_in_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - /* Check that we have op */ - EXPECT_NOT_NULL(op); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); - EXPECT_NOT_NULL(chain_and_key); - - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Perform the op */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(op, pkey)); - - /* Test that op can be applied inside the callback */ - EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); - - /* Free the op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(op)); - - return S2N_SUCCESS; -} - -int async_pkey_store_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - return S2N_SUCCESS; -} - -int async_pkey_signature_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - - s2n_async_pkey_op_type type = { 0 }; - EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); - EXPECT_EQUAL(type, S2N_ASYNC_SIGN); - - uint8_t expected_size = 0; - EXPECT_SUCCESS(s2n_hash_digest_size(S2N_HASH_SHA256, &expected_size)); - - uint32_t input_size = 0; - EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); - EXPECT_EQUAL(input_size, expected_size); - - struct s2n_blob input1 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input1, input_size)); - - struct s2n_blob input2 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input2, input_size)); - - struct s2n_blob expected_digest = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&expected_digest, expected_size)); - - struct s2n_hash_state digest = { 0 }; - EXPECT_SUCCESS(s2n_hash_new(&digest)); - EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); - EXPECT_SUCCESS(s2n_hash_digest(&digest, expected_digest.data, expected_digest.size)); - EXPECT_SUCCESS(s2n_hash_free(&digest)); - - /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input1.data, input1.size)); - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input2.data, input2.size)); - - EXPECT_EQUAL(input1.size, input2.size); - EXPECT_BYTEARRAY_EQUAL(input1.data, input2.data, input1.size); - EXPECT_BYTEARRAY_EQUAL(input1.data, expected_digest.data, expected_size); - - EXPECT_SUCCESS(s2n_free(&input1)); - EXPECT_SUCCESS(s2n_free(&input2)); - EXPECT_SUCCESS(s2n_free(&expected_digest)); - - EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_signature_data, test_signature_size)); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int async_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - - s2n_async_pkey_op_type type = { 0 }; - EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); - EXPECT_EQUAL(type, S2N_ASYNC_DECRYPT); - - uint32_t input_size = 0; - EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); - EXPECT_EQUAL(input_size, test_encrypted_size); - - struct s2n_blob input_buffer1 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input_buffer1, input_size)); - - struct s2n_blob input_buffer2 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input_buffer2, input_size)); - - /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer1.data, input_buffer1.size)); - EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, test_encrypted_data, test_encrypted_size); - - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer2.data, input_buffer2.size)); - EXPECT_BYTEARRAY_EQUAL(input_buffer2.data, test_encrypted_data, test_encrypted_size); - - EXPECT_EQUAL(input_buffer1.size, input_buffer2.size); - EXPECT_EQUAL(input_buffer1.size, test_encrypted_size); - EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, input_buffer2.data, test_encrypted_size); - - EXPECT_SUCCESS(s2n_free(&input_buffer1)); - EXPECT_SUCCESS(s2n_free(&input_buffer2)); - - EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_decrypted_data, test_decrypted_size)); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int s2n_async_sign_complete(struct s2n_connection *conn, struct s2n_blob *signature) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(signature); - - EXPECT_EQUAL(signature->size, test_signature_size); - EXPECT_BYTEARRAY_EQUAL(signature->data, test_signature_data, test_signature_size); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int s2n_async_decrypt_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(decrypted); - EXPECT_FALSE(rsa_failed); - - EXPECT_EQUAL(decrypted->size, test_decrypted_size); - EXPECT_BYTEARRAY_EQUAL(decrypted->data, test_decrypted_data, test_decrypted_size); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int async_pkey_invalid_input_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - - EXPECT_FAILURE(s2n_async_pkey_op_get_op_type(op, NULL)); - EXPECT_FAILURE(s2n_async_pkey_op_get_input_size(op, NULL)); - EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, NULL, 0)); - - uint32_t input_size = 0; - EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); - - uint8_t placeholder_buffer[] = { 0x0, 0x0, 0x0, 0x0 }; - - /* Buffer too small to contain data. */ - EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, placeholder_buffer, input_size - 1)); - - EXPECT_FAILURE(s2n_async_pkey_op_set_output(op, NULL, test_signature_size)); - offload_callback_count++; - - return S2N_FAILURE; -} - -int async_pkey_invalid_complete(struct s2n_connection *conn, struct s2n_blob *signature) -{ - FAIL_MSG("Invalid async pkey callback was invoked. The callback should never be invoked if there was an earlier" - " failure in the async_pkey_op."); - return S2N_FAILURE; -} - -static int s2n_test_bad_sign(const struct s2n_pkey *pub_key, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *digest, struct s2n_blob *signature) -{ - /* Just write all zeroes. - * This could accidentally be the correct signature, but it's very unlikely. - */ - POSIX_GUARD(s2n_blob_zero(signature)); - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - /* Run all tests for 2 cipher suites to test both sign and decrypt operations */ - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - }; - - for (size_t i = 0; i < s2n_array_len(test_cipher_suites); i++) { - struct s2n_cipher_preferences server_cipher_preferences = { - .count = 1, - .suites = &test_cipher_suites[i], - }; - - struct s2n_security_policy server_security_policy = { - .minimum_protocol_version = S2N_TLS12, - .cipher_preferences = &server_cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20200310, - }; - - EXPECT_TRUE(test_cipher_suites[i]->available); - - TEST_DEBUG_PRINT("Testing %s\n", test_cipher_suites[i]->name); - - /* Test: apply while invoking callback */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_apply_in_callback)); - server_config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_fail)); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: wipe connection and then perform and apply pkey op */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_EQUAL(try_handshake(server_conn, client_conn, async_handler_wipe_connection_and_apply), S2N_FAILURE); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: free the pkey op and try s2n_negotiate again */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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( - try_handshake(server_conn, client_conn, async_handler_free_pkey_op), S2N_ERR_ASYNC_BLOCKED); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: Apply invalid signature */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - /* Enable signature validation for async sign call */ - EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(server_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: Apply invalid signature, when signature validation is enabled for all sync / async signatures */ - { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - 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)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - 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)); - - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); - } - } - - /* Test if sign operation was called at least once for 'Test: Apply invalid signature', - * the flag holds the value after executing handshakes for all cipher_suites */ - EXPECT_EQUAL(async_handler_sign_operation_called, true); - - DEFER_CLEANUP(struct s2n_hash_state digest = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&digest)); - EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); - - /* Test: signature offload. */ - { - EXPECT_EQUAL(0, offload_callback_count); - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - conn->config->async_pkey_cb = async_pkey_signature_callback; - - EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, s2n_async_sign_complete))); - EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(1, offload_callback_count); - - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - EXPECT_EQUAL(2, offload_callback_count); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Test: decrypt offload. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - conn->config->async_pkey_cb = async_pkey_decrypt_callback; - - struct s2n_blob encrypted_data = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&encrypted_data, test_encrypted_data, test_encrypted_size)); - - struct s2n_blob decrypted_data = { 0 }; - /* Re-use the encrypted data buffer to make sure that the data was actually transformed in the callback. - * If we filled this with the decrypted data, we would not know if the decryption happened in the callback. */ - EXPECT_SUCCESS(s2n_blob_init(&decrypted_data, test_encrypted_data, test_encrypted_size)); - - EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_decrypt(conn, &encrypted_data, &decrypted_data, s2n_async_decrypt_complete))); - EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(3, offload_callback_count); - - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - EXPECT_EQUAL(4, offload_callback_count); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Test: errors in callback. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - conn->config->async_pkey_cb = async_pkey_invalid_input_callback; - - EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, async_pkey_invalid_complete))); - EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_CALLBACK_FAILED); - EXPECT_EQUAL(5, offload_callback_count); - - EXPECT_FAILURE(s2n_async_pkey_op_apply(pkey_op, conn)); - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - EXPECT_EQUAL(5, offload_callback_count); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - /* Test: Apply invalid signature to sync operation */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - - 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, config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_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)); - - /* Start the handshake. - * We need to perform enough of the handshake to choose a certificate / private key. - */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); - - /* Setup the pkey verify operation to fail for the chosen private key */ - EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key); - EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key->private_key); - struct s2n_pkey *original_pkey = server_conn->handshake_params.our_chain_and_key->private_key; - struct s2n_pkey bad_pkey = *original_pkey; - bad_pkey.sign = s2n_test_bad_sign; - server_conn->handshake_params.our_chain_and_key->private_key = &bad_pkey; - - /* Verify after sign should fail */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_VERIFY_SIGNATURE); - - /* Reset pkey for cleanup */ - server_conn->handshake_params.our_chain_and_key->private_key = original_pkey; - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_async_pkey.h" + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +struct s2n_async_pkey_op *pkey_op = NULL; + +uint8_t test_digest_data[] = "I hashed this"; +const uint32_t test_digest_size = sizeof(test_digest_data); +const uint8_t test_signature_data[] = "I signed this"; +const uint32_t test_signature_size = sizeof(test_signature_data); +uint8_t test_encrypted_data[] = "I encrypted this"; +const uint32_t test_encrypted_size = sizeof(test_encrypted_data); +uint8_t test_decrypted_data[] = "I decrypted this"; +const uint32_t test_decrypted_size = sizeof(test_decrypted_data); + +uint8_t offload_callback_count = 0; + +typedef int(async_handler)(struct s2n_connection *conn); + +/* Declaring a flag to check if sign operation is called at least once for all cipher_suites + * while performing handshake through handler (async_handler_sign_with_different_pkey_and_apply) */ +static bool async_handler_sign_operation_called = false; + +static int async_handler_fail(struct s2n_connection *conn) +{ + FAIL_MSG("async_handler_fail should never get invoked"); + return S2N_FAILURE; +} + +static int async_handler_wipe_connection_and_apply(struct s2n_connection *conn) +{ + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Test that we can perform pkey operation, even if original connection was wiped */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Test that pkey op can't be applied to wiped connection */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_ASYNC_WRONG_CONNECTION); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + return S2N_FAILURE; +} + +static int async_handler_sign_with_different_pkey_and_apply(struct s2n_connection *conn) +{ + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Test that we can perform pkey operation */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Get type for pkey_op */ + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(pkey_op, &type)); + + /* Test apply with different certificate chain only for sign operation */ + if (type == S2N_ASYNC_SIGN) { + /* Create new chain and key, and modify current server conn */ + struct s2n_cert_chain_and_key *chain_and_key_2 = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key_2, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Change server conn cert data */ + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + conn->handshake_params.our_chain_and_key = chain_and_key_2; + + /* Test that async sign operation will fail as signature was performed over different private key */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_VERIFY_SIGNATURE); + + /* Set pkey_op's validation mode to S2N_ASYNC_PKEY_VALIDATION_FAST and test that async sign apply will pass now */ + EXPECT_SUCCESS(s2n_async_pkey_op_set_validation_mode(pkey_op, S2N_ASYNC_PKEY_VALIDATION_FAST)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + + /* Set chain_and_key back to original value and free new chain_and_key */ + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key_2)); + + /* Update async_handler_sign_operation_called flag to true */ + async_handler_sign_operation_called = true; + } else { + /* Test decrypt operation passes */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + } + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + return S2N_SUCCESS; +} + +static int async_handler_free_pkey_op(struct s2n_connection *conn) +{ + static int function_entered = 0; + + /* Return failure on the second entrance into function so that we drop from try_handshake */ + if (function_entered++ % 2 == 1) { + return S2N_FAILURE; + } + + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + /* Return success so that try_handshake calls s2n_negotiate again */ + return S2N_SUCCESS; +} + +static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn, async_handler handler) +{ + s2n_blocked_status server_blocked; + s2n_blocked_status client_blocked; + + int tries = 0; + do { + int client_rc = s2n_negotiate(client_conn, &client_blocked); + if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + int server_rc = s2n_negotiate(server_conn, &server_blocked); + if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { + POSIX_GUARD(handler(server_conn)); + } + + EXPECT_NOT_EQUAL(++tries, 5); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + return S2N_SUCCESS; +} + +int async_pkey_apply_in_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + /* Check that we have op */ + EXPECT_NOT_NULL(op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Perform the op */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(op, pkey)); + + /* Test that op can be applied inside the callback */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); + + /* Free the op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(op)); + + return S2N_SUCCESS; +} + +int async_pkey_store_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + return S2N_SUCCESS; +} + +int async_pkey_signature_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); + EXPECT_EQUAL(type, S2N_ASYNC_SIGN); + + uint8_t expected_size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(S2N_HASH_SHA256, &expected_size)); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_EQUAL(input_size, expected_size); + + struct s2n_blob input1 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input1, input_size)); + + struct s2n_blob input2 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input2, input_size)); + + struct s2n_blob expected_digest = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&expected_digest, expected_size)); + + struct s2n_hash_state digest = { 0 }; + EXPECT_SUCCESS(s2n_hash_new(&digest)); + EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); + EXPECT_SUCCESS(s2n_hash_digest(&digest, expected_digest.data, expected_digest.size)); + EXPECT_SUCCESS(s2n_hash_free(&digest)); + + /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input1.data, input1.size)); + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input2.data, input2.size)); + + EXPECT_EQUAL(input1.size, input2.size); + EXPECT_BYTEARRAY_EQUAL(input1.data, input2.data, input1.size); + EXPECT_BYTEARRAY_EQUAL(input1.data, expected_digest.data, expected_size); + + EXPECT_SUCCESS(s2n_free(&input1)); + EXPECT_SUCCESS(s2n_free(&input2)); + EXPECT_SUCCESS(s2n_free(&expected_digest)); + + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_signature_data, test_signature_size)); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int async_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); + EXPECT_EQUAL(type, S2N_ASYNC_DECRYPT); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_EQUAL(input_size, test_encrypted_size); + + struct s2n_blob input_buffer1 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input_buffer1, input_size)); + + struct s2n_blob input_buffer2 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input_buffer2, input_size)); + + /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer1.data, input_buffer1.size)); + EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, test_encrypted_data, test_encrypted_size); + + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer2.data, input_buffer2.size)); + EXPECT_BYTEARRAY_EQUAL(input_buffer2.data, test_encrypted_data, test_encrypted_size); + + EXPECT_EQUAL(input_buffer1.size, input_buffer2.size); + EXPECT_EQUAL(input_buffer1.size, test_encrypted_size); + EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, input_buffer2.data, test_encrypted_size); + + EXPECT_SUCCESS(s2n_free(&input_buffer1)); + EXPECT_SUCCESS(s2n_free(&input_buffer2)); + + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_decrypted_data, test_decrypted_size)); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int s2n_async_sign_complete(struct s2n_connection *conn, struct s2n_blob *signature) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(signature); + + EXPECT_EQUAL(signature->size, test_signature_size); + EXPECT_BYTEARRAY_EQUAL(signature->data, test_signature_data, test_signature_size); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int s2n_async_decrypt_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(decrypted); + EXPECT_FALSE(rsa_failed); + + EXPECT_EQUAL(decrypted->size, test_decrypted_size); + EXPECT_BYTEARRAY_EQUAL(decrypted->data, test_decrypted_data, test_decrypted_size); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int async_pkey_invalid_input_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + EXPECT_FAILURE(s2n_async_pkey_op_get_op_type(op, NULL)); + EXPECT_FAILURE(s2n_async_pkey_op_get_input_size(op, NULL)); + EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, NULL, 0)); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + + uint8_t placeholder_buffer[] = { 0x0, 0x0, 0x0, 0x0 }; + + /* Buffer too small to contain data. */ + EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, placeholder_buffer, input_size - 1)); + + EXPECT_FAILURE(s2n_async_pkey_op_set_output(op, NULL, test_signature_size)); + offload_callback_count++; + + return S2N_FAILURE; +} + +int async_pkey_invalid_complete(struct s2n_connection *conn, struct s2n_blob *signature) +{ + FAIL_MSG("Invalid async pkey callback was invoked. The callback should never be invoked if there was an earlier" + " failure in the async_pkey_op."); + return S2N_FAILURE; +} + +static int s2n_test_bad_sign(const struct s2n_pkey *pub_key, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature) +{ + /* Just write all zeroes. + * This could accidentally be the correct signature, but it's very unlikely. + */ + POSIX_GUARD(s2n_blob_zero(signature)); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Run all tests for 2 cipher suites to test both sign and decrypt operations */ + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + }; + + for (size_t i = 0; i < s2n_array_len(test_cipher_suites); i++) { + struct s2n_cipher_preferences server_cipher_preferences = { + .count = 1, + .suites = &test_cipher_suites[i], + }; + + struct s2n_security_policy server_security_policy = { + .minimum_protocol_version = S2N_TLS12, + .cipher_preferences = &server_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + EXPECT_TRUE(test_cipher_suites[i]->available); + + TEST_DEBUG_PRINT("Testing %s\n", test_cipher_suites[i]->name); + + /* Test: apply while invoking callback */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_apply_in_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_fail)); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: wipe connection and then perform and apply pkey op */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_EQUAL(try_handshake(server_conn, client_conn, async_handler_wipe_connection_and_apply), S2N_FAILURE); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: free the pkey op and try s2n_negotiate again */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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( + try_handshake(server_conn, client_conn, async_handler_free_pkey_op), S2N_ERR_ASYNC_BLOCKED); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: Apply invalid signature */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + /* Enable signature validation for async sign call */ + EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(server_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: Apply invalid signature, when signature validation is enabled for all sync / async signatures */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + 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)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + 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)); + + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); + } + } + + /* Test if sign operation was called at least once for 'Test: Apply invalid signature', + * the flag holds the value after executing handshakes for all cipher_suites */ + EXPECT_EQUAL(async_handler_sign_operation_called, true); + + DEFER_CLEANUP(struct s2n_hash_state digest = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&digest)); + EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); + + /* Test: signature offload. */ + { + EXPECT_EQUAL(0, offload_callback_count); + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_signature_callback; + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, s2n_async_sign_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(1, offload_callback_count); + + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(2, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test: decrypt offload. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_decrypt_callback; + + struct s2n_blob encrypted_data = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&encrypted_data, test_encrypted_data, test_encrypted_size)); + + struct s2n_blob decrypted_data = { 0 }; + /* Re-use the encrypted data buffer to make sure that the data was actually transformed in the callback. + * If we filled this with the decrypted data, we would not know if the decryption happened in the callback. */ + EXPECT_SUCCESS(s2n_blob_init(&decrypted_data, test_encrypted_data, test_encrypted_size)); + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_decrypt(conn, &encrypted_data, &decrypted_data, s2n_async_decrypt_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(3, offload_callback_count); + + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(4, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test: errors in callback. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_invalid_input_callback; + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, async_pkey_invalid_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_CALLBACK_FAILED); + EXPECT_EQUAL(5, offload_callback_count); + + EXPECT_FAILURE(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(5, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Test: Apply invalid signature to sync operation */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + 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, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_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)); + + /* Start the handshake. + * We need to perform enough of the handshake to choose a certificate / private key. + */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + + /* Setup the pkey verify operation to fail for the chosen private key */ + EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key); + EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key->private_key); + struct s2n_pkey *original_pkey = server_conn->handshake_params.our_chain_and_key->private_key; + struct s2n_pkey bad_pkey = *original_pkey; + bad_pkey.sign = s2n_test_bad_sign; + server_conn->handshake_params.our_chain_and_key->private_key = &bad_pkey; + + /* Verify after sign should fail */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_VERIFY_SIGNATURE); + + /* Reset pkey for cleanup */ + server_conn->handshake_params.our_chain_and_key->private_key = original_pkey; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cbc_test.c b/tests/unit/s2n_cbc_test.c index 31f8a174851..5b652d94a5d 100644 --- a/tests/unit/s2n_cbc_test.c +++ b/tests/unit/s2n_cbc_test.c @@ -1,109 +1,110 @@ -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - /* Self-talk test */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - - /* Test both composite and non-composite CBC ciphers for all CBC cipher suites. */ - size_t record_algs_tested = 0; - for (size_t cipher_suite_idx = 0; cipher_suite_idx < cipher_preferences_test_all.count; cipher_suite_idx++) { - uint8_t record_algs = cipher_preferences_test_all.suites[cipher_suite_idx]->num_record_algs; - for (size_t record_alg_idx = 0; record_alg_idx < record_algs; record_alg_idx++) { - struct s2n_cipher_suite test_cipher_suite = *cipher_preferences_test_all.suites[cipher_suite_idx]; - test_cipher_suite.record_alg = test_cipher_suite.all_record_algs[record_alg_idx]; - - /* Skip non-CBC ciphers. */ - uint8_t cipher = test_cipher_suite.record_alg->cipher->type; - if (cipher != S2N_CBC && cipher != S2N_COMPOSITE) { - continue; - } - - /* Skip unsupported ciphers. */ - if (!test_cipher_suite.record_alg->cipher->is_available()) { - continue; - } - - struct s2n_cipher_suite *test_cipher_suite_ptr = &test_cipher_suite; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = 1, - .suites = &test_cipher_suite_ptr, - }; - - struct s2n_security_policy test_security_policy = security_policy_test_all; - test_security_policy.cipher_preferences = &test_cipher_preferences; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - client->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - server->security_policy_override = &test_security_policy; - - 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, &io_pair)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - uint8_t negotiated_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server, negotiated_cipher_suite, - negotiated_cipher_suite + 1)); - EXPECT_BYTEARRAY_EQUAL(negotiated_cipher_suite, test_cipher_suite.iana_value, - S2N_TLS_CIPHER_SUITE_LEN); - - EXPECT_OK(s2n_send_and_recv_test(client, server)); - EXPECT_OK(s2n_send_and_recv_test(server, client)); - - record_algs_tested += 1; - } - } - - EXPECT_TRUE(record_algs_tested > 0); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Self-talk test */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + + /* Test both composite and non-composite CBC ciphers for all CBC cipher suites. */ + size_t record_algs_tested = 0; + for (size_t cipher_suite_idx = 0; cipher_suite_idx < cipher_preferences_test_all.count; cipher_suite_idx++) { + uint8_t record_algs = cipher_preferences_test_all.suites[cipher_suite_idx]->num_record_algs; + for (size_t record_alg_idx = 0; record_alg_idx < record_algs; record_alg_idx++) { + struct s2n_cipher_suite test_cipher_suite = *cipher_preferences_test_all.suites[cipher_suite_idx]; + test_cipher_suite.record_alg = test_cipher_suite.all_record_algs[record_alg_idx]; + + /* Skip non-CBC ciphers. */ + uint8_t cipher = test_cipher_suite.record_alg->cipher->type; + if (cipher != S2N_CBC && cipher != S2N_COMPOSITE) { + continue; + } + + /* Skip unsupported ciphers. */ + if (!test_cipher_suite.record_alg->cipher->is_available()) { + continue; + } + + struct s2n_cipher_suite *test_cipher_suite_ptr = &test_cipher_suite; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = 1, + .suites = &test_cipher_suite_ptr, + }; + + struct s2n_security_policy test_security_policy = security_policy_test_all; + test_security_policy.cipher_preferences = &test_cipher_preferences; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + client->security_policy_override = &test_security_policy; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + server->security_policy_override = &test_security_policy; + + 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, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + uint8_t negotiated_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server, negotiated_cipher_suite, + negotiated_cipher_suite + 1)); + EXPECT_BYTEARRAY_EQUAL(negotiated_cipher_suite, test_cipher_suite.iana_value, + S2N_TLS_CIPHER_SUITE_LEN); + + EXPECT_OK(s2n_send_and_recv_test(client, server)); + EXPECT_OK(s2n_send_and_recv_test(server, client)); + + record_algs_tested += 1; + } + } + + EXPECT_TRUE(record_algs_tested > 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cert_chain_and_key_load_test.c b/tests/unit/s2n_cert_chain_and_key_load_test.c index 271cdc0e085..0bf3f360905 100644 --- a/tests/unit/s2n_cert_chain_and_key_load_test.c +++ b/tests/unit/s2n_cert_chain_and_key_load_test.c @@ -1,171 +1,172 @@ -/* - * 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 "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Test each combination of s2n_pkey_types to validate that only keys of - * the same type can be compared */ - { - struct s2n_cert_chain_and_key *chain_and_key = NULL; - char rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Keys of the same type can be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS( - s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - /* Keys of different types cannot be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, ecdsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO( - s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_pss_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, ecdsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_pss_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - }; - - /* Test the same as above but with non null terminated chain and key and - * api that accepts length */ - { - struct s2n_cert_chain_and_key *chain_and_key = NULL; - uint8_t rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - uint32_t rsa_cert_chain_len = 0; - uint32_t rsa_pss_cert_chain_len = 0; - uint32_t ecdsa_cert_chain_len = 0; - uint32_t rsa_private_key_len = 0; - uint32_t rsa_pss_private_key_len = 0; - uint32_t ecdsa_private_key_len = 0; - - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, &rsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, &rsa_pss_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, &ecdsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, &rsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, &rsa_pss_private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, &ecdsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); - - /* Keys of the same type can be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS( - s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - /* Keys of different types cannot be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO( - s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test each combination of s2n_pkey_types to validate that only keys of + * the same type can be compared */ + { + struct s2n_cert_chain_and_key *chain_and_key = NULL; + char rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Keys of the same type can be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS( + s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + /* Keys of different types cannot be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, ecdsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO( + s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_pss_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, ecdsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_pss_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + }; + + /* Test the same as above but with non null terminated chain and key and + * api that accepts length */ + { + struct s2n_cert_chain_and_key *chain_and_key = NULL; + uint8_t rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + uint32_t rsa_cert_chain_len = 0; + uint32_t rsa_pss_cert_chain_len = 0; + uint32_t ecdsa_cert_chain_len = 0; + uint32_t rsa_private_key_len = 0; + uint32_t rsa_pss_private_key_len = 0; + uint32_t ecdsa_private_key_len = 0; + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, &rsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, &rsa_pss_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, &ecdsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, &rsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, &rsa_pss_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, &ecdsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Keys of the same type can be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS( + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + /* Keys of different types cannot be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO( + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_cert_chain_and_key_test.c b/tests/unit/s2n_cert_chain_and_key_test.c index f036cf665f1..7f322fe002b 100644 --- a/tests/unit/s2n_cert_chain_and_key_test.c +++ b/tests/unit/s2n_cert_chain_and_key_test.c @@ -1,228 +1,229 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -#define NUM_TIED_CERTS 100 - -struct s2n_connection *create_conn(s2n_mode mode, struct s2n_config *config) -{ - PTR_GUARD_RESULT(s2n_config_set_tls12_security_policy(config)); - struct s2n_connection *conn = s2n_connection_new(mode); - PTR_GUARD_POSIX(s2n_connection_set_config(conn, config)); - return conn; -} - -static int num_times_cb_executed = 0; -static struct s2n_cert_chain_and_key *test_cert_tiebreak_cb(struct s2n_cert_chain_and_key *cert1, - struct s2n_cert_chain_and_key *cert2, - uint8_t *name, - uint32_t name_len) -{ - const int priority1 = *((int *) s2n_cert_chain_and_key_get_ctx(cert1)); - const int priority2 = *((int *) s2n_cert_chain_and_key_get_ctx(cert2)); - num_times_cb_executed++; - return (priority1 > priority2 ? cert1 : cert2); -} - -int main(int argc, char **argv) -{ - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - char *alligator_cert = NULL; - char *alligator_key = NULL; - char *cert_chain = NULL; - char *private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(alligator_cert = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(alligator_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, alligator_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, alligator_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - /* Create config with s2n_config_add_cert_chain_and_key_to_store API with multiple certs */ - { - struct s2n_cert_chain_and_key *default_cert = NULL; - /* Associated data to attach to each certificate to use in the tiebreak callback. */ - int tiebreak_priorites[NUM_TIED_CERTS] = { 0 }; - /* Collection of certs with the same domain name that need to have ties resolved. */ - struct s2n_cert_chain_and_key *tied_certs[NUM_TIED_CERTS] = { NULL }; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cert_tiebreak_callback(server_config, test_cert_tiebreak_cb)); - - /* Need to add at least one cert with a different domain name to make cert lookup utilize hashmap */ - EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); - - /* Add NUM_TIED_CERTS that are actually the same certificate(www.alligator.com) to trigger the tiebreak callback. */ - for (unsigned int i = 0; i < NUM_TIED_CERTS; i++) { - EXPECT_NOT_NULL(tied_certs[i] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tied_certs[i], alligator_cert, alligator_key)); - tiebreak_priorites[i] = i; - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ctx(tied_certs[i], (void *) &tiebreak_priorites[i])); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tied_certs[i])); - } - - EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); - EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "www.alligator.com")); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_EQUAL(num_times_cb_executed, NUM_TIED_CERTS - 1); - struct s2n_cert_chain_and_key *selected_cert = s2n_connection_get_selected_cert(server_conn); - /* The last alligator certificate should have the highest priority */ - EXPECT_EQUAL(selected_cert, tied_certs[(NUM_TIED_CERTS - 1)]); - EXPECT_EQUAL(s2n_cert_chain_and_key_get_ctx(selected_cert), (void *) &tiebreak_priorites[(NUM_TIED_CERTS - 1)]); - EXPECT_EQUAL(*((int *) s2n_cert_chain_and_key_get_ctx(selected_cert)), NUM_TIED_CERTS - 1); - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - for (size_t i = 0; i < NUM_TIED_CERTS; i++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tied_certs[i])); - } - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Create config with deprecated s2n_config_add_cert_chain_and_key API */ - { - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - - EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); - EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Do not allow configs to call both - * s2n_config_add_cert_chain_and_key and s2n_config_add_cert_chain_and_key_to_store */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Config first uses s2n_config_add_cert_chain_and_key: library owns chain */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* Add first chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - /* Try to add second chain of same type */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), - S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - /* Try to add chain using other method */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key_to_store(config, chain), - S2N_ERR_CERT_OWNERSHIP); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - }; - - /* Config first uses s2n_config_add_cert_chain_and_key_to_store: application owns chain */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* Add first chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Add second chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Try to add chain using other method */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), - S2N_ERR_CERT_OWNERSHIP); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - }; - }; - - /* s2n_cert_chain_and_key_load_pem */ - { - /* when loading a chain, all certs have a info associated with them and root is self-signed */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&chain, "ec", "ecdsa", - "p384", "sha256")); - struct s2n_cert *leaf = chain->cert_chain->head; - EXPECT_EQUAL(leaf->info.self_signed, false); - EXPECT_EQUAL(leaf->info.signature_nid, NID_ecdsa_with_SHA256); - EXPECT_EQUAL(leaf->info.signature_digest_nid, NID_sha256); - - struct s2n_cert *intermediate = leaf->next; - EXPECT_NOT_NULL(intermediate); - EXPECT_EQUAL(intermediate->info.self_signed, false); - EXPECT_EQUAL(intermediate->info.signature_nid, NID_ecdsa_with_SHA256); - EXPECT_EQUAL(intermediate->info.signature_digest_nid, NID_sha256); - - struct s2n_cert *root = intermediate->next; - EXPECT_NOT_NULL(intermediate); - EXPECT_NULL(root->next); - EXPECT_EQUAL(root->info.self_signed, true); - EXPECT_EQUAL(root->info.signature_nid, NID_ecdsa_with_SHA256); - EXPECT_EQUAL(root->info.signature_digest_nid, NID_sha256); - }; - }; - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(cert_chain); - free(private_key); - free(alligator_cert); - free(alligator_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +#define NUM_TIED_CERTS 100 + +struct s2n_connection *create_conn(s2n_mode mode, struct s2n_config *config) +{ + PTR_GUARD_RESULT(s2n_config_set_tls12_security_policy(config)); + struct s2n_connection *conn = s2n_connection_new(mode); + PTR_GUARD_POSIX(s2n_connection_set_config(conn, config)); + return conn; +} + +static int num_times_cb_executed = 0; +static struct s2n_cert_chain_and_key *test_cert_tiebreak_cb(struct s2n_cert_chain_and_key *cert1, + struct s2n_cert_chain_and_key *cert2, + uint8_t *name, + uint32_t name_len) +{ + const int priority1 = *((int *) s2n_cert_chain_and_key_get_ctx(cert1)); + const int priority2 = *((int *) s2n_cert_chain_and_key_get_ctx(cert2)); + num_times_cb_executed++; + return (priority1 > priority2 ? cert1 : cert2); +} + +int main(int argc, char **argv) +{ + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + char *alligator_cert = NULL; + char *alligator_key = NULL; + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(alligator_cert = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(alligator_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, alligator_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, alligator_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + /* Create config with s2n_config_add_cert_chain_and_key_to_store API with multiple certs */ + { + struct s2n_cert_chain_and_key *default_cert = NULL; + /* Associated data to attach to each certificate to use in the tiebreak callback. */ + int tiebreak_priorites[NUM_TIED_CERTS] = { 0 }; + /* Collection of certs with the same domain name that need to have ties resolved. */ + struct s2n_cert_chain_and_key *tied_certs[NUM_TIED_CERTS] = { NULL }; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cert_tiebreak_callback(server_config, test_cert_tiebreak_cb)); + + /* Need to add at least one cert with a different domain name to make cert lookup utilize hashmap */ + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + + /* Add NUM_TIED_CERTS that are actually the same certificate(www.alligator.com) to trigger the tiebreak callback. */ + for (unsigned int i = 0; i < NUM_TIED_CERTS; i++) { + EXPECT_NOT_NULL(tied_certs[i] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tied_certs[i], alligator_cert, alligator_key)); + tiebreak_priorites[i] = i; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ctx(tied_certs[i], (void *) &tiebreak_priorites[i])); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tied_certs[i])); + } + + EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); + EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "www.alligator.com")); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(num_times_cb_executed, NUM_TIED_CERTS - 1); + struct s2n_cert_chain_and_key *selected_cert = s2n_connection_get_selected_cert(server_conn); + /* The last alligator certificate should have the highest priority */ + EXPECT_EQUAL(selected_cert, tied_certs[(NUM_TIED_CERTS - 1)]); + EXPECT_EQUAL(s2n_cert_chain_and_key_get_ctx(selected_cert), (void *) &tiebreak_priorites[(NUM_TIED_CERTS - 1)]); + EXPECT_EQUAL(*((int *) s2n_cert_chain_and_key_get_ctx(selected_cert)), NUM_TIED_CERTS - 1); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + for (size_t i = 0; i < NUM_TIED_CERTS; i++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tied_certs[i])); + } + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Create config with deprecated s2n_config_add_cert_chain_and_key API */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + + EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); + EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Do not allow configs to call both + * s2n_config_add_cert_chain_and_key and s2n_config_add_cert_chain_and_key_to_store */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Config first uses s2n_config_add_cert_chain_and_key: library owns chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Add first chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + /* Try to add second chain of same type */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + /* Try to add chain using other method */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key_to_store(config, chain), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + }; + + /* Config first uses s2n_config_add_cert_chain_and_key_to_store: application owns chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Add first chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Add second chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Try to add chain using other method */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + }; + }; + + /* s2n_cert_chain_and_key_load_pem */ + { + /* when loading a chain, all certs have a info associated with them and root is self-signed */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&chain, "ec", "ecdsa", + "p384", "sha256")); + struct s2n_cert *leaf = chain->cert_chain->head; + EXPECT_EQUAL(leaf->info.self_signed, false); + EXPECT_EQUAL(leaf->info.signature_nid, NID_ecdsa_with_SHA256); + EXPECT_EQUAL(leaf->info.signature_digest_nid, NID_sha256); + + struct s2n_cert *intermediate = leaf->next; + EXPECT_NOT_NULL(intermediate); + EXPECT_EQUAL(intermediate->info.self_signed, false); + EXPECT_EQUAL(intermediate->info.signature_nid, NID_ecdsa_with_SHA256); + EXPECT_EQUAL(intermediate->info.signature_digest_nid, NID_sha256); + + struct s2n_cert *root = intermediate->next; + EXPECT_NOT_NULL(intermediate); + EXPECT_NULL(root->next); + EXPECT_EQUAL(root->info.self_signed, true); + EXPECT_EQUAL(root->info.signature_nid, NID_ecdsa_with_SHA256); + EXPECT_EQUAL(root->info.signature_digest_nid, NID_sha256); + }; + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(cert_chain); + free(private_key); + free(alligator_cert); + free(alligator_key); + END_TEST(); +} diff --git a/tests/unit/s2n_cert_status_extension_test.c b/tests/unit/s2n_cert_status_extension_test.c index d385c702a49..75b72b07ad2 100644 --- a/tests/unit/s2n_cert_status_extension_test.c +++ b/tests/unit/s2n_cert_status_extension_test.c @@ -1,408 +1,409 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_cert_status.h" - -const uint8_t ocsp_data[] = "OCSP DATA"; - -int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) -{ - conn->status_type = S2N_STATUS_REQUEST_OCSP; - conn->handshake_params.our_chain_and_key = chain_and_key; - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); - conn->x509_validator.state = VALIDATED; - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* should_send */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Don't send by default */ - EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); - - /* Send if all prerequisites met */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); - - /* Send if client */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - conn->mode = S2N_CLIENT; - EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); - - /* Send if server */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - conn->mode = S2N_SERVER; - EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); - - /* Don't send if no certificate set */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - conn->handshake_params.our_chain_and_key = NULL; - EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); - - /* Don't send if no ocsp data */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->ocsp_status)); - EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test send */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); - - uint8_t request_type = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &request_type)); - EXPECT_EQUAL(request_type, S2N_STATUS_REQUEST_OCSP); - - uint32_t ocsp_size = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &ocsp_size)); - EXPECT_EQUAL(ocsp_size, s2n_stuffer_data_available(&stuffer)); - EXPECT_EQUAL(ocsp_size, s2n_array_len(ocsp_data)); - - uint8_t *actual_ocsp_data = NULL; - EXPECT_NOT_NULL(actual_ocsp_data = s2n_stuffer_raw_read(&stuffer, ocsp_size)); - EXPECT_BYTEARRAY_EQUAL(actual_ocsp_data, ocsp_data, ocsp_size); - - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* 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 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); - - EXPECT_EQUAL(conn->status_response.size, 0); - EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); - EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, ocsp_data, s2n_array_len(ocsp_data)); - - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test recv - not ocsp */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, S2N_STATUS_REQUEST_NONE)); - - EXPECT_EQUAL(conn->status_response.size, 0); - EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); - EXPECT_EQUAL(conn->status_response.size, 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test recv - bad ocsp data */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); - - if (s2n_x509_ocsp_stapling_supported()) { - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), - S2N_ERR_INVALID_OCSP_RESPONSE); - } else { - /* s2n_x509_validator_validate_cert_stapled_ocsp_response returns untrusted error if ocsp is not supported */ - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), - S2N_ERR_CERT_UNTRUSTED); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Self-talk tests */ - if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { - uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t ocsp_response_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_TRUE(ocsp_response_len > 0); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); - - /* Client requests OCSP staple, and server sends OCSP response */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - EXPECT_NOT_NULL(client_received_ocsp_response); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - /* Only the client requested a response, the server should not have received one. */ - EXPECT_NULL(server_received_ocsp_response); - - /* The server sent an OCSP response, and the client received an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - } - - /* Server requests OCSP staple, and client sends OCSP response */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); - - /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is - * being used as a client certificate. Intent verification is disabled to allow this - * certificate to be used as a client certificate anyway. - */ - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - /* Only the server requested a response, the client should not have received one. */ - EXPECT_NULL(client_received_ocsp_response); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - EXPECT_NOT_NULL(server_received_ocsp_response); - - /* The server did not send an OCSP response, and the client did not receive an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - } - - /* Client and server both request OCSP staples, and client and server both send responses */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); - - /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is - * being used as a client certificate. Intent verification is disabled to allow this - * certificate to be used as a client certificate anyway. - */ - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - EXPECT_NOT_NULL(client_received_ocsp_response); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - EXPECT_NOT_NULL(server_received_ocsp_response); - - /* The server sent an OCSP response, and the client received an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - } - - /* Server sets an OCSP response but client does not request OCSP stapling */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - - /* Both the server and client did not request OCSP responses, so neither should have received them. */ - EXPECT_NULL(client_received_ocsp_response); - EXPECT_NULL(server_received_ocsp_response); - - /* The server did not send an OCSP response, and the client did not receive an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_status.h" + +const uint8_t ocsp_data[] = "OCSP DATA"; + +int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) +{ + conn->status_type = S2N_STATUS_REQUEST_OCSP; + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); + conn->x509_validator.state = VALIDATED; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* should_send */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Don't send by default */ + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + /* Send if all prerequisites met */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Send if client */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_CLIENT; + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Send if server */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_SERVER; + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Don't send if no certificate set */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->handshake_params.our_chain_and_key = NULL; + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + /* Don't send if no ocsp data */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->ocsp_status)); + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + uint8_t request_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &request_type)); + EXPECT_EQUAL(request_type, S2N_STATUS_REQUEST_OCSP); + + uint32_t ocsp_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &ocsp_size)); + EXPECT_EQUAL(ocsp_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(ocsp_size, s2n_array_len(ocsp_data)); + + uint8_t *actual_ocsp_data = NULL; + EXPECT_NOT_NULL(actual_ocsp_data = s2n_stuffer_raw_read(&stuffer, ocsp_size)); + EXPECT_BYTEARRAY_EQUAL(actual_ocsp_data, ocsp_data, ocsp_size); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* 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 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); + EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, ocsp_data, s2n_array_len(ocsp_data)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - not ocsp */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, S2N_STATUS_REQUEST_NONE)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->status_response.size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - bad ocsp data */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + if (s2n_x509_ocsp_stapling_supported()) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), + S2N_ERR_INVALID_OCSP_RESPONSE); + } else { + /* s2n_x509_validator_validate_cert_stapled_ocsp_response returns untrusted error if ocsp is not supported */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), + S2N_ERR_CERT_UNTRUSTED); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Self-talk tests */ + if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { + uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t ocsp_response_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_TRUE(ocsp_response_len > 0); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); + + /* Client requests OCSP staple, and server sends OCSP response */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + EXPECT_NOT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + /* Only the client requested a response, the server should not have received one. */ + EXPECT_NULL(server_received_ocsp_response); + + /* The server sent an OCSP response, and the client received an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + } + + /* Server requests OCSP staple, and client sends OCSP response */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); + + /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is + * being used as a client certificate. Intent verification is disabled to allow this + * certificate to be used as a client certificate anyway. + */ + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + /* Only the server requested a response, the client should not have received one. */ + EXPECT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + EXPECT_NOT_NULL(server_received_ocsp_response); + + /* The server did not send an OCSP response, and the client did not receive an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + } + + /* Client and server both request OCSP staples, and client and server both send responses */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); + + /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is + * being used as a client certificate. Intent verification is disabled to allow this + * certificate to be used as a client certificate anyway. + */ + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + EXPECT_NOT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + EXPECT_NOT_NULL(server_received_ocsp_response); + + /* The server sent an OCSP response, and the client received an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + } + + /* Server sets an OCSP response but client does not request OCSP stapling */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + + /* Both the server and client did not request OCSP responses, so neither should have received them. */ + EXPECT_NULL(client_received_ocsp_response); + EXPECT_NULL(server_received_ocsp_response); + + /* The server did not send an OCSP response, and the client did not receive an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cert_validation_callback_test.c b/tests/unit/s2n_cert_validation_callback_test.c index 69b9c8294ef..7ffa74cdc49 100644 --- a/tests/unit/s2n_cert_validation_callback_test.c +++ b/tests/unit/s2n_cert_validation_callback_test.c @@ -1,576 +1,577 @@ -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_x509_validator.h" - -struct s2n_cert_validation_data { - unsigned call_accept_or_reject : 1; - unsigned accept : 1; - unsigned return_success : 1; - - int invoked_count; - struct s2n_cert_validation_info *info; -}; - -static int s2n_test_cert_validation_callback(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *ctx) -{ - struct s2n_cert_validation_data *data = (struct s2n_cert_validation_data *) ctx; - - data->invoked_count += 1; - /* Pass the `s2n_cert_validation_info` struct to application-defined `ctx` */ - data->info = info; - - int ret = S2N_FAILURE; - if (data->return_success) { - ret = S2N_SUCCESS; - } - - if (!data->call_accept_or_reject) { - return ret; - } - - if (data->accept) { - EXPECT_SUCCESS(s2n_cert_validation_accept(info)); - } else { - EXPECT_SUCCESS(s2n_cert_validation_reject(info)); - } - - return ret; -} - -static int s2n_test_cert_validation_callback_self_talk(struct s2n_connection *conn, - struct s2n_cert_validation_info *info, void *ctx) -{ - DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(peer_cert_chain); - - /* Ensure that the peer's certificate chain can be retrieved at the time the callback is invoked */ - EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(conn, peer_cert_chain)); - uint32_t peer_cert_chain_len = 0; - EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); - EXPECT_TRUE(peer_cert_chain_len > 0); - - return s2n_test_cert_validation_callback(conn, info, ctx); -} - -static int s2n_test_cert_validation_callback_self_talk_server(struct s2n_connection *conn, - struct s2n_cert_validation_info *info, void *ctx) -{ - /* Ensure that the callback was invoked on the server connection */ - EXPECT_EQUAL(conn->mode, S2N_SERVER); - - /* Ensure that the client's certificate chain can be retrieved at the time the callback was invoked */ - uint8_t *der_cert_chain = 0; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(conn, &der_cert_chain, &cert_chain_len)); - EXPECT_TRUE(cert_chain_len > 0); - - return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); -} - -static int s2n_test_cert_validation_callback_self_talk_ocsp(struct s2n_connection *conn, - struct s2n_cert_validation_info *info, void *ctx) -{ - /* Ensure that the OCSP response was received prior to invoking the callback */ - uint32_t ocsp_response_length = 0; - const uint8_t *ocsp_response = s2n_connection_get_ocsp_response(conn, &ocsp_response_length); - EXPECT_NOT_NULL(ocsp_response); - EXPECT_TRUE(ocsp_response_length > 0); - - return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* Accept/reject tests */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(NULL), S2N_ERR_NULL); - - /* Accept sets the proper state */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); - - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, true); - } - - /* Reject sets the proper state */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); - - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, false); - } - - /* Calls to accept/reject fail if accept has already been called */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); - - for (int i = 0; i < 10; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); - } - - /* State was updated from the successful call */ - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, true); - } - - /* Calls to accept/reject fail if reject has already been called */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); - - for (int i = 0; i < 10; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); - } - - /* State was updated from the successful call */ - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, false); - } - } - - /* Test s2n_cert_validation_callback */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* clang-format off */ - struct { - const struct s2n_cert_validation_data data; - s2n_error expected_error; - } test_cases[] = { - /* No error when accept is called from the callback */ - { - .data = { .call_accept_or_reject = true, .accept = true, .return_success = true }, - .expected_error = S2N_ERR_OK - }, - - /* Error if reject was called from the callback */ - { - .data = { .call_accept_or_reject = true, .accept = false, .return_success = true }, - .expected_error = S2N_ERR_CERT_REJECTED - }, - - /* Error if the callback doesn't return successfully */ - { - .data = { .call_accept_or_reject = true, .accept = true, .return_success = false }, - .expected_error = S2N_ERR_CANCELLED - }, - { - .data = { .call_accept_or_reject = true, .accept = false, .return_success = false }, - .expected_error = S2N_ERR_CANCELLED - }, - }; - /* clang-format on */ - - /* s2n_x509_validator test */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out)); - } else { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, - chain_len, &pkey_type, &public_key_out), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* The callback is invoked even if cert verification is disabled */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - /* Initialize the x509_validator with skip_cert_validation enabled */ - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out)); - } else { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, - chain_len, &pkey_type, &public_key_out), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Self-talk: callback is invoked on the client after receiving the server's certificate */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Self-talk: callback is invoked on the server after receiving the client's certificate */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, - s2n_test_cert_validation_callback_self_talk_server, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - 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)); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Self-talk: callback is invoked after an OCSP response is received in TLS 1.3 - * - * Currently, the cert validation callback is invoked after validating the certificate - * chain and after processing the Certificate message extensions. In TLS 1.3, the OCSP - * response is sent in a Certificate message extension, and should be accessible to the - * cert validation callback. - * - * In TLS 1.2, the OCSP response is sent in a separate CertificateStatus message which is - * received after the cert validation callback is invoked. So, OCSP information won't be - * accessible from the callback in TLS 1.2. - */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - if (!s2n_x509_ocsp_stapling_supported() || !s2n_is_tls13_fully_supported()) { - break; - } - - uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t ocsp_response_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, - S2N_MAX_TEST_PEM_SIZE)); - EXPECT_TRUE(ocsp_response_len > 0); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, - S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(client_config, - s2n_test_cert_validation_callback_self_talk_ocsp, &data)); - - 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, "s2n Test Cert")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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)); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* For async cases, accept or reject API will be called outside of the validation callback. - * Iterate over both TLS 1.3 and 1.2 policies to ensure the stuffer reset logic works in all cases. - */ - struct s2n_cert_validation_data async_test_cases[] = { - { .call_accept_or_reject = false, .accept = true, .return_success = true }, - { .call_accept_or_reject = false, .accept = false, .return_success = true }, - }; - const char *versions[] = { "20240501", "20170210" }; - - /* Async callback is invoked on the client after receiving the server's certificate */ - for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { - for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, versions[version_idx])); - - struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - for (int i = 0; i < 3; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Ensure that the server's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ - DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(peer_cert_chain); - EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, peer_cert_chain)); - /* Ensure the certificate chain is non-empty */ - uint32_t peer_cert_chain_len = 0; - EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); - EXPECT_TRUE(peer_cert_chain_len > 0); - - struct s2n_cert_validation_info *info = data.info; - EXPECT_NOT_NULL(info); - - if (async_test_cases[test_case_idx].accept) { - EXPECT_SUCCESS(s2n_cert_validation_accept(info)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_SUCCESS(s2n_cert_validation_reject(info)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_REJECTED); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - } - - /* Async callback is invoked on the server after receiving the client's certificate */ - for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { - for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, versions[version_idx])); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, - s2n_test_cert_validation_callback_self_talk_server, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, versions[version_idx])); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - 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)); - - for (int i = 0; i < 3; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Ensure that the client's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ - uint8_t *der_cert_chain = 0; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(server_conn, &der_cert_chain, &cert_chain_len)); - /* Ensure the certificate chain is non-empty */ - EXPECT_TRUE(cert_chain_len > 0); - - struct s2n_cert_validation_info *info = data.info; - EXPECT_NOT_NULL(info); - - if (async_test_cases[test_case_idx].accept) { - EXPECT_SUCCESS(s2n_cert_validation_accept(info)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_SUCCESS(s2n_cert_validation_reject(info)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_REJECTED); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_x509_validator.h" + +struct s2n_cert_validation_data { + unsigned call_accept_or_reject : 1; + unsigned accept : 1; + unsigned return_success : 1; + + int invoked_count; + struct s2n_cert_validation_info *info; +}; + +static int s2n_test_cert_validation_callback(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *ctx) +{ + struct s2n_cert_validation_data *data = (struct s2n_cert_validation_data *) ctx; + + data->invoked_count += 1; + /* Pass the `s2n_cert_validation_info` struct to application-defined `ctx` */ + data->info = info; + + int ret = S2N_FAILURE; + if (data->return_success) { + ret = S2N_SUCCESS; + } + + if (!data->call_accept_or_reject) { + return ret; + } + + if (data->accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + } + + return ret; +} + +static int s2n_test_cert_validation_callback_self_talk(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(peer_cert_chain); + + /* Ensure that the peer's certificate chain can be retrieved at the time the callback is invoked */ + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(conn, peer_cert_chain)); + uint32_t peer_cert_chain_len = 0; + EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); + EXPECT_TRUE(peer_cert_chain_len > 0); + + return s2n_test_cert_validation_callback(conn, info, ctx); +} + +static int s2n_test_cert_validation_callback_self_talk_server(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + /* Ensure that the callback was invoked on the server connection */ + EXPECT_EQUAL(conn->mode, S2N_SERVER); + + /* Ensure that the client's certificate chain can be retrieved at the time the callback was invoked */ + uint8_t *der_cert_chain = 0; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(conn, &der_cert_chain, &cert_chain_len)); + EXPECT_TRUE(cert_chain_len > 0); + + return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); +} + +static int s2n_test_cert_validation_callback_self_talk_ocsp(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + /* Ensure that the OCSP response was received prior to invoking the callback */ + uint32_t ocsp_response_length = 0; + const uint8_t *ocsp_response = s2n_connection_get_ocsp_response(conn, &ocsp_response_length); + EXPECT_NOT_NULL(ocsp_response); + EXPECT_TRUE(ocsp_response_length > 0); + + return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Accept/reject tests */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(NULL), S2N_ERR_NULL); + + /* Accept sets the proper state */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); + + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, true); + } + + /* Reject sets the proper state */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); + + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, false); + } + + /* Calls to accept/reject fail if accept has already been called */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); + + for (int i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); + } + + /* State was updated from the successful call */ + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, true); + } + + /* Calls to accept/reject fail if reject has already been called */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); + + for (int i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); + } + + /* State was updated from the successful call */ + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, false); + } + } + + /* Test s2n_cert_validation_callback */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* clang-format off */ + struct { + const struct s2n_cert_validation_data data; + s2n_error expected_error; + } test_cases[] = { + /* No error when accept is called from the callback */ + { + .data = { .call_accept_or_reject = true, .accept = true, .return_success = true }, + .expected_error = S2N_ERR_OK + }, + + /* Error if reject was called from the callback */ + { + .data = { .call_accept_or_reject = true, .accept = false, .return_success = true }, + .expected_error = S2N_ERR_CERT_REJECTED + }, + + /* Error if the callback doesn't return successfully */ + { + .data = { .call_accept_or_reject = true, .accept = true, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + { + .data = { .call_accept_or_reject = true, .accept = false, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + }; + /* clang-format on */ + + /* s2n_x509_validator test */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, + chain_len, &pkey_type, &public_key_out), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* The callback is invoked even if cert verification is disabled */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + /* Initialize the x509_validator with skip_cert_validation enabled */ + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, + chain_len, &pkey_type, &public_key_out), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked on the client after receiving the server's certificate */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked on the server after receiving the client's certificate */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, + s2n_test_cert_validation_callback_self_talk_server, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + 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)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked after an OCSP response is received in TLS 1.3 + * + * Currently, the cert validation callback is invoked after validating the certificate + * chain and after processing the Certificate message extensions. In TLS 1.3, the OCSP + * response is sent in a Certificate message extension, and should be accessible to the + * cert validation callback. + * + * In TLS 1.2, the OCSP response is sent in a separate CertificateStatus message which is + * received after the cert validation callback is invoked. So, OCSP information won't be + * accessible from the callback in TLS 1.2. + */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + if (!s2n_x509_ocsp_stapling_supported() || !s2n_is_tls13_fully_supported()) { + break; + } + + uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t ocsp_response_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, + S2N_MAX_TEST_PEM_SIZE)); + EXPECT_TRUE(ocsp_response_len > 0); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, + S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(client_config, + s2n_test_cert_validation_callback_self_talk_ocsp, &data)); + + 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, "s2n Test Cert")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* For async cases, accept or reject API will be called outside of the validation callback. + * Iterate over both TLS 1.3 and 1.2 policies to ensure the stuffer reset logic works in all cases. + */ + struct s2n_cert_validation_data async_test_cases[] = { + { .call_accept_or_reject = false, .accept = true, .return_success = true }, + { .call_accept_or_reject = false, .accept = false, .return_success = true }, + }; + const char *versions[] = { "20240501", "20170210" }; + + /* Async callback is invoked on the client after receiving the server's certificate */ + for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { + for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, versions[version_idx])); + + struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + for (int i = 0; i < 3; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Ensure that the server's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(peer_cert_chain); + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, peer_cert_chain)); + /* Ensure the certificate chain is non-empty */ + uint32_t peer_cert_chain_len = 0; + EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); + EXPECT_TRUE(peer_cert_chain_len > 0); + + struct s2n_cert_validation_info *info = data.info; + EXPECT_NOT_NULL(info); + + if (async_test_cases[test_case_idx].accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_REJECTED); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + } + + /* Async callback is invoked on the server after receiving the client's certificate */ + for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { + for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, versions[version_idx])); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, + s2n_test_cert_validation_callback_self_talk_server, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, versions[version_idx])); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + 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)); + + for (int i = 0; i < 3; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Ensure that the client's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ + uint8_t *der_cert_chain = 0; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(server_conn, &der_cert_chain, &cert_chain_len)); + /* Ensure the certificate chain is non-empty */ + EXPECT_TRUE(cert_chain_len > 0); + + struct s2n_cert_validation_info *info = data.info; + EXPECT_NOT_NULL(info); + + if (async_test_cases[test_case_idx].accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_REJECTED); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cipher_suite_match_test.c b/tests/unit/s2n_cipher_suite_match_test.c index f68117ea1d9..74305f680a5 100644 --- a/tests/unit/s2n_cipher_suite_match_test.c +++ b/tests/unit/s2n_cipher_suite_match_test.c @@ -1,1361 +1,1362 @@ -/* - * 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 "crypto/s2n_ecc_evp.h" -#include "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" - -static s2n_result s2n_conn_set_chosen_psk(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - uint8_t psk_identity[] = "psk identity"; - RESULT_GUARD(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &conn->psk_params.chosen_psk)); - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - RESULT_GUARD(s2n_psk_init(conn->psk_params.chosen_psk, S2N_PSK_TYPE_EXTERNAL)); - RESULT_GUARD_POSIX(s2n_psk_set_identity(conn->psk_params.chosen_psk, psk_identity, sizeof(psk_identity))); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, - S2N_MAX_TEST_PEM_SIZE)); - - /* Test client cipher selection */ - { - /* Setup connections */ - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - /* Setup config */ - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Test that the client allows the server to select ciphers that were offered in ClientHello */ - { - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - - /* The client will offer the default tls13 ciphersuites */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); - - /* The server will send a TLS13 cipher over the wire */ - uint8_t valid_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256 - }; - - /* We expect to succeed because the cipher was offered by the client */ - EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_wire_ciphers)); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test that the client rejects a cipher that was not originally offered in ClientHello */ - { - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - - /* The client will offer the default tls13 ciphersuites */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); - - /* The server will send a TLS12 cipher over the wire */ - uint8_t invalid_wire_ciphers[] = { - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - }; - - /* We expect to fail because the cipher was not offered by the client */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, invalid_wire_ciphers), S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /** Clients MUST verify - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *= type=test - *# that the server selected a cipher suite - *# indicating a Hash associated with the PSK - **/ - { - /* If chosen PSK is set, test error case for incorrect hash match */ - { - s2n_connection_set_cipher_preferences(conn, "default_tls13"); - - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - - uint8_t valid_tls13_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, - }; - - /* S2N_HMAC_SHA1 is not a matching hmac algorithm */ - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers), - S2N_ERR_CIPHER_NOT_SUPPORTED); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_null_cipher_suite); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* If chosen PSK is set, test success case for matching hash algorithm */ - { - s2n_connection_set_cipher_preferences(conn, "default_tls13"); - - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - - uint8_t valid_tls13_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, - }; - - /* S2N_HMAC_SHA256 is a matching hmac algorithm for the cipher suite present in valid_tls13_wire_ciphers */ - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; - EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test server cipher selection and scsv detection */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *server_config = NULL; - char *rsa_cert_chain_pem = NULL, *rsa_private_key_pem = NULL, *ecdsa_cert_chain_pem = NULL, *ecdsa_private_key_pem = NULL; - struct s2n_cert_chain_and_key *rsa_cert = NULL, *ecdsa_cert = NULL; - /* Allocate all of the objects and PEMs we'll need for this test. */ - EXPECT_NOT_NULL(rsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(rsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(ecdsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(ecdsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(rsa_cert = s2n_cert_chain_and_key_new()); - EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(rsa_cert, rsa_cert_chain_pem, rsa_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(ecdsa_cert, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); - - uint8_t wire_ciphers[] = { - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - }; - const uint8_t cipher_count = sizeof(wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; - - uint8_t wire_ciphers_fallback[] = { - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_FALLBACK_SCSV, /* At the end to verify it isn't missed */ - }; - const uint8_t cipher_count_fallback = sizeof(wire_ciphers_fallback) / S2N_TLS_CIPHER_SUITE_LEN; - - uint8_t wire_ciphers_renegotiation[] = { - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* At the end to verify it isn't missed */ - }; - const uint8_t cipher_count_renegotiation = sizeof(wire_ciphers_renegotiation) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Only two ciphers for testing RSA vs ECDSA. */ - uint8_t wire_ciphers_with_ecdsa[] = { - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - }; - const uint8_t cipher_count_ecdsa = sizeof(wire_ciphers_with_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Only ECDSA ciphers */ - uint8_t wire_ciphers_only_ecdsa[] = { - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - }; - const uint8_t cipher_count_only_ecdsa = sizeof(wire_ciphers_only_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; - - uint8_t wire_ciphers_rsa_fallback[] = { - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - }; - const uint8_t cipher_count_rsa_fallback = sizeof(wire_ciphers_rsa_fallback) / S2N_TLS_CIPHER_SUITE_LEN; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - /* Security policy must allow all test cipher suites */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - /* TEST RSA */ - conn->actual_protocol_version = S2N_TLS10; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); - EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* TEST RENEGOTIATION - * - *= https://www.rfc-editor.org/rfc/rfc5746#3.6 - *= type=test - *# o When a ClientHello is received, the server MUST check if it - *# includes the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, - *# set the secure_renegotiation flag to TRUE. - */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_renegotiation, cipher_count_renegotiation)); - EXPECT_EQUAL(conn->secure_renegotiation, 1); - EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); - EXPECT_EQUAL(-1, s2n_connection_is_valid_for_cipher_preferences(conn, "not_exist")); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* Simulate a TLSv11 client to trigger the fallback error */ - conn->actual_protocol_version = S2N_TLS11; - EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers_fallback, cipher_count_fallback)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); - EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2018")); - EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2019")); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* TEST RSA cipher chosen when ECDSA cipher is at top */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - - const struct s2n_ecc_preferences *ecc_pref = NULL; - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - EXPECT_NOT_NULL(ecc_pref); - - /* Assume default for negotiated curve. */ - /* Shouldn't be necessary unless the test fails, but we want the failure to be obvious. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - const struct s2n_cipher_suite *expected_rsa_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_rsa_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* Clean+free to setup for ECDSA tests */ - EXPECT_SUCCESS(s2n_config_free(server_config)); - - /* Set ECDSA CERT in s2n_config */ - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - EXPECT_NOT_NULL(ecc_pref); - - /* TEST ECDSA */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); - const struct s2n_cipher_suite *expected_ecdsa_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - /* Assume default for negotiated curve. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* TEST ECDSA cipher chosen when RSA cipher is at top */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - /* Assume default for negotiated curve. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - - /* TEST two certificates. Use two certs with different key types(RSA, ECDSA) and add them to a single - * s2n_config. - */ - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends both RSA and ECDSA ciphers, server only configures RSA ciphers, - * ECDSA + RSA cert is configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - /* 20170328 only supports RSA ciphers */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170328")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends both RSA and ECDSA ciphers, server only configures ECDSA ciphers, ECDSA + RSA cert is - * configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client only sends RSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is - * configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_rc4_128_md5; - if (!expected_wire_choice->available) { - expected_wire_choice = &s2n_rsa_with_3des_ede_cbc_sha; - } - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client only sends ECDSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is - * configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_only_ecdsa, cipher_count_only_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends ECDHE-ECDSA, RSA, ECDHE-RSA ciphers. Server prioritizes ECDSA but also supports RSA. - * No mutually supported elliptic curves between client and server. ECDSA + RSA cert is configured. - */ - { - /* If there are no shared elliptic curves, we must fall through to a cipher that supports RSA kx. - * This is the first RSA kx cipher that CloudFront-Upstream supports. - */ - const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_aes_256_gcm_sha384; - /* Selecting this preference list because it prioritizes ECDHE-ECDSA and ECDHE-RSA over plain RSA kx. */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "CloudFront-Upstream")); - /* No shared curve */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_rsa_fallback, cipher_count_rsa_fallback)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - EXPECT_SUCCESS(s2n_config_free(server_config)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - /* Override auto-chosen defaults with only RSA cert default. ECDSA still loaded, but not default. */ - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured, - * only RSA is default. Expect default RSA used instead of previous test that expects ECDSA for this case. */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Override auto-chosen defaults with only ECDSA cert default. RSA still loaded, but not default. */ - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &ecdsa_cert, 1)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured, - * only ECDSA is default. Expect default ECDSA used instead of previous test that expects RSA for this case. */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test override back to both RSA and ECDSA defaults. */ - struct s2n_cert_chain_and_key *certs_list[] = { rsa_cert, ecdsa_cert }; - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, certs_list, 2)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test that defaults are not overriden after failures to set new default certificates */ - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, NULL, 0), S2N_ERR_NULL); - EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NULL"), 0); - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 0), - S2N_ERR_NUM_DEFAULT_CERTIFICATES); - EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NUM_DEFAULT_CERTIFICATES"), 0); - struct s2n_cert_chain_and_key *rsa_certs_list[] = { rsa_cert, rsa_cert }; - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, rsa_certs_list, 2), - S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE"), 0); - - /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured. - * RSA default certificate should be chosen. */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - struct s2n_cipher_suite *tls12_cipher_suite = cipher_preferences_20170210.suites[cipher_preferences_20170210.count - 1]; - uint8_t wire_ciphers_with_tls13[] = { - TLS_AES_128_GCM_SHA256, - TLS_AES_256_GCM_SHA384, - TLS_CHACHA20_POLY1305_SHA256, - tls12_cipher_suite->iana_value[0], tls12_cipher_suite->iana_value[1] - }; - const uint8_t cipher_count_tls13 = sizeof(wire_ciphers_with_tls13) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Client sends TLS1.3 cipher suites, but server does not support TLS1.3 */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite, tls12_cipher_suite); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends TLS1.3 cipher suites, server selects correct TLS1.3 ciphersuite */ - if (s2n_is_tls13_fully_supported()) { - struct test_case { - const char *cipher_pref; - const struct s2n_cipher_suite *expected_cipher_wire; - }; - - struct test_case test_cases[] = { - { .cipher_pref = "default_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, - { .cipher_pref = "test_all", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, - { .cipher_pref = "test_all_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].cipher_pref)); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite, test_cases[i].expected_cipher_wire); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - } - } - - /* Check wire's cipher suites with preferred tls12 ordering does not affect tls13 selection */ - { - uint8_t wire_ciphers2[] = { - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - TLS_CHACHA20_POLY1305_SHA256, /* tls 1.3 */ - }; - - const uint8_t count = sizeof(wire_ciphers2) / S2N_TLS_CIPHER_SUITE_LEN; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - - if (s2n_chacha20_poly1305.is_available()) { - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); - } else { - EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); - } - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test cipher suite with a required version higher than what connection supports should not be selected */ - { - uint8_t test_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - }; - - const uint8_t count = sizeof(test_wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->actual_protocol_version = S2N_TLS12; - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, count)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* We should skip cipher suites with a minimum protocol version unsupported by the connection. - * If no valid cipher suite is found, we should fall back to a cipher suite with a higher protocol version, - * but we should NEVER use a TLS1.3 suite on a pre-TLS1.3 connection or vice versa. */ - { - /* Skip but fall back to cipher suite with protocol version higher than connection */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - uint8_t test_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* ssl v3 */ - }; - - conn->actual_protocol_version = S2N_TLS10; - - /* If a match exists, skip the invalid cipher and choose it */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 3)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_cbc_sha); - - /* If a match does not exist, choose the invalid cipher */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Skip and do NOT fall back to a TLS1.3 cipher suite if using TLS1.2 */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - uint8_t test_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - }; - - conn->actual_protocol_version = S2N_TLS12; - - /* If a match exists, skip the invalid cipher and choose it */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); - - /* If a match does not exist, fail to negotiate a cipher suite. - * We cannot fall back to the TLS1.3 choice. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - } - - /* Skip and do NOT fall back to a TLS1.2 cipher suite if using TLS1.3 */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - uint8_t test_wire_ciphers[] = { - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - }; - - conn->actual_protocol_version = S2N_TLS13; - - /* If a match exists, skip the invalid cipher and choose it */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - - /* If a match does not exist, fail to negotiate a cipher suite. - * We cannot fall back to the TLS1.2 choice. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - } - }; - - /* If a PSK is being used, then the cipher suite must match the PSK's HMAC algorithm. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *= type=test - *# The server MUST ensure that it selects a compatible PSK - *# (if any) and cipher suite. - **/ - { - /* If chosen PSK is set, a cipher suite with matching HMAC algorithm must be selected */ - { - s2n_connection_set_cipher_preferences(conn, "test_all"); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->actual_protocol_version = S2N_TLS13; - - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); - - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA384; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* If chosen PSK is set but there is no matching cipher, the server MUST fail to set a cipher */ - { - s2n_connection_set_cipher_preferences(conn, "test_all"); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->actual_protocol_version = S2N_TLS13; - - /* S2N_HMAC_SHA1 is not a matching HMAC algorithm for any TLS1.3 cipher */ - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; - - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - }; - - /* Client sends cipher which is not in the configured suite */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - uint8_t invalid_cipher_pref[] = { - TLS_NULL_WITH_NULL_NULL - }; - - const uint8_t invalid_cipher_count = sizeof(invalid_cipher_pref) / S2N_TLS_CIPHER_SUITE_LEN; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, invalid_cipher_pref, invalid_cipher_count), S2N_ERR_CIPHER_NOT_SUPPORTED); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Client sends cipher that requires DH params */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - /* The client only offers one cipher suite, which requires dh kex */ - uint8_t wire[] = { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 }; - - /* By default, the server does not accept cipher suites with dh kex. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(server, wire, 1), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - /* With dh params configured, the server accepts cipher suites with dh kex. */ - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(server, wire, 1)); - EXPECT_EQUAL(server->secure->cipher_suite, &s2n_dhe_rsa_with_aes_128_gcm_sha256); - }; - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); - free(ecdsa_cert_chain_pem); - free(ecdsa_private_key_pem); - free(rsa_cert_chain_pem); - free(rsa_private_key_pem); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test chacha20 boosting behaviour */ - { - /* Setup cipher preferences + security policy */ - struct s2n_cipher_preferences cipher_preferences = { 0 }; - struct s2n_security_policy security_policy = { - .minimum_protocol_version = S2N_SSLv2, - .cipher_preferences = &cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20201021, - .ecc_preferences = &s2n_ecc_preferences_test_all, - }; - - /* Initialise config and relevant certs */ - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - struct s2n_cert_chain_and_key *rsa_cert = NULL; - struct s2n_cert_chain_and_key *ecdsa_cert = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(rsa_cert); - EXPECT_NOT_NULL(ecdsa_cert); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - - if (s2n_chacha20_poly1305.is_available()) { - /* Test chacha20 boosting when ciphersuites fail auth validation */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - DEFER_CLEANUP(struct s2n_config *rsa_only_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(rsa_only_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_only_config, rsa_cert)); - /* Connection only supports rsa auth. */ - EXPECT_SUCCESS(s2n_connection_set_config(connection, rsa_only_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Not negotiated because invalid (ecdsa) */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - /* Only negotiated if chacha20 boosting is disabled */ - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - /* First valid chacha20 cipher suite and is negotiated */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting; valid chacha20 ciphersuite and is negotiated */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Not negotiated because invalid (ecdsa) */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify chacha20 RSA ciphersuite chosen with chacha20 boosting enabled */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: non-chacha20 RSA ciphersuite chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_gcm_sha384); - }; - - /* Server is able to negotiate its most preferred chacha20 ciphersuite */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Skipped because it is not a chacha20 ciphersuite. Is negotiated if chacha20 boosting is disabled. */ - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - /* First chacha20 ciphersuite and is negotiated */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - /* Second chacha20 ciphersuite and is not negotiated (not server's most preferred chacha20 ciphersuite) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting: not negotiated because it's not server's most preferred chacha20 ciphersuite */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is on */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify most preferred chacha20 ciphersuite is chosen with chacha20 boosting enabled */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred cipehrsuite is chosen when chacha20 boosting is off */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); - }; - - /* Server's most preferred chacha20 is not offered by the client */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Server's most preferred chacha20 ciphersuite; not offered by the client */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - /* Negotiated if chacha20 boosting is off */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - /* First valid chacha20 ciphersuite; negotiated if chacha20 boosting is on */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting; negotiated if chacha20 boosting is on */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - /* Verify server selects its second most preferred chacha20 ciphersuite */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256); - }; - - /* Server does not negotiate the client's most preferred chacha20 ciphersuite */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Skipped because not a chacha20 ciphersuite. If chacha20 boosting is off then this is negotiated.*/ - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - /* First chacha20 ciphersuite and is negotiated (client's second preferred ciphersuite) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - /* Second chacha20 ciphersuite and is the client's most preferred ciphersuite. Not negotiated. */ - &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. This is not negotiated as it's not the server's most preferred chacha20 ciphersuite */ - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is on */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify client's second preferred (ecdhe_rsa_chacha20) is negotiated when chacha20 boosting enabled */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); - }; - - /* Chacha20 boosting is disabled when client did not indicate chacha20 preference */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Negotiated because client did not signal chacha20 boosting and it is present in client wire */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - /* Never considered; if client did signal chacha20 boosting we expect this to be negotiated */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Chacha20 boosting is off. This ciphersuite is negotiated. */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - /* Not negotiated */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that client negotiates non-chacha20 ciphersuite when chacha20 boosting is not signalled by client */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); - }; - - /* Server negotiates its most preferred chacha20 ciphersuite for tls 1.3 */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Most preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ - &s2n_tls13_aes_128_gcm_sha256, - /* Second preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ - &s2n_tls13_aes_256_gcm_sha384, - /* Negotiated if chacha20 boosting behaviour is on. Otherwise, one of the two ciphersuites above is choosen. */ - &s2n_tls13_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t test_wire_1[] = { - /* Client signalled chacha20 boosting. Negotiated. */ - TLS_CHACHA20_POLY1305_SHA256, - /* Not negotiated even when chacha20 boosting is off. Server prefers aes 128 gcm. */ - TLS_AES_256_GCM_SHA384, - /* Negotiated if chacha20 boosting is off. This is the server's most preferred ciphersuite. */ - TLS_AES_128_GCM_SHA256, - }; - uint8_t count = sizeof(test_wire_1) / S2N_TLS_CIPHER_SUITE_LEN; - - cipher_preferences.allow_chacha20_boosting = true; - /* Verify that client negotiates chacha20 when chacha20 boosting is on */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - - /* - * Client did not signal chacha20 boosting. - * TLS_AES_256_GCM_SHA384 > TLS_CHACHA20_POLY1305_SHA256 - */ - uint8_t test_wire_2[] = { - /* Client did not signal chacha20 boosting. Negotiated if chacha20 boosting is off. */ - TLS_AES_256_GCM_SHA384, - /* Not negotiated since the server prefers aes 256 gcm over chacha20 */ - TLS_CHACHA20_POLY1305_SHA256, - }; - count = sizeof(test_wire_2) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that client negotiates server preferred non-chacha20 aes_256 when client did not signal */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_2, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_256_gcm_sha384); - }; - - /* Server can negotiate chacha20 ciphersuite, even when boosting is disabled */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Negotiated (only available option) */ - &s2n_tls13_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = false, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Negotiated as this is the only negotiable option. */ - TLS_CHACHA20_POLY1305_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify server can still negotiate chacha20 if it's the only option even when chacha20 boosting is off */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); - }; - - /* - * sslv2 server correctly negotiates ciphersuite when chacha20 boosting is enabled. - */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_SSLv2)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* First 'valid' ciphersuite that is saved as a higher_vers_match and is negotiated */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ - &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ - &s2n_dhe_rsa_with_aes_256_gcm_sha384 - }; - - /* cppcheck-suppress redundantAssignment */ - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Not negotiated as it's not offered by the server. */ - 0x00, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Not saved as a higher_vers_match as ecdhe_ecdsa_aes_128 was already saved as one; not negotiated */ - 0x00, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Saved by server as higher_vers_match; negotiated as a 'fall-back' ciphersuite */ - 0x00, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - }; - uint8_t count = sizeof(wire) / S2N_SSLv2_CIPHER_SUITE_LEN; - - /* Verify server negotiate its higher_vers_match ecdhe_ecdsa_aes_128_cbc instead of a chacha20 ciphersuite */ - cipher_preferences.allow_chacha20_boosting = true; - /* - * For an sslv2 connection, all of the ciphersuites in test_cipher_suite_list will not meet the minimum - * required tls version validation (they all require at least sslv3). This means that the server will save this - * cipher as a higher_vers_match and will use this ciphersuite as a fallback if no other ciphersuite can be identified. - * When this logic happens, we bypass chacha20 boosting altogether; therefore because the first ciphersuite - * ecdhe_ecdsa_with_aes_128_cbc is offered by both the client and server and only fails the minimum required version check, - * then we save this ciphersuite as a higher_vers_match and continue. - */ - EXPECT_SUCCESS(s2n_set_cipher_as_sslv2_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); - }; - - /* Server is able to negotiate a ciphersuite even without chacha20 in its cipher pref */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Negotiated because it is the server's most preferred */ - &s2n_tls13_aes_128_gcm_sha256, - /* Not considered */ - &s2n_tls13_aes_256_gcm_sha384, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting; not negotiated as server does not have any chacha20 ciphersuites */ - TLS_CHACHA20_POLY1305_SHA256, - /* Not negotiated as the server prefers aes 128 over aes 256 */ - TLS_AES_256_GCM_SHA384, - /* Negotiated */ - TLS_AES_128_GCM_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that server is able to negotiate aes_128 even without any chacha20 ciphersuites in its preferences */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test chacha20 boosting when the most preferred ciphersuite fails version validation */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Only negotiated if chacha20 boosting is off */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - /* Invalid; never use tls 1.3 ciphers on pre-tls 1.3 connections */ - &s2n_tls13_chacha20_poly1305_sha256, - /* Negotiated (if chacha20 boosting is on) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Not negotiated by server since it is invalid for the connection. */ - TLS_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - /* Negotiated if chacha20 boosting is on */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify server negotiates second preferred chacha20 ciphersuite ecdhe_rsa_chacha20 */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); - }; - } - - if (!s2n_chacha20_poly1305.is_available()) { - /* Chacha20 can't be negotiated when it's not available in libcrypto */ - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Invalid (no libcrypto) */ - &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - /* Negotiated (only valid option) */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - /* Not considered + invalid (no libcrypto) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Not negotiated because of missing libcrypto for chacha20 */ - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Not negotiated because of missing libcrypto for chacha20 */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated whether chacha20 boosting is enabled or not by server */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that server negotiated non-chacha20 ciphersuite ecdhe_ecdsa_aes_128 */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); - } - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_ecc_evp.h" +#include "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" + +static s2n_result s2n_conn_set_chosen_psk(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + uint8_t psk_identity[] = "psk identity"; + RESULT_GUARD(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &conn->psk_params.chosen_psk)); + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + RESULT_GUARD(s2n_psk_init(conn->psk_params.chosen_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(conn->psk_params.chosen_psk, psk_identity, sizeof(psk_identity))); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, + S2N_MAX_TEST_PEM_SIZE)); + + /* Test client cipher selection */ + { + /* Setup connections */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Test that the client allows the server to select ciphers that were offered in ClientHello */ + { + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + /* The client will offer the default tls13 ciphersuites */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + + /* The server will send a TLS13 cipher over the wire */ + uint8_t valid_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256 + }; + + /* We expect to succeed because the cipher was offered by the client */ + EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_wire_ciphers)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test that the client rejects a cipher that was not originally offered in ClientHello */ + { + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + /* The client will offer the default tls13 ciphersuites */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); + + /* The server will send a TLS12 cipher over the wire */ + uint8_t invalid_wire_ciphers[] = { + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + + /* We expect to fail because the cipher was not offered by the client */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, invalid_wire_ciphers), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /** Clients MUST verify + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# that the server selected a cipher suite + *# indicating a Hash associated with the PSK + **/ + { + /* If chosen PSK is set, test error case for incorrect hash match */ + { + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + uint8_t valid_tls13_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, + }; + + /* S2N_HMAC_SHA1 is not a matching hmac algorithm */ + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers), + S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_null_cipher_suite); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* If chosen PSK is set, test success case for matching hash algorithm */ + { + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + uint8_t valid_tls13_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, + }; + + /* S2N_HMAC_SHA256 is a matching hmac algorithm for the cipher suite present in valid_tls13_wire_ciphers */ + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test server cipher selection and scsv detection */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *server_config = NULL; + char *rsa_cert_chain_pem = NULL, *rsa_private_key_pem = NULL, *ecdsa_cert_chain_pem = NULL, *ecdsa_private_key_pem = NULL; + struct s2n_cert_chain_and_key *rsa_cert = NULL, *ecdsa_cert = NULL; + /* Allocate all of the objects and PEMs we'll need for this test. */ + EXPECT_NOT_NULL(rsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(rsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(rsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(rsa_cert, rsa_cert_chain_pem, rsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(ecdsa_cert, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); + + uint8_t wire_ciphers[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + const uint8_t cipher_count = sizeof(wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_fallback[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_FALLBACK_SCSV, /* At the end to verify it isn't missed */ + }; + const uint8_t cipher_count_fallback = sizeof(wire_ciphers_fallback) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_renegotiation[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* At the end to verify it isn't missed */ + }; + const uint8_t cipher_count_renegotiation = sizeof(wire_ciphers_renegotiation) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Only two ciphers for testing RSA vs ECDSA. */ + uint8_t wire_ciphers_with_ecdsa[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + }; + const uint8_t cipher_count_ecdsa = sizeof(wire_ciphers_with_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Only ECDSA ciphers */ + uint8_t wire_ciphers_only_ecdsa[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + }; + const uint8_t cipher_count_only_ecdsa = sizeof(wire_ciphers_only_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_rsa_fallback[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + }; + const uint8_t cipher_count_rsa_fallback = sizeof(wire_ciphers_rsa_fallback) / S2N_TLS_CIPHER_SUITE_LEN; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + /* Security policy must allow all test cipher suites */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* TEST RSA */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST RENEGOTIATION + * + *= https://www.rfc-editor.org/rfc/rfc5746#3.6 + *= type=test + *# o When a ClientHello is received, the server MUST check if it + *# includes the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, + *# set the secure_renegotiation flag to TRUE. + */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_renegotiation, cipher_count_renegotiation)); + EXPECT_EQUAL(conn->secure_renegotiation, 1); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); + EXPECT_EQUAL(-1, s2n_connection_is_valid_for_cipher_preferences(conn, "not_exist")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Simulate a TLSv11 client to trigger the fallback error */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers_fallback, cipher_count_fallback)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2018")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2019")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST RSA cipher chosen when ECDSA cipher is at top */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Assume default for negotiated curve. */ + /* Shouldn't be necessary unless the test fails, but we want the failure to be obvious. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + const struct s2n_cipher_suite *expected_rsa_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_rsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Clean+free to setup for ECDSA tests */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* Set ECDSA CERT in s2n_config */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* TEST ECDSA */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); + const struct s2n_cipher_suite *expected_ecdsa_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + /* Assume default for negotiated curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST ECDSA cipher chosen when RSA cipher is at top */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + /* Assume default for negotiated curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* TEST two certificates. Use two certs with different key types(RSA, ECDSA) and add them to a single + * s2n_config. + */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends both RSA and ECDSA ciphers, server only configures RSA ciphers, + * ECDSA + RSA cert is configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + /* 20170328 only supports RSA ciphers */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170328")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends both RSA and ECDSA ciphers, server only configures ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client only sends RSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_rc4_128_md5; + if (!expected_wire_choice->available) { + expected_wire_choice = &s2n_rsa_with_3des_ede_cbc_sha; + } + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client only sends ECDSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_only_ecdsa, cipher_count_only_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends ECDHE-ECDSA, RSA, ECDHE-RSA ciphers. Server prioritizes ECDSA but also supports RSA. + * No mutually supported elliptic curves between client and server. ECDSA + RSA cert is configured. + */ + { + /* If there are no shared elliptic curves, we must fall through to a cipher that supports RSA kx. + * This is the first RSA kx cipher that CloudFront-Upstream supports. + */ + const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_aes_256_gcm_sha384; + /* Selecting this preference list because it prioritizes ECDHE-ECDSA and ECDHE-RSA over plain RSA kx. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "CloudFront-Upstream")); + /* No shared curve */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_rsa_fallback, cipher_count_rsa_fallback)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + EXPECT_SUCCESS(s2n_config_free(server_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + /* Override auto-chosen defaults with only RSA cert default. ECDSA still loaded, but not default. */ + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured, + * only RSA is default. Expect default RSA used instead of previous test that expects ECDSA for this case. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Override auto-chosen defaults with only ECDSA cert default. RSA still loaded, but not default. */ + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &ecdsa_cert, 1)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured, + * only ECDSA is default. Expect default ECDSA used instead of previous test that expects RSA for this case. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test override back to both RSA and ECDSA defaults. */ + struct s2n_cert_chain_and_key *certs_list[] = { rsa_cert, ecdsa_cert }; + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, certs_list, 2)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test that defaults are not overriden after failures to set new default certificates */ + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, NULL, 0), S2N_ERR_NULL); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NULL"), 0); + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 0), + S2N_ERR_NUM_DEFAULT_CERTIFICATES); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NUM_DEFAULT_CERTIFICATES"), 0); + struct s2n_cert_chain_and_key *rsa_certs_list[] = { rsa_cert, rsa_cert }; + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, rsa_certs_list, 2), + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE"), 0); + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured. + * RSA default certificate should be chosen. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + struct s2n_cipher_suite *tls12_cipher_suite = cipher_preferences_20170210.suites[cipher_preferences_20170210.count - 1]; + uint8_t wire_ciphers_with_tls13[] = { + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + tls12_cipher_suite->iana_value[0], tls12_cipher_suite->iana_value[1] + }; + const uint8_t cipher_count_tls13 = sizeof(wire_ciphers_with_tls13) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Client sends TLS1.3 cipher suites, but server does not support TLS1.3 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite, tls12_cipher_suite); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends TLS1.3 cipher suites, server selects correct TLS1.3 ciphersuite */ + if (s2n_is_tls13_fully_supported()) { + struct test_case { + const char *cipher_pref; + const struct s2n_cipher_suite *expected_cipher_wire; + }; + + struct test_case test_cases[] = { + { .cipher_pref = "default_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher_pref = "test_all", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher_pref = "test_all_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].cipher_pref)); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite, test_cases[i].expected_cipher_wire); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + } + + /* Check wire's cipher suites with preferred tls12 ordering does not affect tls13 selection */ + { + uint8_t wire_ciphers2[] = { + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_CHACHA20_POLY1305_SHA256, /* tls 1.3 */ + }; + + const uint8_t count = sizeof(wire_ciphers2) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + } else { + EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); + } + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test cipher suite with a required version higher than what connection supports should not be selected */ + { + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + }; + + const uint8_t count = sizeof(test_wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->actual_protocol_version = S2N_TLS12; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, count)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* We should skip cipher suites with a minimum protocol version unsupported by the connection. + * If no valid cipher suite is found, we should fall back to a cipher suite with a higher protocol version, + * but we should NEVER use a TLS1.3 suite on a pre-TLS1.3 connection or vice versa. */ + { + /* Skip but fall back to cipher suite with protocol version higher than connection */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* ssl v3 */ + }; + + conn->actual_protocol_version = S2N_TLS10; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 3)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_cbc_sha); + + /* If a match does not exist, choose the invalid cipher */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Skip and do NOT fall back to a TLS1.3 cipher suite if using TLS1.2 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + }; + + conn->actual_protocol_version = S2N_TLS12; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + + /* If a match does not exist, fail to negotiate a cipher suite. + * We cannot fall back to the TLS1.3 choice. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + + /* Skip and do NOT fall back to a TLS1.2 cipher suite if using TLS1.3 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + }; + + conn->actual_protocol_version = S2N_TLS13; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + /* If a match does not exist, fail to negotiate a cipher suite. + * We cannot fall back to the TLS1.2 choice. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + }; + + /* If a PSK is being used, then the cipher suite must match the PSK's HMAC algorithm. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# The server MUST ensure that it selects a compatible PSK + *# (if any) and cipher suite. + **/ + { + /* If chosen PSK is set, a cipher suite with matching HMAC algorithm must be selected */ + { + s2n_connection_set_cipher_preferences(conn, "test_all"); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); + + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA384; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* If chosen PSK is set but there is no matching cipher, the server MUST fail to set a cipher */ + { + s2n_connection_set_cipher_preferences(conn, "test_all"); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->actual_protocol_version = S2N_TLS13; + + /* S2N_HMAC_SHA1 is not a matching HMAC algorithm for any TLS1.3 cipher */ + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + }; + + /* Client sends cipher which is not in the configured suite */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + uint8_t invalid_cipher_pref[] = { + TLS_NULL_WITH_NULL_NULL + }; + + const uint8_t invalid_cipher_count = sizeof(invalid_cipher_pref) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, invalid_cipher_pref, invalid_cipher_count), S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Client sends cipher that requires DH params */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* The client only offers one cipher suite, which requires dh kex */ + uint8_t wire[] = { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 }; + + /* By default, the server does not accept cipher suites with dh kex. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(server, wire, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + /* With dh params configured, the server accepts cipher suites with dh kex. */ + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(server, wire, 1)); + EXPECT_EQUAL(server->secure->cipher_suite, &s2n_dhe_rsa_with_aes_128_gcm_sha256); + }; + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + free(ecdsa_cert_chain_pem); + free(ecdsa_private_key_pem); + free(rsa_cert_chain_pem); + free(rsa_private_key_pem); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test chacha20 boosting behaviour */ + { + /* Setup cipher preferences + security policy */ + struct s2n_cipher_preferences cipher_preferences = { 0 }; + struct s2n_security_policy security_policy = { + .minimum_protocol_version = S2N_SSLv2, + .cipher_preferences = &cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20201021, + .ecc_preferences = &s2n_ecc_preferences_test_all, + }; + + /* Initialise config and relevant certs */ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + struct s2n_cert_chain_and_key *rsa_cert = NULL; + struct s2n_cert_chain_and_key *ecdsa_cert = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(rsa_cert); + EXPECT_NOT_NULL(ecdsa_cert); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + + if (s2n_chacha20_poly1305.is_available()) { + /* Test chacha20 boosting when ciphersuites fail auth validation */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + DEFER_CLEANUP(struct s2n_config *rsa_only_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(rsa_only_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_only_config, rsa_cert)); + /* Connection only supports rsa auth. */ + EXPECT_SUCCESS(s2n_connection_set_config(connection, rsa_only_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Not negotiated because invalid (ecdsa) */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Only negotiated if chacha20 boosting is disabled */ + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + /* First valid chacha20 cipher suite and is negotiated */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; valid chacha20 ciphersuite and is negotiated */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not negotiated because invalid (ecdsa) */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify chacha20 RSA ciphersuite chosen with chacha20 boosting enabled */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: non-chacha20 RSA ciphersuite chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_gcm_sha384); + }; + + /* Server is able to negotiate its most preferred chacha20 ciphersuite */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Skipped because it is not a chacha20 ciphersuite. Is negotiated if chacha20 boosting is disabled. */ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + /* First chacha20 ciphersuite and is negotiated */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Second chacha20 ciphersuite and is not negotiated (not server's most preferred chacha20 ciphersuite) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting: not negotiated because it's not server's most preferred chacha20 ciphersuite */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify most preferred chacha20 ciphersuite is chosen with chacha20 boosting enabled */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred cipehrsuite is chosen when chacha20 boosting is off */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); + }; + + /* Server's most preferred chacha20 is not offered by the client */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Server's most preferred chacha20 ciphersuite; not offered by the client */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Negotiated if chacha20 boosting is off */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + /* First valid chacha20 ciphersuite; negotiated if chacha20 boosting is on */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + /* Verify server selects its second most preferred chacha20 ciphersuite */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256); + }; + + /* Server does not negotiate the client's most preferred chacha20 ciphersuite */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Skipped because not a chacha20 ciphersuite. If chacha20 boosting is off then this is negotiated.*/ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + /* First chacha20 ciphersuite and is negotiated (client's second preferred ciphersuite) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + /* Second chacha20 ciphersuite and is the client's most preferred ciphersuite. Not negotiated. */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. This is not negotiated as it's not the server's most preferred chacha20 ciphersuite */ + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify client's second preferred (ecdhe_rsa_chacha20) is negotiated when chacha20 boosting enabled */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); + }; + + /* Chacha20 boosting is disabled when client did not indicate chacha20 preference */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated because client did not signal chacha20 boosting and it is present in client wire */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Never considered; if client did signal chacha20 boosting we expect this to be negotiated */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Chacha20 boosting is off. This ciphersuite is negotiated. */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + /* Not negotiated */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that client negotiates non-chacha20 ciphersuite when chacha20 boosting is not signalled by client */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + }; + + /* Server negotiates its most preferred chacha20 ciphersuite for tls 1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Most preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ + &s2n_tls13_aes_128_gcm_sha256, + /* Second preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ + &s2n_tls13_aes_256_gcm_sha384, + /* Negotiated if chacha20 boosting behaviour is on. Otherwise, one of the two ciphersuites above is choosen. */ + &s2n_tls13_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t test_wire_1[] = { + /* Client signalled chacha20 boosting. Negotiated. */ + TLS_CHACHA20_POLY1305_SHA256, + /* Not negotiated even when chacha20 boosting is off. Server prefers aes 128 gcm. */ + TLS_AES_256_GCM_SHA384, + /* Negotiated if chacha20 boosting is off. This is the server's most preferred ciphersuite. */ + TLS_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(test_wire_1) / S2N_TLS_CIPHER_SUITE_LEN; + + cipher_preferences.allow_chacha20_boosting = true; + /* Verify that client negotiates chacha20 when chacha20 boosting is on */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + /* + * Client did not signal chacha20 boosting. + * TLS_AES_256_GCM_SHA384 > TLS_CHACHA20_POLY1305_SHA256 + */ + uint8_t test_wire_2[] = { + /* Client did not signal chacha20 boosting. Negotiated if chacha20 boosting is off. */ + TLS_AES_256_GCM_SHA384, + /* Not negotiated since the server prefers aes 256 gcm over chacha20 */ + TLS_CHACHA20_POLY1305_SHA256, + }; + count = sizeof(test_wire_2) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that client negotiates server preferred non-chacha20 aes_256 when client did not signal */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_2, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_256_gcm_sha384); + }; + + /* Server can negotiate chacha20 ciphersuite, even when boosting is disabled */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated (only available option) */ + &s2n_tls13_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = false, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Negotiated as this is the only negotiable option. */ + TLS_CHACHA20_POLY1305_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify server can still negotiate chacha20 if it's the only option even when chacha20 boosting is off */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + }; + + /* + * sslv2 server correctly negotiates ciphersuite when chacha20 boosting is enabled. + */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_SSLv2)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* First 'valid' ciphersuite that is saved as a higher_vers_match and is negotiated */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ + &s2n_dhe_rsa_with_aes_256_gcm_sha384 + }; + + /* cppcheck-suppress redundantAssignment */ + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated as it's not offered by the server. */ + 0x00, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not saved as a higher_vers_match as ecdhe_ecdsa_aes_128 was already saved as one; not negotiated */ + 0x00, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Saved by server as higher_vers_match; negotiated as a 'fall-back' ciphersuite */ + 0x00, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + }; + uint8_t count = sizeof(wire) / S2N_SSLv2_CIPHER_SUITE_LEN; + + /* Verify server negotiate its higher_vers_match ecdhe_ecdsa_aes_128_cbc instead of a chacha20 ciphersuite */ + cipher_preferences.allow_chacha20_boosting = true; + /* + * For an sslv2 connection, all of the ciphersuites in test_cipher_suite_list will not meet the minimum + * required tls version validation (they all require at least sslv3). This means that the server will save this + * cipher as a higher_vers_match and will use this ciphersuite as a fallback if no other ciphersuite can be identified. + * When this logic happens, we bypass chacha20 boosting altogether; therefore because the first ciphersuite + * ecdhe_ecdsa_with_aes_128_cbc is offered by both the client and server and only fails the minimum required version check, + * then we save this ciphersuite as a higher_vers_match and continue. + */ + EXPECT_SUCCESS(s2n_set_cipher_as_sslv2_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + }; + + /* Server is able to negotiate a ciphersuite even without chacha20 in its cipher pref */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated because it is the server's most preferred */ + &s2n_tls13_aes_128_gcm_sha256, + /* Not considered */ + &s2n_tls13_aes_256_gcm_sha384, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; not negotiated as server does not have any chacha20 ciphersuites */ + TLS_CHACHA20_POLY1305_SHA256, + /* Not negotiated as the server prefers aes 128 over aes 256 */ + TLS_AES_256_GCM_SHA384, + /* Negotiated */ + TLS_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that server is able to negotiate aes_128 even without any chacha20 ciphersuites in its preferences */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test chacha20 boosting when the most preferred ciphersuite fails version validation */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Only negotiated if chacha20 boosting is off */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Invalid; never use tls 1.3 ciphers on pre-tls 1.3 connections */ + &s2n_tls13_chacha20_poly1305_sha256, + /* Negotiated (if chacha20 boosting is on) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated by server since it is invalid for the connection. */ + TLS_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify server negotiates second preferred chacha20 ciphersuite ecdhe_rsa_chacha20 */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + }; + } + + if (!s2n_chacha20_poly1305.is_available()) { + /* Chacha20 can't be negotiated when it's not available in libcrypto */ + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Invalid (no libcrypto) */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + /* Negotiated (only valid option) */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Not considered + invalid (no libcrypto) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated because of missing libcrypto for chacha20 */ + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not negotiated because of missing libcrypto for chacha20 */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated whether chacha20 boosting is enabled or not by server */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that server negotiated non-chacha20 ciphersuite ecdhe_ecdsa_aes_128 */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_auth_handshake_test.c b/tests/unit/s2n_client_auth_handshake_test.c index 168242bf451..8166315fb8b 100644 --- a/tests/unit/s2n_client_auth_handshake_test.c +++ b/tests/unit/s2n_client_auth_handshake_test.c @@ -1,532 +1,533 @@ -/* - * 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 - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* To get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_tls13_handshake.c" - -int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2n_config *client_config, struct s2n_cert_chain_and_key *ecdsa_cert, bool no_cert) -{ - /* Set up client and server connections */ - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->x509_validator.skip_cert_validation = 1; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->client_protocol_version = S2N_TLS13; - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; - client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_sha256; - client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - if (!no_cert) { - client_conn->handshake_params.our_chain_and_key = ecdsa_cert; - } - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - server_conn->server_protocol_version = S2N_TLS13; - server_conn->client_protocol_version = S2N_TLS13; - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; - server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - if (no_cert) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); - } else { - server_conn->x509_validator.skip_cert_validation = 1; - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - } - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); - EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(server_conn), no_cert); - EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(client_conn), no_cert); - - const char *app_data_str = "APPLICATION_DATA"; - EXPECT_EQUAL(strcmp(app_data_str, s2n_connection_get_last_message_name(client_conn)), 0); - - /* Clean up */ - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - - return 0; -} - -/* Test to verify the explicit ordering of client_auth handshake with and without a client - * certificate. This includes some pre and post condition checks that pertain to client - * authentication between messages. - */ -int s2n_test_client_auth_message_by_message(bool no_cert) -{ - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); - - char *cert_chain = NULL; - char *private_key = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_cert_chain_and_key *default_cert = NULL; - EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); - if (!no_cert) { - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); - } - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - if (no_cert) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); - } else { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - client_conn->x509_validator.skip_cert_validation = 1; - server_conn->x509_validator.skip_cert_validation = 1; - } - - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - /* Client sends ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, 0); - - EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); - - /* Server reads ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); - - /* Server sends ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends EncryptedExtensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CertificateRequest */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_REQ); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Client reads ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads EncryptedExtensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - if (no_cert) { - /* Client reads CertificateRequest but expects Cert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); - } else { - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_REQ); - } - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - /* Client reads ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Client sends ClientCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - if (no_cert) { - EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); - } else { - EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - /* Client sends CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - } - - /* Client sends ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Server reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - /* Server reads ClientCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - if (no_cert) { - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); - } else { - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - /* Server reads CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - } - - /* Server reads ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(private_key); - free(cert_chain); - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* client_auth handshake negotiation */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - uint8_t *cert_chain_pem = NULL; - uint8_t *private_key_pem = NULL; - uint32_t cert_chain_len = 0; - uint32_t private_key_len = 0; - struct s2n_cert_chain_and_key *ecdsa_cert = NULL; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); - - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(ecdsa_cert, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); - - /* client_auth with no cert */ - EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 1)); - - /* client_auth with cert */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 0)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - free(cert_chain_pem); - free(private_key_pem); - }; - - /* Test each message is sent and in the correct order */ - { - /* Test messsage by message with no cert */ - s2n_test_client_auth_message_by_message(1); - - /* Test message by message with a cert */ - s2n_test_client_auth_message_by_message(0); - }; - - /* Test unexpected certificate request */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - /* Enable client auth on the server, but not on the client */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), - S2N_ERR_UNEXPECTED_CERT_REQUEST); - }; - - /* Test missing certificate request */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - /* Require client auth on the client, but not on the server */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_NONE)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), - S2N_ERR_MISSING_CERT_REQUEST); - }; - - /* By default, client accepts certificate requests */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - - /* Enable client auth on the server */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - }; - - /* Test: all combinations of client and server mutual auth settings produce useful errors - * - * Customers have struggled to correctly configure client auth in the past. - * We should provide very specific and helpful errors to facilitate debugging. - * Do not allow any generic errors like S2N_ERR_BAD_MESSAGE. - */ - { - const int S2N_CERT_AUTH_DEFAULT = -255; - int all_options[] = { - S2N_CERT_AUTH_DEFAULT, - S2N_CERT_AUTH_NONE, - S2N_CERT_AUTH_OPTIONAL, - S2N_CERT_AUTH_REQUIRED - }; - - struct { - int client_auth_type; - int server_auth_type; - bool client_cert_exists; - uint8_t version; - } test_cases[100] = { 0 }; - size_t test_case_count = 0; - - for (size_t client_i = 0; client_i < s2n_array_len(all_options); client_i++) { - for (size_t server_i = 0; server_i < s2n_array_len(all_options); server_i++) { - for (size_t cert_i = 0; cert_i <= 1; cert_i++) { - for (size_t version = S2N_TLS12; version <= S2N_TLS13; version++) { - EXPECT_TRUE(test_case_count < s2n_array_len(test_cases)); - test_cases[test_case_count].client_auth_type = all_options[client_i]; - test_cases[test_case_count].server_auth_type = all_options[server_i]; - test_cases[test_case_count].client_cert_exists = (cert_i == 1); - test_cases[test_case_count].version = version; - test_case_count++; - } - } - } - } - - for (size_t i = 0; i < test_case_count; i++) { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - if (test_cases[i].client_cert_exists) { - 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_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - server->server_protocol_version = test_cases[i].version; - - if (test_cases[i].client_auth_type != S2N_CERT_AUTH_DEFAULT) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, test_cases[i].client_auth_type)); - } - - if (test_cases[i].server_auth_type != S2N_CERT_AUTH_DEFAULT) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, test_cases[i].server_auth_type)); - } - - 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_connections_set_io_pair(client, server, &io_pair)); - - int result = s2n_negotiate_test_server_and_client(server, client); - EXPECT_EQUAL(client->actual_protocol_version, test_cases[i].version); - EXPECT_EQUAL(server->actual_protocol_version, test_cases[i].version); - if (result != S2N_SUCCESS) { - EXPECT_TRUE(s2n_errno == S2N_ERR_MISSING_CERT_REQUEST - || s2n_errno == S2N_ERR_UNEXPECTED_CERT_REQUEST - || s2n_errno == S2N_ERR_MISSING_CLIENT_CERT); - } - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* To get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_tls13_handshake.c" + +int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2n_config *client_config, struct s2n_cert_chain_and_key *ecdsa_cert, bool no_cert) +{ + /* Set up client and server connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->x509_validator.skip_cert_validation = 1; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->client_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; + client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_sha256; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + if (!no_cert) { + client_conn->handshake_params.our_chain_and_key = ecdsa_cert; + } + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + server_conn->server_protocol_version = S2N_TLS13; + server_conn->client_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + if (no_cert) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + } else { + server_conn->x509_validator.skip_cert_validation = 1; + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + } + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(server_conn), no_cert); + EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(client_conn), no_cert); + + const char *app_data_str = "APPLICATION_DATA"; + EXPECT_EQUAL(strcmp(app_data_str, s2n_connection_get_last_message_name(client_conn)), 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + + return 0; +} + +/* Test to verify the explicit ordering of client_auth handshake with and without a client + * certificate. This includes some pre and post condition checks that pertain to client + * authentication between messages. + */ +int s2n_test_client_auth_message_by_message(bool no_cert) +{ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); + + char *cert_chain = NULL; + char *private_key = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_cert_chain_and_key *default_cert = NULL; + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + if (!no_cert) { + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + } + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + if (no_cert) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + } else { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + client_conn->x509_validator.skip_cert_validation = 1; + server_conn->x509_validator.skip_cert_validation = 1; + } + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Client sends ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, 0); + + EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); + + /* Server sends ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertificateRequest */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_REQ); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + if (no_cert) { + /* Client reads CertificateRequest but expects Cert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + } else { + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_REQ); + } + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Client reads ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Client sends ClientCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + if (no_cert) { + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); + } else { + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Client sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + } + + /* Client sends ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Server reads ClientCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + if (no_cert) { + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); + } else { + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Server reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + } + + /* Server reads ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(private_key); + free(cert_chain); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* client_auth handshake negotiation */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + uint8_t *cert_chain_pem = NULL; + uint8_t *private_key_pem = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + struct s2n_cert_chain_and_key *ecdsa_cert = NULL; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(ecdsa_cert, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); + + /* client_auth with no cert */ + EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 1)); + + /* client_auth with cert */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 0)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + free(cert_chain_pem); + free(private_key_pem); + }; + + /* Test each message is sent and in the correct order */ + { + /* Test messsage by message with no cert */ + s2n_test_client_auth_message_by_message(1); + + /* Test message by message with a cert */ + s2n_test_client_auth_message_by_message(0); + }; + + /* Test unexpected certificate request */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* Enable client auth on the server, but not on the client */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_UNEXPECTED_CERT_REQUEST); + }; + + /* Test missing certificate request */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* Require client auth on the client, but not on the server */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_NONE)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_MISSING_CERT_REQUEST); + }; + + /* By default, client accepts certificate requests */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + /* Enable client auth on the server */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + }; + + /* Test: all combinations of client and server mutual auth settings produce useful errors + * + * Customers have struggled to correctly configure client auth in the past. + * We should provide very specific and helpful errors to facilitate debugging. + * Do not allow any generic errors like S2N_ERR_BAD_MESSAGE. + */ + { + const int S2N_CERT_AUTH_DEFAULT = -255; + int all_options[] = { + S2N_CERT_AUTH_DEFAULT, + S2N_CERT_AUTH_NONE, + S2N_CERT_AUTH_OPTIONAL, + S2N_CERT_AUTH_REQUIRED + }; + + struct { + int client_auth_type; + int server_auth_type; + bool client_cert_exists; + uint8_t version; + } test_cases[100] = { 0 }; + size_t test_case_count = 0; + + for (size_t client_i = 0; client_i < s2n_array_len(all_options); client_i++) { + for (size_t server_i = 0; server_i < s2n_array_len(all_options); server_i++) { + for (size_t cert_i = 0; cert_i <= 1; cert_i++) { + for (size_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + EXPECT_TRUE(test_case_count < s2n_array_len(test_cases)); + test_cases[test_case_count].client_auth_type = all_options[client_i]; + test_cases[test_case_count].server_auth_type = all_options[server_i]; + test_cases[test_case_count].client_cert_exists = (cert_i == 1); + test_cases[test_case_count].version = version; + test_case_count++; + } + } + } + } + + for (size_t i = 0; i < test_case_count; i++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + if (test_cases[i].client_cert_exists) { + 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_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + server->server_protocol_version = test_cases[i].version; + + if (test_cases[i].client_auth_type != S2N_CERT_AUTH_DEFAULT) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, test_cases[i].client_auth_type)); + } + + if (test_cases[i].server_auth_type != S2N_CERT_AUTH_DEFAULT) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, test_cases[i].server_auth_type)); + } + + 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_connections_set_io_pair(client, server, &io_pair)); + + int result = s2n_negotiate_test_server_and_client(server, client); + EXPECT_EQUAL(client->actual_protocol_version, test_cases[i].version); + EXPECT_EQUAL(server->actual_protocol_version, test_cases[i].version); + if (result != S2N_SUCCESS) { + EXPECT_TRUE(s2n_errno == S2N_ERR_MISSING_CERT_REQUEST + || s2n_errno == S2N_ERR_UNEXPECTED_CERT_REQUEST + || s2n_errno == S2N_ERR_MISSING_CLIENT_CERT); + } + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_extensions_test.c b/tests/unit/s2n_client_extensions_test.c index c8d25d93325..5088d943df1 100644 --- a/tests/unit/s2n_client_extensions_test.c +++ b/tests/unit/s2n_client_extensions_test.c @@ -1,1265 +1,1266 @@ -/* - * 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 - -#include "api/s2n.h" -#include "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -static uint8_t server_ocsp_status_bytes[] = { - /* clang-format off */ - 0x30, 0x82, 0x06, 0x45, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x06, 0x3e, 0x30, 0x82, 0x06, 0x3a, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01, 0x04, 0x82, 0x06, 0x2b, 0x30, 0x82, 0x06, 0x27, 0x30, 0x81, 0xeb, 0xa1, 0x70, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x66, 0x30, 0x64, 0x30, 0x3c, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x65, 0x68, 0x87, 0x4f, 0x40, 0x75, 0x0f, 0x01, 0x6a, 0x34, 0x75, 0x62, 0x5e, 0x1f, 0x5c, 0x93, 0xe5, 0xa2, 0x6d, 0x58, 0x04, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x02, 0x03, 0x0f, 0x87, 0x2c, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, - 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x33, 0x30, 0x31, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0x16, 0x25, 0xa2, 0x0f, 0x46, 0xc2, 0xa6, 0xac, 0xb1, 0x6e, 0x54, 0xc8, 0xf1, 0x7f, 0xa9, 0xbe, 0x58, 0xf0, 0xdb, 0x81, 0x37, 0x23, 0x76, 0x65, 0x56, 0x90, 0x15, 0xb1, 0x30, 0x6f, 0x43, 0xe2, 0x59, 0x0d, 0x97, 0xa8, 0xa6, 0x05, 0x25, 0xe7, 0x94, 0x21, 0xd5, 0xda, 0x4b, 0x55, 0x13, 0xc7, 0xdf, 0x5d, 0xf6, 0x31, 0xe8, 0x2f, 0x0d, 0xa0, 0xac, 0xd4, 0xfe, 0xf8, 0x22, 0xe7, 0x12, 0xf4, 0x32, 0xcd, 0x53, - 0x03, 0x56, 0x98, 0x0a, 0xf8, 0x9e, 0xda, 0x2c, 0x0a, 0x43, 0x66, 0x6e, 0x0e, 0x9c, 0x9b, 0xf2, 0x0c, 0x66, 0x65, 0x1c, 0x65, 0xc4, 0xf0, 0x82, 0xc3, 0x17, 0x3d, 0x27, 0x11, 0xcc, 0xac, 0x37, 0xe3, 0xa8, 0x35, 0x46, 0x26, 0xcd, 0x08, 0x04, 0xfa, 0xb4, 0xdf, 0x9d, 0x12, 0xdf, 0x45, 0x8d, 0xf2, 0xef, 0x1a, 0xd1, 0x53, 0x50, 0x9a, 0xe3, 0xe8, 0x22, 0xda, 0xec, 0xeb, 0xc0, 0xa8, 0xea, 0xc4, 0x83, 0xc4, 0x47, 0xf2, 0x05, 0x3c, 0x14, 0x11, 0x3b, 0x25, 0xdc, 0xb9, 0x09, 0x5c, 0xd7, 0x74, 0x88, 0x96, 0x82, 0x4d, 0xbb, 0x8b, 0x7f, 0x6a, 0xbf, 0xa1, 0x44, 0x1b, 0x89, 0x67, 0xce, 0x45, 0xab, 0xca, 0xef, 0x48, 0xa6, 0x80, 0x76, 0x7d, 0xbe, 0xb7, 0x8a, 0xdf, 0x7a, 0x32, 0x8c, 0xa5, 0x86, 0x4e, 0x26, 0xf7, 0x15, 0x63, 0xbb, - 0xb1, 0xcc, 0xe0, 0x32, 0x82, 0x02, 0x5d, 0x2b, 0x60, 0x39, 0xdb, 0xd2, 0x04, 0x56, 0xb4, 0x7e, 0xe6, 0x3a, 0x69, 0x0c, 0x8a, 0xf0, 0x00, 0xf4, 0x56, 0xb0, 0xa7, 0x1a, 0x37, 0x05, 0x4b, 0xeb, 0x8c, 0x87, 0x05, 0x37, 0x92, 0xf7, 0x93, 0x5d, 0x93, 0x32, 0x7d, 0x6e, 0xa6, 0xda, 0x10, 0x4b, 0x49, 0xae, 0x86, 0xe4, 0xb4, 0x4d, 0x98, 0x42, 0x3e, 0xd3, 0x42, 0x46, 0x5d, 0xdd, 0x2f, 0x97, 0xd4, 0xb9, 0x7f, 0xbe, 0xa0, 0x82, 0x04, 0x21, 0x30, 0x82, 0x04, 0x1d, 0x30, 0x82, 0x04, 0x19, 0x30, 0x82, 0x03, 0x01, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x15, 0xfa, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x31, 0x32, 0x32, 0x31, 0x38, 0x33, 0x35, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x31, 0x36, 0x33, 0x34, 0x5a, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, - 0x23, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x56, 0x1b, 0x4c, 0x45, 0x31, 0x87, 0x17, 0x17, 0x80, 0x84, 0xe9, 0x6e, 0x17, 0x8d, 0xf2, 0x25, 0x5e, 0x18, 0xed, 0x8d, 0x8e, 0xcc, 0x7c, 0x2b, 0x7b, 0x51, 0xa6, 0xc1, 0xc2, 0xe6, 0xbf, 0x0a, 0xa3, 0x60, 0x30, 0x66, 0xf1, 0x32, 0xfe, 0x10, 0xae, 0x97, 0xb5, 0x0e, 0x99, 0xfa, 0x24, 0xb8, 0x3f, 0xc5, - 0x3d, 0xd2, 0x77, 0x74, 0x96, 0x38, 0x7d, 0x14, 0xe1, 0xc3, 0xa9, 0xb6, 0xa4, 0x93, 0x3e, 0x2a, 0xc1, 0x24, 0x13, 0xd0, 0x85, 0x57, 0x0a, 0x95, 0xb8, 0x14, 0x74, 0x14, 0xa0, 0xbc, 0x00, 0x7c, 0x7b, 0xcf, 0x22, 0x24, 0x46, 0xef, 0x7f, 0x1a, 0x15, 0x6d, 0x7e, 0xa1, 0xc5, 0x77, 0xfc, 0x5f, 0x0f, 0xac, 0xdf, 0xd4, 0x2e, 0xb0, 0xf5, 0x97, 0x49, 0x90, 0xcb, 0x2f, 0x5c, 0xef, 0xeb, 0xce, 0xef, 0x4d, 0x1b, 0xdc, 0x7a, 0xe5, 0xc1, 0x07, 0x5c, 0x5a, 0x99, 0xa9, 0x31, 0x71, 0xf2, 0xb0, 0x84, 0x5b, 0x4f, 0xf0, 0x86, 0x4e, 0x97, 0x3f, 0xcf, 0xe3, 0x2f, 0x9d, 0x75, 0x11, 0xff, 0x87, 0xa3, 0xe9, 0x43, 0x41, 0x0c, 0x90, 0xa4, 0x49, 0x3a, 0x30, 0x6b, 0x69, 0x44, 0x35, 0x93, 0x40, 0xa9, 0xca, 0x96, 0xf0, 0x2b, 0x66, 0xce, 0x67, - 0xf0, 0x28, 0xdf, 0x29, 0x80, 0xa6, 0xaa, 0xee, 0x8d, 0x5d, 0x5d, 0x45, 0x2b, 0x8b, 0x0e, 0xb9, 0x3f, 0x92, 0x3c, 0xc1, 0xe2, 0x3f, 0xcc, 0xcb, 0xdb, 0xe7, 0xff, 0xcb, 0x11, 0x4d, 0x08, 0xfa, 0x7a, 0x6a, 0x3c, 0x40, 0x4f, 0x82, 0x5d, 0x1a, 0x0e, 0x71, 0x59, 0x35, 0xcf, 0x62, 0x3a, 0x8c, 0x7b, 0x59, 0x67, 0x00, 0x14, 0xed, 0x06, 0x22, 0xf6, 0x08, 0x9a, 0x94, 0x47, 0xa7, 0xa1, 0x90, 0x10, 0xf7, 0xfe, 0x58, 0xf8, 0x41, 0x29, 0xa2, 0x76, 0x5e, 0xa3, 0x67, 0x82, 0x4d, 0x1c, 0x3b, 0xb2, 0xfd, 0xa3, 0x08, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa0, 0x30, 0x81, 0x9d, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, - 0x30, 0x1e, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x17, 0x30, 0x15, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0xe0, 0xa3, 0x66, 0x95, 0x41, 0x4c, 0x5d, 0xd4, 0x49, 0xbc, 0x00, 0xe3, 0x3c, 0xdc, 0xdb, 0xd2, 0x34, 0x3e, 0x17, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x42, 0xcd, 0x4c, 0x03, 0xd2, 0x9a, 0x55, 0xb2, 0xd6, 0x3e, 0x90, 0x4c, 0x89, 0x27, 0xd0, 0xcf, 0x87, 0xf6, 0x91, 0x9b, 0x86, 0x6a, 0x6d, 0x76, 0xd9, 0x5e, 0xbc, 0xc8, 0xfe, 0x74, 0xbe, 0x97, 0x29, 0xd1, 0xac, 0x92, 0x9b, 0x9e, 0x48, 0xab, 0xb1, 0xf4, 0xbe, 0xd5, 0x3f, 0xa8, 0x4c, 0xce, 0x0e, 0x2f, 0x39, 0x96, 0x4b, 0xde, 0xda, 0xac, 0x40, 0xce, 0xbb, 0x93, 0xdb, 0x1c, 0x39, 0x02, 0x03, 0x25, 0x32, 0x45, 0xde, 0x94, 0x5a, 0x63, 0xaf, 0xf7, 0xb0, 0x70, 0xc8, 0xcc, 0x2b, 0x34, 0x7b, 0x5f, 0x7d, 0xc6, 0x96, 0x1d, 0x59, - 0x1d, 0xdd, 0x8f, 0x7e, 0x55, 0xc4, 0x92, 0x11, 0x8d, 0xd9, 0x11, 0x11, 0x22, 0x20, 0xd3, 0x56, 0x1e, 0x11, 0xae, 0x97, 0xf2, 0x71, 0xea, 0x8c, 0xf5, 0x15, 0x2d, 0xb1, 0x59, 0xdd, 0x3e, 0x43, 0x9c, 0xf1, 0xda, 0x81, 0xd7, 0xc8, 0x6c, 0xf6, 0x08, 0x5d, 0x6f, 0xdf, 0x26, 0xa8, 0xfe, 0x84, 0xa2, 0x08, 0xaf, 0xdb, 0x9b, 0x39, 0xf5, 0x46, 0xfa, 0x5b, 0xfa, 0x97, 0x64, 0x1d, 0xf1, 0xd4, 0xbc, 0xb0, 0xa4, 0x2f, 0x36, 0xf1, 0x90, 0xb5, 0x3b, 0x67, 0x0b, 0x5b, 0xf3, 0x24, 0x50, 0x27, 0x63, 0xdc, 0xeb, 0xb6, 0x55, 0x0f, 0xb7, 0xbe, 0xee, 0x2e, 0xfb, 0xc8, 0x6a, 0x10, 0xab, 0xee, 0x9a, 0x27, 0xe4, 0x13, 0x16, 0xcf, 0xdd, 0x13, 0xa7, 0x0f, 0xde, 0x61, 0x8c, 0xfa, 0xed, 0x2d, 0x00, 0x60, 0xf9, 0xc4, 0x3d, 0xad, 0xd6, 0xa2, - 0xc0, 0xa3, 0x29, 0x11, 0x61, 0x0b, 0x65, 0xdb, 0x14, 0x79, 0xb1, 0x7d, 0x8a, 0x57, 0x91, 0x59, 0xa4, 0xfc, 0x4c, 0x60, 0x4f, 0x3c, 0xc8, 0x31, 0x9b, 0x69, 0x70, 0xb9, 0xae, 0xed, 0xb1, 0xde, 0x58, 0x8d, 0x62, 0x30, 0xb4, 0x7b, 0x46, 0xf2, 0xda, 0x7b, 0xbb, 0x72, 0xcf, 0xf0, 0x47, 0x8b, 0x84, - /* clang-format on */ -}; - -/* This data format is bogus, but sufficient to test the server is able - to return correctly what has been configured. Once the client does - validation we will need real data here. - */ -static uint8_t sct_list[] = { - 0xff, 0xff, 0xff, 0xff, 0xff -}; - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - /* Client doesn't use the server name extension. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server didn't receive the server name. */ - EXPECT_NULL(s2n_get_server_name(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client uses the server name extension. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - const char *sent_server_name = "www.alligator.com"; - const char *received_server_name = NULL; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - /* Set the server name */ - EXPECT_SUCCESS(s2n_set_server_name(client_conn, sent_server_name)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server name was received intact. */ - EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); - EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); - EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends multiple server names. */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - const char *sent_server_name = "svr"; - const char *received_server_name = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - uint32_t cert_chain_len = 0; - uint32_t private_key_len = 0; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x0C, - /* All server names len */ - 0x00, - 0x0A, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - /* Second server name type - host name */ - 0x00, - /* Second server name len */ - 0x00, - 0x01, - /* Second server name */ - 0xFF, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version garbage value. s2n should still accept this. */ - 0x01, - 0x01, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, (uint8_t *) cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, (uint8_t *) private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, (uint8_t *) cert_chain, cert_chain_len, (uint8_t *) private_key, private_key_len)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that the CLIENT HELLO is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - - /* Verify that the server name was received intact. */ - EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); - EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); - EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - EXPECT_TRUE(server_conn->alert_sent); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client sends duplicate server name extension */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x0C, - /* All server names len */ - 0x00, - 0x0A, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - /* Second server name type - host name */ - 0x00, - /* Second server name len */ - 0x00, - 0x01, - /* Second server name */ - 0xFF, - /* And all that again... */ - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x0C, - /* All server names len */ - 0x00, - 0x0A, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - /* Second server name type - host name */ - 0x00, - /* Second server name len */ - 0x00, - 0x01, - /* Second server name */ - 0xFF, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that we fail for duplicated extension type Bad Message */ - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - s2n_negotiate(server_conn, &server_blocked); - EXPECT_EQUAL(s2n_errno, S2N_ERR_DUPLICATE_EXTENSION); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client sends a valid initial renegotiation_info */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x01, - /* Empty renegotiated_connection */ - 0x00, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that the CLIENT HELLO is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type & NEGOTIATED, NEGOTIATED); - - /* Verify that the that we detected secure_renegotiation */ - EXPECT_EQUAL(server_conn->secure_renegotiation, 1); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - EXPECT_TRUE(server_conn->alert_sent); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client sends a non-empty initial renegotiation_info */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - uint8_t buf[5120]; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x21, - /* renegotiated_connection len */ - 0x20, - /* fake renegotiated_connection */ - ZERO_TO_THIRTY_ONE, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that we fail for non-empty renegotiated_connection */ - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - s2n_negotiate(server_conn, &server_blocked); - EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - - /* Clear pipe since negotiation failed mid-handshake */ - EXPECT_SUCCESS(read(io_pair.client, buf, sizeof(buf))); - }; - - /* Client doesn't use the OCSP extension. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - uint32_t length = 0; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server didn't send an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - - /* Verify that the client didn't receive an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Cannot enable OCSP stapling if there's no support for it */ - if (!s2n_x509_ocsp_stapling_supported()) { - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_FAILURE(s2n_config_set_check_stapled_ocsp_response(client_config, 1)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Server doesn't support the OCSP extension. We can't run this test if ocsp isn't supported by the client. */ - if (s2n_x509_ocsp_stapling_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - uint32_t length = 0; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server didn't send an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - - /* Verify that the client didn't receive an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test with s2n_config_set_extension_data(). Can be removed once API is deprecated */ - if (s2n_x509_ocsp_stapling_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - const uint8_t *server_ocsp_reply = NULL; - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server sent an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - - /* Verify that the client received an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); - - for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { - EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Server and client support the OCSP extension. Test only runs if ocsp stapled responses are supported by the client */ - if (s2n_x509_ocsp_stapling_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - const uint8_t *server_ocsp_reply = NULL; - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server sent an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - - /* Verify that the client received an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); - - for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { - EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Server and client support the OCSP extension. Test Behavior for TLS 1.3 */ - if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - const uint8_t *server_ocsp_reply = NULL; - uint32_t length = 0; - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->client_protocol_version = S2N_TLS13; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->server_protocol_version = S2N_TLS13; - server_conn->client_protocol_version = S2N_TLS13; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server sent an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - - /* Verify that the client received an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - - EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); - - for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { - EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* Client does not request SCT, but server is configured to serve them. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - - uint32_t length = 0; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, - sct_list, sizeof(sct_list))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the client did *not* receive an SCT list */ - EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client requests SCT and server does have it. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - /* Indicate that the client wants CT if available */ - EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, - sct_list, sizeof(sct_list))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the client did receive an SCT list */ - EXPECT_NOT_NULL(s2n_connection_get_sct_list(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(sct_list)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client requests SCT and server does *not* have it. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - /* Indicate that the client wants CT if available */ - EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the client does not get a list */ - EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client requests 512, 1024, 2048, and 4096 maximum fragment lengths */ - for (uint8_t mfl_code = S2N_TLS_MAX_FRAG_LEN_512; mfl_code <= S2N_TLS_MAX_FRAG_LEN_4096; mfl_code++) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, mfl_code)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Preference should be ignored as the TlS Maximum Fragment Length Extension is Set */ - EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, mfl_code_to_length[mfl_code]); - EXPECT_EQUAL(server_conn->negotiated_mfl_code, mfl_code); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Client requests invalid maximum fragment length */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_FAILURE(s2n_config_send_max_fragment_length(client_config, 5)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* check that max_fragment_length did not get set due to invalid mfl_code */ - EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Server ignores client's request of S2N_TLS_MAX_FRAG_LEN_2048 maximum fragment length when accept_mfl is not set*/ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_2048)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* check that max_fragment_length did not get set since accept_mfl is not set */ - EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +static uint8_t server_ocsp_status_bytes[] = { + /* clang-format off */ + 0x30, 0x82, 0x06, 0x45, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x06, 0x3e, 0x30, 0x82, 0x06, 0x3a, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01, 0x04, 0x82, 0x06, 0x2b, 0x30, 0x82, 0x06, 0x27, 0x30, 0x81, 0xeb, 0xa1, 0x70, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x66, 0x30, 0x64, 0x30, 0x3c, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x65, 0x68, 0x87, 0x4f, 0x40, 0x75, 0x0f, 0x01, 0x6a, 0x34, 0x75, 0x62, 0x5e, 0x1f, 0x5c, 0x93, 0xe5, 0xa2, 0x6d, 0x58, 0x04, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x02, 0x03, 0x0f, 0x87, 0x2c, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, + 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x33, 0x30, 0x31, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0x16, 0x25, 0xa2, 0x0f, 0x46, 0xc2, 0xa6, 0xac, 0xb1, 0x6e, 0x54, 0xc8, 0xf1, 0x7f, 0xa9, 0xbe, 0x58, 0xf0, 0xdb, 0x81, 0x37, 0x23, 0x76, 0x65, 0x56, 0x90, 0x15, 0xb1, 0x30, 0x6f, 0x43, 0xe2, 0x59, 0x0d, 0x97, 0xa8, 0xa6, 0x05, 0x25, 0xe7, 0x94, 0x21, 0xd5, 0xda, 0x4b, 0x55, 0x13, 0xc7, 0xdf, 0x5d, 0xf6, 0x31, 0xe8, 0x2f, 0x0d, 0xa0, 0xac, 0xd4, 0xfe, 0xf8, 0x22, 0xe7, 0x12, 0xf4, 0x32, 0xcd, 0x53, + 0x03, 0x56, 0x98, 0x0a, 0xf8, 0x9e, 0xda, 0x2c, 0x0a, 0x43, 0x66, 0x6e, 0x0e, 0x9c, 0x9b, 0xf2, 0x0c, 0x66, 0x65, 0x1c, 0x65, 0xc4, 0xf0, 0x82, 0xc3, 0x17, 0x3d, 0x27, 0x11, 0xcc, 0xac, 0x37, 0xe3, 0xa8, 0x35, 0x46, 0x26, 0xcd, 0x08, 0x04, 0xfa, 0xb4, 0xdf, 0x9d, 0x12, 0xdf, 0x45, 0x8d, 0xf2, 0xef, 0x1a, 0xd1, 0x53, 0x50, 0x9a, 0xe3, 0xe8, 0x22, 0xda, 0xec, 0xeb, 0xc0, 0xa8, 0xea, 0xc4, 0x83, 0xc4, 0x47, 0xf2, 0x05, 0x3c, 0x14, 0x11, 0x3b, 0x25, 0xdc, 0xb9, 0x09, 0x5c, 0xd7, 0x74, 0x88, 0x96, 0x82, 0x4d, 0xbb, 0x8b, 0x7f, 0x6a, 0xbf, 0xa1, 0x44, 0x1b, 0x89, 0x67, 0xce, 0x45, 0xab, 0xca, 0xef, 0x48, 0xa6, 0x80, 0x76, 0x7d, 0xbe, 0xb7, 0x8a, 0xdf, 0x7a, 0x32, 0x8c, 0xa5, 0x86, 0x4e, 0x26, 0xf7, 0x15, 0x63, 0xbb, + 0xb1, 0xcc, 0xe0, 0x32, 0x82, 0x02, 0x5d, 0x2b, 0x60, 0x39, 0xdb, 0xd2, 0x04, 0x56, 0xb4, 0x7e, 0xe6, 0x3a, 0x69, 0x0c, 0x8a, 0xf0, 0x00, 0xf4, 0x56, 0xb0, 0xa7, 0x1a, 0x37, 0x05, 0x4b, 0xeb, 0x8c, 0x87, 0x05, 0x37, 0x92, 0xf7, 0x93, 0x5d, 0x93, 0x32, 0x7d, 0x6e, 0xa6, 0xda, 0x10, 0x4b, 0x49, 0xae, 0x86, 0xe4, 0xb4, 0x4d, 0x98, 0x42, 0x3e, 0xd3, 0x42, 0x46, 0x5d, 0xdd, 0x2f, 0x97, 0xd4, 0xb9, 0x7f, 0xbe, 0xa0, 0x82, 0x04, 0x21, 0x30, 0x82, 0x04, 0x1d, 0x30, 0x82, 0x04, 0x19, 0x30, 0x82, 0x03, 0x01, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x15, 0xfa, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x31, 0x32, 0x32, 0x31, 0x38, 0x33, 0x35, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x31, 0x36, 0x33, 0x34, 0x5a, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x23, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x56, 0x1b, 0x4c, 0x45, 0x31, 0x87, 0x17, 0x17, 0x80, 0x84, 0xe9, 0x6e, 0x17, 0x8d, 0xf2, 0x25, 0x5e, 0x18, 0xed, 0x8d, 0x8e, 0xcc, 0x7c, 0x2b, 0x7b, 0x51, 0xa6, 0xc1, 0xc2, 0xe6, 0xbf, 0x0a, 0xa3, 0x60, 0x30, 0x66, 0xf1, 0x32, 0xfe, 0x10, 0xae, 0x97, 0xb5, 0x0e, 0x99, 0xfa, 0x24, 0xb8, 0x3f, 0xc5, + 0x3d, 0xd2, 0x77, 0x74, 0x96, 0x38, 0x7d, 0x14, 0xe1, 0xc3, 0xa9, 0xb6, 0xa4, 0x93, 0x3e, 0x2a, 0xc1, 0x24, 0x13, 0xd0, 0x85, 0x57, 0x0a, 0x95, 0xb8, 0x14, 0x74, 0x14, 0xa0, 0xbc, 0x00, 0x7c, 0x7b, 0xcf, 0x22, 0x24, 0x46, 0xef, 0x7f, 0x1a, 0x15, 0x6d, 0x7e, 0xa1, 0xc5, 0x77, 0xfc, 0x5f, 0x0f, 0xac, 0xdf, 0xd4, 0x2e, 0xb0, 0xf5, 0x97, 0x49, 0x90, 0xcb, 0x2f, 0x5c, 0xef, 0xeb, 0xce, 0xef, 0x4d, 0x1b, 0xdc, 0x7a, 0xe5, 0xc1, 0x07, 0x5c, 0x5a, 0x99, 0xa9, 0x31, 0x71, 0xf2, 0xb0, 0x84, 0x5b, 0x4f, 0xf0, 0x86, 0x4e, 0x97, 0x3f, 0xcf, 0xe3, 0x2f, 0x9d, 0x75, 0x11, 0xff, 0x87, 0xa3, 0xe9, 0x43, 0x41, 0x0c, 0x90, 0xa4, 0x49, 0x3a, 0x30, 0x6b, 0x69, 0x44, 0x35, 0x93, 0x40, 0xa9, 0xca, 0x96, 0xf0, 0x2b, 0x66, 0xce, 0x67, + 0xf0, 0x28, 0xdf, 0x29, 0x80, 0xa6, 0xaa, 0xee, 0x8d, 0x5d, 0x5d, 0x45, 0x2b, 0x8b, 0x0e, 0xb9, 0x3f, 0x92, 0x3c, 0xc1, 0xe2, 0x3f, 0xcc, 0xcb, 0xdb, 0xe7, 0xff, 0xcb, 0x11, 0x4d, 0x08, 0xfa, 0x7a, 0x6a, 0x3c, 0x40, 0x4f, 0x82, 0x5d, 0x1a, 0x0e, 0x71, 0x59, 0x35, 0xcf, 0x62, 0x3a, 0x8c, 0x7b, 0x59, 0x67, 0x00, 0x14, 0xed, 0x06, 0x22, 0xf6, 0x08, 0x9a, 0x94, 0x47, 0xa7, 0xa1, 0x90, 0x10, 0xf7, 0xfe, 0x58, 0xf8, 0x41, 0x29, 0xa2, 0x76, 0x5e, 0xa3, 0x67, 0x82, 0x4d, 0x1c, 0x3b, 0xb2, 0xfd, 0xa3, 0x08, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa0, 0x30, 0x81, 0x9d, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, + 0x30, 0x1e, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x17, 0x30, 0x15, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0xe0, 0xa3, 0x66, 0x95, 0x41, 0x4c, 0x5d, 0xd4, 0x49, 0xbc, 0x00, 0xe3, 0x3c, 0xdc, 0xdb, 0xd2, 0x34, 0x3e, 0x17, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x42, 0xcd, 0x4c, 0x03, 0xd2, 0x9a, 0x55, 0xb2, 0xd6, 0x3e, 0x90, 0x4c, 0x89, 0x27, 0xd0, 0xcf, 0x87, 0xf6, 0x91, 0x9b, 0x86, 0x6a, 0x6d, 0x76, 0xd9, 0x5e, 0xbc, 0xc8, 0xfe, 0x74, 0xbe, 0x97, 0x29, 0xd1, 0xac, 0x92, 0x9b, 0x9e, 0x48, 0xab, 0xb1, 0xf4, 0xbe, 0xd5, 0x3f, 0xa8, 0x4c, 0xce, 0x0e, 0x2f, 0x39, 0x96, 0x4b, 0xde, 0xda, 0xac, 0x40, 0xce, 0xbb, 0x93, 0xdb, 0x1c, 0x39, 0x02, 0x03, 0x25, 0x32, 0x45, 0xde, 0x94, 0x5a, 0x63, 0xaf, 0xf7, 0xb0, 0x70, 0xc8, 0xcc, 0x2b, 0x34, 0x7b, 0x5f, 0x7d, 0xc6, 0x96, 0x1d, 0x59, + 0x1d, 0xdd, 0x8f, 0x7e, 0x55, 0xc4, 0x92, 0x11, 0x8d, 0xd9, 0x11, 0x11, 0x22, 0x20, 0xd3, 0x56, 0x1e, 0x11, 0xae, 0x97, 0xf2, 0x71, 0xea, 0x8c, 0xf5, 0x15, 0x2d, 0xb1, 0x59, 0xdd, 0x3e, 0x43, 0x9c, 0xf1, 0xda, 0x81, 0xd7, 0xc8, 0x6c, 0xf6, 0x08, 0x5d, 0x6f, 0xdf, 0x26, 0xa8, 0xfe, 0x84, 0xa2, 0x08, 0xaf, 0xdb, 0x9b, 0x39, 0xf5, 0x46, 0xfa, 0x5b, 0xfa, 0x97, 0x64, 0x1d, 0xf1, 0xd4, 0xbc, 0xb0, 0xa4, 0x2f, 0x36, 0xf1, 0x90, 0xb5, 0x3b, 0x67, 0x0b, 0x5b, 0xf3, 0x24, 0x50, 0x27, 0x63, 0xdc, 0xeb, 0xb6, 0x55, 0x0f, 0xb7, 0xbe, 0xee, 0x2e, 0xfb, 0xc8, 0x6a, 0x10, 0xab, 0xee, 0x9a, 0x27, 0xe4, 0x13, 0x16, 0xcf, 0xdd, 0x13, 0xa7, 0x0f, 0xde, 0x61, 0x8c, 0xfa, 0xed, 0x2d, 0x00, 0x60, 0xf9, 0xc4, 0x3d, 0xad, 0xd6, 0xa2, + 0xc0, 0xa3, 0x29, 0x11, 0x61, 0x0b, 0x65, 0xdb, 0x14, 0x79, 0xb1, 0x7d, 0x8a, 0x57, 0x91, 0x59, 0xa4, 0xfc, 0x4c, 0x60, 0x4f, 0x3c, 0xc8, 0x31, 0x9b, 0x69, 0x70, 0xb9, 0xae, 0xed, 0xb1, 0xde, 0x58, 0x8d, 0x62, 0x30, 0xb4, 0x7b, 0x46, 0xf2, 0xda, 0x7b, 0xbb, 0x72, 0xcf, 0xf0, 0x47, 0x8b, 0x84, + /* clang-format on */ +}; + +/* This data format is bogus, but sufficient to test the server is able + to return correctly what has been configured. Once the client does + validation we will need real data here. + */ +static uint8_t sct_list[] = { + 0xff, 0xff, 0xff, 0xff, 0xff +}; + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Client doesn't use the server name extension. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't receive the server name. */ + EXPECT_NULL(s2n_get_server_name(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client uses the server name extension. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + const char *sent_server_name = "www.alligator.com"; + const char *received_server_name = NULL; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Set the server name */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, sent_server_name)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server name was received intact. */ + EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); + EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); + EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends multiple server names. */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + const char *sent_server_name = "svr"; + const char *received_server_name = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version garbage value. s2n should still accept this. */ + 0x01, + 0x01, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, (uint8_t *) cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, (uint8_t *) private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, (uint8_t *) cert_chain, cert_chain_len, (uint8_t *) private_key, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that the CLIENT HELLO is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + /* Verify that the server name was received intact. */ + EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); + EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); + EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + EXPECT_TRUE(server_conn->alert_sent); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends duplicate server name extension */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + /* And all that again... */ + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that we fail for duplicated extension type Bad Message */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_errno, S2N_ERR_DUPLICATE_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends a valid initial renegotiation_info */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x01, + /* Empty renegotiated_connection */ + 0x00, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that the CLIENT HELLO is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type & NEGOTIATED, NEGOTIATED); + + /* Verify that the that we detected secure_renegotiation */ + EXPECT_EQUAL(server_conn->secure_renegotiation, 1); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + EXPECT_TRUE(server_conn->alert_sent); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends a non-empty initial renegotiation_info */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + uint8_t buf[5120]; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x21, + /* renegotiated_connection len */ + 0x20, + /* fake renegotiated_connection */ + ZERO_TO_THIRTY_ONE, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that we fail for non-empty renegotiated_connection */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* Clear pipe since negotiation failed mid-handshake */ + EXPECT_SUCCESS(read(io_pair.client, buf, sizeof(buf))); + }; + + /* Client doesn't use the OCSP extension. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + uint32_t length = 0; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't send an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + + /* Verify that the client didn't receive an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Cannot enable OCSP stapling if there's no support for it */ + if (!s2n_x509_ocsp_stapling_supported()) { + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_FAILURE(s2n_config_set_check_stapled_ocsp_response(client_config, 1)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server doesn't support the OCSP extension. We can't run this test if ocsp isn't supported by the client. */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + uint32_t length = 0; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't send an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + + /* Verify that the client didn't receive an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test with s2n_config_set_extension_data(). Can be removed once API is deprecated */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + const uint8_t *server_ocsp_reply = NULL; + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); + + for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server and client support the OCSP extension. Test only runs if ocsp stapled responses are supported by the client */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + const uint8_t *server_ocsp_reply = NULL; + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); + + for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server and client support the OCSP extension. Test Behavior for TLS 1.3 */ + if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + const uint8_t *server_ocsp_reply = NULL; + uint32_t length = 0; + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->client_protocol_version = S2N_TLS13; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server_protocol_version = S2N_TLS13; + server_conn->client_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); + + for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Client does not request SCT, but server is configured to serve them. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + + uint32_t length = 0; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, + sct_list, sizeof(sct_list))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client did *not* receive an SCT list */ + EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client requests SCT and server does have it. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Indicate that the client wants CT if available */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, + sct_list, sizeof(sct_list))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client did receive an SCT list */ + EXPECT_NOT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(sct_list)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client requests SCT and server does *not* have it. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Indicate that the client wants CT if available */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client does not get a list */ + EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client requests 512, 1024, 2048, and 4096 maximum fragment lengths */ + for (uint8_t mfl_code = S2N_TLS_MAX_FRAG_LEN_512; mfl_code <= S2N_TLS_MAX_FRAG_LEN_4096; mfl_code++) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, mfl_code)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Preference should be ignored as the TlS Maximum Fragment Length Extension is Set */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, mfl_code_to_length[mfl_code]); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, mfl_code); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Client requests invalid maximum fragment length */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_FAILURE(s2n_config_send_max_fragment_length(client_config, 5)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* check that max_fragment_length did not get set due to invalid mfl_code */ + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Server ignores client's request of S2N_TLS_MAX_FRAG_LEN_2048 maximum fragment length when accept_mfl is not set*/ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_2048)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* check that max_fragment_length did not get set since accept_mfl is not set */ + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_recv_test.c b/tests/unit/s2n_client_hello_recv_test.c index 07da5496655..4afcecb8702 100644 --- a/tests/unit/s2n_client_hello_recv_test.c +++ b/tests/unit/s2n_client_hello_recv_test.c @@ -1,561 +1,562 @@ -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_sslv2_client_hello.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_client_hello.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -int main(int argc, char **argv) -{ - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - struct s2n_stuffer *hello_stuffer = NULL; - struct s2n_config *tls12_config = NULL; - struct s2n_config *tls13_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char *cert_chain = NULL; - char *tls13_cert_chain = NULL; - char *private_key = NULL; - char *tls13_private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(tls12_config = s2n_config_new()); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_config, "test_all_tls12")); - - EXPECT_NOT_NULL(tls13_cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(tls13_private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(tls13_config = s2n_config_new()); - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "test_all")); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, tls13_chain_and_key)); - - /* These tests verify the logic behind the setting of these three connection fields: - server_protocol_version, client_protocol_version, and actual_protocol version. */ - - /* Test we can successfully receive an sslv2 client hello and set a - * tls12 connection */ - { - /* "enable" tls13, to test under default s2n-tls behavior */ - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - /* Record version and protocol version are in the header for SSLv2 */ - server_conn->client_hello.sslv2 = true; - server_conn->client_hello.legacy_version = S2N_TLS12; - - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - struct s2n_blob client_hello = { - .data = sslv2_client_hello, - .size = sizeof(sslv2_client_hello), - .allocated = 0, - .growable = 0 - }; - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.sslv2, true); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.callback_invoked, 1); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server_conn), S2N_SSLv2); - - s2n_connection_free(server_conn); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* Test that a tls12 client legacy version and tls12 server version - will successfully set a tls12 connection, since tls13 is not enabled. */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - }; - - /* Test that a tls12 client legacy version and tls12 server version - will successfully set a tls12 connection, even when tls13 is enabled. */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test that a tls11 client legacy version and tls12 server version - will successfully set a tls11 connection. */ - for (uint8_t i = 0; i < 2; i++) { - if (i == 1) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - } - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - client_conn->client_protocol_version = S2N_TLS11; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS11); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS11); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS11); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS11); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - /* Test that a tls12 client and tls13 server will successfully - set a tls12 connection. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - /* Test that an erroneous client legacy version and tls13 server version - will still successfully set a tls13 connection, when real client version is tls13. */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - hello_stuffer = &client_conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ - uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - incorrect_protocol_version[0] = S2N_TLS13 / 10; - incorrect_protocol_version[1] = S2N_TLS13 % 10; - EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test that a tls12 client legacy version and tls13 server version - will still successfully set a tls13 connection, if possible. */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - /* Test that an erroneous(tls13) client legacy version and tls13 server version - will still successfully set a tls12 connection, if tls12 is the true client version. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - hello_stuffer = &client_conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ - uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - incorrect_protocol_version[0] = S2N_TLS13 / 10; - incorrect_protocol_version[1] = S2N_TLS13 % 10; - EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* s2n receiving a client hello will error when parsing an empty cipher suite */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - - hello_stuffer = &client_conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - uint8_t empty_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - - /* Move write_cursor to cipher_suite position */ - EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN + 1)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, empty_cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - }; - - /* s2n receiving a sslv2 client hello will error when parsing an empty cipher suite */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - - /* Record version and protocol version are in the header for SSLv2 */ - server_conn->client_hello.sslv2 = true; - server_conn->client_hello.legacy_version = S2N_TLS12; - - /* Writing a sslv2 client hello with a length 0 cipher suite list */ - uint8_t sslv2_client_hello[] = { - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x20, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - struct s2n_blob client_hello = { - .data = sslv2_client_hello, - .size = sizeof(sslv2_client_hello), - .allocated = 0, - .growable = 0 - }; - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); - - s2n_connection_free(server_conn); - }; - - /* Test that S2N will accept a ClientHello with legacy_session_id set when running with QUIC. - * Since this requirement is a SHOULD, we're accepting it for non-compliant endpoints. - * https://tools.ietf.org/html/draft-ietf-quic-tls-32#section-8.4*/ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - const size_t test_session_id_len = 10; - - struct s2n_config *quic_config = NULL; - EXPECT_NOT_NULL(quic_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(quic_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(quic_config, "default_tls13")); - - /* Succeeds without a session id */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - s2n_connection_free(client_conn); - s2n_connection_free(server_conn); - }; - - /* Also, succeeds with a session id */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); - - /* Directly set session id, which is not set by default when using QUIC */ - client_conn->session_id_len = test_session_id_len; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - s2n_connection_free(client_conn); - s2n_connection_free(server_conn); - }; - - s2n_config_free(quic_config); - } - - /* Test that the server will not choose a signature algorithm or certificate if using PSKs */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - struct s2n_psk chosen_psk = { 0 }; - chosen_psk.hmac_alg = S2N_HMAC_SHA256; - server_conn->psk_params.chosen_psk = &chosen_psk; - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->iana_value, 0); - EXPECT_NULL(server_conn->handshake_params.our_chain_and_key); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test that curve selection will be NIST P-256 when tls12 client does not send curve extension. - * - *= https://www.rfc-editor.org/rfc/rfc4492#section-4 - *= type=test - *# A client that proposes ECC cipher suites may choose not to include these extensions. - *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. - */ - { - S2N_BLOB_FROM_HEX(tls12_client_hello_no_curves, - /* clang-format off */ - "030307de81928fe1" "7cba77904c2798da" "2521a76b013a16e4" "21ade32208f658d4" "327d000048000400" - "05000a0016002f00" "3300350039003c00" "3d0067006b009c00" "9d009e009fc009c0" "0ac011c012c013c0" - "14c023c024c027c0" "28c02bc02cc02fc0" "30cca8cca9ccaaff" "04ff0800ff010000" "30000d0016001404" - "0105010601030104" "0305030603030302" "010203000b000201" "00fe01000c000a00" "17000d0013000100" - "0a" /* clang-format on */); - - /* The above code is generated the following code, - disabling s2n_client_supported_groups_extension - from client_hello_extensions (s2n_extension_type_lists.c) - and exporting the resulting client_conn->handshake.io.blob - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); - s2n_connection_free(client_conn); - */ - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &tls12_client_hello_no_curves)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - /* ensure negotiated_curve == secp256r1 for maximum client compatibility */ - EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, &s2n_ecc_curve_secp256r1); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test: Parse fragmented sslv2 client hello. - * - * Even if the sslv2 client hello is sent in one packet, there is no requirement - * that our first call to conn->recv returns the whole message. sslv2 uses separate - * record parsing code, so we need to ensure that those paths can handle partial reads. - */ - { - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_config)); - - struct s2n_stuffer server_in = { 0 }; - uint8_t sslv2_client_hello_bytes[] = { - SSLv2_CLIENT_HELLO_HEADER, - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - EXPECT_SUCCESS(s2n_blob_init(&server_in.blob, - sslv2_client_hello_bytes, sizeof(sslv2_client_hello_bytes))); - EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&server_in, server)); - - /* Read message one byte at a time */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - for (size_t i = 0; i < sizeof(sslv2_client_hello_bytes); i++) { - EXPECT_ERROR_WITH_ERRNO( - s2n_negotiate_until_message(server, &blocked, SERVER_HELLO), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&server_in, 1)); - } - - /* Successfully read the full message */ - EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); - EXPECT_EQUAL(server->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->client_hello.legacy_version, S2N_TLS12); - EXPECT_TRUE(server->client_hello.sslv2); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), S2N_SSLv2); - }; - - s2n_config_free(tls12_config); - s2n_config_free(tls13_config); - s2n_cert_chain_and_key_free(chain_and_key); - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(tls13_chain_and_key); - free(tls13_cert_chain); - free(tls13_private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + struct s2n_stuffer *hello_stuffer = NULL; + struct s2n_config *tls12_config = NULL; + struct s2n_config *tls13_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char *cert_chain = NULL; + char *tls13_cert_chain = NULL; + char *private_key = NULL; + char *tls13_private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls12_config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_config, "test_all_tls12")); + + EXPECT_NOT_NULL(tls13_cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls13_private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls13_config = s2n_config_new()); + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "test_all")); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, tls13_chain_and_key)); + + /* These tests verify the logic behind the setting of these three connection fields: + server_protocol_version, client_protocol_version, and actual_protocol version. */ + + /* Test we can successfully receive an sslv2 client hello and set a + * tls12 connection */ + { + /* "enable" tls13, to test under default s2n-tls behavior */ + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello.sslv2 = true; + server_conn->client_hello.legacy_version = S2N_TLS12; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { + .data = sslv2_client_hello, + .size = sizeof(sslv2_client_hello), + .allocated = 0, + .growable = 0 + }; + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.sslv2, true); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.callback_invoked, 1); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server_conn), S2N_SSLv2); + + s2n_connection_free(server_conn); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Test that a tls12 client legacy version and tls12 server version + will successfully set a tls12 connection, since tls13 is not enabled. */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + }; + + /* Test that a tls12 client legacy version and tls12 server version + will successfully set a tls12 connection, even when tls13 is enabled. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test that a tls11 client legacy version and tls12 server version + will successfully set a tls11 connection. */ + for (uint8_t i = 0; i < 2; i++) { + if (i == 1) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + client_conn->client_protocol_version = S2N_TLS11; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS11); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS11); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS11); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS11); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + /* Test that a tls12 client and tls13 server will successfully + set a tls12 connection. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that an erroneous client legacy version and tls13 server version + will still successfully set a tls13 connection, when real client version is tls13. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ + uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + incorrect_protocol_version[0] = S2N_TLS13 / 10; + incorrect_protocol_version[1] = S2N_TLS13 % 10; + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test that a tls12 client legacy version and tls13 server version + will still successfully set a tls13 connection, if possible. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that an erroneous(tls13) client legacy version and tls13 server version + will still successfully set a tls12 connection, if tls12 is the true client version. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ + uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + incorrect_protocol_version[0] = S2N_TLS13 / 10; + incorrect_protocol_version[1] = S2N_TLS13 % 10; + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* s2n receiving a client hello will error when parsing an empty cipher suite */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + uint8_t empty_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + + /* Move write_cursor to cipher_suite position */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN + 1)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, empty_cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + }; + + /* s2n receiving a sslv2 client hello will error when parsing an empty cipher suite */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello.sslv2 = true; + server_conn->client_hello.legacy_version = S2N_TLS12; + + /* Writing a sslv2 client hello with a length 0 cipher suite list */ + uint8_t sslv2_client_hello[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x20, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { + .data = sslv2_client_hello, + .size = sizeof(sslv2_client_hello), + .allocated = 0, + .growable = 0 + }; + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(server_conn); + }; + + /* Test that S2N will accept a ClientHello with legacy_session_id set when running with QUIC. + * Since this requirement is a SHOULD, we're accepting it for non-compliant endpoints. + * https://tools.ietf.org/html/draft-ietf-quic-tls-32#section-8.4*/ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + const size_t test_session_id_len = 10; + + struct s2n_config *quic_config = NULL; + EXPECT_NOT_NULL(quic_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(quic_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(quic_config, "default_tls13")); + + /* Succeeds without a session id */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + /* Also, succeeds with a session id */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + /* Directly set session id, which is not set by default when using QUIC */ + client_conn->session_id_len = test_session_id_len; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + s2n_config_free(quic_config); + } + + /* Test that the server will not choose a signature algorithm or certificate if using PSKs */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + struct s2n_psk chosen_psk = { 0 }; + chosen_psk.hmac_alg = S2N_HMAC_SHA256; + server_conn->psk_params.chosen_psk = &chosen_psk; + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->iana_value, 0); + EXPECT_NULL(server_conn->handshake_params.our_chain_and_key); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that curve selection will be NIST P-256 when tls12 client does not send curve extension. + * + *= https://www.rfc-editor.org/rfc/rfc4492#section-4 + *= type=test + *# A client that proposes ECC cipher suites may choose not to include these extensions. + *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. + */ + { + S2N_BLOB_FROM_HEX(tls12_client_hello_no_curves, + /* clang-format off */ + "030307de81928fe1" "7cba77904c2798da" "2521a76b013a16e4" "21ade32208f658d4" "327d000048000400" + "05000a0016002f00" "3300350039003c00" "3d0067006b009c00" "9d009e009fc009c0" "0ac011c012c013c0" + "14c023c024c027c0" "28c02bc02cc02fc0" "30cca8cca9ccaaff" "04ff0800ff010000" "30000d0016001404" + "0105010601030104" "0305030603030302" "010203000b000201" "00fe01000c000a00" "17000d0013000100" + "0a" /* clang-format on */); + + /* The above code is generated the following code, + disabling s2n_client_supported_groups_extension + from client_hello_extensions (s2n_extension_type_lists.c) + and exporting the resulting client_conn->handshake.io.blob + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + s2n_connection_free(client_conn); + */ + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &tls12_client_hello_no_curves)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* ensure negotiated_curve == secp256r1 for maximum client compatibility */ + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, &s2n_ecc_curve_secp256r1); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test: Parse fragmented sslv2 client hello. + * + * Even if the sslv2 client hello is sent in one packet, there is no requirement + * that our first call to conn->recv returns the whole message. sslv2 uses separate + * record parsing code, so we need to ensure that those paths can handle partial reads. + */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_config)); + + struct s2n_stuffer server_in = { 0 }; + uint8_t sslv2_client_hello_bytes[] = { + SSLv2_CLIENT_HELLO_HEADER, + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + EXPECT_SUCCESS(s2n_blob_init(&server_in.blob, + sslv2_client_hello_bytes, sizeof(sslv2_client_hello_bytes))); + EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&server_in, server)); + + /* Read message one byte at a time */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + for (size_t i = 0; i < sizeof(sslv2_client_hello_bytes); i++) { + EXPECT_ERROR_WITH_ERRNO( + s2n_negotiate_until_message(server, &blocked, SERVER_HELLO), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&server_in, 1)); + } + + /* Successfully read the full message */ + EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); + EXPECT_EQUAL(server->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->client_hello.legacy_version, S2N_TLS12); + EXPECT_TRUE(server->client_hello.sslv2); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), S2N_SSLv2); + }; + + s2n_config_free(tls12_config); + s2n_config_free(tls13_config); + s2n_cert_chain_and_key_free(chain_and_key); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(tls13_chain_and_key); + free(tls13_cert_chain); + free(tls13_private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_retry_test.c b/tests/unit/s2n_client_hello_retry_test.c index 97c4a650277..f64223f484b 100644 --- a/tests/unit/s2n_client_hello_retry_test.c +++ b/tests/unit/s2n_client_hello_retry_test.c @@ -1,2010 +1,2011 @@ -/* - * 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 "crypto/s2n_pq.h" -#include "s2n.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_client_renegotiation_info.h" -#include "tls/extensions/s2n_cookie.h" -#include "tls/extensions/s2n_extension_type_lists.h" -#include "tls/extensions/s2n_server_supported_versions.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" - -/* This include is required to access static function s2n_server_hello_parse */ -#include "error/s2n_errno.h" -#include "tls/extensions/s2n_early_data_indication.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/s2n_server_hello.c" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define HELLO_RETRY_MSG_NO 1 -#define SERVER_HELLO_MSG_NO 5 - -int s2n_parse_client_hello(struct s2n_connection *conn); - -static int s2n_client_hello_cb_with_get_server_name(struct s2n_connection *conn, void *ctx) -{ - const char *expected_server_name = (const char *) ctx; - const char *actual_server_name = s2n_get_server_name(conn); - POSIX_ENSURE_EQ(strcmp(expected_server_name, actual_server_name), 0); - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Test s2n_server_hello_retry_recv */ - { - /* s2n_server_hello_retry_recv must fail when a keyshare for a matching curve was already present */ - { - struct s2n_config *config = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - const struct s2n_ecc_preferences *ecc_pref = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - EXPECT_NOT_NULL(ecc_pref); - - conn->actual_protocol_version = S2N_TLS13; - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - - EXPECT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* s2n_server_hello_retry_recv must fail for a connection with actual protocol version less than TLS13 */ - { - struct s2n_config *config = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - conn->actual_protocol_version = S2N_TLS12; - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test ECC success case for s2n_server_hello_retry_recv */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server sends HelloRetryMessage */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - /* Read the message off the wire */ - EXPECT_SUCCESS(s2n_server_hello_parse(client_conn)); - client_conn->actual_protocol_version_established = 1; - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(client_conn)); - /* Client receives the HelloRetryRequest mesage */ - EXPECT_SUCCESS(s2n_server_hello_retry_recv(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - { - const struct s2n_security_policy test_security_policy = { - .minimum_protocol_version = S2N_SSLv3, - .cipher_preferences = &cipher_preferences_test_all_tls13, - .kem_preferences = &kem_preferences_all, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20200310, - }; - - if (!s2n_pq_is_enabled()) { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->actual_protocol_version = S2N_TLS13; - conn->security_policy_override = &test_security_policy; - - const struct s2n_kem_preferences *kem_pref = NULL; - POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); - EXPECT_NOT_NULL(kem_pref); - - conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; - EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } else { - /* s2n_server_hello_retry_recv must fail when a keyshare for a matching PQ KEM was already present */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->actual_protocol_version = S2N_TLS13; - conn->security_policy_override = &test_security_policy; - - const struct s2n_kem_preferences *kem_pref = NULL; - POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); - EXPECT_NOT_NULL(kem_pref); - - const struct s2n_kem_group *kem_group = s2n_kem_preferences_get_highest_priority_group(kem_pref); - EXPECT_NOT_NULL(kem_group); - - conn->kex_params.server_kem_group_params.kem_group = kem_group; - EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); - - struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params; - client_params->kem_group = kem_group; - client_params->kem_params.kem = kem_group->kem; - client_params->ecc_params.negotiated_curve = kem_group->curve; - - EXPECT_NULL(client_params->ecc_params.evp_pkey); - EXPECT_NULL(client_params->kem_params.private_key.data); - - kem_public_key_size public_key_size = kem_group->kem->public_key_length; - EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, public_key_size)); - - EXPECT_OK(s2n_kem_generate_keypair(&client_params->kem_params)); - EXPECT_NOT_NULL(client_params->kem_params.private_key.data); - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); - EXPECT_NOT_NULL(client_params->ecc_params.evp_pkey); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_free(&client_params->kem_params.public_key)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - /* Test failure if exactly one of {named_curve, kem_group} isn't non-null */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->actual_protocol_version = S2N_TLS13; - conn->security_policy_override = &test_security_policy; - - conn->kex_params.server_kem_group_params.kem_group = &s2n_x25519_mlkem_768; - conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - conn->kex_params.server_kem_group_params.kem_group = NULL; - conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - /* Test PQ KEM success case for s2n_server_hello_retry_recv. */ - /* Need at least two KEM's to test fallback */ - uint32_t available_groups = 0; - EXPECT_OK(s2n_kem_preferences_groups_available(test_security_policy.kem_preferences, &available_groups)); - if (available_groups >= 2) { - struct s2n_config *config = NULL; - struct s2n_connection *conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->security_policy_override = &test_security_policy; - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); - - /* Client sends ClientHello containing key share for X25519MLKEM768 - * (but indicates support for SecP256r1MLKEM768 in supported_groups) */ - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - conn->session_id_len = 0; /* Wipe the session id to match the HRR hex */ - - /* Server responds with HRR indicating SecP256r1MLKEM768 as choice for negotiation; - * the last 6 bytes (0033 0002 11EB) are the key share extension with SecP256r1MLKEM768 */ - DEFER_CLEANUP(struct s2n_stuffer hrr = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_stuffer_alloc_from_hex(&hrr, - "0303CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C00130200000C002B000203040033000211EB")); - - EXPECT_SUCCESS(s2n_stuffer_copy(&hrr, &conn->handshake.io, s2n_stuffer_data_available(&hrr))); - conn->handshake.message_number = HELLO_RETRY_MSG_NO; - /* Read the message off the wire */ - EXPECT_SUCCESS(s2n_server_hello_parse(conn)); - conn->actual_protocol_version_established = 1; - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - /* Client receives the HelloRetryRequest message */ - EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - } - } - }; - }; - - /* Verify that the hash transcript recreation function is called correctly, - * within the s2n_server_hello_retry_send and s2n_server_hello_retry_recv functions. - * The hash transcript recreation function, if called correctly takes the existing ClientHello1 - * hash, and generates a synthetic message. This test verifies that transcript hash recreated is the same - * on both the server and client side. */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - /* Server sends HelloRetryRequest message, note that s2n_server_hello_retry_recreate_transcript - * is called within the s2n_server_hello_retry_send function */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - s2n_tls13_connection_keys(server_keys, server_conn); - uint8_t hash_digest_length = server_keys.size; - - /* Obtain the transcript hash recreated within the HelloRetryRequest message */ - DEFER_CLEANUP(struct s2n_hash_state server_hash = { 0 }, s2n_hash_free); - uint8_t server_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD(s2n_hash_new(&server_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(server_conn, server_keys.hash_algorithm, &server_hash)); - POSIX_GUARD(s2n_hash_digest(&server_hash, server_digest_out, hash_digest_length)); - - struct s2n_blob server_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&server_blob, server_digest_out, hash_digest_length)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - /* Client receives the HelloRetryRequest mesage, note that s2n_server_hello_retry_recreate_transcript - * is called within the s2n_server_hello_recv function */ - EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); - - s2n_tls13_connection_keys(client_keys, client_conn); - hash_digest_length = client_keys.size; - - /* Obtain the transcript hash recreated within ClientHello2 message */ - DEFER_CLEANUP(struct s2n_hash_state client_hash = { 0 }, s2n_hash_free); - uint8_t client_digest_out[S2N_MAX_DIGEST_LEN]; - POSIX_GUARD(s2n_hash_new(&client_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(client_conn, client_keys.hash_algorithm, &client_hash)); - POSIX_GUARD(s2n_hash_digest(&client_hash, client_digest_out, hash_digest_length)); - - struct s2n_blob client_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&client_blob, client_digest_out, hash_digest_length)); - - /* Test that the transcript hash recreated MUST be the same on the server and client side */ - S2N_BLOB_EXPECT_EQUAL(client_blob, server_blob); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - /** - * Self-Talk test: the client initiates a handshake with an unknown keyshare. - * The server sends a HelloRetryRequest that requires the client to generate a - * key share on the server negotiated curve. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Otherwise, the client MUST process all extensions in the - *# HelloRetryRequest and send a second updated ClientHello. - **/ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - server_conn->x509_validator.skip_cert_validation = 1; - client_conn->x509_validator.skip_cert_validation = 1; - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /** - * Self-Talk test: the client initiates a handshake with an X25519 share. - * The server, however does not support x25519 and prefers P-256. - * The server then sends a HelloRetryRequest that requires the - * client to generate a key share on the P-256 curve. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1 - *= type=test - *# If the server selects an (EC)DHE group and the client did not offer a - *# compatible "key_share" extension in the initial ClientHello, the - *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message. - **/ - if (s2n_is_evp_apis_supported()) { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); /* contains x25519 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190802")); /* does not contain x25519 */ - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - server_conn->x509_validator.skip_cert_validation = 1; - client_conn->x509_validator.skip_cert_validation = 1; - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - /* Ensure the handshake included a hello retry request */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - /** - * Ensure the client aborts the handshake if more than one - * HelloRetryRequest is received - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# If a client receives a second - *# HelloRetryRequest in the same connection (i.e., where the ClientHello - *# was itself in response to a HelloRetryRequest), it MUST abort the - *# handshake with an "unexpected_message" alert. - **/ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server HelloRetryRequest 1 */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); - - /* ClientHello 2 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - /* Server HelloRetryRequest 2 */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - /** - * Ensure that s2n_random_value_is_hello_retry returns true for hello - * retry random values, and false otherwise - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 - *= type=test - *# Upon receiving a message with type server_hello, implementations MUST - *# first examine the Random value and, if it matches this value, process - *# it as described in Section 4.1.4). - **/ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - const uint8_t not_hello_retry_request_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, not_hello_retry_request_random, - S2N_TLS_RANDOM_DATA_LEN); - - EXPECT_FAILURE_WITH_ERRNO(s2n_random_value_is_hello_retry(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_random_value_is_hello_retry(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Upon receiving - *# the ServerHello, clients MUST check that the cipher suite supplied in - *# the ServerHello is the same as that in the HelloRetryRequest and - *# otherwise abort the handshake with an "illegal_parameter" alert. - **/ - { - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - /* A Hello Retry Request has been processed */ - EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); - client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->handshake.handshake_type |= NEGOTIATED; - client_conn->handshake.handshake_type |= FULL_HANDSHAKE; - client_conn->handshake.message_number = SERVER_HELLO_MSG_NO; - - /* Server Hello with cipher suite that does not match Hello Retry Request cipher suite */ - server_conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; - EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* - * Self-Talk - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *= type=test - *# The client will also send a - *# ClientHello when the server has responded to its ClientHello with a - *# HelloRetryRequest. In that case, the client MUST send the same - *# ClientHello without modification - */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* Sanity Check: The server accepts an unchanged ClientHello */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Finish handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - }; - - /* Test: The server rejects a second ClientHello with a changed legacy version */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Change the legacy version. */ - client_conn->client_protocol_version = S2N_TLS11; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: The server rejects a second ClientHello with changed client random */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Change client random */ - client_conn->client_hello.random[0]++; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: Outside of testing, the server accepts a second ClientHello with a changed client random */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Change client random */ - client_conn->client_hello.random[0]++; - - /* Expect success if we pretend that this isn't a unit test */ - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - } - - /* Test: The server accepts a second ClientHello with a changed session ID */ - for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path. */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Change session id */ - client_conn->session_id[0]++; - - if (test_in_test_mode) { - /* Ensure that validation fails in test mode to prevent regressions. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - } else { - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - } - }; - - /* Test: The server accepts a second ClientHello with a changed cipher suite list */ - for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - struct s2n_security_policy test_policy = security_policy_test_tls13_retry; - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384 - }; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = s2n_array_len(test_cipher_suites), - .suites = test_cipher_suites, - }; - test_policy.cipher_preferences = &test_cipher_preferences; - - /* Force the HRR path. */ - client_conn->security_policy_override = &test_policy; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Modify a cipher suite. */ - test_cipher_suites[1] = &s2n_tls13_chacha20_poly1305_sha256; - - if (test_in_test_mode) { - /* Ensure that validation fails in test mode to prevent regressions. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - } else { - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - } - }; - - /* Test: Ensure that the connection fails if the cipher suite list changes such that the - * server cannot negotiate its original selection from the first ClientHello - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Servers MUST ensure that they negotiate the - *# same cipher suite when receiving a conformant updated ClientHello (if - *# the server selects the cipher suite as the first step in the - *# negotiation, then this will happen automatically). - **/ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - struct s2n_security_policy test_policy = security_policy_test_tls13_retry; - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384 - }; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = s2n_array_len(test_cipher_suites), - .suites = test_cipher_suites, - }; - test_policy.cipher_preferences = &test_cipher_preferences; - - /* Force the HRR path. */ - client_conn->security_policy_override = &test_policy; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Replace the most preferred cipher suite, forcing the server to select a different - * cipher suite when processing the second ClientHello. - */ - test_cipher_suites[0] = &s2n_tls13_chacha20_poly1305_sha256; - - /* Test mode is disabled to skip the failing ClientHello comparison check due to a - * changed cipher suite list. - */ - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - }; - - /* The server rejects a second ClientHello with a changed compression methods field */ - for (uint8_t test_compression_method = 0; test_compression_method <= 1; test_compression_method++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path. */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Send the second ClientHello. */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - struct s2n_stuffer client_hello_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_init_written(&client_hello_stuffer, &server_conn->handshake.io.blob)); - - /* Read up to the single null compression method byte */ - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_RANDOM_DATA_LEN)); - uint8_t session_id_len = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &session_id_len)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, session_id_len)); - uint16_t cipher_suites_len = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(&client_hello_stuffer, &cipher_suites_len)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, cipher_suites_len)); - uint8_t compression_methods_len = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &compression_methods_len)); - EXPECT_EQUAL(compression_methods_len, 1); - uint32_t compression_method_pos = client_hello_stuffer.read_cursor; - - /* Overwrite the compression method in the second ClientHello. */ - EXPECT_SUCCESS(s2n_stuffer_rewrite(&client_hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_hello_stuffer, compression_method_pos)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_hello_stuffer, test_compression_method)); - - if (test_compression_method == 0) { - /* A second ClientHello with a compression method of 0 shouldn't be different from - * the first ClientHello, so validation should succeed. - */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); - } - }; - - /* The server accepts a second ClientHello with a changed supported versions extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - struct s2n_security_policy test_policy = security_policy_test_tls13_retry; - test_policy.minimum_protocol_version = S2N_TLS10; - - /* Force the HRR path. */ - client_conn->security_policy_override = &test_policy; - - /* Skip to after the first ClientHello is received. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - ssize_t first_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS); - - /* Skip to before the client sends the second ClientHello. */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Increase the minimum protocol version for the security policy to send fewer - * supported versions in the second ClientHello. - */ - test_policy.minimum_protocol_version = S2N_TLS11; - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Ensure that the supported versions extension changed. */ - ssize_t second_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS); - EXPECT_TRUE(first_supported_versions_length != second_supported_versions_length); - } - - /* Test: The server rejects a supported versions extension with < TLS 1.3 in the second ClientHello */ - for (uint8_t test_version = S2N_TLS10; test_version <= S2N_TLS13; test_version++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path. */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Send the second ClientHello. */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Parse the ClientHello, but don't process the extensions yet. */ - EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); - server_conn->client_hello.parsed = true; - - uint8_t extension_data[3] = { 0 }; - struct s2n_blob extension_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); - struct s2n_stuffer extension_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); - - /* Overwrite the received supported versions extension with only the test version. */ - uint8_t extension_length = 2; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, extension_length)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version / 10)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version % 10)); - - struct s2n_client_hello *second_client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(second_client_hello); - s2n_extension_type_id supported_versions_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); - s2n_parsed_extension *extension = &second_client_hello->extensions.parsed_extensions[supported_versions_id]; - extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; - extension->extension = extension_blob; - - int ret = s2n_client_hello_recv(server_conn); - if (test_version == S2N_TLS13) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - } - - /* Test: The server rejects a second ClientHello with a changed extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Change server name */ - client_conn->server_name[0]++; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: The server rejects a second ClientHello with a removed extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Clear server name. - * Without a server name, we don't send the server name extension. */ - client_conn->server_name[0] = '\0'; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: The server rejects a second ClientHello with an added extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Add a server name. - * Without a server name, we don't send the server name extension. */ - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* - * Test: If the initial ClientHello includes all extensions, so does the second ClientHello. - * - * This includes TLS1.2 extensions, since the ClientHello is sent before - * the client knows what version the server will negotiate. - * - * We have to test with all extensions to ensure that an s2n server will never reject - * an s2n client's second ClientHello. - * - * TLS1.2 and TLS1.3 session tickets are mutually exclusive and use different - * extensions, so we test once with each. - */ - for (size_t tls13_tickets = 0; tls13_tickets < 2; tls13_tickets++) { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry_with_pq; - - /* Setup all extensions */ - uint8_t apn[] = "https"; - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "AWS-CRT-SDK-TLSv1.2-2025-PQ")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_4096)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_append_protocol_preference(client_conn, apn, sizeof(apn))); - EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); - client_conn->config->npn_supported = true; - if (tls13_tickets) { - EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); - } - EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); - /* Need to enable quic on both sides so they can communicate */ - EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); - - /* Send and receive ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* All ClientHello extensions must be present (except very specific exceptions) */ - s2n_extension_type_list *extensions = NULL; - EXPECT_SUCCESS(s2n_extension_type_list_get(S2N_EXTENSION_LIST_CLIENT_HELLO, &extensions)); - for (size_t i = 0; i < extensions->count; i++) { - uint16_t iana = extensions->extension_types[i]->iana_value; - - /* The cookie is a special case and only appears AFTER the retry */ - if (iana == TLS_EXTENSION_COOKIE) { - continue; - } - - /* PQ TLS 1.2 extension is not enabled */ - if (iana == TLS_EXTENSION_PQ_KEM_PARAMETERS) { - continue; - } - - /* TLS1.2 session tickets and TLS1.3 session tickets are mutually exclusive */ - if (tls13_tickets && iana == TLS_EXTENSION_SESSION_TICKET) { - continue; - } else if (!tls13_tickets - && (iana == TLS_EXTENSION_PRE_SHARED_KEY - || iana == TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES - || iana == TLS_EXTENSION_EARLY_DATA)) { - continue; - } - - /* No extension is sent for an initial handshake, - * and TLS1.3 doesn't support renegotiation handshakes. - */ - if (iana == TLS_EXTENSION_RENEGOTIATION_INFO) { - continue; - } - - bool extension_exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server_conn->client_hello, - iana, &extension_exists)); - EXPECT_TRUE(extension_exists); - } - - /* Expect successful retry */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - - /* Processing an extension early does not affect the extension matching check. - * - * This test exists because of a previously released bug. Triggering the bug - * required a specific series of events: - * - The first ClientHello is received. - * - The extensions are parsed. - * - The ClientHello callback triggers. - * - The customer's ClientHello callback implementation calls the s2n_get_server_name API. - * - To retrieve the server name, s2n_get_server_name processes the server name extension early. - * - The server name extension is wiped. Before the "processed" flag, we wiped extensions - * to mark that they had been processed. - * - The server sends a HelloRetryRequest, triggering a retry. - * - The second ClientHello is received. - * - The extensions are parsed. - * - The customer's ClientHello callback does NOT trigger this time. The callback only - * triggers after the first ClientHello. - * - Therefore, s2n_get_server_name is not called and the server name extension is not - * processed early or wiped. - * - The first ClientHello is compared to the second ClientHello. The second ClientHello - * appears to contain a server name extension not present in the first ClientHello. - * - The handshake fails because the ClientHellos must match. - */ - { - char server_name[] = "test server name"; - - DEFER_CLEANUP(struct s2n_config *config_with_cb = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cb, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_cb, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_with_cb)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cb)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cb)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Setup server name and client hello callback */ - EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_cb, - s2n_client_hello_cb_with_get_server_name, server_name)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Handshake should complete as expected */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(strcmp(s2n_get_server_name(server_conn), server_name), 0); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - }; - } - - /** - * Ensure all hello retry extensions sent by the server will have first - * been sent by the client. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any - *# extensions that were not first offered by the client in its - *# ClientHello, with the exception of optionally the "cookie" (see - *# Section 4.2.2) extension. - **/ - { - s2n_extension_type_list *hello_retry_extension_types = 0; - POSIX_GUARD(s2n_extension_type_list_get(S2N_EXTENSION_LIST_HELLO_RETRY_REQUEST, &hello_retry_extension_types)); - - for (int i = 0; i < hello_retry_extension_types->count; ++i) { - const s2n_extension_type *const extension_type = hello_retry_extension_types->extension_types[i]; - - /* with the exception of optionally the "cookie" extension. */ - if (extension_type->iana_value == TLS_EXTENSION_COOKIE) { - continue; - } - - EXPECT_TRUE(extension_type->is_response); - } - }; - - /** - * Ensure each of the following are checked: legacy_version, - * legacy_session_id_echo, cipher_suite, and - * legacy_compression_method - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version, legacy_session_id_echo, cipher_suite, and - *# legacy_compression_method as specified in Section 4.1.3 and then - *# process the extensions, starting with determining the version using - *# "supported_versions". - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* The client MUST check the legacy_version */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Force the server to send an erroneous legacy protocol version in the HelloRetryRequest message */ - server_conn->actual_protocol_version = S2N_TLS11; - - /* Server sends HelloRetryRequest */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_INVALID_HELLO_RETRY); - }; - - /* The client MUST check the legacy_session_id_echo */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Set a session id that's different from the client hello */ - POSIX_CHECKED_MEMSET(&server_conn->session_id, 0, S2N_TLS_SESSION_ID_MAX_LEN); - - /* Server sends HelloRetryRequest */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /** - * The client MUST check the cipher_suite - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# A client which receives a cipher suite that was not offered MUST - *# abort the handshake. - **/ - { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Receive ClientHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* - * Pick a cipher that wasn't offered in the CH, and should cause the - * handshake to abort. - */ - server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - /* Finish handshake */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CIPHER_NOT_SUPPORTED); - }; - - /* The client MUST check the legacy_compression_method */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server sends HelloRetryRequest */ - POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_server_hello_write_message(server_conn)); - - /* Overwrite compression method to 1 */ - struct s2n_stuffer *io = &server_conn->handshake.io; - io->write_cursor -= 1; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 1)); - - /* Write the extensions */ - EXPECT_SUCCESS(s2n_server_extensions_send(server_conn, &server_conn->handshake.io)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_BAD_MESSAGE); - }; - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# The server's extensions MUST contain "supported_versions". - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - POSIX_GUARD(s2n_server_hello_write_message(server_conn)); - struct s2n_stuffer_reservation total_extensions_size = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint16(&server_conn->handshake.io, &total_extensions_size)); - - /* Only send key share extension - exclude supported_versions */ - s2n_extension_send(&s2n_server_key_share_extension, server_conn, &server_conn->handshake.io); - - POSIX_GUARD(s2n_stuffer_write_vector_size(&total_extensions_size)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_MISSING_EXTENSION); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Servers MUST ensure that they negotiate the - *# same cipher suite when receiving a conformant updated ClientHello (if - *# the server selects the cipher suite as the first step in the - *# negotiation, then this will happen automatically). - **/ - { - /* Create a custom security policy so it can be changed mid-handshake */ - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384 - }; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = s2n_array_len(test_cipher_suites), - .suites = test_cipher_suites, - }; - struct s2n_security_policy security_policy_test_tls13_retry_temp = { - .minimum_protocol_version = S2N_TLS10, - .cipher_preferences = &test_cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20200207, - .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, - .ecc_preferences = &ecc_preferences_for_retry, - }; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Receive ClientHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* Rearrange the cipher preference order so that a different cipher will be - * picked by the server */ - server_conn->config->security_policy->cipher_preferences->suites[0] = &s2n_tls13_aes_256_gcm_sha384; - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /** - * Ensure that the client aborts the handshake if selected_version - * differs in the received server hellos - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# The value of selected_version in the HelloRetryRequest - *# "supported_versions" extension MUST be retained in the ServerHello, - *# and a client MUST abort the handshake with an "illegal_parameter" - *# alert if the value changes. - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Receive ClientHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* Skip to before server sends ServerHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Change the server_protocol_version so the value found in the ServerHello - * differs from the value found in the HelloRetryRequest */ - server_conn->server_protocol_version = S2N_TLS13 + 10; - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 - *= type=test - *# Upon receipt of this extension in a HelloRetryRequest, the client - *# MUST verify that (1) the selected_group field corresponds to a group - *# which was provided in the "supported_groups" extension in the - *# original ClientHello - **/ - { - /* Create a custom security policy without secp521r1 */ - const struct s2n_ecc_named_curve *const test_ecc_pref_list_for_retry[] = { - &s2n_ecc_curve_secp256r1, - &s2n_ecc_curve_secp384r1, - }; - const struct s2n_ecc_preferences test_ecc_preferences_for_retry = { - .count = s2n_array_len(test_ecc_pref_list_for_retry), - .ecc_curves = test_ecc_pref_list_for_retry, - }; - struct s2n_security_policy security_policy_test_tls13_retry_temp = { - .minimum_protocol_version = S2N_TLS10, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20200207, - .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, - .ecc_preferences = &test_ecc_preferences_for_retry, - }; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server sends HelloRetryRequest */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Set the curve to secp521r1, which was not provided in supported_groups */ - client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_INVALID_HELLO_RETRY); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 - *= type=test - *# If using (EC)DHE key establishment and a HelloRetryRequest containing a - *# "key_share" extension was received by the client, the client MUST - *# verify that the selected NamedGroup in the ServerHello is the same as - *# that in the HelloRetryRequest. If this check fails, the client MUST - *# abort the handshake with an "illegal_parameter" alert. - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before client receives ServerHello 2 */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, ENCRYPTED_EXTENSIONS)); - - /* Set the negotiated curve to something other than what was sent in the HRR */ - client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; - - /* Client receives ServerHello 2 */ - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(client_conn, &blocked, ENCRYPTED_EXTENSIONS), - S2N_ERR_BAD_MESSAGE); - }; - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_pq.h" +#include "s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_renegotiation_info.h" +#include "tls/extensions/s2n_cookie.h" +#include "tls/extensions/s2n_extension_type_lists.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +/* This include is required to access static function s2n_server_hello_parse */ +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_early_data_indication.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_server_hello.c" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define HELLO_RETRY_MSG_NO 1 +#define SERVER_HELLO_MSG_NO 5 + +int s2n_parse_client_hello(struct s2n_connection *conn); + +static int s2n_client_hello_cb_with_get_server_name(struct s2n_connection *conn, void *ctx) +{ + const char *expected_server_name = (const char *) ctx; + const char *actual_server_name = s2n_get_server_name(conn); + POSIX_ENSURE_EQ(strcmp(expected_server_name, actual_server_name), 0); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_server_hello_retry_recv */ + { + /* s2n_server_hello_retry_recv must fail when a keyshare for a matching curve was already present */ + { + struct s2n_config *config = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->actual_protocol_version = S2N_TLS13; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + EXPECT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_server_hello_retry_recv must fail for a connection with actual protocol version less than TLS13 */ + { + struct s2n_config *config = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test ECC success case for s2n_server_hello_retry_recv */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryMessage */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Read the message off the wire */ + EXPECT_SUCCESS(s2n_server_hello_parse(client_conn)); + client_conn->actual_protocol_version_established = 1; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(client_conn)); + /* Client receives the HelloRetryRequest mesage */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + { + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &kem_preferences_all, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + if (!s2n_pq_is_enabled()) { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } else { + /* s2n_server_hello_retry_recv must fail when a keyshare for a matching PQ KEM was already present */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + const struct s2n_kem_group *kem_group = s2n_kem_preferences_get_highest_priority_group(kem_pref); + EXPECT_NOT_NULL(kem_group); + + conn->kex_params.server_kem_group_params.kem_group = kem_group; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params; + client_params->kem_group = kem_group; + client_params->kem_params.kem = kem_group->kem; + client_params->ecc_params.negotiated_curve = kem_group->curve; + + EXPECT_NULL(client_params->ecc_params.evp_pkey); + EXPECT_NULL(client_params->kem_params.private_key.data); + + kem_public_key_size public_key_size = kem_group->kem->public_key_length; + EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, public_key_size)); + + EXPECT_OK(s2n_kem_generate_keypair(&client_params->kem_params)); + EXPECT_NOT_NULL(client_params->kem_params.private_key.data); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); + EXPECT_NOT_NULL(client_params->ecc_params.evp_pkey); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_free(&client_params->kem_params.public_key)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test failure if exactly one of {named_curve, kem_group} isn't non-null */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + conn->kex_params.server_kem_group_params.kem_group = &s2n_x25519_mlkem_768; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + conn->kex_params.server_kem_group_params.kem_group = NULL; + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test PQ KEM success case for s2n_server_hello_retry_recv. */ + /* Need at least two KEM's to test fallback */ + uint32_t available_groups = 0; + EXPECT_OK(s2n_kem_preferences_groups_available(test_security_policy.kem_preferences, &available_groups)); + if (available_groups >= 2) { + struct s2n_config *config = NULL; + struct s2n_connection *conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->security_policy_override = &test_security_policy; + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); + + /* Client sends ClientHello containing key share for X25519MLKEM768 + * (but indicates support for SecP256r1MLKEM768 in supported_groups) */ + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + conn->session_id_len = 0; /* Wipe the session id to match the HRR hex */ + + /* Server responds with HRR indicating SecP256r1MLKEM768 as choice for negotiation; + * the last 6 bytes (0033 0002 11EB) are the key share extension with SecP256r1MLKEM768 */ + DEFER_CLEANUP(struct s2n_stuffer hrr = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_stuffer_alloc_from_hex(&hrr, + "0303CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C00130200000C002B000203040033000211EB")); + + EXPECT_SUCCESS(s2n_stuffer_copy(&hrr, &conn->handshake.io, s2n_stuffer_data_available(&hrr))); + conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Read the message off the wire */ + EXPECT_SUCCESS(s2n_server_hello_parse(conn)); + conn->actual_protocol_version_established = 1; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + /* Client receives the HelloRetryRequest message */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + } + } + }; + }; + + /* Verify that the hash transcript recreation function is called correctly, + * within the s2n_server_hello_retry_send and s2n_server_hello_retry_recv functions. + * The hash transcript recreation function, if called correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. This test verifies that transcript hash recreated is the same + * on both the server and client side. */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + /* Server sends HelloRetryRequest message, note that s2n_server_hello_retry_recreate_transcript + * is called within the s2n_server_hello_retry_send function */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + s2n_tls13_connection_keys(server_keys, server_conn); + uint8_t hash_digest_length = server_keys.size; + + /* Obtain the transcript hash recreated within the HelloRetryRequest message */ + DEFER_CLEANUP(struct s2n_hash_state server_hash = { 0 }, s2n_hash_free); + uint8_t server_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_new(&server_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(server_conn, server_keys.hash_algorithm, &server_hash)); + POSIX_GUARD(s2n_hash_digest(&server_hash, server_digest_out, hash_digest_length)); + + struct s2n_blob server_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&server_blob, server_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Client receives the HelloRetryRequest mesage, note that s2n_server_hello_retry_recreate_transcript + * is called within the s2n_server_hello_recv function */ + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + s2n_tls13_connection_keys(client_keys, client_conn); + hash_digest_length = client_keys.size; + + /* Obtain the transcript hash recreated within ClientHello2 message */ + DEFER_CLEANUP(struct s2n_hash_state client_hash = { 0 }, s2n_hash_free); + uint8_t client_digest_out[S2N_MAX_DIGEST_LEN]; + POSIX_GUARD(s2n_hash_new(&client_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(client_conn, client_keys.hash_algorithm, &client_hash)); + POSIX_GUARD(s2n_hash_digest(&client_hash, client_digest_out, hash_digest_length)); + + struct s2n_blob client_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&client_blob, client_digest_out, hash_digest_length)); + + /* Test that the transcript hash recreated MUST be the same on the server and client side */ + S2N_BLOB_EXPECT_EQUAL(client_blob, server_blob); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /** + * Self-Talk test: the client initiates a handshake with an unknown keyshare. + * The server sends a HelloRetryRequest that requires the client to generate a + * key share on the server negotiated curve. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Otherwise, the client MUST process all extensions in the + *# HelloRetryRequest and send a second updated ClientHello. + **/ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->x509_validator.skip_cert_validation = 1; + client_conn->x509_validator.skip_cert_validation = 1; + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + * Self-Talk test: the client initiates a handshake with an X25519 share. + * The server, however does not support x25519 and prefers P-256. + * The server then sends a HelloRetryRequest that requires the + * client to generate a key share on the P-256 curve. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1 + *= type=test + *# If the server selects an (EC)DHE group and the client did not offer a + *# compatible "key_share" extension in the initial ClientHello, the + *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message. + **/ + if (s2n_is_evp_apis_supported()) { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); /* contains x25519 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190802")); /* does not contain x25519 */ + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->x509_validator.skip_cert_validation = 1; + client_conn->x509_validator.skip_cert_validation = 1; + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Ensure the handshake included a hello retry request */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /** + * Ensure the client aborts the handshake if more than one + * HelloRetryRequest is received + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# If a client receives a second + *# HelloRetryRequest in the same connection (i.e., where the ClientHello + *# was itself in response to a HelloRetryRequest), it MUST abort the + *# handshake with an "unexpected_message" alert. + **/ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server HelloRetryRequest 1 */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* ClientHello 2 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* Server HelloRetryRequest 2 */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /** + * Ensure that s2n_random_value_is_hello_retry returns true for hello + * retry random values, and false otherwise + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 + *= type=test + *# Upon receiving a message with type server_hello, implementations MUST + *# first examine the Random value and, if it matches this value, process + *# it as described in Section 4.1.4). + **/ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + const uint8_t not_hello_retry_request_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, not_hello_retry_request_random, + S2N_TLS_RANDOM_DATA_LEN); + + EXPECT_FAILURE_WITH_ERRNO(s2n_random_value_is_hello_retry(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_random_value_is_hello_retry(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Upon receiving + *# the ServerHello, clients MUST check that the cipher suite supplied in + *# the ServerHello is the same as that in the HelloRetryRequest and + *# otherwise abort the handshake with an "illegal_parameter" alert. + **/ + { + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* A Hello Retry Request has been processed */ + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->handshake.handshake_type |= NEGOTIATED; + client_conn->handshake.handshake_type |= FULL_HANDSHAKE; + client_conn->handshake.message_number = SERVER_HELLO_MSG_NO; + + /* Server Hello with cipher suite that does not match Hello Retry Request cipher suite */ + server_conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* + * Self-Talk + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *= type=test + *# The client will also send a + *# ClientHello when the server has responded to its ClientHello with a + *# HelloRetryRequest. In that case, the client MUST send the same + *# ClientHello without modification + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* Sanity Check: The server accepts an unchanged ClientHello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Finish handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test: The server rejects a second ClientHello with a changed legacy version */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change the legacy version. */ + client_conn->client_protocol_version = S2N_TLS11; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with changed client random */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change client random */ + client_conn->client_hello.random[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: Outside of testing, the server accepts a second ClientHello with a changed client random */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change client random */ + client_conn->client_hello.random[0]++; + + /* Expect success if we pretend that this isn't a unit test */ + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + + /* Test: The server accepts a second ClientHello with a changed session ID */ + for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path. */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change session id */ + client_conn->session_id[0]++; + + if (test_in_test_mode) { + /* Ensure that validation fails in test mode to prevent regressions. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } else { + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + }; + + /* Test: The server accepts a second ClientHello with a changed cipher suite list */ + for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_security_policy test_policy = security_policy_test_tls13_retry; + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + test_policy.cipher_preferences = &test_cipher_preferences; + + /* Force the HRR path. */ + client_conn->security_policy_override = &test_policy; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Modify a cipher suite. */ + test_cipher_suites[1] = &s2n_tls13_chacha20_poly1305_sha256; + + if (test_in_test_mode) { + /* Ensure that validation fails in test mode to prevent regressions. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } else { + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + }; + + /* Test: Ensure that the connection fails if the cipher suite list changes such that the + * server cannot negotiate its original selection from the first ClientHello + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_security_policy test_policy = security_policy_test_tls13_retry; + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + test_policy.cipher_preferences = &test_cipher_preferences; + + /* Force the HRR path. */ + client_conn->security_policy_override = &test_policy; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Replace the most preferred cipher suite, forcing the server to select a different + * cipher suite when processing the second ClientHello. + */ + test_cipher_suites[0] = &s2n_tls13_chacha20_poly1305_sha256; + + /* Test mode is disabled to skip the failing ClientHello comparison check due to a + * changed cipher suite list. + */ + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + }; + + /* The server rejects a second ClientHello with a changed compression methods field */ + for (uint8_t test_compression_method = 0; test_compression_method <= 1; test_compression_method++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path. */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Send the second ClientHello. */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + struct s2n_stuffer client_hello_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init_written(&client_hello_stuffer, &server_conn->handshake.io.blob)); + + /* Read up to the single null compression method byte */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_RANDOM_DATA_LEN)); + uint8_t session_id_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &session_id_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, session_id_len)); + uint16_t cipher_suites_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&client_hello_stuffer, &cipher_suites_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, cipher_suites_len)); + uint8_t compression_methods_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &compression_methods_len)); + EXPECT_EQUAL(compression_methods_len, 1); + uint32_t compression_method_pos = client_hello_stuffer.read_cursor; + + /* Overwrite the compression method in the second ClientHello. */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(&client_hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_hello_stuffer, compression_method_pos)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_hello_stuffer, test_compression_method)); + + if (test_compression_method == 0) { + /* A second ClientHello with a compression method of 0 shouldn't be different from + * the first ClientHello, so validation should succeed. + */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + } + }; + + /* The server accepts a second ClientHello with a changed supported versions extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_security_policy test_policy = security_policy_test_tls13_retry; + test_policy.minimum_protocol_version = S2N_TLS10; + + /* Force the HRR path. */ + client_conn->security_policy_override = &test_policy; + + /* Skip to after the first ClientHello is received. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + ssize_t first_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS); + + /* Skip to before the client sends the second ClientHello. */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Increase the minimum protocol version for the security policy to send fewer + * supported versions in the second ClientHello. + */ + test_policy.minimum_protocol_version = S2N_TLS11; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Ensure that the supported versions extension changed. */ + ssize_t second_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS); + EXPECT_TRUE(first_supported_versions_length != second_supported_versions_length); + } + + /* Test: The server rejects a supported versions extension with < TLS 1.3 in the second ClientHello */ + for (uint8_t test_version = S2N_TLS10; test_version <= S2N_TLS13; test_version++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path. */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Send the second ClientHello. */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Parse the ClientHello, but don't process the extensions yet. */ + EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); + server_conn->client_hello.parsed = true; + + uint8_t extension_data[3] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + + /* Overwrite the received supported versions extension with only the test version. */ + uint8_t extension_length = 2; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, extension_length)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version % 10)); + + struct s2n_client_hello *second_client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(second_client_hello); + s2n_extension_type_id supported_versions_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + s2n_parsed_extension *extension = &second_client_hello->extensions.parsed_extensions[supported_versions_id]; + extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; + extension->extension = extension_blob; + + int ret = s2n_client_hello_recv(server_conn); + if (test_version == S2N_TLS13) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + } + + /* Test: The server rejects a second ClientHello with a changed extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change server name */ + client_conn->server_name[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with a removed extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Clear server name. + * Without a server name, we don't send the server name extension. */ + client_conn->server_name[0] = '\0'; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with an added extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Add a server name. + * Without a server name, we don't send the server name extension. */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* + * Test: If the initial ClientHello includes all extensions, so does the second ClientHello. + * + * This includes TLS1.2 extensions, since the ClientHello is sent before + * the client knows what version the server will negotiate. + * + * We have to test with all extensions to ensure that an s2n server will never reject + * an s2n client's second ClientHello. + * + * TLS1.2 and TLS1.3 session tickets are mutually exclusive and use different + * extensions, so we test once with each. + */ + for (size_t tls13_tickets = 0; tls13_tickets < 2; tls13_tickets++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_with_pq; + + /* Setup all extensions */ + uint8_t apn[] = "https"; + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "AWS-CRT-SDK-TLSv1.2-2025-PQ")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_4096)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(client_conn, apn, sizeof(apn))); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + client_conn->config->npn_supported = true; + if (tls13_tickets) { + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + } + EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); + /* Need to enable quic on both sides so they can communicate */ + EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); + + /* Send and receive ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* All ClientHello extensions must be present (except very specific exceptions) */ + s2n_extension_type_list *extensions = NULL; + EXPECT_SUCCESS(s2n_extension_type_list_get(S2N_EXTENSION_LIST_CLIENT_HELLO, &extensions)); + for (size_t i = 0; i < extensions->count; i++) { + uint16_t iana = extensions->extension_types[i]->iana_value; + + /* The cookie is a special case and only appears AFTER the retry */ + if (iana == TLS_EXTENSION_COOKIE) { + continue; + } + + /* PQ TLS 1.2 extension is not enabled */ + if (iana == TLS_EXTENSION_PQ_KEM_PARAMETERS) { + continue; + } + + /* TLS1.2 session tickets and TLS1.3 session tickets are mutually exclusive */ + if (tls13_tickets && iana == TLS_EXTENSION_SESSION_TICKET) { + continue; + } else if (!tls13_tickets + && (iana == TLS_EXTENSION_PRE_SHARED_KEY + || iana == TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES + || iana == TLS_EXTENSION_EARLY_DATA)) { + continue; + } + + /* No extension is sent for an initial handshake, + * and TLS1.3 doesn't support renegotiation handshakes. + */ + if (iana == TLS_EXTENSION_RENEGOTIATION_INFO) { + continue; + } + + bool extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server_conn->client_hello, + iana, &extension_exists)); + EXPECT_TRUE(extension_exists); + } + + /* Expect successful retry */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + /* Processing an extension early does not affect the extension matching check. + * + * This test exists because of a previously released bug. Triggering the bug + * required a specific series of events: + * - The first ClientHello is received. + * - The extensions are parsed. + * - The ClientHello callback triggers. + * - The customer's ClientHello callback implementation calls the s2n_get_server_name API. + * - To retrieve the server name, s2n_get_server_name processes the server name extension early. + * - The server name extension is wiped. Before the "processed" flag, we wiped extensions + * to mark that they had been processed. + * - The server sends a HelloRetryRequest, triggering a retry. + * - The second ClientHello is received. + * - The extensions are parsed. + * - The customer's ClientHello callback does NOT trigger this time. The callback only + * triggers after the first ClientHello. + * - Therefore, s2n_get_server_name is not called and the server name extension is not + * processed early or wiped. + * - The first ClientHello is compared to the second ClientHello. The second ClientHello + * appears to contain a server name extension not present in the first ClientHello. + * - The handshake fails because the ClientHellos must match. + */ + { + char server_name[] = "test server name"; + + DEFER_CLEANUP(struct s2n_config *config_with_cb = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cb, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_cb, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_with_cb)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cb)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cb)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Setup server name and client hello callback */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_cb, + s2n_client_hello_cb_with_get_server_name, server_name)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Handshake should complete as expected */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(strcmp(s2n_get_server_name(server_conn), server_name), 0); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + }; + } + + /** + * Ensure all hello retry extensions sent by the server will have first + * been sent by the client. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + { + s2n_extension_type_list *hello_retry_extension_types = 0; + POSIX_GUARD(s2n_extension_type_list_get(S2N_EXTENSION_LIST_HELLO_RETRY_REQUEST, &hello_retry_extension_types)); + + for (int i = 0; i < hello_retry_extension_types->count; ++i) { + const s2n_extension_type *const extension_type = hello_retry_extension_types->extension_types[i]; + + /* with the exception of optionally the "cookie" extension. */ + if (extension_type->iana_value == TLS_EXTENSION_COOKIE) { + continue; + } + + EXPECT_TRUE(extension_type->is_response); + } + }; + + /** + * Ensure each of the following are checked: legacy_version, + * legacy_session_id_echo, cipher_suite, and + * legacy_compression_method + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo, cipher_suite, and + *# legacy_compression_method as specified in Section 4.1.3 and then + *# process the extensions, starting with determining the version using + *# "supported_versions". + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* The client MUST check the legacy_version */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Force the server to send an erroneous legacy protocol version in the HelloRetryRequest message */ + server_conn->actual_protocol_version = S2N_TLS11; + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_INVALID_HELLO_RETRY); + }; + + /* The client MUST check the legacy_session_id_echo */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Set a session id that's different from the client hello */ + POSIX_CHECKED_MEMSET(&server_conn->session_id, 0, S2N_TLS_SESSION_ID_MAX_LEN); + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + * The client MUST check the cipher_suite + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# A client which receives a cipher suite that was not offered MUST + *# abort the handshake. + **/ + { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* + * Pick a cipher that wasn't offered in the CH, and should cause the + * handshake to abort. + */ + server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + /* Finish handshake */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CIPHER_NOT_SUPPORTED); + }; + + /* The client MUST check the legacy_compression_method */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_server_hello_write_message(server_conn)); + + /* Overwrite compression method to 1 */ + struct s2n_stuffer *io = &server_conn->handshake.io; + io->write_cursor -= 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 1)); + + /* Write the extensions */ + EXPECT_SUCCESS(s2n_server_extensions_send(server_conn, &server_conn->handshake.io)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_BAD_MESSAGE); + }; + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# The server's extensions MUST contain "supported_versions". + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + POSIX_GUARD(s2n_server_hello_write_message(server_conn)); + struct s2n_stuffer_reservation total_extensions_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(&server_conn->handshake.io, &total_extensions_size)); + + /* Only send key share extension - exclude supported_versions */ + s2n_extension_send(&s2n_server_key_share_extension, server_conn, &server_conn->handshake.io); + + POSIX_GUARD(s2n_stuffer_write_vector_size(&total_extensions_size)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_MISSING_EXTENSION); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + { + /* Create a custom security policy so it can be changed mid-handshake */ + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + struct s2n_security_policy security_policy_test_tls13_retry_temp = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &test_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &ecc_preferences_for_retry, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* Rearrange the cipher preference order so that a different cipher will be + * picked by the server */ + server_conn->config->security_policy->cipher_preferences->suites[0] = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + * Ensure that the client aborts the handshake if selected_version + * differs in the received server hellos + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# The value of selected_version in the HelloRetryRequest + *# "supported_versions" extension MUST be retained in the ServerHello, + *# and a client MUST abort the handshake with an "illegal_parameter" + *# alert if the value changes. + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* Skip to before server sends ServerHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change the server_protocol_version so the value found in the ServerHello + * differs from the value found in the HelloRetryRequest */ + server_conn->server_protocol_version = S2N_TLS13 + 10; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 + *= type=test + *# Upon receipt of this extension in a HelloRetryRequest, the client + *# MUST verify that (1) the selected_group field corresponds to a group + *# which was provided in the "supported_groups" extension in the + *# original ClientHello + **/ + { + /* Create a custom security policy without secp521r1 */ + const struct s2n_ecc_named_curve *const test_ecc_pref_list_for_retry[] = { + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_secp384r1, + }; + const struct s2n_ecc_preferences test_ecc_preferences_for_retry = { + .count = s2n_array_len(test_ecc_pref_list_for_retry), + .ecc_curves = test_ecc_pref_list_for_retry, + }; + struct s2n_security_policy security_policy_test_tls13_retry_temp = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &test_ecc_preferences_for_retry, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Set the curve to secp521r1, which was not provided in supported_groups */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_INVALID_HELLO_RETRY); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 + *= type=test + *# If using (EC)DHE key establishment and a HelloRetryRequest containing a + *# "key_share" extension was received by the client, the client MUST + *# verify that the selected NamedGroup in the ServerHello is the same as + *# that in the HelloRetryRequest. If this check fails, the client MUST + *# abort the handshake with an "illegal_parameter" alert. + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before client receives ServerHello 2 */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, ENCRYPTED_EXTENSIONS)); + + /* Set the negotiated curve to something other than what was sent in the HRR */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + /* Client receives ServerHello 2 */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(client_conn, &blocked, ENCRYPTED_EXTENSIONS), + S2N_ERR_BAD_MESSAGE); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_test.c b/tests/unit/s2n_client_hello_test.c index 681277f7058..fe2a477a1d0 100644 --- a/tests/unit/s2n_client_hello_test.c +++ b/tests/unit/s2n_client_hello_test.c @@ -1,2145 +1,2146 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_client_hello.h" - -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_sslv2_client_hello.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -#define LENGTH_TO_SESSION_ID (S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN) -#define TLS12_LENGTH_TO_CIPHER_LIST (LENGTH_TO_SESSION_ID + 1) -#define TLS13_LENGTH_TO_CIPHER_LIST (TLS12_LENGTH_TO_CIPHER_LIST + S2N_TLS_SESSION_ID_MAX_LEN) - -#define COMPRESSION_METHODS 0x00, 0x01, 0x02, 0x03, 0x04 -#define COMPRESSION_METHODS_LEN 0x05 - -int s2n_parse_client_hello(struct s2n_connection *conn); -S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, - struct s2n_blob *raw_extensions, struct s2n_blob *extension); - -int main(int argc, char **argv) -{ - struct s2n_cert_chain_and_key *chain_and_key = NULL, *ecdsa_chain_and_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Test s2n_client_hello_get_extension_by_id */ - { - /* Test with invalid parsed extensions */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - s2n_tls_extension_type test_extension_type = S2N_EXTENSION_SERVER_NAME; - - s2n_extension_type_id test_extension_type_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_extension_type, &test_extension_type_id)); - - uint8_t data[] = "data"; - s2n_parsed_extension *parsed_extension = &conn->client_hello.extensions.parsed_extensions[test_extension_type_id]; - parsed_extension->extension_type = test_extension_type; - parsed_extension->extension.data = data; - parsed_extension->extension.size = sizeof(data); - - /* Succeeds with correct extension type */ - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, - test_extension_type, data, sizeof(data)), - sizeof(data)); - - /* Fails with wrong extension type */ - parsed_extension->extension_type = test_extension_type + 1; - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, - test_extension_type, data, sizeof(data)), - 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test s2n_client_hello_has_extension */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - uint8_t data[] = { - /* arbitrary extension with 2 data */ - 0xFF, 0x00, /* extension type */ - 0x00, 0x02, /* extension payload length */ - 0xAB, 0xCD, /* extension payload */ - /* Encrypt then mac extension without data */ - 0x00, 0x16, - 0x00, 0x00 - }; - - struct s2n_blob *raw_extension = &conn->client_hello.extensions.raw; - raw_extension->data = data; - raw_extension->size = sizeof(data); - - /* Succeeds on an unsupported extension with no payload */ - bool exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0x0016, &exists)); - EXPECT_TRUE(exists); - - /* Succeeds on an unsupported extension with payload */ - exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFF00, &exists)); - EXPECT_TRUE(exists); - - /* Succeeds with an invalid extension */ - exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFFFF, &exists)); - EXPECT_FALSE(exists); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test s2n_client_hello_has_extension with a zero-length extension */ - for (int send_sct = 0; send_sct <= 1; send_sct++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - /* The SCT extension is zero-length. */ - if (send_sct) { - EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST)); - } - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, - s2n_stuffer_data_available(&client->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); - EXPECT_NOT_NULL(client_hello); - - s2n_parsed_extension *sct_extension = NULL; - int ret = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &client_hello->extensions, - &sct_extension); - - if (send_sct) { - /* Ensure that the extension was received. */ - EXPECT_SUCCESS(ret); - POSIX_ENSURE_REF(sct_extension); - - /* Ensure that the extension is zero-length. */ - EXPECT_EQUAL(sct_extension->extension.size, 0); - } else { - /* The extension shouldn't have been received because it wasn't requested. */ - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); - } - } - - /* Test s2n_client_hello_get_raw_extension */ - { - uint8_t data[] = { - /* arbitrary extension with 2 data */ - 0xFF, 0x00, /* extension type */ - 0x00, 0x02, /* extension payload length */ - 0xAB, 0xCD, /* extension payload */ - /* NPN extension without data */ - 0x33, 0x74, - 0x00, 0x00 - }; - struct s2n_blob raw_extension = { - .data = data, - .size = sizeof(data), - }; - - struct s2n_blob extension = { 0 }; - /* Succeeds with extension exists without payload */ - EXPECT_OK(s2n_client_hello_get_raw_extension(0x3374, &raw_extension, &extension)); - EXPECT_EQUAL(extension.size, 0); - EXPECT_NOT_NULL(extension.data); - - /* Succeeds with extension exists with payload */ - extension = (struct s2n_blob){ 0 }; - EXPECT_OK(s2n_client_hello_get_raw_extension(0xFF00, &raw_extension, &extension)); - EXPECT_EQUAL(extension.size, 2); - EXPECT_NOT_NULL(extension.data); - EXPECT_BYTEARRAY_EQUAL(extension.data, &data[4], 2); - - /* Failed with extension not exist */ - extension = (struct s2n_blob){ 0 }; - EXPECT_OK(s2n_client_hello_get_raw_extension(0xFFFF, &raw_extension, &extension)); - EXPECT_EQUAL(extension.size, 0); - EXPECT_NULL(extension.data); - }; - - /* Test setting cert chain on recv */ - { - s2n_enable_tls13_in_test(); - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - - /* TLS13 fails to parse client hello when no certs set */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->client_protocol_version = conn->server_protocol_version; - conn->actual_protocol_version = conn->client_protocol_version; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(conn), S2N_ERR_NO_VALID_SIGNATURE_SCHEME); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - - /* TLS13 successfully sets certs */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->client_protocol_version = conn->server_protocol_version; - conn->actual_protocol_version = conn->client_protocol_version; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); - EXPECT_SUCCESS(s2n_client_hello_recv(conn)); - - EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - s2n_disable_tls13_in_test(); - }; - - /* Test getting supported versions from the client hello */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - /* TLS13 has supported versions in the client hello */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - uint8_t supported_versions[256] = { 0 }; - uint8_t size_of_version_list = 0; - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, - &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); - size_of_version_list = supported_versions[0]; - /* No supported versions before the handshake is received */ - EXPECT_EQUAL(0, size_of_version_list); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); - size_of_version_list = supported_versions[0]; - EXPECT_TRUE(size_of_version_list > 0); - bool found_tls13 = false; - const uint8_t tls13_bytes[] = { 0x03, 0x04 }; - const size_t supported_version_size = sizeof(tls13_bytes); - for (uint16_t offset = 1; offset < size_of_version_list; offset += supported_version_size) { - if (memcmp(tls13_bytes, &supported_versions[offset], supported_version_size) == 0) { - found_tls13 = true; - } - } - EXPECT_TRUE(found_tls13); - }; - s2n_disable_tls13_in_test(); - }; - - /* Test generating session id */ - { - const uint8_t test_session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 7 }; - - /* Use session id if already generated */ - for (uint8_t i = S2N_TLS10; i <= S2N_TLS13; i++) { - if (i >= S2N_TLS13) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - } - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - conn->actual_protocol_version = i; - - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - EXPECT_MEMCPY_SUCCESS(conn->session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - - uint8_t *session_id = NULL; - EXPECT_NOT_NULL(session_id = s2n_stuffer_raw_read(hello_stuffer, S2N_TLS_SESSION_ID_MAX_LEN)); - EXPECT_BYTEARRAY_EQUAL(session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* With TLS1.3 */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Generate a session id by default */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Do NOT generate a session id if middlebox compatibility mode is disabled. - * For now, middlebox compatibility mode is only disabled by QUIC. - */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_enable_quic(config)); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Generate a session id if trying to resume a handshake.io; - conn->resume_protocol_version = S2N_TLS12; - EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); - EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - }; - - /* Fail if we need to generate a session id to resume a resume_protocol_version = S2N_TLS12; - EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); - EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); - conn->quic_enabled = true; - - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), - S2N_ERR_UNSUPPORTED_WITH_QUIC); - }; - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* With TLS1.2 */ - { - /* Do NOT generate a session id by default */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Generate a session id if using tickets */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - }; - }; - - /* Test cipher suites list */ - { - /* When TLS 1.3 NOT supported */ - { - /* TLS 1.3 cipher suites NOT written by client by default */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); - - uint16_t list_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); - EXPECT_NOT_EQUAL(list_length, 0); - - uint8_t first_cipher_byte = 0; - for (int i = 0; i < list_length; i++) { - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); - EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS 1.3 cipher suites NOT written by client even if included in security policy */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); - - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); - - uint16_t list_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); - EXPECT_NOT_EQUAL(list_length, 0); - - uint8_t first_cipher_byte = 0; - for (int i = 0; i < list_length; i++) { - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); - EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* When TLS 1.3 supported */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - s2n_config_set_session_tickets_onoff(config, 0); - - /* TLS 1.3 cipher suites written by client */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - - EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS13_LENGTH_TO_CIPHER_LIST)); - - uint16_t list_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); - EXPECT_NOT_EQUAL(list_length, 0); - - uint8_t first_cipher_byte = 0; - int tls13_ciphers_found = 0; - for (int i = 0; i < list_length; i++) { - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); - if (first_cipher_byte == 0x13) { - tls13_ciphers_found++; - } - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); - } - EXPECT_NOT_EQUAL(tls13_ciphers_found, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* TLS_EMPTY_RENEGOTIATION_INFO_SCSV included if TLS1.2 ciphers included - * - *= https://www.rfc-editor.org/rfc/rfc5746#3.4 - *= type=test - *# o The client MUST include either an empty "renegotiation_info" - *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling - *# cipher suite value in the ClientHello. - */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - const uint8_t empty_renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; - - struct { - const char *security_policy; - bool expect_renegotiation_info; - } test_cases[] = { - { .security_policy = "test_all_tls13", .expect_renegotiation_info = false }, - { .security_policy = "default_tls13", .expect_renegotiation_info = true }, - /* 20240501 can only negotiate up to tls12 */ - { .security_policy = "20240501", .expect_renegotiation_info = true }, - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_parse_client_hello(conn)); - - struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; - EXPECT_TRUE(cipher_suites->size > 0); - - uint8_t *iana = cipher_suites->data; - bool found_renegotiation_info = false; - for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { - if (memcmp(iana + j, empty_renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN) == 0) { - found_renegotiation_info = true; - } - } - - EXPECT_EQUAL(found_renegotiation_info, test_cases[i].expect_renegotiation_info); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - } - - /* TLS1.2 cipher suites not written if QUIC enabled */ - { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - bool quic_enabled[] = { false, s2n_is_tls13_fully_supported() }; - - /* TLS 1.2 cipher suites only written if QUIC not enabled */ - for (size_t i = 0; i < s2n_array_len(quic_enabled); i++) { - config->quic_enabled = quic_enabled[i]; - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_parse_client_hello(conn)); - - struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; - EXPECT_TRUE(cipher_suites->size > 0); - - bool tls12_cipher_found = false; - uint8_t *iana = cipher_suites->data; - for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { - /* All TLS1.3 cipher suites have IANAs starting with 0x13 */ - if (iana[j] != 0x13) { - tls12_cipher_found = true; - } - } - - /* TLS1.2 and QUIC are mutually exclusive */ - EXPECT_TRUE(tls12_cipher_found != quic_enabled[i]); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Error if no cipher suites written. */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - struct s2n_cipher_suite cipher_suite = s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - struct s2n_cipher_suite *cipher_suites = &cipher_suite; - struct s2n_cipher_preferences cipher_preferences = { - .suites = &cipher_suites, - .count = 1, - }; - struct s2n_security_policy policy = *conn->config->security_policy; - policy.cipher_preferences = &cipher_preferences; - conn->security_policy_override = &policy; - - /* Fails with no cipher suites available / written */ - cipher_suite.available = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), - S2N_ERR_INVALID_CIPHER_PREFERENCES); - - /* Succeeds with one cipher suite available / written */ - /* cppcheck-suppress redundantAssignment */ - cipher_suite.available = true; - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - }; - }; - - /* Test that negotiating TLS1.2 with QUIC-enabled server fails */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_enable_quic(config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - - /* Succeeds when negotiating TLS1.3 */ - if (s2n_is_tls13_fully_supported()) { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, - &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - } - - /* Fails when negotiating TLS1.2 */ - { - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all_tls12")); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, - &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* Test that cipher suites enforce proper highest supported versions. - * Eg. server configs TLS 1.2 only ciphers should never negotiate TLS 1.3 - */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - - { - /* TLS 1.3 client cipher preference uses TLS13 version */ - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - { - /* TLS 1.2 client cipher preference uses TLS12 version */ - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20240501")); - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - { - /* TLS 1.3 client cipher preference uses TLS13 version */ - struct s2n_connection *client_conn = NULL, *server_conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - - POSIX_GUARD(s2n_connection_get_security_policy(client_conn, &security_policy)); - EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - /* Server configured with TLS 1.2 negotiates TLS12 version */ - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - struct s2n_config *server_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); - - POSIX_GUARD(s2n_connection_get_security_policy(server_conn, &security_policy)); - EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* SSlv2 client hello */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - int sslv2_client_hello_len = sizeof(sslv2_client_hello); - - uint8_t sslv2_client_hello_header[] = { - SSLv2_CLIENT_HELLO_HEADER, - }; - - int sslv2_client_hello_header_len = sizeof(sslv2_client_hello_header); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* The security policy does not need to support SSLv2. - * - * s2n-tls does NOT support SSLv2. However, it does accept ClientHellos in the SSLv2 - * format but advertising higher protocol versions. Clients use this strategy to - * communicate with servers in a backwards-compatible way. - * - * Our test SSLv2 ClientHello advertises TLS1.2. - * So the security policy only needs to support TLS1.2. - * (and at least one of the ciphers in the hard coded sslv2 client hello) - */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); - - /* Send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello_header, sslv2_client_hello_header_len), sslv2_client_hello_header_len); - EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello, sslv2_client_hello_len), sslv2_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_TRUE(IS_NEGOTIATED(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - - /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ - EXPECT_EQUAL(client_hello, &server_conn->client_hello); - - uint8_t *collected_client_hello = client_hello->raw_message.data; - uint16_t collected_client_hello_len = client_hello->raw_message.size; - - /* Verify correctly identified as SSLv2 */ - EXPECT_TRUE(client_hello->sslv2); - - /* Verify collected client hello message length */ - EXPECT_EQUAL(collected_client_hello_len, sslv2_client_hello_len); - - /* Verify the collected client hello matches what was sent */ - EXPECT_BYTEARRAY_EQUAL(collected_client_hello, sslv2_client_hello, sslv2_client_hello_len); - - /* Verify s2n_client_hello_get_raw_message_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sslv2_client_hello_len); - - uint8_t expected_cs[] = { - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - }; - - /* Verify collected cipher_suites size correct */ - EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); - - /* Verify collected cipher_suites correct */ - EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); - - /* Verify s2n_client_hello_get_cipher_suites_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); - - /* Verify collected extensions size correct */ - EXPECT_EQUAL(client_hello->extensions.raw.size, 0); - - /* Verify s2n_client_hello_get_extensions_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), 0); - - /* Verify s2n_client_hello_get_session_id_length correct */ - uint32_t ch_session_id_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); - EXPECT_EQUAL(ch_session_id_length, 0); - - /* Free all handshake data */ - EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); - - /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - - /* Wipe connection */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ - EXPECT_EQUAL(client_hello->cipher_suites.size, 0); - EXPECT_NULL(client_hello->cipher_suites.data); - EXPECT_EQUAL(client_hello->extensions.raw.size, 0); - EXPECT_NULL(client_hello->extensions.raw.data); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Minimal TLS 1.2 client hello. */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - uint8_t *sent_client_hello = NULL; - uint8_t *expected_client_hello = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x08, - /* Server names len */ - 0x00, - 0x06, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - }; - - uint8_t server_name_extension[] = { - /* Server names len */ - 0x00, - 0x06, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - }; - int server_name_extension_len = sizeof(server_name_extension); - - size_t client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_prefix[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int client_hello_prefix_len = sizeof(client_hello_prefix); - int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (sent_client_hello_len >> 16) & 0xff, - (sent_client_hello_len >> 8) & 0xff, - (sent_client_hello_len & 0xff), - }; - int message_len = sizeof(message_header) + sent_client_hello_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); - EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); - EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Verify s2n_connection_get_client_hello returns null if client hello not yet processed */ - EXPECT_NULL(s2n_connection_get_client_hello(server_conn)); - - uint8_t *ext_data = NULL; - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); - /* Verify we don't get extension and it's length when client hello is not yet processed */ - EXPECT_FAILURE(s2n_client_hello_get_extension_length(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME)); - EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len)); - free(ext_data); - ext_data = NULL; - - /* Send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - - /* Verify correctly identified as NOT sslv2 */ - EXPECT_FALSE(client_hello->sslv2); - - /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ - EXPECT_EQUAL(client_hello, &server_conn->client_hello); - - uint8_t *collected_client_hello = client_hello->raw_message.data; - uint16_t collected_client_hello_len = client_hello->raw_message.size; - - /* Verify collected client hello message length */ - EXPECT_EQUAL(collected_client_hello_len, sent_client_hello_len); - - /* Verify the collected client hello has client random zero-ed out */ - uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; - uint8_t expected_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_BYTEARRAY_EQUAL(collected_client_hello + client_random_offset, expected_client_random, S2N_TLS_RANDOM_DATA_LEN); - - /* Verify the collected client hello matches what was sent except for the zero-ed client random */ - EXPECT_NOT_NULL(expected_client_hello = malloc(sent_client_hello_len)); - EXPECT_MEMCPY_SUCCESS(expected_client_hello, sent_client_hello, sent_client_hello_len); - POSIX_CHECKED_MEMSET(expected_client_hello + client_random_offset, 0, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); - - /* Verify s2n_client_hello_get_raw_message_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sent_client_hello_len); - - uint8_t *raw_ch_out = NULL; - - /* Verify s2n_client_hello_get_raw_message retrieves the full message when its len <= max_len */ - EXPECT_TRUE(collected_client_hello_len < S2N_LARGE_RECORD_LENGTH); - EXPECT_NOT_NULL(raw_ch_out = malloc(S2N_LARGE_RECORD_LENGTH)); - EXPECT_EQUAL(sent_client_hello_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, S2N_LARGE_RECORD_LENGTH)); - EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, sent_client_hello_len); - free(raw_ch_out); - raw_ch_out = NULL; - - /* Verify s2n_client_hello_get_raw_message retrieves truncated message when its len > max_len */ - EXPECT_TRUE(collected_client_hello_len > 0); - uint32_t max_len = collected_client_hello_len - 1; - EXPECT_NOT_NULL(raw_ch_out = malloc(max_len)); - EXPECT_EQUAL(max_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, max_len)); - EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, max_len); - free(raw_ch_out); - raw_ch_out = NULL; - - uint8_t expected_cs[] = { 0x00, 0x3C }; - - /* Verify collected cipher_suites size correct */ - EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); - - /* Verify collected cipher_suites correct */ - EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); - - /* Verify s2n_client_hello_get_cipher_suites_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); - - /* Verify s2n_client_hello_get_cipher_suites correct */ - uint8_t *cs_out = NULL; - - /* Verify s2n_client_hello_get_cipher_suites retrieves the full cipher_suites when its len <= max_len */ - EXPECT_TRUE(client_hello->cipher_suites.size < S2N_LARGE_RECORD_LENGTH); - EXPECT_NOT_NULL(cs_out = malloc(S2N_LARGE_RECORD_LENGTH)); - EXPECT_EQUAL(sizeof(expected_cs), s2n_client_hello_get_cipher_suites(client_hello, cs_out, S2N_LARGE_RECORD_LENGTH)); - EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, sizeof(expected_cs)); - free(cs_out); - cs_out = NULL; - - /* Verify s2n_client_hello_get_cipher_suites retrieves truncated message when cipher_suites len > max_len */ - max_len = sizeof(expected_cs) - 1; - EXPECT_TRUE(max_len > 0); - - EXPECT_NOT_NULL(cs_out = malloc(max_len)); - EXPECT_EQUAL(max_len, s2n_client_hello_get_cipher_suites(client_hello, cs_out, max_len)); - EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, max_len); - free(cs_out); - cs_out = NULL; - - /* Verify collected extensions size correct */ - EXPECT_EQUAL(client_hello->extensions.raw.size, client_extensions_len); - - /* Verify collected extensions correct */ - EXPECT_BYTEARRAY_EQUAL(client_hello->extensions.raw.data, client_extensions, client_extensions_len); - - /* Verify s2n_client_hello_get_extensions_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), client_extensions_len); - - /* Verify s2n_client_hello_get_extensions correct */ - uint8_t *extensions_out = NULL; - - /* Verify s2n_client_hello_get_extensions retrieves the full cipher_suites when its len <= max_len */ - EXPECT_TRUE(client_hello->extensions.raw.size < S2N_LARGE_RECORD_LENGTH); - EXPECT_NOT_NULL(extensions_out = malloc(S2N_LARGE_RECORD_LENGTH)); - EXPECT_EQUAL(client_extensions_len, s2n_client_hello_get_extensions(client_hello, extensions_out, S2N_LARGE_RECORD_LENGTH)); - EXPECT_BYTEARRAY_EQUAL(extensions_out, client_extensions, client_extensions_len); - free(extensions_out); - extensions_out = NULL; - - /* Verify s2n_client_hello_get_extensions retrieves truncated message when cipher_suites len > max_len */ - max_len = client_extensions_len - 1; - EXPECT_TRUE(max_len > 0); - - EXPECT_NOT_NULL(extensions_out = malloc(max_len)); - EXPECT_EQUAL(max_len, s2n_client_hello_get_extensions(client_hello, extensions_out, max_len)); - EXPECT_BYTEARRAY_EQUAL(extensions_out, client_hello->extensions.raw.data, max_len); - free(extensions_out); - extensions_out = NULL; - - /* Verify server name extension and it's length are returned correctly */ - EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_SERVER_NAME), server_name_extension_len); - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len), server_name_extension_len); - EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len); - free(ext_data); - ext_data = NULL; - - /* Verify server name extension is truncated if extension_size > max_len */ - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len - 1)); - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len - 1), server_name_extension_len - 1); - EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len - 1); - free(ext_data); - ext_data = NULL; - - /* Verify get extension and it's length calls for a non-existing extension type */ - EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY), 0); - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, ext_data, server_name_extension_len), 0); - EXPECT_EQUAL(s2n_errno, S2N_ERR_EXTENSION_NOT_RECEIVED); - free(ext_data); - ext_data = NULL; - - /* Verify server name extension exists */ - bool extension_exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_SERVER_NAME, &extension_exists)); - EXPECT_TRUE(extension_exists); - - /* Verify expected result for non-existing extension */ - extension_exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &extension_exists)); - EXPECT_FALSE(extension_exists); - - /* Verify s2n_client_hello_get_session_id is what we received in ClientHello */ - uint8_t expected_ch_session_id[] = { ZERO_TO_THIRTY_ONE }; - uint8_t ch_session_id[sizeof(expected_ch_session_id)]; - uint32_t ch_session_id_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); - EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); - EXPECT_SUCCESS(s2n_client_hello_get_session_id(client_hello, ch_session_id, &ch_session_id_length, sizeof(ch_session_id))); - EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); - EXPECT_BYTEARRAY_EQUAL(ch_session_id, expected_ch_session_id, sizeof(expected_ch_session_id)); - - /* Verify s2n_connection_get_session_id is different from the one we received in ClientHello, as we generated a new one in ServerHello */ - uint8_t conn_session_id[sizeof(expected_ch_session_id)]; - EXPECT_EQUAL(s2n_connection_get_session_id_length(server_conn), sizeof(conn_session_id)); - EXPECT_SUCCESS(s2n_connection_get_session_id(server_conn, conn_session_id, sizeof(conn_session_id))); - EXPECT_BYTEARRAY_NOT_EQUAL(conn_session_id, ch_session_id, sizeof(expected_ch_session_id)); - - /* Free all handshake data */ - EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); - - /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - - /* Wipe connection */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ - EXPECT_EQUAL(client_hello->cipher_suites.size, 0); - EXPECT_NULL(client_hello->cipher_suites.data); - EXPECT_EQUAL(client_hello->extensions.raw.size, 0); - EXPECT_NULL(client_hello->extensions.raw.data); - - /* Verify the connection is successfully reused after connection_wipe */ - - /* Re-configure connection */ - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - /* Recreate config */ - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Re-send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - - /* Verify the collected client hello on the reused connection matches the expected client hello */ - client_hello = s2n_connection_get_client_hello(server_conn); - collected_client_hello = client_hello->raw_message.data; - EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - free(expected_client_hello); - free(sent_client_hello); - }; - - /* Client hello api with NULL inputs */ - { - uint32_t len = 128; - uint8_t *out = NULL; - EXPECT_NOT_NULL(out = malloc(len)); - - EXPECT_FAILURE(s2n_client_hello_get_raw_message_length(NULL)); - EXPECT_FAILURE(s2n_client_hello_get_raw_message(NULL, out, len)); - EXPECT_FAILURE(s2n_client_hello_get_cipher_suites_length(NULL)); - EXPECT_FAILURE(s2n_client_hello_get_cipher_suites(NULL, out, len)); - EXPECT_FAILURE(s2n_client_hello_get_extensions_length(NULL)); - EXPECT_FAILURE(s2n_client_hello_get_extensions(NULL, out, len)); - EXPECT_FAILURE(s2n_client_hello_get_extension_length(NULL, S2N_EXTENSION_SERVER_NAME)); - EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(NULL, S2N_EXTENSION_SERVER_NAME, out, len)); - free(out); - out = NULL; - - bool exists = false; - EXPECT_FAILURE(s2n_client_hello_has_extension(NULL, S2N_EXTENSION_SERVER_NAME, &exists)); - EXPECT_FALSE(exists); - }; - - /* test_weird_client_hello_version() */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - uint8_t *sent_client_hello = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x08, - /* Server names len */ - 0x00, - 0x06, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - }; - - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_prefix[] = { - /* Protocol version TLS ??? */ - 0xFF, - 0xFF, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int client_hello_prefix_len = sizeof(client_hello_prefix); - int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (sent_client_hello_len >> 16) & 0xff, - (sent_client_hello_len >> 8) & 0xff, - (sent_client_hello_len & 0xff), - }; - int message_len = sizeof(message_header) + sent_client_hello_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); - EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); - EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - /* Client sent an invalid legacy protocol version. We should still have negotiate the maximum value(TLS1.2) */ - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - s2n_connection_free(server_conn); - s2n_config_free(server_config); - free(sent_client_hello); - }; - - { - struct s2n_cipher_suite *client_cipher_suites[] = { - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - }; - - struct s2n_cipher_preferences client_cipher_preferences = { - .count = s2n_array_len(client_cipher_suites), - .suites = client_cipher_suites, - }; - - const struct s2n_signature_scheme *const client_sig_scheme_pref_list[] = { - &s2n_rsa_pkcs1_sha1, - - /* Intentionally do not send and ECDSA SignatureScheme in the Client Hello. This is malformed since the - * Client's only Ciphersuite uses ECDSA, meaning that technically the Server could reject it, but there are - * some clients that send this form of malformed Client Hello's in the wild. So ensure we are compatible - * with them by assuming that the Client does support ECDSA, even though it's missing from the ClientHello. - */ - - /* &s2n_ecdsa_sha1, */ - }; - - struct s2n_signature_preferences client_signature_preferences = { - .count = s2n_array_len(client_sig_scheme_pref_list), - .signature_schemes = client_sig_scheme_pref_list, - }; - - struct s2n_security_policy client_security_policy = { - .minimum_protocol_version = S2N_TLS10, - .cipher_preferences = &client_cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &client_signature_preferences, - .ecc_preferences = &s2n_ecc_preferences_20140601, - }; - - EXPECT_TRUE(client_cipher_suites[0]->available); - - struct s2n_cert_chain_and_key *ecdsa_cert_chain = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Create Configs */ - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert_chain)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - server_config->security_policy = &security_policy_20190214; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - client_config->security_policy = &client_security_policy; - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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)); - - /* We have to update the client's security policy after it sends the ClientHello. - * The client sends all signature algorithms in its security policy, and - * won't accept any signature algorithm it receives that's not in its security policy. - * So we need to change the security policy between sending and receiving. - */ - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); - client_config->security_policy = &security_policy_20190214; - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); - EXPECT_EQUAL(client_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); - EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); - EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* s2n_client_hello_recv should fail when reading an SSLv2 client hello during a hello retry handshake */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - /* Handshake is hello retry and TLS1.3 was negotiated */ - server_conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_handshake_type_set_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); - - /* Second client hello has version SSLv2 */ - server_conn->client_hello.sslv2 = true; - - /* Mock having some data in the client hello */ - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&server_conn->handshake.io, 100)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_parse_client_hello(server_conn), S2N_ERR_BAD_MESSAGE); - }; - - /* Test s2n_client_hello_parse_message - * - * Comparing ClientHellos produced by connection IO parsing vs - * produced by s2n_client_hello_parse_message is difficult, but we can - * use JA3 fingerprints as an approximation. See s2n_fingerprint_ja3_test.c - */ - { - /* 20240501 can only negotiate tls12 */ - const char *security_policies[] = { "20240501", "default_tls13", "test_all" }; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - /* Test: Can parse ClientHellos sent by the s2n client */ - for (size_t i = 0; i < s2n_array_len(security_policies); i++) { - const char *security_policy = security_policies[i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policy)); - - EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); - - uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); - EXPECT_NOT_EQUAL(raw_size, 0); - uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); - EXPECT_NOT_NULL(raw); - - DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); - EXPECT_NOT_NULL(client_hello = s2n_client_hello_parse_message(raw, raw_size)); - EXPECT_TRUE(client_hello->alloced); - }; - - /* Test: Rejects invalid ClientHellos - * - * This test is important to verify that no memory is leaked when parsing fails. - */ - { - struct s2n_client_hello *client_hello = NULL; - - uint8_t wrong_message_type[50] = { 0x02, 0x00, 0x00, 1 }; - client_hello = s2n_client_hello_parse_message(wrong_message_type, sizeof(wrong_message_type)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - - uint8_t wrong_message_size[50] = { 0x01, 0x00, 0x00, UINT8_MAX }; - client_hello = s2n_client_hello_parse_message(wrong_message_size, sizeof(wrong_message_size)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - - uint8_t too_short[5] = { 0x01, 0x00, 0x00, 1 }; - client_hello = s2n_client_hello_parse_message(too_short, sizeof(too_short)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_STUFFER_OUT_OF_DATA); - - uint8_t all_zeroes[50] = { 0x01, 0x00, 0x00, 46 }; - client_hello = s2n_client_hello_parse_message(all_zeroes, sizeof(all_zeroes)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - }; - - /* Test: Rejects SSLv2 */ - { - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_HEADER, - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - /* Try parsing variations on the complete record vs just the message. - * The sslv2 record header is technically the first two bytes, - * but s2n-tls usually starts parsing after the first five bytes. - */ - for (size_t i = 0; i <= S2N_TLS_RECORD_HEADER_LENGTH; i++) { - struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( - sslv2_client_hello + i, sizeof(sslv2_client_hello) - i); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - } - - /* Sanity check: s2n accepts the test sslv2 message via the connection */ - { - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); - - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->header_in, - sslv2_client_hello, S2N_TLS_RECORD_HEADER_LENGTH)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->in, - sslv2_client_hello + S2N_TLS_RECORD_HEADER_LENGTH, - sizeof(sslv2_client_hello) - S2N_TLS_RECORD_HEADER_LENGTH)); - - EXPECT_FALSE(server->client_hello.sslv2); - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); - EXPECT_TRUE(server->client_hello.sslv2); - EXPECT_FALSE(server->client_hello.alloced); - } - }; - }; - - /* Test s2n_client_hello_free */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(NULL), S2N_ERR_NULL); - - /* Test: Accepts but ignores NULL / already freed */ - { - struct s2n_client_hello *client_hello = NULL; - for (size_t i = 0; i < 3; i++) { - EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); - EXPECT_NULL(client_hello); - } - }; - - /* Test: Errors on client hello associated with a connection */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, - s2n_stuffer_data_available(&client->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); - EXPECT_NOT_NULL(client_hello); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(&client_hello), S2N_ERR_INVALID_ARGUMENT); - EXPECT_NOT_NULL(s2n_connection_get_client_hello(server)); - EXPECT_NOT_EQUAL(server->client_hello.raw_message.size, 0); - }; - - /* Test: Frees client hello from raw message */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client)); - - EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); - - uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); - EXPECT_NOT_EQUAL(raw_size, 0); - uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); - EXPECT_NOT_NULL(raw); - - struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( - raw, raw_size); - EXPECT_NOT_NULL(client_hello); - - for (size_t i = 0; i < 3; i++) { - EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); - EXPECT_NULL(client_hello); - } - }; - }; - - /* s2n_client_hello_get_compression_methods */ - { - /* Safety */ - { - uint32_t length = 0; - struct s2n_client_hello client_hello = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(NULL, &length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(&client_hello, NULL), S2N_ERR_NULL); - - uint8_t list = 0; - uint32_t list_length = 0; - uint32_t out_length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(NULL, &list, list_length, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, NULL, list_length, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, NULL), S2N_ERR_NULL); - - /* User did not provide a large enough buffer to write the compression methods */ - uint8_t data[] = { 1, 2, 3, 4, 5 }; - EXPECT_SUCCESS(s2n_blob_init(&client_hello.compression_methods, data, sizeof(data))); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, &out_length), - S2N_ERR_INSUFFICIENT_MEM_SIZE); - }; - - /* Retrieves the compression methods list */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); - EXPECT_EQUAL(length, 1); - uint8_t list = 0; - uint32_t out_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, &list, sizeof(list), &out_length)); - EXPECT_EQUAL(out_length, 1); - }; - - /* Retrieves compression methods list longer than one byte */ - { - /* Compression methods were deprecated in TLS13 and s2n has never - * supported them. However, it is conceivable that a client could send - * us a list that contains more than the "null" byte. Therefore, we construct - * a fake Client Hello that contains a longer list of compression methods - * for testing. - */ - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, 0x02, - /* Cipher suite - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */ - 0xC0, 0x2F, - COMPRESSION_METHODS_LEN, - COMPRESSION_METHODS, - /* Extensions len */ - 0x00, 0x00 - }; - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_conn->handshake.io, client_hello_message, sizeof(client_hello_message))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); - EXPECT_EQUAL(length, COMPRESSION_METHODS_LEN); - uint8_t list[5] = { 0 }; - uint32_t out_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, list, sizeof(list), &out_length)); - EXPECT_EQUAL(out_length, COMPRESSION_METHODS_LEN); - - uint8_t compression_data[] = { COMPRESSION_METHODS }; - EXPECT_BYTEARRAY_EQUAL(list, compression_data, out_length); - } - }; - - /* s2n_client_hello_get_legacy_protocol_version */ - { - /* Safety */ - { - uint8_t out = 0; - struct s2n_client_hello client_hello = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(NULL, &out), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(&client_hello, NULL), S2N_ERR_NULL); - }; - - /* Retrieves the Client Hello protocol version */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint8_t version = 0; - EXPECT_SUCCESS(s2n_client_hello_get_legacy_protocol_version(client_hello, &version)); - EXPECT_EQUAL(version, S2N_TLS12); - }; - }; - - /* s2n_client_hello_get_legacy_record_version */ - { - /* Safety */ - { - uint8_t out = 0; - struct s2n_client_hello client_hello = { 0 }; - - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(NULL, &out), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(&client_hello, NULL), S2N_ERR_NULL); - }; - - /* Retrieves record version */ - { - uint8_t out = 0; - struct s2n_client_hello client_hello = { 0 }; - client_hello.legacy_record_version = S2N_TLS12; - client_hello.record_version_recorded = 1; - EXPECT_SUCCESS(s2n_client_hello_get_legacy_record_version(&client_hello, &out)); - EXPECT_EQUAL(out, S2N_TLS12); - }; - }; - - /* s2n_client_hello_get_server_name() */ - { - /* Safety */ - { - struct s2n_client_hello ch = { 0 }; - uint16_t length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(NULL, &length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(&ch, NULL), S2N_ERR_NULL); - - uint8_t buffer = 0; - uint16_t out_length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(NULL, &buffer, 0, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, NULL, 0, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, &buffer, 0, NULL), S2N_ERR_NULL); - }; - - /* Retrieves the first entry in the server_name extension */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - const char *test_server_name = "test server name!"; - EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint16_t length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_server_name_length(client_hello, &length)); - EXPECT_EQUAL(strlen(test_server_name), length); - uint8_t buffer[20] = { 0 }; - uint16_t out_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_server_name(client_hello, buffer, sizeof(buffer), &out_length)); - EXPECT_EQUAL(length, out_length); - - EXPECT_BYTEARRAY_EQUAL(buffer, test_server_name, out_length); - - /* Check error occurs if buffer is too small to hold server name */ - uint8_t small_buf[2] = { 0 }; - out_length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(client_hello, small_buf, sizeof(small_buf), &out_length), S2N_ERR_SAFETY); - }; - }; - - /* Test: large Client Hellos */ - { - const uint16_t cipher_suites_max_length = (1 << 16) - 2; - const uint16_t num_of_cipher_suites_to_drop = 150; - - /** - * S2N-TLS automatically includes TLS_EMPTY_RENEGOTIATION_INFO_SCSV in TLS 1.2 ClientHello, - * so we subtract 1 from the maximum number of cipher suites to reserve space for it. - */ - const uint16_t max_cipher_suite_count = (cipher_suites_max_length / S2N_TLS_CIPHER_SUITE_LEN) - 1; - - /* Drop 150 cipher suites from max, so that the total handshake message length won't exceed 64KB */ - const uint16_t reduced_cipher_suite_count = max_cipher_suite_count - num_of_cipher_suites_to_drop; - - uint16_t cipher_suites_counts[] = { reduced_cipher_suite_count, max_cipher_suite_count }; - - for (size_t i = 0; i < s2n_array_len(cipher_suites_counts); i++) { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* We need to generate a large Client Hello. - * We do this by manipulating the number of cipher suites, - * which is the easiest way to make Client Hello large. - */ - uint16_t cipher_suites_count = cipher_suites_counts[i]; - struct s2n_cipher_suite *test_cipher_suites[UINT16_MAX] = { 0 }; - for (size_t j = 0; j < cipher_suites_count; j++) { - test_cipher_suites[j] = &s2n_rsa_with_aes_128_gcm_sha256; - } - const struct s2n_cipher_preferences test_cipher_suites_preferences = { - .count = cipher_suites_count, - .suites = test_cipher_suites, - }; - const struct s2n_security_policy *default_policy = NULL; - /* 20240501 is a policy that can only negotiate tls12 */ - EXPECT_SUCCESS(s2n_find_security_policy_from_version("20240501", &default_policy)); - struct s2n_security_policy test_security_policy = *default_policy; - test_security_policy.cipher_preferences = &test_cipher_suites_preferences; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); - client->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - server->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - - /** - * Write Client Hello into io_pair.server_in. - * The server_in buffer contains the Client Hello message plus a 5-byte record header. - */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client, &blocked), S2N_ERR_IO_BLOCKED); - - /* Add one extra cipher suite length to account for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ - uint16_t cipher_suites_length = (cipher_suites_count + 1) * S2N_TLS_CIPHER_SUITE_LEN; - - if (cipher_suites_length < cipher_suites_max_length) { - /** - * The Client Hello message size should be less than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, even with - * the five byte record header. - */ - EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - } else { - /** - * When using maximum cipher suites, the Client Hello message size exceeds - * S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH by more than five byte. - * - * Hence, if server_in's available data is greater than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, - * then the Client Hello itself exceeds the maximum allowed size, even after accounting for the record header. - */ - EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), S2N_ERR_BAD_MESSAGE); - } - } - } - - /* Test s2n_client_hello_get_random */ - { - /* Safety */ - { - uint8_t out[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - struct s2n_client_hello client_hello = { 0 }; - - EXPECT_FAILURE_WITH_ERRNO( - s2n_client_hello_get_random(NULL, out, sizeof(out)), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO( - s2n_client_hello_get_random(&client_hello, NULL, sizeof(out)), - S2N_ERR_NULL); - - /* Buffer size must be large enough to hold the full client random */ - uint8_t small_buffer[S2N_TLS_RANDOM_DATA_LEN - 1] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO( - s2n_client_hello_get_random(&client_hello, small_buffer, sizeof(small_buffer)), - S2N_ERR_INSUFFICIENT_MEM_SIZE); - }; - - /* Retrieves the client random and zeroes it from the raw message */ - { - /* Construct a minimal ClientHello with a custom random value */ - uint8_t custom_random[S2N_TLS_RANDOM_DATA_LEN] = { ZERO_TO_THIRTY_ONE }; - - /* clang-format off */ - uint8_t raw_client_hello[] = { - /* message type */ - TLS_CLIENT_HELLO, - /* message size */ - 0x00, 0x00, 43, - /* protocol version - TLS1.2 */ - 0x03, 0x03, - /* random - custom value */ - ZERO_TO_THIRTY_ONE, - /* session id */ - 0x00, - /* cipher suites */ - 0x00, 0x02, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - /* legacy compression methods */ - 0x01, 0x00, - /* extensions - empty */ - 0x00, 0x00, - }; - /* clang-format on */ - - DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); - client_hello = s2n_client_hello_parse_message(raw_client_hello, sizeof(raw_client_hello)); - EXPECT_NOT_NULL(client_hello); - - /* Exact-size buffer: retrieve the client random */ - uint8_t retrieved_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_client_hello_get_random( - client_hello, retrieved_random, S2N_TLS_RANDOM_DATA_LEN)); - EXPECT_BYTEARRAY_EQUAL(retrieved_random, custom_random, S2N_TLS_RANDOM_DATA_LEN); - - /* Buffer size > 32 bytes must also succeed and only write the first 32 bytes */ - { - uint8_t large_buffer[S2N_TLS_RANDOM_DATA_LEN + 10] = { 0 }; - EXPECT_SUCCESS(s2n_client_hello_get_random( - client_hello, large_buffer, sizeof(large_buffer))); - - /* First 32 bytes match the client random */ - EXPECT_BYTEARRAY_EQUAL(large_buffer, custom_random, S2N_TLS_RANDOM_DATA_LEN); - - /* Tail remains untouched */ - uint8_t zero_tail[10] = { 0 }; - EXPECT_BYTEARRAY_EQUAL( - large_buffer + S2N_TLS_RANDOM_DATA_LEN, - zero_tail, - sizeof(zero_tail)); - } - - /* The raw message should have the random zeroed out after retrieval */ - uint8_t *raw_message = client_hello->raw_message.data; - uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; - uint8_t zeroed_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_BYTEARRAY_EQUAL( - raw_message + client_random_offset, - zeroed_random, - S2N_TLS_RANDOM_DATA_LEN); - }; - }; - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_client_hello.h" + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +#define LENGTH_TO_SESSION_ID (S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN) +#define TLS12_LENGTH_TO_CIPHER_LIST (LENGTH_TO_SESSION_ID + 1) +#define TLS13_LENGTH_TO_CIPHER_LIST (TLS12_LENGTH_TO_CIPHER_LIST + S2N_TLS_SESSION_ID_MAX_LEN) + +#define COMPRESSION_METHODS 0x00, 0x01, 0x02, 0x03, 0x04 +#define COMPRESSION_METHODS_LEN 0x05 + +int s2n_parse_client_hello(struct s2n_connection *conn); +S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, + struct s2n_blob *raw_extensions, struct s2n_blob *extension); + +int main(int argc, char **argv) +{ + struct s2n_cert_chain_and_key *chain_and_key = NULL, *ecdsa_chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Test s2n_client_hello_get_extension_by_id */ + { + /* Test with invalid parsed extensions */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + s2n_tls_extension_type test_extension_type = S2N_EXTENSION_SERVER_NAME; + + s2n_extension_type_id test_extension_type_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_extension_type, &test_extension_type_id)); + + uint8_t data[] = "data"; + s2n_parsed_extension *parsed_extension = &conn->client_hello.extensions.parsed_extensions[test_extension_type_id]; + parsed_extension->extension_type = test_extension_type; + parsed_extension->extension.data = data; + parsed_extension->extension.size = sizeof(data); + + /* Succeeds with correct extension type */ + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, + test_extension_type, data, sizeof(data)), + sizeof(data)); + + /* Fails with wrong extension type */ + parsed_extension->extension_type = test_extension_type + 1; + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, + test_extension_type, data, sizeof(data)), + 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_client_hello_has_extension */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + uint8_t data[] = { + /* arbitrary extension with 2 data */ + 0xFF, 0x00, /* extension type */ + 0x00, 0x02, /* extension payload length */ + 0xAB, 0xCD, /* extension payload */ + /* Encrypt then mac extension without data */ + 0x00, 0x16, + 0x00, 0x00 + }; + + struct s2n_blob *raw_extension = &conn->client_hello.extensions.raw; + raw_extension->data = data; + raw_extension->size = sizeof(data); + + /* Succeeds on an unsupported extension with no payload */ + bool exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0x0016, &exists)); + EXPECT_TRUE(exists); + + /* Succeeds on an unsupported extension with payload */ + exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFF00, &exists)); + EXPECT_TRUE(exists); + + /* Succeeds with an invalid extension */ + exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFFFF, &exists)); + EXPECT_FALSE(exists); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_client_hello_has_extension with a zero-length extension */ + for (int send_sct = 0; send_sct <= 1; send_sct++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* The SCT extension is zero-length. */ + if (send_sct) { + EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST)); + } + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + s2n_parsed_extension *sct_extension = NULL; + int ret = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &client_hello->extensions, + &sct_extension); + + if (send_sct) { + /* Ensure that the extension was received. */ + EXPECT_SUCCESS(ret); + POSIX_ENSURE_REF(sct_extension); + + /* Ensure that the extension is zero-length. */ + EXPECT_EQUAL(sct_extension->extension.size, 0); + } else { + /* The extension shouldn't have been received because it wasn't requested. */ + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); + } + } + + /* Test s2n_client_hello_get_raw_extension */ + { + uint8_t data[] = { + /* arbitrary extension with 2 data */ + 0xFF, 0x00, /* extension type */ + 0x00, 0x02, /* extension payload length */ + 0xAB, 0xCD, /* extension payload */ + /* NPN extension without data */ + 0x33, 0x74, + 0x00, 0x00 + }; + struct s2n_blob raw_extension = { + .data = data, + .size = sizeof(data), + }; + + struct s2n_blob extension = { 0 }; + /* Succeeds with extension exists without payload */ + EXPECT_OK(s2n_client_hello_get_raw_extension(0x3374, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 0); + EXPECT_NOT_NULL(extension.data); + + /* Succeeds with extension exists with payload */ + extension = (struct s2n_blob){ 0 }; + EXPECT_OK(s2n_client_hello_get_raw_extension(0xFF00, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 2); + EXPECT_NOT_NULL(extension.data); + EXPECT_BYTEARRAY_EQUAL(extension.data, &data[4], 2); + + /* Failed with extension not exist */ + extension = (struct s2n_blob){ 0 }; + EXPECT_OK(s2n_client_hello_get_raw_extension(0xFFFF, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 0); + EXPECT_NULL(extension.data); + }; + + /* Test setting cert chain on recv */ + { + s2n_enable_tls13_in_test(); + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* TLS13 fails to parse client hello when no certs set */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->client_protocol_version = conn->server_protocol_version; + conn->actual_protocol_version = conn->client_protocol_version; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(conn), S2N_ERR_NO_VALID_SIGNATURE_SCHEME); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + /* TLS13 successfully sets certs */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->client_protocol_version = conn->server_protocol_version; + conn->actual_protocol_version = conn->client_protocol_version; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); + EXPECT_SUCCESS(s2n_client_hello_recv(conn)); + + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + s2n_disable_tls13_in_test(); + }; + + /* Test getting supported versions from the client hello */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + /* TLS13 has supported versions in the client hello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + uint8_t supported_versions[256] = { 0 }; + uint8_t size_of_version_list = 0; + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); + size_of_version_list = supported_versions[0]; + /* No supported versions before the handshake is received */ + EXPECT_EQUAL(0, size_of_version_list); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); + size_of_version_list = supported_versions[0]; + EXPECT_TRUE(size_of_version_list > 0); + bool found_tls13 = false; + const uint8_t tls13_bytes[] = { 0x03, 0x04 }; + const size_t supported_version_size = sizeof(tls13_bytes); + for (uint16_t offset = 1; offset < size_of_version_list; offset += supported_version_size) { + if (memcmp(tls13_bytes, &supported_versions[offset], supported_version_size) == 0) { + found_tls13 = true; + } + } + EXPECT_TRUE(found_tls13); + }; + s2n_disable_tls13_in_test(); + }; + + /* Test generating session id */ + { + const uint8_t test_session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 7 }; + + /* Use session id if already generated */ + for (uint8_t i = S2N_TLS10; i <= S2N_TLS13; i++) { + if (i >= S2N_TLS13) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + conn->actual_protocol_version = i; + + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + EXPECT_MEMCPY_SUCCESS(conn->session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + uint8_t *session_id = NULL; + EXPECT_NOT_NULL(session_id = s2n_stuffer_raw_read(hello_stuffer, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_BYTEARRAY_EQUAL(session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* With TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Generate a session id by default */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Do NOT generate a session id if middlebox compatibility mode is disabled. + * For now, middlebox compatibility mode is only disabled by QUIC. + */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Generate a session id if trying to resume a handshake.io; + conn->resume_protocol_version = S2N_TLS12; + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + }; + + /* Fail if we need to generate a session id to resume a resume_protocol_version = S2N_TLS12; + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); + conn->quic_enabled = true; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), + S2N_ERR_UNSUPPORTED_WITH_QUIC); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* With TLS1.2 */ + { + /* Do NOT generate a session id by default */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Generate a session id if using tickets */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + }; + + /* Test cipher suites list */ + { + /* When TLS 1.3 NOT supported */ + { + /* TLS 1.3 cipher suites NOT written by client by default */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS 1.3 cipher suites NOT written by client even if included in security policy */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* When TLS 1.3 supported */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + s2n_config_set_session_tickets_onoff(config, 0); + + /* TLS 1.3 cipher suites written by client */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS13_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte = 0; + int tls13_ciphers_found = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + if (first_cipher_byte == 0x13) { + tls13_ciphers_found++; + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + EXPECT_NOT_EQUAL(tls13_ciphers_found, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* TLS_EMPTY_RENEGOTIATION_INFO_SCSV included if TLS1.2 ciphers included + * + *= https://www.rfc-editor.org/rfc/rfc5746#3.4 + *= type=test + *# o The client MUST include either an empty "renegotiation_info" + *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling + *# cipher suite value in the ClientHello. + */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + const uint8_t empty_renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; + + struct { + const char *security_policy; + bool expect_renegotiation_info; + } test_cases[] = { + { .security_policy = "test_all_tls13", .expect_renegotiation_info = false }, + { .security_policy = "default_tls13", .expect_renegotiation_info = true }, + /* 20240501 can only negotiate up to tls12 */ + { .security_policy = "20240501", .expect_renegotiation_info = true }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; + EXPECT_TRUE(cipher_suites->size > 0); + + uint8_t *iana = cipher_suites->data; + bool found_renegotiation_info = false; + for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { + if (memcmp(iana + j, empty_renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN) == 0) { + found_renegotiation_info = true; + } + } + + EXPECT_EQUAL(found_renegotiation_info, test_cases[i].expect_renegotiation_info); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + } + + /* TLS1.2 cipher suites not written if QUIC enabled */ + { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + bool quic_enabled[] = { false, s2n_is_tls13_fully_supported() }; + + /* TLS 1.2 cipher suites only written if QUIC not enabled */ + for (size_t i = 0; i < s2n_array_len(quic_enabled); i++) { + config->quic_enabled = quic_enabled[i]; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; + EXPECT_TRUE(cipher_suites->size > 0); + + bool tls12_cipher_found = false; + uint8_t *iana = cipher_suites->data; + for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { + /* All TLS1.3 cipher suites have IANAs starting with 0x13 */ + if (iana[j] != 0x13) { + tls12_cipher_found = true; + } + } + + /* TLS1.2 and QUIC are mutually exclusive */ + EXPECT_TRUE(tls12_cipher_found != quic_enabled[i]); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Error if no cipher suites written. */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + struct s2n_cipher_suite cipher_suite = s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + struct s2n_cipher_suite *cipher_suites = &cipher_suite; + struct s2n_cipher_preferences cipher_preferences = { + .suites = &cipher_suites, + .count = 1, + }; + struct s2n_security_policy policy = *conn->config->security_policy; + policy.cipher_preferences = &cipher_preferences; + conn->security_policy_override = &policy; + + /* Fails with no cipher suites available / written */ + cipher_suite.available = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), + S2N_ERR_INVALID_CIPHER_PREFERENCES); + + /* Succeeds with one cipher suite available / written */ + /* cppcheck-suppress redundantAssignment */ + cipher_suite.available = true; + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + }; + }; + + /* Test that negotiating TLS1.2 with QUIC-enabled server fails */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + /* Succeeds when negotiating TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Fails when negotiating TLS1.2 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all_tls12")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Test that cipher suites enforce proper highest supported versions. + * Eg. server configs TLS 1.2 only ciphers should never negotiate TLS 1.3 + */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + { + /* TLS 1.3 client cipher preference uses TLS13 version */ + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* TLS 1.2 client cipher preference uses TLS12 version */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20240501")); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* TLS 1.3 client cipher preference uses TLS13 version */ + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + POSIX_GUARD(s2n_connection_get_security_policy(client_conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + /* Server configured with TLS 1.2 negotiates TLS12 version */ + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + struct s2n_config *server_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + + POSIX_GUARD(s2n_connection_get_security_policy(server_conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* SSlv2 client hello */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + int sslv2_client_hello_len = sizeof(sslv2_client_hello); + + uint8_t sslv2_client_hello_header[] = { + SSLv2_CLIENT_HELLO_HEADER, + }; + + int sslv2_client_hello_header_len = sizeof(sslv2_client_hello_header); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* The security policy does not need to support SSLv2. + * + * s2n-tls does NOT support SSLv2. However, it does accept ClientHellos in the SSLv2 + * format but advertising higher protocol versions. Clients use this strategy to + * communicate with servers in a backwards-compatible way. + * + * Our test SSLv2 ClientHello advertises TLS1.2. + * So the security policy only needs to support TLS1.2. + * (and at least one of the ciphers in the hard coded sslv2 client hello) + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello_header, sslv2_client_hello_header_len), sslv2_client_hello_header_len); + EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello, sslv2_client_hello_len), sslv2_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + + /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ + EXPECT_EQUAL(client_hello, &server_conn->client_hello); + + uint8_t *collected_client_hello = client_hello->raw_message.data; + uint16_t collected_client_hello_len = client_hello->raw_message.size; + + /* Verify correctly identified as SSLv2 */ + EXPECT_TRUE(client_hello->sslv2); + + /* Verify collected client hello message length */ + EXPECT_EQUAL(collected_client_hello_len, sslv2_client_hello_len); + + /* Verify the collected client hello matches what was sent */ + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, sslv2_client_hello, sslv2_client_hello_len); + + /* Verify s2n_client_hello_get_raw_message_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sslv2_client_hello_len); + + uint8_t expected_cs[] = { + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + }; + + /* Verify collected cipher_suites size correct */ + EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); + + /* Verify collected cipher_suites correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); + + /* Verify collected extensions size correct */ + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + + /* Verify s2n_client_hello_get_extensions_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), 0); + + /* Verify s2n_client_hello_get_session_id_length correct */ + uint32_t ch_session_id_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); + EXPECT_EQUAL(ch_session_id_length, 0); + + /* Free all handshake data */ + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ + EXPECT_EQUAL(client_hello->cipher_suites.size, 0); + EXPECT_NULL(client_hello->cipher_suites.data); + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + EXPECT_NULL(client_hello->extensions.raw.data); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Minimal TLS 1.2 client hello. */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + uint8_t *sent_client_hello = NULL; + uint8_t *expected_client_hello = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x08, + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + + uint8_t server_name_extension[] = { + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + int server_name_extension_len = sizeof(server_name_extension); + + size_t client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_prefix[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int client_hello_prefix_len = sizeof(client_hello_prefix); + int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (sent_client_hello_len >> 16) & 0xff, + (sent_client_hello_len >> 8) & 0xff, + (sent_client_hello_len & 0xff), + }; + int message_len = sizeof(message_header) + sent_client_hello_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); + EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Verify s2n_connection_get_client_hello returns null if client hello not yet processed */ + EXPECT_NULL(s2n_connection_get_client_hello(server_conn)); + + uint8_t *ext_data = NULL; + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + /* Verify we don't get extension and it's length when client hello is not yet processed */ + EXPECT_FAILURE(s2n_client_hello_get_extension_length(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME)); + EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len)); + free(ext_data); + ext_data = NULL; + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + + /* Verify correctly identified as NOT sslv2 */ + EXPECT_FALSE(client_hello->sslv2); + + /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ + EXPECT_EQUAL(client_hello, &server_conn->client_hello); + + uint8_t *collected_client_hello = client_hello->raw_message.data; + uint16_t collected_client_hello_len = client_hello->raw_message.size; + + /* Verify collected client hello message length */ + EXPECT_EQUAL(collected_client_hello_len, sent_client_hello_len); + + /* Verify the collected client hello has client random zero-ed out */ + uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; + uint8_t expected_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_BYTEARRAY_EQUAL(collected_client_hello + client_random_offset, expected_client_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Verify the collected client hello matches what was sent except for the zero-ed client random */ + EXPECT_NOT_NULL(expected_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(expected_client_hello, sent_client_hello, sent_client_hello_len); + POSIX_CHECKED_MEMSET(expected_client_hello + client_random_offset, 0, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); + + /* Verify s2n_client_hello_get_raw_message_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sent_client_hello_len); + + uint8_t *raw_ch_out = NULL; + + /* Verify s2n_client_hello_get_raw_message retrieves the full message when its len <= max_len */ + EXPECT_TRUE(collected_client_hello_len < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(raw_ch_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(sent_client_hello_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, sent_client_hello_len); + free(raw_ch_out); + raw_ch_out = NULL; + + /* Verify s2n_client_hello_get_raw_message retrieves truncated message when its len > max_len */ + EXPECT_TRUE(collected_client_hello_len > 0); + uint32_t max_len = collected_client_hello_len - 1; + EXPECT_NOT_NULL(raw_ch_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, max_len); + free(raw_ch_out); + raw_ch_out = NULL; + + uint8_t expected_cs[] = { 0x00, 0x3C }; + + /* Verify collected cipher_suites size correct */ + EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); + + /* Verify collected cipher_suites correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites correct */ + uint8_t *cs_out = NULL; + + /* Verify s2n_client_hello_get_cipher_suites retrieves the full cipher_suites when its len <= max_len */ + EXPECT_TRUE(client_hello->cipher_suites.size < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(cs_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(sizeof(expected_cs), s2n_client_hello_get_cipher_suites(client_hello, cs_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, sizeof(expected_cs)); + free(cs_out); + cs_out = NULL; + + /* Verify s2n_client_hello_get_cipher_suites retrieves truncated message when cipher_suites len > max_len */ + max_len = sizeof(expected_cs) - 1; + EXPECT_TRUE(max_len > 0); + + EXPECT_NOT_NULL(cs_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_cipher_suites(client_hello, cs_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, max_len); + free(cs_out); + cs_out = NULL; + + /* Verify collected extensions size correct */ + EXPECT_EQUAL(client_hello->extensions.raw.size, client_extensions_len); + + /* Verify collected extensions correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->extensions.raw.data, client_extensions, client_extensions_len); + + /* Verify s2n_client_hello_get_extensions_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), client_extensions_len); + + /* Verify s2n_client_hello_get_extensions correct */ + uint8_t *extensions_out = NULL; + + /* Verify s2n_client_hello_get_extensions retrieves the full cipher_suites when its len <= max_len */ + EXPECT_TRUE(client_hello->extensions.raw.size < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(extensions_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(client_extensions_len, s2n_client_hello_get_extensions(client_hello, extensions_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(extensions_out, client_extensions, client_extensions_len); + free(extensions_out); + extensions_out = NULL; + + /* Verify s2n_client_hello_get_extensions retrieves truncated message when cipher_suites len > max_len */ + max_len = client_extensions_len - 1; + EXPECT_TRUE(max_len > 0); + + EXPECT_NOT_NULL(extensions_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_extensions(client_hello, extensions_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(extensions_out, client_hello->extensions.raw.data, max_len); + free(extensions_out); + extensions_out = NULL; + + /* Verify server name extension and it's length are returned correctly */ + EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_SERVER_NAME), server_name_extension_len); + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len), server_name_extension_len); + EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len); + free(ext_data); + ext_data = NULL; + + /* Verify server name extension is truncated if extension_size > max_len */ + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len - 1)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len - 1), server_name_extension_len - 1); + EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len - 1); + free(ext_data); + ext_data = NULL; + + /* Verify get extension and it's length calls for a non-existing extension type */ + EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY), 0); + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, ext_data, server_name_extension_len), 0); + EXPECT_EQUAL(s2n_errno, S2N_ERR_EXTENSION_NOT_RECEIVED); + free(ext_data); + ext_data = NULL; + + /* Verify server name extension exists */ + bool extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_SERVER_NAME, &extension_exists)); + EXPECT_TRUE(extension_exists); + + /* Verify expected result for non-existing extension */ + extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &extension_exists)); + EXPECT_FALSE(extension_exists); + + /* Verify s2n_client_hello_get_session_id is what we received in ClientHello */ + uint8_t expected_ch_session_id[] = { ZERO_TO_THIRTY_ONE }; + uint8_t ch_session_id[sizeof(expected_ch_session_id)]; + uint32_t ch_session_id_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); + EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); + EXPECT_SUCCESS(s2n_client_hello_get_session_id(client_hello, ch_session_id, &ch_session_id_length, sizeof(ch_session_id))); + EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); + EXPECT_BYTEARRAY_EQUAL(ch_session_id, expected_ch_session_id, sizeof(expected_ch_session_id)); + + /* Verify s2n_connection_get_session_id is different from the one we received in ClientHello, as we generated a new one in ServerHello */ + uint8_t conn_session_id[sizeof(expected_ch_session_id)]; + EXPECT_EQUAL(s2n_connection_get_session_id_length(server_conn), sizeof(conn_session_id)); + EXPECT_SUCCESS(s2n_connection_get_session_id(server_conn, conn_session_id, sizeof(conn_session_id))); + EXPECT_BYTEARRAY_NOT_EQUAL(conn_session_id, ch_session_id, sizeof(expected_ch_session_id)); + + /* Free all handshake data */ + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ + EXPECT_EQUAL(client_hello->cipher_suites.size, 0); + EXPECT_NULL(client_hello->cipher_suites.data); + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + EXPECT_NULL(client_hello->extensions.raw.data); + + /* Verify the connection is successfully reused after connection_wipe */ + + /* Re-configure connection */ + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Recreate config */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Re-send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + /* Verify the collected client hello on the reused connection matches the expected client hello */ + client_hello = s2n_connection_get_client_hello(server_conn); + collected_client_hello = client_hello->raw_message.data; + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + free(expected_client_hello); + free(sent_client_hello); + }; + + /* Client hello api with NULL inputs */ + { + uint32_t len = 128; + uint8_t *out = NULL; + EXPECT_NOT_NULL(out = malloc(len)); + + EXPECT_FAILURE(s2n_client_hello_get_raw_message_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_raw_message(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_cipher_suites_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_cipher_suites(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_extensions_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_extensions(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_extension_length(NULL, S2N_EXTENSION_SERVER_NAME)); + EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(NULL, S2N_EXTENSION_SERVER_NAME, out, len)); + free(out); + out = NULL; + + bool exists = false; + EXPECT_FAILURE(s2n_client_hello_has_extension(NULL, S2N_EXTENSION_SERVER_NAME, &exists)); + EXPECT_FALSE(exists); + }; + + /* test_weird_client_hello_version() */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + uint8_t *sent_client_hello = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x08, + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_prefix[] = { + /* Protocol version TLS ??? */ + 0xFF, + 0xFF, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int client_hello_prefix_len = sizeof(client_hello_prefix); + int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (sent_client_hello_len >> 16) & 0xff, + (sent_client_hello_len >> 8) & 0xff, + (sent_client_hello_len & 0xff), + }; + int message_len = sizeof(message_header) + sent_client_hello_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); + EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + /* Client sent an invalid legacy protocol version. We should still have negotiate the maximum value(TLS1.2) */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + s2n_connection_free(server_conn); + s2n_config_free(server_config); + free(sent_client_hello); + }; + + { + struct s2n_cipher_suite *client_cipher_suites[] = { + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + }; + + struct s2n_cipher_preferences client_cipher_preferences = { + .count = s2n_array_len(client_cipher_suites), + .suites = client_cipher_suites, + }; + + const struct s2n_signature_scheme *const client_sig_scheme_pref_list[] = { + &s2n_rsa_pkcs1_sha1, + + /* Intentionally do not send and ECDSA SignatureScheme in the Client Hello. This is malformed since the + * Client's only Ciphersuite uses ECDSA, meaning that technically the Server could reject it, but there are + * some clients that send this form of malformed Client Hello's in the wild. So ensure we are compatible + * with them by assuming that the Client does support ECDSA, even though it's missing from the ClientHello. + */ + + /* &s2n_ecdsa_sha1, */ + }; + + struct s2n_signature_preferences client_signature_preferences = { + .count = s2n_array_len(client_sig_scheme_pref_list), + .signature_schemes = client_sig_scheme_pref_list, + }; + + struct s2n_security_policy client_security_policy = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &client_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &client_signature_preferences, + .ecc_preferences = &s2n_ecc_preferences_20140601, + }; + + EXPECT_TRUE(client_cipher_suites[0]->available); + + struct s2n_cert_chain_and_key *ecdsa_cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Create Configs */ + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert_chain)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + server_config->security_policy = &security_policy_20190214; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + client_config->security_policy = &client_security_policy; + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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)); + + /* We have to update the client's security policy after it sends the ClientHello. + * The client sends all signature algorithms in its security policy, and + * won't accept any signature algorithm it receives that's not in its security policy. + * So we need to change the security policy between sending and receiving. + */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); + client_config->security_policy = &security_policy_20190214; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + EXPECT_EQUAL(client_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); + EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); + EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* s2n_client_hello_recv should fail when reading an SSLv2 client hello during a hello retry handshake */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Handshake is hello retry and TLS1.3 was negotiated */ + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); + + /* Second client hello has version SSLv2 */ + server_conn->client_hello.sslv2 = true; + + /* Mock having some data in the client hello */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&server_conn->handshake.io, 100)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_parse_client_hello(server_conn), S2N_ERR_BAD_MESSAGE); + }; + + /* Test s2n_client_hello_parse_message + * + * Comparing ClientHellos produced by connection IO parsing vs + * produced by s2n_client_hello_parse_message is difficult, but we can + * use JA3 fingerprints as an approximation. See s2n_fingerprint_ja3_test.c + */ + { + /* 20240501 can only negotiate tls12 */ + const char *security_policies[] = { "20240501", "default_tls13", "test_all" }; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Test: Can parse ClientHellos sent by the s2n client */ + for (size_t i = 0; i < s2n_array_len(security_policies); i++) { + const char *security_policy = security_policies[i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policy)); + + EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); + + uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); + EXPECT_NOT_EQUAL(raw_size, 0); + uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); + EXPECT_NOT_NULL(raw); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + EXPECT_NOT_NULL(client_hello = s2n_client_hello_parse_message(raw, raw_size)); + EXPECT_TRUE(client_hello->alloced); + }; + + /* Test: Rejects invalid ClientHellos + * + * This test is important to verify that no memory is leaked when parsing fails. + */ + { + struct s2n_client_hello *client_hello = NULL; + + uint8_t wrong_message_type[50] = { 0x02, 0x00, 0x00, 1 }; + client_hello = s2n_client_hello_parse_message(wrong_message_type, sizeof(wrong_message_type)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + + uint8_t wrong_message_size[50] = { 0x01, 0x00, 0x00, UINT8_MAX }; + client_hello = s2n_client_hello_parse_message(wrong_message_size, sizeof(wrong_message_size)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + + uint8_t too_short[5] = { 0x01, 0x00, 0x00, 1 }; + client_hello = s2n_client_hello_parse_message(too_short, sizeof(too_short)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_STUFFER_OUT_OF_DATA); + + uint8_t all_zeroes[50] = { 0x01, 0x00, 0x00, 46 }; + client_hello = s2n_client_hello_parse_message(all_zeroes, sizeof(all_zeroes)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + }; + + /* Test: Rejects SSLv2 */ + { + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_HEADER, + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + /* Try parsing variations on the complete record vs just the message. + * The sslv2 record header is technically the first two bytes, + * but s2n-tls usually starts parsing after the first five bytes. + */ + for (size_t i = 0; i <= S2N_TLS_RECORD_HEADER_LENGTH; i++) { + struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( + sslv2_client_hello + i, sizeof(sslv2_client_hello) - i); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + } + + /* Sanity check: s2n accepts the test sslv2 message via the connection */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->header_in, + sslv2_client_hello, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->in, + sslv2_client_hello + S2N_TLS_RECORD_HEADER_LENGTH, + sizeof(sslv2_client_hello) - S2N_TLS_RECORD_HEADER_LENGTH)); + + EXPECT_FALSE(server->client_hello.sslv2); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); + EXPECT_TRUE(server->client_hello.sslv2); + EXPECT_FALSE(server->client_hello.alloced); + } + }; + }; + + /* Test s2n_client_hello_free */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(NULL), S2N_ERR_NULL); + + /* Test: Accepts but ignores NULL / already freed */ + { + struct s2n_client_hello *client_hello = NULL; + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); + EXPECT_NULL(client_hello); + } + }; + + /* Test: Errors on client hello associated with a connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(&client_hello), S2N_ERR_INVALID_ARGUMENT); + EXPECT_NOT_NULL(s2n_connection_get_client_hello(server)); + EXPECT_NOT_EQUAL(server->client_hello.raw_message.size, 0); + }; + + /* Test: Frees client hello from raw message */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client)); + + EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); + + uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); + EXPECT_NOT_EQUAL(raw_size, 0); + uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); + EXPECT_NOT_NULL(raw); + + struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( + raw, raw_size); + EXPECT_NOT_NULL(client_hello); + + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); + EXPECT_NULL(client_hello); + } + }; + }; + + /* s2n_client_hello_get_compression_methods */ + { + /* Safety */ + { + uint32_t length = 0; + struct s2n_client_hello client_hello = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(NULL, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(&client_hello, NULL), S2N_ERR_NULL); + + uint8_t list = 0; + uint32_t list_length = 0; + uint32_t out_length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(NULL, &list, list_length, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, NULL, list_length, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, NULL), S2N_ERR_NULL); + + /* User did not provide a large enough buffer to write the compression methods */ + uint8_t data[] = { 1, 2, 3, 4, 5 }; + EXPECT_SUCCESS(s2n_blob_init(&client_hello.compression_methods, data, sizeof(data))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, &out_length), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Retrieves the compression methods list */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); + EXPECT_EQUAL(length, 1); + uint8_t list = 0; + uint32_t out_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, &list, sizeof(list), &out_length)); + EXPECT_EQUAL(out_length, 1); + }; + + /* Retrieves compression methods list longer than one byte */ + { + /* Compression methods were deprecated in TLS13 and s2n has never + * supported them. However, it is conceivable that a client could send + * us a list that contains more than the "null" byte. Therefore, we construct + * a fake Client Hello that contains a longer list of compression methods + * for testing. + */ + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, 0x02, + /* Cipher suite - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */ + 0xC0, 0x2F, + COMPRESSION_METHODS_LEN, + COMPRESSION_METHODS, + /* Extensions len */ + 0x00, 0x00 + }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_conn->handshake.io, client_hello_message, sizeof(client_hello_message))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); + EXPECT_EQUAL(length, COMPRESSION_METHODS_LEN); + uint8_t list[5] = { 0 }; + uint32_t out_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, list, sizeof(list), &out_length)); + EXPECT_EQUAL(out_length, COMPRESSION_METHODS_LEN); + + uint8_t compression_data[] = { COMPRESSION_METHODS }; + EXPECT_BYTEARRAY_EQUAL(list, compression_data, out_length); + } + }; + + /* s2n_client_hello_get_legacy_protocol_version */ + { + /* Safety */ + { + uint8_t out = 0; + struct s2n_client_hello client_hello = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(NULL, &out), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(&client_hello, NULL), S2N_ERR_NULL); + }; + + /* Retrieves the Client Hello protocol version */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint8_t version = 0; + EXPECT_SUCCESS(s2n_client_hello_get_legacy_protocol_version(client_hello, &version)); + EXPECT_EQUAL(version, S2N_TLS12); + }; + }; + + /* s2n_client_hello_get_legacy_record_version */ + { + /* Safety */ + { + uint8_t out = 0; + struct s2n_client_hello client_hello = { 0 }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(NULL, &out), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(&client_hello, NULL), S2N_ERR_NULL); + }; + + /* Retrieves record version */ + { + uint8_t out = 0; + struct s2n_client_hello client_hello = { 0 }; + client_hello.legacy_record_version = S2N_TLS12; + client_hello.record_version_recorded = 1; + EXPECT_SUCCESS(s2n_client_hello_get_legacy_record_version(&client_hello, &out)); + EXPECT_EQUAL(out, S2N_TLS12); + }; + }; + + /* s2n_client_hello_get_server_name() */ + { + /* Safety */ + { + struct s2n_client_hello ch = { 0 }; + uint16_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(NULL, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(&ch, NULL), S2N_ERR_NULL); + + uint8_t buffer = 0; + uint16_t out_length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(NULL, &buffer, 0, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, NULL, 0, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, &buffer, 0, NULL), S2N_ERR_NULL); + }; + + /* Retrieves the first entry in the server_name extension */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + const char *test_server_name = "test server name!"; + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint16_t length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_server_name_length(client_hello, &length)); + EXPECT_EQUAL(strlen(test_server_name), length); + uint8_t buffer[20] = { 0 }; + uint16_t out_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_server_name(client_hello, buffer, sizeof(buffer), &out_length)); + EXPECT_EQUAL(length, out_length); + + EXPECT_BYTEARRAY_EQUAL(buffer, test_server_name, out_length); + + /* Check error occurs if buffer is too small to hold server name */ + uint8_t small_buf[2] = { 0 }; + out_length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(client_hello, small_buf, sizeof(small_buf), &out_length), S2N_ERR_SAFETY); + }; + }; + + /* Test: large Client Hellos */ + { + const uint16_t cipher_suites_max_length = (1 << 16) - 2; + const uint16_t num_of_cipher_suites_to_drop = 150; + + /** + * S2N-TLS automatically includes TLS_EMPTY_RENEGOTIATION_INFO_SCSV in TLS 1.2 ClientHello, + * so we subtract 1 from the maximum number of cipher suites to reserve space for it. + */ + const uint16_t max_cipher_suite_count = (cipher_suites_max_length / S2N_TLS_CIPHER_SUITE_LEN) - 1; + + /* Drop 150 cipher suites from max, so that the total handshake message length won't exceed 64KB */ + const uint16_t reduced_cipher_suite_count = max_cipher_suite_count - num_of_cipher_suites_to_drop; + + uint16_t cipher_suites_counts[] = { reduced_cipher_suite_count, max_cipher_suite_count }; + + for (size_t i = 0; i < s2n_array_len(cipher_suites_counts); i++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* We need to generate a large Client Hello. + * We do this by manipulating the number of cipher suites, + * which is the easiest way to make Client Hello large. + */ + uint16_t cipher_suites_count = cipher_suites_counts[i]; + struct s2n_cipher_suite *test_cipher_suites[UINT16_MAX] = { 0 }; + for (size_t j = 0; j < cipher_suites_count; j++) { + test_cipher_suites[j] = &s2n_rsa_with_aes_128_gcm_sha256; + } + const struct s2n_cipher_preferences test_cipher_suites_preferences = { + .count = cipher_suites_count, + .suites = test_cipher_suites, + }; + const struct s2n_security_policy *default_policy = NULL; + /* 20240501 is a policy that can only negotiate tls12 */ + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20240501", &default_policy)); + struct s2n_security_policy test_security_policy = *default_policy; + test_security_policy.cipher_preferences = &test_cipher_suites_preferences; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + client->security_policy_override = &test_security_policy; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + server->security_policy_override = &test_security_policy; + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + /** + * Write Client Hello into io_pair.server_in. + * The server_in buffer contains the Client Hello message plus a 5-byte record header. + */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client, &blocked), S2N_ERR_IO_BLOCKED); + + /* Add one extra cipher suite length to account for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ + uint16_t cipher_suites_length = (cipher_suites_count + 1) * S2N_TLS_CIPHER_SUITE_LEN; + + if (cipher_suites_length < cipher_suites_max_length) { + /** + * The Client Hello message size should be less than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, even with + * the five byte record header. + */ + EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + } else { + /** + * When using maximum cipher suites, the Client Hello message size exceeds + * S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH by more than five byte. + * + * Hence, if server_in's available data is greater than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, + * then the Client Hello itself exceeds the maximum allowed size, even after accounting for the record header. + */ + EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), S2N_ERR_BAD_MESSAGE); + } + } + } + + /* Test s2n_client_hello_get_random */ + { + /* Safety */ + { + uint8_t out[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + struct s2n_client_hello client_hello = { 0 }; + + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_random(NULL, out, sizeof(out)), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_random(&client_hello, NULL, sizeof(out)), + S2N_ERR_NULL); + + /* Buffer size must be large enough to hold the full client random */ + uint8_t small_buffer[S2N_TLS_RANDOM_DATA_LEN - 1] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_random(&client_hello, small_buffer, sizeof(small_buffer)), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Retrieves the client random and zeroes it from the raw message */ + { + /* Construct a minimal ClientHello with a custom random value */ + uint8_t custom_random[S2N_TLS_RANDOM_DATA_LEN] = { ZERO_TO_THIRTY_ONE }; + + /* clang-format off */ + uint8_t raw_client_hello[] = { + /* message type */ + TLS_CLIENT_HELLO, + /* message size */ + 0x00, 0x00, 43, + /* protocol version - TLS1.2 */ + 0x03, 0x03, + /* random - custom value */ + ZERO_TO_THIRTY_ONE, + /* session id */ + 0x00, + /* cipher suites */ + 0x00, 0x02, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + /* legacy compression methods */ + 0x01, 0x00, + /* extensions - empty */ + 0x00, 0x00, + }; + /* clang-format on */ + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + client_hello = s2n_client_hello_parse_message(raw_client_hello, sizeof(raw_client_hello)); + EXPECT_NOT_NULL(client_hello); + + /* Exact-size buffer: retrieve the client random */ + uint8_t retrieved_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_client_hello_get_random( + client_hello, retrieved_random, S2N_TLS_RANDOM_DATA_LEN)); + EXPECT_BYTEARRAY_EQUAL(retrieved_random, custom_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Buffer size > 32 bytes must also succeed and only write the first 32 bytes */ + { + uint8_t large_buffer[S2N_TLS_RANDOM_DATA_LEN + 10] = { 0 }; + EXPECT_SUCCESS(s2n_client_hello_get_random( + client_hello, large_buffer, sizeof(large_buffer))); + + /* First 32 bytes match the client random */ + EXPECT_BYTEARRAY_EQUAL(large_buffer, custom_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Tail remains untouched */ + uint8_t zero_tail[10] = { 0 }; + EXPECT_BYTEARRAY_EQUAL( + large_buffer + S2N_TLS_RANDOM_DATA_LEN, + zero_tail, + sizeof(zero_tail)); + } + + /* The raw message should have the random zeroed out after retrieval */ + uint8_t *raw_message = client_hello->raw_message.data; + uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; + uint8_t zeroed_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_BYTEARRAY_EQUAL( + raw_message + client_random_offset, + zeroed_random, + S2N_TLS_RANDOM_DATA_LEN); + }; + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + END_TEST(); +} diff --git a/tests/unit/s2n_client_record_version_test.c b/tests/unit/s2n_client_record_version_test.c index a7729345af3..12a9a88cef1 100644 --- a/tests/unit/s2n_client_record_version_test.c +++ b/tests/unit/s2n_client_record_version_test.c @@ -1,315 +1,316 @@ -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Server negotiates TLS1.2 */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - int body_len = sizeof(server_hello_message); - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Read ClientHello s2n wrote */ - uint8_t buf[1024]; - size_t buf_occupied = 0; - - /* we need only first 10 bytes to get to ClientHello protocol version */ - while (buf_occupied < 10) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 10 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Record Type is Handshake */ - EXPECT_EQUAL(buf[0], 0x16); - /* Protocol version is TLS1.0 */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x01); - /* Handshake Type is ClientHello */ - EXPECT_EQUAL(buf[5], 0x01); - /* Handshake Protocol Version is TLS1.2 */ - EXPECT_EQUAL(buf[9], 0x03); - EXPECT_EQUAL(buf[10], 0x03); - - /* Read the rest of the pipe */ - while (1) { - ssize_t n = read(io_pair.server, buf, sizeof(buf)); - - if (n > 0) { - continue; - } - - EXPECT_EQUAL(n, -1); - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - } - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Verify that protocol versions are TLS1.2 now */ - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS12); - - /* Now lets shutdown the connection and verify that alert is sent in record with protocol version TLS1.2 */ - EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); - - /* Receive the next record from client and ensure that record protocol version is TLS1.2 */ - buf_occupied = 0; - /* We need only first 5 bytes to get to record protocol version */ - while (buf_occupied < 5) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 5 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Protocol version is TLS1.2 now */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x03); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Server negotiates SSLv3 */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_hello_message[] = { - /* Protocol version SSLv3 */ - 0x03, 0x00, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - DES-CBC3-SHA */ - 0x00, 0x0A, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - int body_len = sizeof(server_hello_message); - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version SSLv3 */ - 0x03, - 0x00, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Read ClientHello s2n wrote */ - uint8_t buf[1024]; - size_t buf_occupied = 0; - - /* we need only first 10 bytes to get to ClientHello protocol version */ - while (buf_occupied < 10) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 10 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Record Type is Handshake */ - EXPECT_EQUAL(buf[0], 0x16); - /* Protocol version is TLS1.0 */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x01); - /* Handshake Type is ClientHello */ - EXPECT_EQUAL(buf[5], 0x01); - /* Handshake Protocol Version is TLS1.2 */ - EXPECT_EQUAL(buf[9], 0x03); - EXPECT_EQUAL(buf[10], 0x03); - - /* Read the rest of the pipe */ - while (1) { - ssize_t n = read(io_pair.server, buf, sizeof(buf)); - - if (n > 0) { - continue; - } - - EXPECT_EQUAL(n, -1); - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - } - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Verify that protocol versions are SSLv3 with the exeption of client which supports TLS1.2 */ - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); - EXPECT_EQUAL(client_conn->server_protocol_version, S2N_SSLv3); - - /* Now lets shutdown the connection and verify that alert is sent in record with protocol version SSLv3 */ - EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); - - /* Receive the next record from client and ensure that record protocol version is SSLv3 */ - buf_occupied = 0; - /* We need only first 5 bytes to get to record protocol version */ - while (buf_occupied < 5) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 5 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Protocol version is SSLv3 now */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x00); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Server negotiates TLS1.2 */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Read ClientHello s2n wrote */ + uint8_t buf[1024]; + size_t buf_occupied = 0; + + /* we need only first 10 bytes to get to ClientHello protocol version */ + while (buf_occupied < 10) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 10 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Record Type is Handshake */ + EXPECT_EQUAL(buf[0], 0x16); + /* Protocol version is TLS1.0 */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x01); + /* Handshake Type is ClientHello */ + EXPECT_EQUAL(buf[5], 0x01); + /* Handshake Protocol Version is TLS1.2 */ + EXPECT_EQUAL(buf[9], 0x03); + EXPECT_EQUAL(buf[10], 0x03); + + /* Read the rest of the pipe */ + while (1) { + ssize_t n = read(io_pair.server, buf, sizeof(buf)); + + if (n > 0) { + continue; + } + + EXPECT_EQUAL(n, -1); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + } + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Verify that protocol versions are TLS1.2 now */ + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS12); + + /* Now lets shutdown the connection and verify that alert is sent in record with protocol version TLS1.2 */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); + + /* Receive the next record from client and ensure that record protocol version is TLS1.2 */ + buf_occupied = 0; + /* We need only first 5 bytes to get to record protocol version */ + while (buf_occupied < 5) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 5 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Protocol version is TLS1.2 now */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x03); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Server negotiates SSLv3 */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version SSLv3 */ + 0x03, 0x00, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - DES-CBC3-SHA */ + 0x00, 0x0A, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version SSLv3 */ + 0x03, + 0x00, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Read ClientHello s2n wrote */ + uint8_t buf[1024]; + size_t buf_occupied = 0; + + /* we need only first 10 bytes to get to ClientHello protocol version */ + while (buf_occupied < 10) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 10 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Record Type is Handshake */ + EXPECT_EQUAL(buf[0], 0x16); + /* Protocol version is TLS1.0 */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x01); + /* Handshake Type is ClientHello */ + EXPECT_EQUAL(buf[5], 0x01); + /* Handshake Protocol Version is TLS1.2 */ + EXPECT_EQUAL(buf[9], 0x03); + EXPECT_EQUAL(buf[10], 0x03); + + /* Read the rest of the pipe */ + while (1) { + ssize_t n = read(io_pair.server, buf, sizeof(buf)); + + if (n > 0) { + continue; + } + + EXPECT_EQUAL(n, -1); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + } + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Verify that protocol versions are SSLv3 with the exeption of client which supports TLS1.2 */ + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_SSLv3); + + /* Now lets shutdown the connection and verify that alert is sent in record with protocol version SSLv3 */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); + + /* Receive the next record from client and ensure that record protocol version is SSLv3 */ + buf_occupied = 0; + /* We need only first 5 bytes to get to record protocol version */ + while (buf_occupied < 5) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 5 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Protocol version is SSLv3 now */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x00); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_client_secure_renegotiation_test.c b/tests/unit/s2n_client_secure_renegotiation_test.c index f54b79af6d6..10922261afc 100644 --- a/tests/unit/s2n_client_secure_renegotiation_test.c +++ b/tests/unit/s2n_client_secure_renegotiation_test.c @@ -1,309 +1,310 @@ -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Success: server sends an empty initial renegotiation_info */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x01, - /* renegotiated_connection len */ - 0x00, - }; - int server_extensions_len = sizeof(server_extensions); - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (server_extensions_len >> 8) & 0xff, - (server_extensions_len & 0xff), - }; - int body_len = sizeof(server_hello_message) + server_extensions_len; - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into server hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Secure renegotiation is set */ - EXPECT_EQUAL(client_conn->secure_renegotiation, 1); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Success: server doesn't send an renegotiation_info extension */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - int body_len = sizeof(server_hello_message); - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into server hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Secure renegotiation is not set, as server doesn't support it */ - EXPECT_EQUAL(client_conn->secure_renegotiation, 0); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Failure: server sends a non-empty initial renegotiation_info */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x21, - /* renegotiated_connection len */ - 0x20, - /* fake renegotiated_connection */ - ZERO_TO_THIRTY_ONE, - }; - int server_extensions_len = sizeof(server_extensions); - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (server_extensions_len >> 8) & 0xff, - (server_extensions_len & 0xff), - }; - int body_len = sizeof(server_hello_message) + server_extensions_len; - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); - - /* Verify that we fail for non-empty renegotiated_connection */ - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Success: server sends an empty initial renegotiation_info */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x01, + /* renegotiated_connection len */ + 0x00, + }; + int server_extensions_len = sizeof(server_extensions); + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (server_extensions_len >> 8) & 0xff, + (server_extensions_len & 0xff), + }; + int body_len = sizeof(server_hello_message) + server_extensions_len; + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into server hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Secure renegotiation is set */ + EXPECT_EQUAL(client_conn->secure_renegotiation, 1); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Success: server doesn't send an renegotiation_info extension */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into server hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Secure renegotiation is not set, as server doesn't support it */ + EXPECT_EQUAL(client_conn->secure_renegotiation, 0); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Failure: server sends a non-empty initial renegotiation_info */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x21, + /* renegotiated_connection len */ + 0x20, + /* fake renegotiated_connection */ + ZERO_TO_THIRTY_ONE, + }; + int server_extensions_len = sizeof(server_extensions); + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (server_extensions_len >> 8) & 0xff, + (server_extensions_len & 0xff), + }; + int body_len = sizeof(server_hello_message) + server_extensions_len; + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); + + /* Verify that we fail for non-empty renegotiated_connection */ + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_config_test.c b/tests/unit/s2n_config_test.c index dfb91656445..06731b7a554 100644 --- a/tests/unit/s2n_config_test.c +++ b/tests/unit/s2n_config_test.c @@ -1,1384 +1,1385 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_config.h" - -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_openssl.h" -#include "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_client_supported_groups.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_record.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "unstable/npn.h" -#include "utils/s2n_map.h" -#include "utils/s2n_mem.h" - -#define S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT 30 - -/* forward declaration */ -int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair); - -static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, - struct s2n_offered_psk_list *psk_identity_list) -{ - return S2N_SUCCESS; -} - -static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) -{ - return S2N_SUCCESS; -} - -static int s2n_test_crl_lookup_cb(struct s2n_crl_lookup *lookup, void *context) -{ - return S2N_SUCCESS; -} - -static int s2n_test_cert_validation_cb(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *context) -{ - return S2N_SUCCESS; -} - -static int s2n_test_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - return S2N_SUCCESS; -} - -/* A malloc callback that always fails. Used by the regression test - * to force s2n_map_add to fail during cert map construction. - */ -static int s2n_test_failing_malloc_cb(void **ptr, uint32_t requested, uint32_t *allocated) -{ - *ptr = NULL; - *allocated = 0; - POSIX_BAIL(S2N_ERR_ALLOC); -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; - - const struct s2n_security_policy *default_security_policy = NULL, *tls13_security_policy = NULL, *fips_security_policy = NULL; - EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); - EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); - EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); - - char cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert, S2N_MAX_TEST_PEM_SIZE)); - char key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, key, S2N_MAX_TEST_PEM_SIZE)); - - /* Test: s2n_config_new and tls13_default_config match */ - { - struct s2n_config *config = NULL, *default_config = NULL; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(default_config = s2n_fetch_default_config()); - - /* s2n_config_new() matches s2n_fetch_default_config() */ - if (default_config->security_policy != config->security_policy) { - /* one possible cause for this is attempting to includes s2n_config.c - * for access to internal `static` functions. This causes two copies - * of the default config to be created. The default_config in *this* - * unit of translation doesn't get properly initialized. */ - const char *default_policy = NULL; - const char *new_policy = NULL; - EXPECT_OK(s2n_security_policy_get_version(default_config->security_policy, &default_policy)); - EXPECT_OK(s2n_security_policy_get_version(config->security_policy, &new_policy)); - printf("default policy is %s but new policy is %s\n", default_policy, new_policy); - } - EXPECT_EQUAL(default_config->security_policy, config->security_policy); - EXPECT_EQUAL(default_config->security_policy->signature_preferences, config->security_policy->signature_preferences); - EXPECT_EQUAL(default_config->client_cert_auth_type, config->client_cert_auth_type); - - /* Calling s2n_fetch_default_config() repeatedly returns the same object */ - EXPECT_EQUAL(default_config, s2n_fetch_default_config()); - - /* TLS1.3 default does not match non-TLS1.3 default */ - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_EQUAL(default_config, s2n_fetch_default_config()); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Connections created with default configs */ - { - /* For TLS1.2 */ - if (!s2n_is_in_fips_mode()) { - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); - - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, default_security_policy); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* For TLS1.3 */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); - - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, tls13_security_policy); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* For fips */ - if (s2n_is_in_fips_mode()) { - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); - - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, fips_security_policy); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - }; - - /* Test for s2n_config_new() and tls 1.3 behavior */ - { - if (!s2n_is_in_fips_mode()) { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_EQUAL(config->security_policy, default_security_policy); - EXPECT_SUCCESS(s2n_config_free(config)); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_EQUAL(config->security_policy, tls13_security_policy); - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - }; - - /* Test setting the callback to select PSK identity */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - uint8_t context = 13; - - /* Safety check */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_psk_selection_callback( - NULL, s2n_test_select_psk_identity_callback, &context), - S2N_ERR_NULL); - EXPECT_NULL(config->psk_selection_cb); - EXPECT_NULL(config->psk_selection_ctx); - - EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &context)); - EXPECT_EQUAL(config->psk_selection_cb, s2n_test_select_psk_identity_callback); - EXPECT_EQUAL(config->psk_selection_ctx, &context); - - EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, NULL, NULL)); - EXPECT_NULL(config->psk_selection_cb); - EXPECT_NULL(config->psk_selection_ctx); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /*Test s2n_connection_set_config */ - { - /* Test that tickets_to_send is set correctly */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - struct s2n_config *config = NULL; - uint8_t num_tickets = 1; - - EXPECT_NOT_NULL(config = s2n_config_new()); - - config->initial_tickets_to_send = num_tickets; - - EXPECT_EQUAL(conn->tickets_to_send, 0); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->tickets_to_send, num_tickets); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test that PSK type is set correctly */ - { - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); - - /* Overrides connection value */ - { - conn->config = NULL; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); - EXPECT_FALSE(conn->psk_mode_overridden); - }; - - /* Does not override connection value if conn->override_psk_mode set */ - { - conn->config = NULL; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - conn->psk_mode_overridden = true; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); - EXPECT_TRUE(conn->psk_mode_overridden); - conn->psk_mode_overridden = false; - }; - - /* Does not override connection value if PSKs already set */ - { - conn->config = NULL; - DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); - EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); - EXPECT_FALSE(conn->psk_mode_overridden); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - }; - - /* s2n_config_set_session_tickets_onoff */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, true), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, false), S2N_ERR_NULL); - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); - EXPECT_TRUE(config->use_tickets); - EXPECT_EQUAL(config->initial_tickets_to_send, 1); - - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); - EXPECT_FALSE(config->use_tickets); - EXPECT_EQUAL(config->initial_tickets_to_send, 1); - - config->initial_tickets_to_send = 10; - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); - EXPECT_TRUE(config->use_tickets); - EXPECT_EQUAL(config->initial_tickets_to_send, 10); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* s2n_config_set_context */ - /* s2n_config_get_context */ - { - uint8_t context = 42; - uint8_t other = 123; - void *returned_context = NULL; - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); - EXPECT_NULL(returned_context); - - EXPECT_SUCCESS(s2n_config_set_ctx(config, &context)); - EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); - EXPECT_NOT_NULL(returned_context); - - EXPECT_EQUAL(*((uint8_t *) returned_context), context); - EXPECT_NOT_EQUAL(*((uint8_t *) returned_context), other); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test s2n_config_set_extension_data */ - { - uint8_t extension_data[] = "extension data"; - - /* Test s2n_config_set_extension_data can be called for owned cert chains */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - - EXPECT_SUCCESS(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, - extension_data, sizeof(extension_data))); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, sizeof(extension_data)); - }; - - /* Test s2n_config_set_extension_data can't be called for unowned cert chains */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, - extension_data, sizeof(extension_data)), - S2N_ERR_CERT_OWNERSHIP); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, 0); - EXPECT_EQUAL(chain->ocsp_status.size, 0); - }; - }; - - /* Test s2n_config_free_cert_chain_and_key */ - { - /* Chain owned by application */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* No-op for application-owned chains */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Still no-op if called again */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - }; - - /* Chain owned by application and freed too early: - * This is arguably incorrect behavior, but did not cause errors in the past. - * We should continue to ensure it doesn't cause any errors. - */ - { - struct s2n_cert_chain_and_key *chain = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Free the chain early */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain)); - - /* No-op for application-owned chains */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* No-op if called again */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - }; - - /* Chain owned by library */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* No-op if called again */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NULL(s2n_config_get_single_default_cert(config)); - }; - - /* Switch from library-owned certs to application-owned certs */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* Now add an application-owned chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - }; - }; - - /* Test s2n_config_set_cert_chain_and_key_defaults */ - { - /* Succeeds if chains owned by app */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_1 = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_1, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_2 = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_2, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_1)); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_1); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &chain_2, 1)); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_2); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - }; - - /* Fails if chains owned by library */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_chain_and_key_defaults( - config, &chain, 1), - S2N_ERR_CERT_OWNERSHIP); - }; - }; - - /* Test s2n_config_set_send_buffer_size */ - { - /* Safety */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_EQUAL(config->send_buffer_size_override, 0); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(NULL, S2N_MIN_SEND_BUFFER_SIZE), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(config, 0), S2N_ERR_INVALID_ARGUMENT); - EXPECT_EQUAL(config->send_buffer_size_override, 0); - }; - - /* Default applied to connection */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_EQUAL(config->send_buffer_size_override, 0); - EXPECT_FALSE(conn->multirecord_send); - }; - - /* Custom applied to connection */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, S2N_MIN_SEND_BUFFER_SIZE)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_EQUAL(config->send_buffer_size_override, S2N_MIN_SEND_BUFFER_SIZE); - EXPECT_TRUE(conn->multirecord_send); - }; - }; - - /* Test s2n_config_set_verify_after_sign */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_FALSE(config->verify_after_sign); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(NULL, S2N_VERIFY_AFTER_SIGN_ENABLED), S2N_ERR_NULL); - - /* Invalid mode */ - config->verify_after_sign = true; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); - EXPECT_TRUE(config->verify_after_sign); - config->verify_after_sign = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FALSE(config->verify_after_sign); - - /* Set and unset */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_TRUE(config->verify_after_sign); - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_DISABLED)); - EXPECT_FALSE(config->verify_after_sign); - }; - - /* Test s2n_config_set_renegotiate_request_cb */ - { - uint8_t context = 0; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Unset by default */ - EXPECT_EQUAL(config->renegotiate_request_cb, NULL); - EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_renegotiate_request_cb(NULL, s2n_test_reneg_req_cb, &context), S2N_ERR_NULL); - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, &context)); - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, NULL)); - - /* Set */ - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, &context)); - EXPECT_EQUAL(config->renegotiate_request_cb, s2n_test_reneg_req_cb); - EXPECT_EQUAL(config->renegotiate_request_ctx, &context); - - /* Unset */ - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, NULL)); - EXPECT_EQUAL(config->renegotiate_request_cb, NULL); - EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); - }; - - /* Test s2n_config_set_npn */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_FALSE(config->npn_supported); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_npn(NULL, true), S2N_ERR_NULL); - - /* Set and unset */ - EXPECT_SUCCESS(s2n_config_set_npn(config, true)); - EXPECT_TRUE(config->npn_supported); - EXPECT_SUCCESS(s2n_config_set_npn(config, false)); - EXPECT_FALSE(config->npn_supported); - }; - - /* Test s2n_config_set_crl_lookup_cb */ - { - uint8_t context = 0; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Unset by default */ - EXPECT_EQUAL(config->crl_lookup_cb, NULL); - EXPECT_EQUAL(config->crl_lookup_ctx, NULL); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_crl_lookup_cb(NULL, s2n_test_crl_lookup_cb, &context), S2N_ERR_NULL); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, &context)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, NULL)); - - /* Set */ - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, &context)); - EXPECT_EQUAL(config->crl_lookup_cb, s2n_test_crl_lookup_cb); - EXPECT_EQUAL(config->crl_lookup_ctx, &context); - - /* Unset */ - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, NULL)); - EXPECT_EQUAL(config->crl_lookup_cb, NULL); - EXPECT_EQUAL(config->crl_lookup_ctx, NULL); - }; - - /* Test s2n_config_set_cert_validation_cb */ - { - uint8_t context = 0; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Unset by default */ - EXPECT_EQUAL(config->cert_validation_cb, NULL); - EXPECT_EQUAL(config->cert_validation_ctx, NULL); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_validation_cb(NULL, s2n_test_cert_validation_cb, &context), - S2N_ERR_NULL); - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, &context)); - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, NULL)); - - /* Set */ - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, &context)); - EXPECT_EQUAL(config->cert_validation_cb, s2n_test_cert_validation_cb); - EXPECT_EQUAL(config->cert_validation_ctx, &context); - - /* Unset */ - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, NULL)); - EXPECT_EQUAL(config->cert_validation_cb, NULL); - EXPECT_EQUAL(config->cert_validation_ctx, NULL); - }; - - /* Test s2n_config_set_status_request_type */ - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - s2n_mode mode = modes[mode_i]; - - if (!s2n_x509_ocsp_stapling_supported()) { - break; - } - - /* request_ocsp_status should be false by default */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_FALSE(config->ocsp_status_requested_by_user); - EXPECT_FALSE(config->ocsp_status_requested_by_s2n); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FALSE(conn->request_ocsp_status); - }; - - /* request_ocsp_status should be true if set via s2n_config_set_status_request_type */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_TRUE(config->ocsp_status_requested_by_user); - EXPECT_FALSE(config->ocsp_status_requested_by_s2n); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_TRUE(conn->request_ocsp_status); - }; - - /* ocsp_status_requested_by_s2n can be set in s2n_config_set_verification_ca_location. For - * backwards compatibility, this should tell clients to request OCSP stapling. However, this - * API should not tell servers to request OCSP stapling. - */ - for (int api_configuration_i = 0; api_configuration_i < 3; api_configuration_i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - switch (api_configuration_i) { - case 0: - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - break; - case 1: - /* If a user intentionally disables OCSP stapling, s2n_config_set_verification_ca_location - * should not re-enable it for servers. - */ - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - break; - default: - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - break; - } - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - if (mode == S2N_CLIENT) { - EXPECT_TRUE(conn->request_ocsp_status); - } else { - EXPECT_FALSE(conn->request_ocsp_status); - } - }; - - /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_OCSP should enable OCSP - * status requests, regardless of s2n_config_set_verification_ca_location. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_TRUE(conn->request_ocsp_status); - }; - - /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_NONE should disable OCSP - * status requests, regardless of s2n_config_set_verification_ca_location. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FALSE(conn->request_ocsp_status); - }; - }; - - /* Test s2n_config_add_cert_chain */ - { - uint32_t pem_len = 0; - uint8_t pem_bytes[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - pem_bytes, &pem_len, sizeof(pem_bytes))); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain(config, pem_bytes, pem_len)); - EXPECT_TRUE(config->no_signing_key); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - struct s2n_cert_chain_and_key *chain = s2n_config_get_single_default_cert(config); - POSIX_ENSURE_REF(chain); - EXPECT_FAILURE(s2n_pkey_check_key_exists(chain->private_key)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_NO_PRIVATE_KEY); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_fn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - }; - - /* Test loading system certs */ - { - /* s2n_config_load_system_certs safety */ - { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(NULL), S2N_ERR_NULL); - } - - /* s2n_config_new_minimal should not load system certs */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_NULL(config->trust_store.trust_store); - EXPECT_FALSE(config->trust_store.loaded_system_certs); - - /* System certs can be loaded onto the minimal config */ - EXPECT_SUCCESS(s2n_config_load_system_certs(config)); - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - - /* Attempting to load system certs multiple times on the same config should error */ - for (int i = 0; i < 20; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - } - - /* s2n_config_new should load system certs */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - - /* Attempting to load system certs multiple times on the same config should error */ - for (int i = 0; i < 20; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - } - - /* The default config should load system certs */ - { - struct s2n_config *config = s2n_fetch_default_config(); - EXPECT_NOT_NULL(config); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - - /* System certs can be loaded again after wiping the trust store */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - for (int i = 0; i < 20; i++) { - /* System certs were already loaded, so an attempt to load them should fail */ - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - - EXPECT_SUCCESS(s2n_config_wipe_trust_store(config)); - - /* The trust store is cleared after a wipe, so it should be possible to load system certs again */ - EXPECT_FALSE(config->trust_store.loaded_system_certs); - EXPECT_SUCCESS(s2n_config_load_system_certs(config)); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - } - - /* Ensure that system certs are properly loaded into the X509_STORE. - * - * The API used to retrieve the contents of an X509_STORE, X509_STORE_get0_objects, - * wasn't added until OpenSSL 1.1.0. - */ -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_NULL(config->trust_store.trust_store); - EXPECT_FALSE(config->trust_store.loaded_system_certs); - - /* Initialize the X509_STORE by adding a cert */ - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&config->trust_store, S2N_RSA_PSS_2048_SHA256_CA_CERT, NULL)); - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_FALSE(config->trust_store.loaded_system_certs); - - /* The X509_STORE should only contain the single cert that was added. */ - STACK_OF(X509_OBJECT) *x509_store_contents = X509_STORE_get0_objects(config->trust_store.trust_store); - EXPECT_NOT_NULL(x509_store_contents); - int initial_contents_count = sk_X509_OBJECT_num(x509_store_contents); - EXPECT_EQUAL(initial_contents_count, 1); - - /* Override the system cert file to guarantee that a system cert will be loaded */ - EXPECT_SUCCESS(setenv("SSL_CERT_FILE", S2N_SHA1_ROOT_SIGNATURE_CA_CERT, 1)); - - /* Load the system cert into the store */ - EXPECT_SUCCESS(s2n_config_load_system_certs(config)); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - int system_certs_contents_count = sk_X509_OBJECT_num(x509_store_contents); - - /* LibreSSL doesn't use the SSL_CERT_FILE environment variable to set the system cert location, - * so we don't know how many system certs will be loaded, if any. - */ - if (!s2n_libcrypto_is_libressl()) { - EXPECT_EQUAL(system_certs_contents_count, initial_contents_count + 1); - } - - /* Additional calls to s2n_config_load_default_certs should not add additional certs to the store */ - for (int i = 0; i < 20; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - int additional_call_contents_count = sk_X509_OBJECT_num(x509_store_contents); - EXPECT_TRUE(additional_call_contents_count == system_certs_contents_count); - } - - EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); - } -#endif - - /* Self-talk tests */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Ensure a handshake succeeds with a minimal server config and no mutual auth */ - { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); - 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_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_FALSE(server_config->trust_store.loaded_system_certs); - EXPECT_NULL(server_config->trust_store.trust_store); - - EXPECT_FALSE(client_config->trust_store.loaded_system_certs); - EXPECT_NOT_NULL(client_config->trust_store.trust_store); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - - /* Ensure a handshake fails gracefully with an uninitialized trust store */ - { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); - 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_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); - EXPECT_NOT_NULL(client_config); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_FALSE(server_config->trust_store.loaded_system_certs); - EXPECT_NULL(server_config->trust_store.trust_store); - - EXPECT_FALSE(client_config->trust_store.loaded_system_certs); - EXPECT_NULL(client_config->trust_store.trust_store); - - /* The client should fail to validate the server's certificate without an initialized trust store */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_UNTRUSTED); - } - } - } - - /* s2n_config_disable_x509_time_verification tests */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_time_verification(NULL), S2N_ERR_NULL); - - /* Ensure s2n_config_disable_x509_time_verification sets the proper state */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_EQUAL(config->disable_x509_time_validation, false); - - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - EXPECT_EQUAL(config->disable_x509_time_validation, true); - } - } - - /* Test s2n_config_get_supported_groups */ - { - /* Safety */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; - uint16_t supported_groups_count = 0; - - int ret = s2n_config_get_supported_groups(NULL, supported_groups, s2n_array_len(supported_groups), - &supported_groups_count); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); - EXPECT_EQUAL(supported_groups_count, 0); - - ret = s2n_config_get_supported_groups(config, NULL, s2n_array_len(supported_groups), &supported_groups_count); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); - EXPECT_EQUAL(supported_groups_count, 0); - - ret = s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), NULL); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); - EXPECT_EQUAL(supported_groups_count, 0); - } - - /* Error if the provided supported groups array is too small */ - { - /* Test a policy with and without PQ kem groups */ - const char *policies[] = { - "20170210", - "PQ-TLS-1-2-2024-10-07", - }; - - for (size_t i = 0; i < s2n_array_len(policies); i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, policies[i])); - - uint32_t policy_groups_count = 0; - EXPECT_OK(s2n_kem_preferences_groups_available(config->security_policy->kem_preferences, - &policy_groups_count)); - policy_groups_count += config->security_policy->ecc_preferences->count; - EXPECT_TRUE(policy_groups_count > 0); - - uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; - uint16_t supported_groups_count = 11; - for (size_t invalid_count = 0; invalid_count < policy_groups_count; invalid_count++) { - int ret = s2n_config_get_supported_groups(config, supported_groups, invalid_count, - &supported_groups_count); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INSUFFICIENT_MEM_SIZE); - EXPECT_EQUAL(supported_groups_count, 0); - } - - EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, policy_groups_count, - &supported_groups_count)); - EXPECT_EQUAL(supported_groups_count, policy_groups_count); - } - } - - /* The groups produced by s2n_config_get_supported_groups should match the groups produced - * by a connection that's configured to send its entire list of supported groups - */ - for (size_t policy_idx = 0; security_policy_selection[policy_idx].version != NULL; policy_idx++) { - const struct s2n_security_policy *security_policy = security_policy_selection[policy_idx].security_policy; - EXPECT_NOT_NULL(security_policy); - - uint32_t expected_groups_count = 0; - EXPECT_OK(s2n_kem_preferences_groups_available(security_policy->kem_preferences, &expected_groups_count)); - expected_groups_count += security_policy->ecc_preferences->count; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = security_policy; - - uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; - uint16_t supported_groups_count = 0; - EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), - &supported_groups_count)); - EXPECT_EQUAL(supported_groups_count, expected_groups_count); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - /* PQ kem groups aren't sent in the supported groups extension before TLS 1.3. */ - conn->actual_protocol_version = S2N_TLS13; - - DEFER_CLEANUP(struct s2n_stuffer extension_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension_stuffer, 0)); - EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &extension_stuffer)); - - uint16_t extension_groups_count = 0; - EXPECT_OK(s2n_supported_groups_parse_count(&extension_stuffer, &extension_groups_count)); - EXPECT_EQUAL(extension_groups_count, expected_groups_count); - - for (size_t i = 0; i < supported_groups_count; i++) { - /* s2n_stuffer_read_uint16 is used to read each of the supported groups in - * network-order endianness. - */ - uint16_t group_iana = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &group_iana)); - - EXPECT_EQUAL(group_iana, supported_groups[i]); - } - } - } - - /* Test s2n_config_validate_loaded_certificates */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *invalid_cert = NULL, s2n_cert_chain_and_key_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *valid_cert = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&invalid_cert, "ec", "ecdsa", "p384", "sha256")); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&valid_cert, "ec", "ecdsa", "p384", "sha384")); - - struct s2n_security_policy rfc9151_applied_locally = security_policy_20250429; - rfc9151_applied_locally.certificate_preferences_apply_locally = true; - - /* rfc9151 doesn't allow SHA256 signatures, but does allow SHA384 signatures, - * so ecdsa_p384_sha256 is invalid and ecdsa_p384_sha384 is valid */ - - /* valid certs are accepted */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); - EXPECT_OK(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally)); - }; - - /* when cert preferences don't apply locally, invalid certs are accepted */ - { - struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; - non_local_rfc9151.certificate_preferences_apply_locally = false; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, invalid_cert)); - EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); - }; - - /* Certs in an s2n_config are stored in default_certs_by_type, domain_name_to_cert_map, or - * both. We want to ensure that the s2n_config_validate_loaded_certificates method will - * validate certs in both locations. - */ - - /* certs in default_certs_by_type are validated */ - { - /* s2n_config_set_cert_chain_and_key_defaults populates default_certs_by_type - * but doesn't populate domain_name_to_cert_map - */ - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &invalid_cert, 1)); - - /* domain certs is empty */ - uint32_t domain_certs_count = 0; - EXPECT_OK(s2n_map_size(config->domain_name_to_cert_map, &domain_certs_count)); - EXPECT_EQUAL(domain_certs_count, 0); - - /* certs in default_certs_by_type are validated */ - EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - }; - - /* certs in the domain map are validated */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_build_domain_name_to_cert_map(config, invalid_cert)); - - /* default_certs_by_type is empty. */ - EXPECT_EQUAL(s2n_config_get_num_default_certs(config), 0); - - /* certs in domain_map are validated */ - EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - }; - - /* when cert preferences don't apply locally, certs in domain map are not iterated over - * - * Some customers load large numbers of certificates, so even iterating - * over every certificate without performing any validation is expensive. - */ - { - struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; - - /* Assert that the security policy WOULD apply, - * if certificate_preferences_apply_locally was true. - */ - EXPECT_NOT_NULL(non_local_rfc9151.certificate_key_preferences); - EXPECT_NOT_NULL(non_local_rfc9151.certificate_signature_preferences); - EXPECT_TRUE(non_local_rfc9151.certificate_key_preferences->count > 0); - EXPECT_TRUE(non_local_rfc9151.certificate_signature_preferences->count > 0); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); - - /* Invalidate the domain map so that attempting to use it will trigger - * an error. We want to ensure that we DON'T use it. - * Iterating over a map requires that map to be immutable / complete. - */ - EXPECT_OK(s2n_map_unlock(config->domain_name_to_cert_map)); - - /* Control case: if local validation needed, attempt to use invalid domain map */ - non_local_rfc9151.certificate_preferences_apply_locally = true; - EXPECT_ERROR_WITH_ERRNO( - s2n_config_validate_loaded_certificates(config, &non_local_rfc9151), - S2N_ERR_MAP_MUTABLE); - - /* Test case: if no local validation needed, do not use invalid domain map */ - non_local_rfc9151.certificate_preferences_apply_locally = false; - EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); - }; - }; - - /* Checks that servers don't use a config before the client hello callback is executed. - * - * We want to assert that a config is never used by a server until the client hello callback - * is called, given that users have the ability to swap out the config during this callback. - */ - { - DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_client_config); - /* Security policy that only supports TLS12 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); - - DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(tls13_client_config); - /* Security policy that supports TLS13 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "20240503")); - - struct s2n_config *config_arr[] = { tls12_client_config, tls13_client_config }; - - /* Checks that the handshake gets as far as the client hello callback with a NULL config */ - for (size_t i = 0; i < s2n_array_len(config_arr); i++) { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_arr[i])); - - /* Server config pointer is explicitly set to NULL */ - server_conn->config = NULL; - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &test_io)); - - /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the - * client hello callback. Therefore, we can assert that if we hit this error, we - * have gotten as far as the client hello callback without dereferencing the config. - */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); - } - } - - /* Checks that servers don't use a config before the client hello callback is executed on a - * SSLv2-formatted client hello. - * - * Parsing SSLv2 hellos uses a different code path and needs to be tested separately. - */ - { - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - struct s2n_blob client_hello = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&client_hello, sslv2_client_hello, sizeof(sslv2_client_hello))); - - /* Checks that the handshake gets as far as the client hello callback with a NULL config */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - server_conn->config = NULL; - - /* Record version and protocol version are in the header for SSLv2 */ - server_conn->client_hello.sslv2 = true; - server_conn->client_hello.legacy_version = S2N_TLS12; - - /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the - * client hello callback. Therefore, we can assert that if we hit this error, we - * have gotten as far as the client hello callback without dereferencing the config. - */ - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); - } - } - - /* s2n_config_set_subscriber */ - { - /* Safety */ - uint64_t fake_subscriber = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (void *) &fake_subscriber), S2N_ERR_NULL); - }; - - /* s2n_config_set_handshake_event */ - { - /* Safety */ - uint64_t fake_callback = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (s2n_event_on_handshake_cb) &fake_callback), S2N_ERR_NULL); - }; - - /* Test: domain_name_to_cert_map remains usable after s2n_map_add fails - * - * Regression test: s2n_config_update_domain_name_to_cert_map - * calls s2n_map_unlock -> s2n_map_add -> s2n_map_complete. If s2n_map_add - * fails (e.g. OOM), the map was left in a mutable (!immutable) state. - * All subsequent s2n_map_lookup calls, used on every ClientHello for - * SNI-based cert selection, would then fail with S2N_ERR_MAP_MUTABLE, - * silently disabling SNI matching for the config's lifetime. - * - * The fix ensures s2n_map_complete is always called after unlock, - * even when add fails. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* The map starts empty and immutable. Verify a lookup succeeds. */ - { - struct s2n_blob lookup_name = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&lookup_name, - (uint8_t *) "test.example.com", strlen("test.example.com"))); - struct s2n_blob lookup_value = { 0 }; - bool found = false; - EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, - &lookup_name, &lookup_value, &found)); - EXPECT_FALSE(found); - } - - /* Create a cert chain to attempt adding. */ - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Install a malloc callback that always fails so s2n_map_add will - * fail during s2n_dup inside the unlock -> add -> complete path. - */ - s2n_mem_init_callback saved_init_cb = NULL; - s2n_mem_cleanup_callback saved_cleanup_cb = NULL; - s2n_mem_malloc_callback saved_malloc_cb = NULL; - s2n_mem_free_callback saved_free_cb = NULL; - EXPECT_OK(s2n_mem_get_callbacks(&saved_init_cb, &saved_cleanup_cb, - &saved_malloc_cb, &saved_free_cb)); - EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, - s2n_test_failing_malloc_cb, saved_free_cb)); - - /* The cert's SANs/CNs are not in the empty map, so the update - * function will take the !key_found branch: unlock -> add (FAIL). - * With the fix, complete is still called before returning the error. - */ - EXPECT_FAILURE(s2n_config_build_domain_name_to_cert_map(config, chain)); - - /* Restore the real malloc callback before any further allocations. */ - EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, - saved_malloc_cb, saved_free_cb)); - - /* The regression check: the map must still be immutable so that - * lookups succeed. Before the fix, this would fail with - * S2N_ERR_MAP_MUTABLE because s2n_map_complete was never called - * after the failed add. - */ - { - struct s2n_blob lookup_name = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&lookup_name, - (uint8_t *) "test.example.com", strlen("test.example.com"))); - struct s2n_blob lookup_value = { 0 }; - bool found = false; - EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, - &lookup_name, &lookup_value, &found)); - /* Lookup should succeed (not error) even though the key isn't found. */ - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_config.h" + +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_openssl.h" +#include "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_record.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "unstable/npn.h" +#include "utils/s2n_map.h" +#include "utils/s2n_mem.h" + +#define S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT 30 + +/* forward declaration */ +int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair); + +static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_identity_list) +{ + return S2N_SUCCESS; +} + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) +{ + return S2N_SUCCESS; +} + +static int s2n_test_crl_lookup_cb(struct s2n_crl_lookup *lookup, void *context) +{ + return S2N_SUCCESS; +} + +static int s2n_test_cert_validation_cb(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *context) +{ + return S2N_SUCCESS; +} + +static int s2n_test_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + return S2N_SUCCESS; +} + +/* A malloc callback that always fails. Used by the regression test + * to force s2n_map_add to fail during cert map construction. + */ +static int s2n_test_failing_malloc_cb(void **ptr, uint32_t requested, uint32_t *allocated) +{ + *ptr = NULL; + *allocated = 0; + POSIX_BAIL(S2N_ERR_ALLOC); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + const struct s2n_security_policy *default_security_policy = NULL, *tls13_security_policy = NULL, *fips_security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); + + char cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert, S2N_MAX_TEST_PEM_SIZE)); + char key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, key, S2N_MAX_TEST_PEM_SIZE)); + + /* Test: s2n_config_new and tls13_default_config match */ + { + struct s2n_config *config = NULL, *default_config = NULL; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(default_config = s2n_fetch_default_config()); + + /* s2n_config_new() matches s2n_fetch_default_config() */ + if (default_config->security_policy != config->security_policy) { + /* one possible cause for this is attempting to includes s2n_config.c + * for access to internal `static` functions. This causes two copies + * of the default config to be created. The default_config in *this* + * unit of translation doesn't get properly initialized. */ + const char *default_policy = NULL; + const char *new_policy = NULL; + EXPECT_OK(s2n_security_policy_get_version(default_config->security_policy, &default_policy)); + EXPECT_OK(s2n_security_policy_get_version(config->security_policy, &new_policy)); + printf("default policy is %s but new policy is %s\n", default_policy, new_policy); + } + EXPECT_EQUAL(default_config->security_policy, config->security_policy); + EXPECT_EQUAL(default_config->security_policy->signature_preferences, config->security_policy->signature_preferences); + EXPECT_EQUAL(default_config->client_cert_auth_type, config->client_cert_auth_type); + + /* Calling s2n_fetch_default_config() repeatedly returns the same object */ + EXPECT_EQUAL(default_config, s2n_fetch_default_config()); + + /* TLS1.3 default does not match non-TLS1.3 default */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_EQUAL(default_config, s2n_fetch_default_config()); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Connections created with default configs */ + { + /* For TLS1.2 */ + if (!s2n_is_in_fips_mode()) { + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, default_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* For TLS1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, tls13_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* For fips */ + if (s2n_is_in_fips_mode()) { + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, fips_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test for s2n_config_new() and tls 1.3 behavior */ + { + if (!s2n_is_in_fips_mode()) { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->security_policy, default_security_policy); + EXPECT_SUCCESS(s2n_config_free(config)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->security_policy, tls13_security_policy); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test setting the callback to select PSK identity */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + uint8_t context = 13; + + /* Safety check */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_psk_selection_callback( + NULL, s2n_test_select_psk_identity_callback, &context), + S2N_ERR_NULL); + EXPECT_NULL(config->psk_selection_cb); + EXPECT_NULL(config->psk_selection_ctx); + + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &context)); + EXPECT_EQUAL(config->psk_selection_cb, s2n_test_select_psk_identity_callback); + EXPECT_EQUAL(config->psk_selection_ctx, &context); + + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, NULL, NULL)); + EXPECT_NULL(config->psk_selection_cb); + EXPECT_NULL(config->psk_selection_ctx); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /*Test s2n_connection_set_config */ + { + /* Test that tickets_to_send is set correctly */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_config *config = NULL; + uint8_t num_tickets = 1; + + EXPECT_NOT_NULL(config = s2n_config_new()); + + config->initial_tickets_to_send = num_tickets; + + EXPECT_EQUAL(conn->tickets_to_send, 0); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->tickets_to_send, num_tickets); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that PSK type is set correctly */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); + + /* Overrides connection value */ + { + conn->config = NULL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_FALSE(conn->psk_mode_overridden); + }; + + /* Does not override connection value if conn->override_psk_mode set */ + { + conn->config = NULL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + conn->psk_mode_overridden = true; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_TRUE(conn->psk_mode_overridden); + conn->psk_mode_overridden = false; + }; + + /* Does not override connection value if PSKs already set */ + { + conn->config = NULL; + DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_FALSE(conn->psk_mode_overridden); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_config_set_session_tickets_onoff */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, true), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, false), S2N_ERR_NULL); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 1); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + EXPECT_FALSE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 1); + + config->initial_tickets_to_send = 10; + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 10); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* s2n_config_set_context */ + /* s2n_config_get_context */ + { + uint8_t context = 42; + uint8_t other = 123; + void *returned_context = NULL; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); + EXPECT_NULL(returned_context); + + EXPECT_SUCCESS(s2n_config_set_ctx(config, &context)); + EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); + EXPECT_NOT_NULL(returned_context); + + EXPECT_EQUAL(*((uint8_t *) returned_context), context); + EXPECT_NOT_EQUAL(*((uint8_t *) returned_context), other); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_config_set_extension_data */ + { + uint8_t extension_data[] = "extension data"; + + /* Test s2n_config_set_extension_data can be called for owned cert chains */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + + EXPECT_SUCCESS(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, + extension_data, sizeof(extension_data))); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, sizeof(extension_data)); + }; + + /* Test s2n_config_set_extension_data can't be called for unowned cert chains */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, + extension_data, sizeof(extension_data)), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, 0); + EXPECT_EQUAL(chain->ocsp_status.size, 0); + }; + }; + + /* Test s2n_config_free_cert_chain_and_key */ + { + /* Chain owned by application */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* No-op for application-owned chains */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Still no-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Chain owned by application and freed too early: + * This is arguably incorrect behavior, but did not cause errors in the past. + * We should continue to ensure it doesn't cause any errors. + */ + { + struct s2n_cert_chain_and_key *chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Free the chain early */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain)); + + /* No-op for application-owned chains */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* No-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Chain owned by library */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* No-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Switch from library-owned certs to application-owned certs */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Now add an application-owned chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + }; + }; + + /* Test s2n_config_set_cert_chain_and_key_defaults */ + { + /* Succeeds if chains owned by app */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_1 = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_1, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_2 = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_2, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_1)); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_1); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &chain_2, 1)); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_2); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + }; + + /* Fails if chains owned by library */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_chain_and_key_defaults( + config, &chain, 1), + S2N_ERR_CERT_OWNERSHIP); + }; + }; + + /* Test s2n_config_set_send_buffer_size */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_EQUAL(config->send_buffer_size_override, 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(NULL, S2N_MIN_SEND_BUFFER_SIZE), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(config, 0), S2N_ERR_INVALID_ARGUMENT); + EXPECT_EQUAL(config->send_buffer_size_override, 0); + }; + + /* Default applied to connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_EQUAL(config->send_buffer_size_override, 0); + EXPECT_FALSE(conn->multirecord_send); + }; + + /* Custom applied to connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, S2N_MIN_SEND_BUFFER_SIZE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_EQUAL(config->send_buffer_size_override, S2N_MIN_SEND_BUFFER_SIZE); + EXPECT_TRUE(conn->multirecord_send); + }; + }; + + /* Test s2n_config_set_verify_after_sign */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->verify_after_sign); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(NULL, S2N_VERIFY_AFTER_SIGN_ENABLED), S2N_ERR_NULL); + + /* Invalid mode */ + config->verify_after_sign = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); + EXPECT_TRUE(config->verify_after_sign); + config->verify_after_sign = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FALSE(config->verify_after_sign); + + /* Set and unset */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_TRUE(config->verify_after_sign); + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_DISABLED)); + EXPECT_FALSE(config->verify_after_sign); + }; + + /* Test s2n_config_set_renegotiate_request_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->renegotiate_request_cb, NULL); + EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_renegotiate_request_cb(NULL, s2n_test_reneg_req_cb, &context), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, &context)); + EXPECT_EQUAL(config->renegotiate_request_cb, s2n_test_reneg_req_cb); + EXPECT_EQUAL(config->renegotiate_request_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->renegotiate_request_cb, NULL); + EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); + }; + + /* Test s2n_config_set_npn */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->npn_supported); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_npn(NULL, true), S2N_ERR_NULL); + + /* Set and unset */ + EXPECT_SUCCESS(s2n_config_set_npn(config, true)); + EXPECT_TRUE(config->npn_supported); + EXPECT_SUCCESS(s2n_config_set_npn(config, false)); + EXPECT_FALSE(config->npn_supported); + }; + + /* Test s2n_config_set_crl_lookup_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->crl_lookup_cb, NULL); + EXPECT_EQUAL(config->crl_lookup_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_crl_lookup_cb(NULL, s2n_test_crl_lookup_cb, &context), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, &context)); + EXPECT_EQUAL(config->crl_lookup_cb, s2n_test_crl_lookup_cb); + EXPECT_EQUAL(config->crl_lookup_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->crl_lookup_cb, NULL); + EXPECT_EQUAL(config->crl_lookup_ctx, NULL); + }; + + /* Test s2n_config_set_cert_validation_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->cert_validation_cb, NULL); + EXPECT_EQUAL(config->cert_validation_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_validation_cb(NULL, s2n_test_cert_validation_cb, &context), + S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, &context)); + EXPECT_EQUAL(config->cert_validation_cb, s2n_test_cert_validation_cb); + EXPECT_EQUAL(config->cert_validation_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->cert_validation_cb, NULL); + EXPECT_EQUAL(config->cert_validation_ctx, NULL); + }; + + /* Test s2n_config_set_status_request_type */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + s2n_mode mode = modes[mode_i]; + + if (!s2n_x509_ocsp_stapling_supported()) { + break; + } + + /* request_ocsp_status should be false by default */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->ocsp_status_requested_by_user); + EXPECT_FALSE(config->ocsp_status_requested_by_s2n); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(conn->request_ocsp_status); + }; + + /* request_ocsp_status should be true if set via s2n_config_set_status_request_type */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_TRUE(config->ocsp_status_requested_by_user); + EXPECT_FALSE(config->ocsp_status_requested_by_s2n); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(conn->request_ocsp_status); + }; + + /* ocsp_status_requested_by_s2n can be set in s2n_config_set_verification_ca_location. For + * backwards compatibility, this should tell clients to request OCSP stapling. However, this + * API should not tell servers to request OCSP stapling. + */ + for (int api_configuration_i = 0; api_configuration_i < 3; api_configuration_i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + switch (api_configuration_i) { + case 0: + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + case 1: + /* If a user intentionally disables OCSP stapling, s2n_config_set_verification_ca_location + * should not re-enable it for servers. + */ + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + default: + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + if (mode == S2N_CLIENT) { + EXPECT_TRUE(conn->request_ocsp_status); + } else { + EXPECT_FALSE(conn->request_ocsp_status); + } + }; + + /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_OCSP should enable OCSP + * status requests, regardless of s2n_config_set_verification_ca_location. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(conn->request_ocsp_status); + }; + + /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_NONE should disable OCSP + * status requests, regardless of s2n_config_set_verification_ca_location. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(conn->request_ocsp_status); + }; + }; + + /* Test s2n_config_add_cert_chain */ + { + uint32_t pem_len = 0; + uint8_t pem_bytes[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + pem_bytes, &pem_len, sizeof(pem_bytes))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain(config, pem_bytes, pem_len)); + EXPECT_TRUE(config->no_signing_key); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + struct s2n_cert_chain_and_key *chain = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(chain); + EXPECT_FAILURE(s2n_pkey_check_key_exists(chain->private_key)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_NO_PRIVATE_KEY); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_fn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + }; + + /* Test loading system certs */ + { + /* s2n_config_load_system_certs safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(NULL), S2N_ERR_NULL); + } + + /* s2n_config_new_minimal should not load system certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* System certs can be loaded onto the minimal config */ + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + + /* Attempting to load system certs multiple times on the same config should error */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* s2n_config_new should load system certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + + /* Attempting to load system certs multiple times on the same config should error */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* The default config should load system certs */ + { + struct s2n_config *config = s2n_fetch_default_config(); + EXPECT_NOT_NULL(config); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + + /* System certs can be loaded again after wiping the trust store */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + for (int i = 0; i < 20; i++) { + /* System certs were already loaded, so an attempt to load them should fail */ + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(config)); + + /* The trust store is cleared after a wipe, so it should be possible to load system certs again */ + EXPECT_FALSE(config->trust_store.loaded_system_certs); + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* Ensure that system certs are properly loaded into the X509_STORE. + * + * The API used to retrieve the contents of an X509_STORE, X509_STORE_get0_objects, + * wasn't added until OpenSSL 1.1.0. + */ +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* Initialize the X509_STORE by adding a cert */ + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&config->trust_store, S2N_RSA_PSS_2048_SHA256_CA_CERT, NULL)); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* The X509_STORE should only contain the single cert that was added. */ + STACK_OF(X509_OBJECT) *x509_store_contents = X509_STORE_get0_objects(config->trust_store.trust_store); + EXPECT_NOT_NULL(x509_store_contents); + int initial_contents_count = sk_X509_OBJECT_num(x509_store_contents); + EXPECT_EQUAL(initial_contents_count, 1); + + /* Override the system cert file to guarantee that a system cert will be loaded */ + EXPECT_SUCCESS(setenv("SSL_CERT_FILE", S2N_SHA1_ROOT_SIGNATURE_CA_CERT, 1)); + + /* Load the system cert into the store */ + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + int system_certs_contents_count = sk_X509_OBJECT_num(x509_store_contents); + + /* LibreSSL doesn't use the SSL_CERT_FILE environment variable to set the system cert location, + * so we don't know how many system certs will be loaded, if any. + */ + if (!s2n_libcrypto_is_libressl()) { + EXPECT_EQUAL(system_certs_contents_count, initial_contents_count + 1); + } + + /* Additional calls to s2n_config_load_default_certs should not add additional certs to the store */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + int additional_call_contents_count = sk_X509_OBJECT_num(x509_store_contents); + EXPECT_TRUE(additional_call_contents_count == system_certs_contents_count); + } + + EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); + } +#endif + + /* Self-talk tests */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Ensure a handshake succeeds with a minimal server config and no mutual auth */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + 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_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_FALSE(server_config->trust_store.loaded_system_certs); + EXPECT_NULL(server_config->trust_store.trust_store); + + EXPECT_FALSE(client_config->trust_store.loaded_system_certs); + EXPECT_NOT_NULL(client_config->trust_store.trust_store); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + /* Ensure a handshake fails gracefully with an uninitialized trust store */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + 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_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_NOT_NULL(client_config); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_FALSE(server_config->trust_store.loaded_system_certs); + EXPECT_NULL(server_config->trust_store.trust_store); + + EXPECT_FALSE(client_config->trust_store.loaded_system_certs); + EXPECT_NULL(client_config->trust_store.trust_store); + + /* The client should fail to validate the server's certificate without an initialized trust store */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_UNTRUSTED); + } + } + } + + /* s2n_config_disable_x509_time_verification tests */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_time_verification(NULL), S2N_ERR_NULL); + + /* Ensure s2n_config_disable_x509_time_verification sets the proper state */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->disable_x509_time_validation, false); + + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + EXPECT_EQUAL(config->disable_x509_time_validation, true); + } + } + + /* Test s2n_config_get_supported_groups */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + + int ret = s2n_config_get_supported_groups(NULL, supported_groups, s2n_array_len(supported_groups), + &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + + ret = s2n_config_get_supported_groups(config, NULL, s2n_array_len(supported_groups), &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + + ret = s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), NULL); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + } + + /* Error if the provided supported groups array is too small */ + { + /* Test a policy with and without PQ kem groups */ + const char *policies[] = { + "20170210", + "PQ-TLS-1-2-2024-10-07", + }; + + for (size_t i = 0; i < s2n_array_len(policies); i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, policies[i])); + + uint32_t policy_groups_count = 0; + EXPECT_OK(s2n_kem_preferences_groups_available(config->security_policy->kem_preferences, + &policy_groups_count)); + policy_groups_count += config->security_policy->ecc_preferences->count; + EXPECT_TRUE(policy_groups_count > 0); + + uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; + uint16_t supported_groups_count = 11; + for (size_t invalid_count = 0; invalid_count < policy_groups_count; invalid_count++) { + int ret = s2n_config_get_supported_groups(config, supported_groups, invalid_count, + &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_EQUAL(supported_groups_count, 0); + } + + EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, policy_groups_count, + &supported_groups_count)); + EXPECT_EQUAL(supported_groups_count, policy_groups_count); + } + } + + /* The groups produced by s2n_config_get_supported_groups should match the groups produced + * by a connection that's configured to send its entire list of supported groups + */ + for (size_t policy_idx = 0; security_policy_selection[policy_idx].version != NULL; policy_idx++) { + const struct s2n_security_policy *security_policy = security_policy_selection[policy_idx].security_policy; + EXPECT_NOT_NULL(security_policy); + + uint32_t expected_groups_count = 0; + EXPECT_OK(s2n_kem_preferences_groups_available(security_policy->kem_preferences, &expected_groups_count)); + expected_groups_count += security_policy->ecc_preferences->count; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = security_policy; + + uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), + &supported_groups_count)); + EXPECT_EQUAL(supported_groups_count, expected_groups_count); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + /* PQ kem groups aren't sent in the supported groups extension before TLS 1.3. */ + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer extension_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension_stuffer, 0)); + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &extension_stuffer)); + + uint16_t extension_groups_count = 0; + EXPECT_OK(s2n_supported_groups_parse_count(&extension_stuffer, &extension_groups_count)); + EXPECT_EQUAL(extension_groups_count, expected_groups_count); + + for (size_t i = 0; i < supported_groups_count; i++) { + /* s2n_stuffer_read_uint16 is used to read each of the supported groups in + * network-order endianness. + */ + uint16_t group_iana = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &group_iana)); + + EXPECT_EQUAL(group_iana, supported_groups[i]); + } + } + } + + /* Test s2n_config_validate_loaded_certificates */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *invalid_cert = NULL, s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *valid_cert = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&invalid_cert, "ec", "ecdsa", "p384", "sha256")); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&valid_cert, "ec", "ecdsa", "p384", "sha384")); + + struct s2n_security_policy rfc9151_applied_locally = security_policy_20250429; + rfc9151_applied_locally.certificate_preferences_apply_locally = true; + + /* rfc9151 doesn't allow SHA256 signatures, but does allow SHA384 signatures, + * so ecdsa_p384_sha256 is invalid and ecdsa_p384_sha384 is valid */ + + /* valid certs are accepted */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); + EXPECT_OK(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally)); + }; + + /* when cert preferences don't apply locally, invalid certs are accepted */ + { + struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; + non_local_rfc9151.certificate_preferences_apply_locally = false; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, invalid_cert)); + EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); + }; + + /* Certs in an s2n_config are stored in default_certs_by_type, domain_name_to_cert_map, or + * both. We want to ensure that the s2n_config_validate_loaded_certificates method will + * validate certs in both locations. + */ + + /* certs in default_certs_by_type are validated */ + { + /* s2n_config_set_cert_chain_and_key_defaults populates default_certs_by_type + * but doesn't populate domain_name_to_cert_map + */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &invalid_cert, 1)); + + /* domain certs is empty */ + uint32_t domain_certs_count = 0; + EXPECT_OK(s2n_map_size(config->domain_name_to_cert_map, &domain_certs_count)); + EXPECT_EQUAL(domain_certs_count, 0); + + /* certs in default_certs_by_type are validated */ + EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + }; + + /* certs in the domain map are validated */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_build_domain_name_to_cert_map(config, invalid_cert)); + + /* default_certs_by_type is empty. */ + EXPECT_EQUAL(s2n_config_get_num_default_certs(config), 0); + + /* certs in domain_map are validated */ + EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + }; + + /* when cert preferences don't apply locally, certs in domain map are not iterated over + * + * Some customers load large numbers of certificates, so even iterating + * over every certificate without performing any validation is expensive. + */ + { + struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; + + /* Assert that the security policy WOULD apply, + * if certificate_preferences_apply_locally was true. + */ + EXPECT_NOT_NULL(non_local_rfc9151.certificate_key_preferences); + EXPECT_NOT_NULL(non_local_rfc9151.certificate_signature_preferences); + EXPECT_TRUE(non_local_rfc9151.certificate_key_preferences->count > 0); + EXPECT_TRUE(non_local_rfc9151.certificate_signature_preferences->count > 0); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); + + /* Invalidate the domain map so that attempting to use it will trigger + * an error. We want to ensure that we DON'T use it. + * Iterating over a map requires that map to be immutable / complete. + */ + EXPECT_OK(s2n_map_unlock(config->domain_name_to_cert_map)); + + /* Control case: if local validation needed, attempt to use invalid domain map */ + non_local_rfc9151.certificate_preferences_apply_locally = true; + EXPECT_ERROR_WITH_ERRNO( + s2n_config_validate_loaded_certificates(config, &non_local_rfc9151), + S2N_ERR_MAP_MUTABLE); + + /* Test case: if no local validation needed, do not use invalid domain map */ + non_local_rfc9151.certificate_preferences_apply_locally = false; + EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); + }; + }; + + /* Checks that servers don't use a config before the client hello callback is executed. + * + * We want to assert that a config is never used by a server until the client hello callback + * is called, given that users have the ability to swap out the config during this callback. + */ + { + DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_client_config); + /* Security policy that only supports TLS12 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); + + DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(tls13_client_config); + /* Security policy that supports TLS13 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "20240503")); + + struct s2n_config *config_arr[] = { tls12_client_config, tls13_client_config }; + + /* Checks that the handshake gets as far as the client hello callback with a NULL config */ + for (size_t i = 0; i < s2n_array_len(config_arr); i++) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_arr[i])); + + /* Server config pointer is explicitly set to NULL */ + server_conn->config = NULL; + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &test_io)); + + /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the + * client hello callback. Therefore, we can assert that if we hit this error, we + * have gotten as far as the client hello callback without dereferencing the config. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); + } + } + + /* Checks that servers don't use a config before the client hello callback is executed on a + * SSLv2-formatted client hello. + * + * Parsing SSLv2 hellos uses a different code path and needs to be tested separately. + */ + { + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&client_hello, sslv2_client_hello, sizeof(sslv2_client_hello))); + + /* Checks that the handshake gets as far as the client hello callback with a NULL config */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->config = NULL; + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello.sslv2 = true; + server_conn->client_hello.legacy_version = S2N_TLS12; + + /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the + * client hello callback. Therefore, we can assert that if we hit this error, we + * have gotten as far as the client hello callback without dereferencing the config. + */ + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); + } + } + + /* s2n_config_set_subscriber */ + { + /* Safety */ + uint64_t fake_subscriber = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (void *) &fake_subscriber), S2N_ERR_NULL); + }; + + /* s2n_config_set_handshake_event */ + { + /* Safety */ + uint64_t fake_callback = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (s2n_event_on_handshake_cb) &fake_callback), S2N_ERR_NULL); + }; + + /* Test: domain_name_to_cert_map remains usable after s2n_map_add fails + * + * Regression test: s2n_config_update_domain_name_to_cert_map + * calls s2n_map_unlock -> s2n_map_add -> s2n_map_complete. If s2n_map_add + * fails (e.g. OOM), the map was left in a mutable (!immutable) state. + * All subsequent s2n_map_lookup calls, used on every ClientHello for + * SNI-based cert selection, would then fail with S2N_ERR_MAP_MUTABLE, + * silently disabling SNI matching for the config's lifetime. + * + * The fix ensures s2n_map_complete is always called after unlock, + * even when add fails. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* The map starts empty and immutable. Verify a lookup succeeds. */ + { + struct s2n_blob lookup_name = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&lookup_name, + (uint8_t *) "test.example.com", strlen("test.example.com"))); + struct s2n_blob lookup_value = { 0 }; + bool found = false; + EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, + &lookup_name, &lookup_value, &found)); + EXPECT_FALSE(found); + } + + /* Create a cert chain to attempt adding. */ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Install a malloc callback that always fails so s2n_map_add will + * fail during s2n_dup inside the unlock -> add -> complete path. + */ + s2n_mem_init_callback saved_init_cb = NULL; + s2n_mem_cleanup_callback saved_cleanup_cb = NULL; + s2n_mem_malloc_callback saved_malloc_cb = NULL; + s2n_mem_free_callback saved_free_cb = NULL; + EXPECT_OK(s2n_mem_get_callbacks(&saved_init_cb, &saved_cleanup_cb, + &saved_malloc_cb, &saved_free_cb)); + EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, + s2n_test_failing_malloc_cb, saved_free_cb)); + + /* The cert's SANs/CNs are not in the empty map, so the update + * function will take the !key_found branch: unlock -> add (FAIL). + * With the fix, complete is still called before returning the error. + */ + EXPECT_FAILURE(s2n_config_build_domain_name_to_cert_map(config, chain)); + + /* Restore the real malloc callback before any further allocations. */ + EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, + saved_malloc_cb, saved_free_cb)); + + /* The regression check: the map must still be immutable so that + * lookups succeed. Before the fix, this would fail with + * S2N_ERR_MAP_MUTABLE because s2n_map_complete was never called + * after the failed add. + */ + { + struct s2n_blob lookup_name = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&lookup_name, + (uint8_t *) "test.example.com", strlen("test.example.com"))); + struct s2n_blob lookup_value = { 0 }; + bool found = false; + EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, + &lookup_name, &lookup_value, &found)); + /* Lookup should succeed (not error) even though the key isn't found. */ + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_protocol_versions_test.c b/tests/unit/s2n_connection_protocol_versions_test.c index dee93409141..fa931cdf3ac 100644 --- a/tests/unit/s2n_connection_protocol_versions_test.c +++ b/tests/unit/s2n_connection_protocol_versions_test.c @@ -1,390 +1,391 @@ -/* -* 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 "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" - -static S2N_RESULT s2n_write_test_supported_versions_extension(struct s2n_blob *supported_versions_blob, uint8_t version, - uint8_t extension_length) -{ - RESULT_ENSURE_REF(supported_versions_blob); - - struct s2n_stuffer supported_versions_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&supported_versions_stuffer, supported_versions_blob)); - - /* Write the length byte. */ - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, extension_length)); - /* Write the supported version. */ - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version / 10)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version % 10)); - - return S2N_RESULT_OK; -} - -struct s2n_overwrite_client_hello_ctx { - uint8_t client_hello_version; - uint8_t client_supported_version; - uint8_t extension_length; - - uint8_t supported_versions_data[3]; - int invoked_count; -}; - -static int s2n_overwrite_client_hello_cb(struct s2n_connection *conn, void *ctx) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(ctx); - - struct s2n_overwrite_client_hello_ctx *context = (struct s2n_overwrite_client_hello_ctx *) ctx; - context->invoked_count += 1; - - if (context->extension_length) { - struct s2n_blob supported_versions_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&supported_versions_blob, context->supported_versions_data, - sizeof(context->supported_versions_data))); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); - EXPECT_NOT_NULL(client_hello); - - s2n_extension_type_id supported_versions_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); - s2n_parsed_extension *extension = &client_hello->extensions.parsed_extensions[supported_versions_id]; - - EXPECT_OK(s2n_write_test_supported_versions_extension(&supported_versions_blob, - context->client_supported_version, context->extension_length)); - - extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; - extension->extension = supported_versions_blob; - } - - /* The client version fields are set when parsing the client hello before the client hello - * callback is invoked. The version fields are overridden to emulate receiving a client hello - * with a different version. - */ - if (context->client_hello_version) { - conn->client_hello.legacy_version = context->client_hello_version; - conn->client_protocol_version = context->client_hello_version; - } - - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Safety */ - { - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_protocol_version(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_hello_version(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_server_protocol_version(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_actual_protocol_version(NULL), S2N_ERR_NULL); - } - - /* Test protocol version getters on the server when a supported versions extension is received */ - for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - for (uint8_t client_supported_version = S2N_SSLv3; client_supported_version <= S2N_TLS13; client_supported_version++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - if (server_version == S2N_TLS12) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - } else if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - } else { - continue; - } - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - .client_supported_version = client_supported_version, - .extension_length = 2, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); - - /* The reported client protocol version should always match the version specified - * in the supported versions extension, even for TLS 1.2 servers which don't - * process the extension for version selection. - */ - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_supported_version); - - uint8_t actual_protocol_version = s2n_connection_get_actual_protocol_version(server); - if (server_version == S2N_TLS12) { - /* For backwards compatibility, TLS 1.2 servers always use the client hello - * version to determine the client's maximum version, even if a supported - * versions extension was received. - */ - EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_hello_version)); - } else { - /* TLS 1.3 servers always use the version in the supported versions extension, - * regardless of the client hello version. - */ - EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_supported_version)); - } - } - } - } - - /* Test protocol version getters on the server when a supported versions extension isn't received */ - for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - - /* A TLS 1.2 security policy is set to prevent the client from sending a supported - * versions extension. - */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - if (server_version == S2N_TLS12) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_tls12")); - } else if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - } else { - continue; - } - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension wasn't received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_FALSE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), client_hello_version); - } - } - - /* Test protocol version getters on the client */ - for (uint8_t server_version = S2N_SSLv3; server_version <= S2N_TLS13; server_version++) { - if (server_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { - continue; - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - server->server_protocol_version = server_version; - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(client), server_version); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(client), s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(client), S2N_TLS12); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(client), server_version); - } - - /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version - * if a malformed supported versions extension was received - */ - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - .client_supported_version = S2N_TLS13, - /* Write an invalid length */ - .extension_length = 11, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); - } - - /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version - * if an invalid supported version is received - */ - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - /* Write an invalid version */ - .client_supported_version = S2N_TLS13 + 10, - .extension_length = 2, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); - } - - /* Ensure that TLS 1.3 servers report an unknown protocol version if a supported versions - * extension can't be processed - */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - - struct s2n_overwrite_client_hello_ctx context = { - .client_supported_version = S2N_TLS13, - /* Write an invalid length */ - .extension_length = 11, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), - S2N_ERR_BAD_MESSAGE); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), S2N_TLS13); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), s2n_unknown_protocol_version); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), s2n_unknown_protocol_version); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static S2N_RESULT s2n_write_test_supported_versions_extension(struct s2n_blob *supported_versions_blob, uint8_t version, + uint8_t extension_length) +{ + RESULT_ENSURE_REF(supported_versions_blob); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&supported_versions_stuffer, supported_versions_blob)); + + /* Write the length byte. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, extension_length)); + /* Write the supported version. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version / 10)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version % 10)); + + return S2N_RESULT_OK; +} + +struct s2n_overwrite_client_hello_ctx { + uint8_t client_hello_version; + uint8_t client_supported_version; + uint8_t extension_length; + + uint8_t supported_versions_data[3]; + int invoked_count; +}; + +static int s2n_overwrite_client_hello_cb(struct s2n_connection *conn, void *ctx) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ctx); + + struct s2n_overwrite_client_hello_ctx *context = (struct s2n_overwrite_client_hello_ctx *) ctx; + context->invoked_count += 1; + + if (context->extension_length) { + struct s2n_blob supported_versions_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&supported_versions_blob, context->supported_versions_data, + sizeof(context->supported_versions_data))); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + EXPECT_NOT_NULL(client_hello); + + s2n_extension_type_id supported_versions_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + s2n_parsed_extension *extension = &client_hello->extensions.parsed_extensions[supported_versions_id]; + + EXPECT_OK(s2n_write_test_supported_versions_extension(&supported_versions_blob, + context->client_supported_version, context->extension_length)); + + extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; + extension->extension = supported_versions_blob; + } + + /* The client version fields are set when parsing the client hello before the client hello + * callback is invoked. The version fields are overridden to emulate receiving a client hello + * with a different version. + */ + if (context->client_hello_version) { + conn->client_hello.legacy_version = context->client_hello_version; + conn->client_protocol_version = context->client_hello_version; + } + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_hello_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_server_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_actual_protocol_version(NULL), S2N_ERR_NULL); + } + + /* Test protocol version getters on the server when a supported versions extension is received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + for (uint8_t client_supported_version = S2N_SSLv3; client_supported_version <= S2N_TLS13; client_supported_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = client_supported_version, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + + /* The reported client protocol version should always match the version specified + * in the supported versions extension, even for TLS 1.2 servers which don't + * process the extension for version selection. + */ + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_supported_version); + + uint8_t actual_protocol_version = s2n_connection_get_actual_protocol_version(server); + if (server_version == S2N_TLS12) { + /* For backwards compatibility, TLS 1.2 servers always use the client hello + * version to determine the client's maximum version, even if a supported + * versions extension was received. + */ + EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_hello_version)); + } else { + /* TLS 1.3 servers always use the version in the supported versions extension, + * regardless of the client hello version. + */ + EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_supported_version)); + } + } + } + } + + /* Test protocol version getters on the server when a supported versions extension isn't received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + + /* A TLS 1.2 security policy is set to prevent the client from sending a supported + * versions extension. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension wasn't received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_FALSE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), client_hello_version); + } + } + + /* Test protocol version getters on the client */ + for (uint8_t server_version = S2N_SSLv3; server_version <= S2N_TLS13; server_version++) { + if (server_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { + continue; + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + server->server_protocol_version = server_version; + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(client), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(client), s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(client), S2N_TLS12); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(client), server_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if a malformed supported versions extension was received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if an invalid supported version is received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + /* Write an invalid version */ + .client_supported_version = S2N_TLS13 + 10, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.3 servers report an unknown protocol version if a supported versions + * extension can't be processed + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), S2N_TLS13); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), s2n_unknown_protocol_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), s2n_unknown_protocol_version); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cookie_test.c b/tests/unit/s2n_cookie_test.c index 406cfe8305f..132acda036a 100644 --- a/tests/unit/s2n_cookie_test.c +++ b/tests/unit/s2n_cookie_test.c @@ -1,376 +1,377 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_cookie.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_handshake_type.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -#define TEST_COOKIE_COUNT 5 - -int main() -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - uint16_t test_cookie_sizes[TEST_COOKIE_COUNT] = { 1, UINT8_MAX, UINT8_MAX + 1, UINT16_MAX - 1, UINT16_MAX }; - struct s2n_blob test_cookies[TEST_COOKIE_COUNT] = { 0 }; - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - EXPECT_SUCCESS(s2n_alloc(&test_cookies[i], test_cookie_sizes[i])); - EXPECT_OK(s2n_get_public_random_data(&test_cookies[i])); - } - - /** - * Test: client only sends extension if cookie present - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.2 - *= type=test - *# - Including a "cookie" extension if one was provided in the - *# HelloRetryRequest. - **/ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - - /* Not sent without a cookie */ - EXPECT_FALSE(s2n_client_cookie_extension.should_send(client_conn)); - - /* Sent with a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &client_conn->cookie)); - EXPECT_TRUE(s2n_client_cookie_extension.should_send(client_conn)); - }; - - /* Test: server only sends extension if cookie present - * (cookie will never be present in production) - */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - /* Not sent without a cookie */ - EXPECT_FALSE(s2n_server_cookie_extension.should_send(server_conn)); - - /* Sent with a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); - EXPECT_TRUE(s2n_server_cookie_extension.should_send(server_conn)); - }; - - /* Test: client can parse server cookie extension */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_stuffer server_extension = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_extension, 0)); - - /* Server sends extension with test cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - EXPECT_SUCCESS(s2n_server_cookie_extension.send(server_conn, &server_extension)); - - /* Client doesn't parse extension if no retry */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_cookie_extension.recv(client_conn, &server_extension), - S2N_ERR_UNSUPPORTED_EXTENSION); - - /* Client parses extension if retry */ - client_conn->handshake.handshake_type = HELLO_RETRY_REQUEST; - EXPECT_SUCCESS(s2n_server_cookie_extension.recv(client_conn, &server_extension)); - S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); - } - - /* Test: client sends correctly formatted extension */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_stuffer client_extension = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_extension, 0)); - - /* Client sends extension with test cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &client_conn->cookie)); - EXPECT_SUCCESS(s2n_client_cookie_extension.send(client_conn, &client_extension)); - - /* Sanity check: Server rejects incorrectly sized cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - server_conn->cookie.size--; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), - S2N_ERR_BAD_MESSAGE); - EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); - EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); - - /* Sanity check: Server rejects incorrect cookie data */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - server_conn->cookie.data[0] = server_conn->cookie.data[0] + 1; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), - S2N_ERR_BAD_MESSAGE); - EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); - EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); - - /* Server accepts correct cookie data */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - EXPECT_SUCCESS(s2n_client_cookie_extension.recv(server_conn, &client_extension)); - } - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* Although the cookie is *technically* allowed to be UINT16_MAX, - * in reality it has to share a uint16_t extensions list length - * with other extensions. - * - * So for the self-talk tests, reduce the size of any large test cookies. - */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - test_cookies[i].size = S2N_MIN(test_cookies[i].size, UINT16_MAX / 2); - } - - /* Sanity check: server fails if client does not provide expected cookie */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Force the server to send a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); - - /* Begin negotiating handshake. - * The first negotiate_until blocks because the client is looking for a SERVER_HELLO, - * not the HELLO_RETRY_MESSAGE. This is fine; it's in the right place in the handshake. - */ - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, HELLO_RETRY_MSG), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_HELLO)); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - - /* Verify HRR path */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - - /* At this point, the server has already sent its HRR request with a cookie. - * The client has stored the server's cookie, but not responded. - * Wipe the cookie on the client, preventing it from sending the response. - */ - EXPECT_NOT_EQUAL(client_conn->cookie.size, 0); - EXPECT_SUCCESS(s2n_free(&client_conn->cookie)); - - /* Continue negotiating. We should fail because of the "missing" cookie. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_MISSING_EXTENSION); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Self-Talk: Server does NOT use cookies */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify HRR path */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - - /* Verify no cookies */ - EXPECT_EQUAL(client_conn->cookie.size, 0); - EXPECT_EQUAL(server_conn->cookie.size, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Self-Talk: Server does use cookies - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.2 - *= type=test - *# When sending a HelloRetryRequest, the server MAY provide a "cookie" - *# extension to the client (this is an exception to the usual rule that - *# the only extensions that may be sent are those that appear in the - *# ClientHello). When sending the new ClientHello, the client MUST copy - *# the contents of the extension received in the HelloRetryRequest into - *# a "cookie" extension in the new ClientHello. - */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Force the server to send a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify HRR path */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - - /* Verify cookies */ - S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); - S2N_BLOB_EXPECT_EQUAL(test_cookies[i], server_conn->cookie); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - /* Self-Talk: Full connection lifecycle with cookies - * We try the handshake multiple times with different possible call patterns. - */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* We need an arbitrary combination of conditions, - * but consistent across test runs. - */ - srand(0); - - for (size_t i = 0; i < 250; i++) { - int r = rand(); - bool hrr = (r % 2) == 0; - bool cookie = (r % 3) == 0; - size_t cookie_i = i % TEST_COOKIE_COUNT; - bool free_handshake = (r % 7) == 0; - - /* Verify calls to s2n_connection_wipe are safe */ - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - if (hrr) { - client_conn->security_policy_override = &security_policy_test_tls13_retry; - } - - /* Force the server to send a cookie */ - if (cookie) { - EXPECT_SUCCESS(s2n_dup(&test_cookies[cookie_i], &server_conn->cookie)); - } - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify HRR path */ - if (hrr) { - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_handshake(client_conn)); - } else { - EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_FALSE(s2n_is_hello_retry_handshake(client_conn)); - } - - /* Verify cookie data */ - if (hrr && cookie) { - S2N_BLOB_EXPECT_EQUAL(test_cookies[cookie_i], client_conn->cookie); - } else { - EXPECT_EQUAL(client_conn->cookie.size, 0); - } - - if (free_handshake) { - EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); - EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - }; - - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - EXPECT_SUCCESS(s2n_free(&test_cookies[i])); - } - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_cookie.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake_type.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +#define TEST_COOKIE_COUNT 5 + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + uint16_t test_cookie_sizes[TEST_COOKIE_COUNT] = { 1, UINT8_MAX, UINT8_MAX + 1, UINT16_MAX - 1, UINT16_MAX }; + struct s2n_blob test_cookies[TEST_COOKIE_COUNT] = { 0 }; + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + EXPECT_SUCCESS(s2n_alloc(&test_cookies[i], test_cookie_sizes[i])); + EXPECT_OK(s2n_get_public_random_data(&test_cookies[i])); + } + + /** + * Test: client only sends extension if cookie present + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.2 + *= type=test + *# - Including a "cookie" extension if one was provided in the + *# HelloRetryRequest. + **/ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + /* Not sent without a cookie */ + EXPECT_FALSE(s2n_client_cookie_extension.should_send(client_conn)); + + /* Sent with a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &client_conn->cookie)); + EXPECT_TRUE(s2n_client_cookie_extension.should_send(client_conn)); + }; + + /* Test: server only sends extension if cookie present + * (cookie will never be present in production) + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Not sent without a cookie */ + EXPECT_FALSE(s2n_server_cookie_extension.should_send(server_conn)); + + /* Sent with a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); + EXPECT_TRUE(s2n_server_cookie_extension.should_send(server_conn)); + }; + + /* Test: client can parse server cookie extension */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer server_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_extension, 0)); + + /* Server sends extension with test cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + EXPECT_SUCCESS(s2n_server_cookie_extension.send(server_conn, &server_extension)); + + /* Client doesn't parse extension if no retry */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_cookie_extension.recv(client_conn, &server_extension), + S2N_ERR_UNSUPPORTED_EXTENSION); + + /* Client parses extension if retry */ + client_conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_server_cookie_extension.recv(client_conn, &server_extension)); + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); + } + + /* Test: client sends correctly formatted extension */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer client_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_extension, 0)); + + /* Client sends extension with test cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &client_conn->cookie)); + EXPECT_SUCCESS(s2n_client_cookie_extension.send(client_conn, &client_extension)); + + /* Sanity check: Server rejects incorrectly sized cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + server_conn->cookie.size--; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); + EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); + + /* Sanity check: Server rejects incorrect cookie data */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + server_conn->cookie.data[0] = server_conn->cookie.data[0] + 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); + EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); + + /* Server accepts correct cookie data */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + EXPECT_SUCCESS(s2n_client_cookie_extension.recv(server_conn, &client_extension)); + } + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* Although the cookie is *technically* allowed to be UINT16_MAX, + * in reality it has to share a uint16_t extensions list length + * with other extensions. + * + * So for the self-talk tests, reduce the size of any large test cookies. + */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + test_cookies[i].size = S2N_MIN(test_cookies[i].size, UINT16_MAX / 2); + } + + /* Sanity check: server fails if client does not provide expected cookie */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Force the server to send a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); + + /* Begin negotiating handshake. + * The first negotiate_until blocks because the client is looking for a SERVER_HELLO, + * not the HELLO_RETRY_MESSAGE. This is fine; it's in the right place in the handshake. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, HELLO_RETRY_MSG), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* At this point, the server has already sent its HRR request with a cookie. + * The client has stored the server's cookie, but not responded. + * Wipe the cookie on the client, preventing it from sending the response. + */ + EXPECT_NOT_EQUAL(client_conn->cookie.size, 0); + EXPECT_SUCCESS(s2n_free(&client_conn->cookie)); + + /* Continue negotiating. We should fail because of the "missing" cookie. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Self-Talk: Server does NOT use cookies */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* Verify no cookies */ + EXPECT_EQUAL(client_conn->cookie.size, 0); + EXPECT_EQUAL(server_conn->cookie.size, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Self-Talk: Server does use cookies + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.2 + *= type=test + *# When sending a HelloRetryRequest, the server MAY provide a "cookie" + *# extension to the client (this is an exception to the usual rule that + *# the only extensions that may be sent are those that appear in the + *# ClientHello). When sending the new ClientHello, the client MUST copy + *# the contents of the extension received in the HelloRetryRequest into + *# a "cookie" extension in the new ClientHello. + */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Force the server to send a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* Verify cookies */ + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], server_conn->cookie); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /* Self-Talk: Full connection lifecycle with cookies + * We try the handshake multiple times with different possible call patterns. + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* We need an arbitrary combination of conditions, + * but consistent across test runs. + */ + srand(0); + + for (size_t i = 0; i < 250; i++) { + int r = rand(); + bool hrr = (r % 2) == 0; + bool cookie = (r % 3) == 0; + size_t cookie_i = i % TEST_COOKIE_COUNT; + bool free_handshake = (r % 7) == 0; + + /* Verify calls to s2n_connection_wipe are safe */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + if (hrr) { + client_conn->security_policy_override = &security_policy_test_tls13_retry; + } + + /* Force the server to send a cookie */ + if (cookie) { + EXPECT_SUCCESS(s2n_dup(&test_cookies[cookie_i], &server_conn->cookie)); + } + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + if (hrr) { + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(client_conn)); + } else { + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_FALSE(s2n_is_hello_retry_handshake(client_conn)); + } + + /* Verify cookie data */ + if (hrr && cookie) { + S2N_BLOB_EXPECT_EQUAL(test_cookies[cookie_i], client_conn->cookie); + } else { + EXPECT_EQUAL(client_conn->cookie.size, 0); + } + + if (free_handshake) { + EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + }; + + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + EXPECT_SUCCESS(s2n_free(&test_cookies[i])); + } + END_TEST(); +} diff --git a/tests/unit/s2n_crl_test.c b/tests/unit/s2n_crl_test.c index b952ec4ae5d..6ad12d9b63b 100644 --- a/tests/unit/s2n_crl_test.c +++ b/tests/unit/s2n_crl_test.c @@ -1,941 +1,942 @@ -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "tls/s2n_crl.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -#define S2N_CRL_ROOT_CERT "../pems/crl/root_cert.pem" -#define S2N_CRL_NONE_REVOKED_CERT_CHAIN "../pems/crl/none_revoked_cert_chain.pem" -#define S2N_CRL_NONE_REVOKED_KEY "../pems/crl/none_revoked_key.pem" -#define S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN "../pems/crl/intermediate_revoked_cert_chain.pem" -#define S2N_CRL_INTERMEDIATE_REVOKED_KEY "../pems/crl/intermediate_revoked_key.pem" -#define S2N_CRL_LEAF_REVOKED_CERT_CHAIN "../pems/crl/leaf_revoked_cert_chain.pem" -#define S2N_CRL_LEAF_REVOKED_KEY "../pems/crl/leaf_revoked_key.pem" -#define S2N_CRL_ALL_REVOKED_CERT_CHAIN "../pems/crl/all_revoked_cert_chain.pem" -#define S2N_CRL_ALL_REVOKED_KEY "../pems/crl/all_revoked_key.pem" -#define S2N_CRL_ROOT_CRL "../pems/crl/root_crl.pem" -#define S2N_CRL_INTERMEDIATE_CRL "../pems/crl/intermediate_crl.pem" -#define S2N_CRL_INTERMEDIATE_REVOKED_CRL "../pems/crl/intermediate_revoked_crl.pem" -#define S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL "../pems/crl/intermediate_invalid_this_update_crl.pem" -#define S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL "../pems/crl/intermediate_invalid_next_update_crl.pem" - -#define CRL_TEST_CHAIN_LEN 2 - -struct crl_lookup_data { - struct s2n_crl *crls[5]; - X509 *certs[5]; - uint8_t callback_invoked_count; -}; - -static int crl_lookup_test_callback(struct s2n_crl_lookup *lookup, void *context) -{ - struct crl_lookup_data *crl_data = (struct crl_lookup_data *) context; - crl_data->callback_invoked_count += 1; - crl_data->certs[lookup->cert_idx] = lookup->cert; - - struct s2n_crl *crl = crl_data->crls[lookup->cert_idx]; - if (crl == NULL) { - POSIX_GUARD(s2n_crl_lookup_ignore(lookup)); - } else { - POSIX_GUARD(s2n_crl_lookup_set(lookup, crl)); - } - - return 0; -} - -static int crl_lookup_noop(struct s2n_crl_lookup *lookup, void *context) -{ - return 0; -} - -static int crl_lookup_callback_fail(struct s2n_crl_lookup *lookup, void *context) -{ - return 1; -} - -static uint8_t verify_host_always_allow(const char *host_name, size_t host_name_len, void *data) -{ - return 1; -} - -static struct s2n_crl *load_test_crl(const char *pem_path) -{ - uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t pem_len = 0; - PTR_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, crl_pem, &pem_len, S2N_MAX_TEST_PEM_SIZE)); - DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); - PTR_ENSURE_REF(crl); - PTR_GUARD_POSIX(s2n_crl_load_pem(crl, crl_pem, pem_len)); - - struct s2n_crl *crl_ret = crl; - ZERO_TO_DISABLE_DEFER_CLEANUP(crl); - - return crl_ret; -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* s2n_crl_new allocates and frees a s2n_crl */ - { - struct s2n_crl *crl = s2n_crl_new(); - EXPECT_NOT_NULL(crl); - - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - - /* Multiple calls to free succeed */ - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - }; - - /* s2n_crl_new allocates and frees a s2n_crl with an internal X509_CRL set */ - { - struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL); - EXPECT_NOT_NULL(crl); - EXPECT_NOT_NULL(crl->crl); - - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - - /* Multiple calls to free succeed */ - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - }; - - /* Ensure s2n_crl_load_pem produces a valid X509_CRL internally */ - { - DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); - EXPECT_NOT_NULL(crl); - EXPECT_NOT_NULL(crl->crl); - - /* Make sure an OpenSSL operation succeeds on the internal X509_CRL */ - X509_NAME *crl_name = X509_CRL_get_issuer(crl->crl); - POSIX_ENSURE_REF(crl_name); - }; - - /* s2n_crl_load_pem fails if provided a bad pem */ - { - uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t crl_pem_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_ROOT_CRL, crl_pem, &crl_pem_len, S2N_MAX_TEST_PEM_SIZE)); - DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); - EXPECT_NOT_NULL(crl); - EXPECT_SUCCESS(s2n_crl_load_pem(crl, crl_pem, crl_pem_len)); - - /* Change a random byte in the pem to make it invalid */ - crl_pem[50] = 1; - - DEFER_CLEANUP(struct s2n_crl *invalid_crl = s2n_crl_new(), s2n_crl_free); - EXPECT_NOT_NULL(invalid_crl); - EXPECT_FAILURE_WITH_ERRNO(s2n_crl_load_pem(invalid_crl, crl_pem, crl_pem_len), - S2N_ERR_INVALID_PEM); - }; - - /* CRL issuer hash is retrieved successfully */ - { - DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); - EXPECT_NOT_NULL(crl); - - uint64_t hash = 0; - EXPECT_SUCCESS(s2n_crl_get_issuer_hash(crl, &hash)); - EXPECT_TRUE(hash != 0); - }; - - DEFER_CLEANUP(struct s2n_crl *root_crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); - EXPECT_NOT_NULL(root_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_crl = load_test_crl(S2N_CRL_INTERMEDIATE_CRL), s2n_crl_free); - EXPECT_NOT_NULL(intermediate_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_revoked_crl = load_test_crl(S2N_CRL_INTERMEDIATE_REVOKED_CRL), s2n_crl_free); - EXPECT_NOT_NULL(intermediate_revoked_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_this_update_crl = - load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL), - s2n_crl_free); - EXPECT_NOT_NULL(intermediate_invalid_this_update_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_next_update_crl = - load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL), - s2n_crl_free); - EXPECT_NOT_NULL(intermediate_invalid_next_update_crl); - - /* Save a list of received X509s for s2n_crl_lookup tests */ - struct crl_lookup_data received_lookup_data = { 0 }; - DEFER_CLEANUP(struct s2n_x509_validator received_lookup_data_validator, s2n_x509_validator_wipe); - - /* CRL validation succeeds for unrevoked certificate chain */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - EXPECT_SUCCESS(s2n_x509_validator_init(&received_lookup_data_validator, &trust_store, 0)); - - received_lookup_data.crls[0] = intermediate_crl; - received_lookup_data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &received_lookup_data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&received_lookup_data_validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out)); - EXPECT_TRUE(received_lookup_data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - - /* Ensure all certificates were received in the callback */ - for (int i = 0; i < CRL_TEST_CHAIN_LEN; i++) { - EXPECT_NOT_NULL(received_lookup_data.certs[i]); - } - }; - - /* CRL validation errors when a leaf certificate is revoked */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation errors when an intermediate certificate is revoked */ - for (int i = 0; i < 2; i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_revoked_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - if (i == 0) { - /* Ensure CRL validation fails when only the intermediate certificate is revoked */ - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - } else if (i == 1) { - /* Ensure CRL validation fails when both the intermediate and leaf certificates are revoked */ - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_ALL_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - } - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - } - - /* CRL validation fails when a certificate is rejected from the callback */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CRL_LOOKUP_FAILED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation succeeds for unrevoked certificate chain when extraneous certificate is rejected */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - - /* Reject the extraneous cert */ - data.crls[2] = NULL; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE * 2]; - uint32_t pem_len_1 = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_NONE_REVOKED_CERT_CHAIN, cert_chain_pem, &pem_len_1, - S2N_MAX_TEST_PEM_SIZE)); - - /* Add an arbitrary cert to the chain that won't be included in the chain of trust */ - uint32_t pem_len_2 = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_SHA256_CLIENT_CERT, cert_chain_pem + pem_len_1, - &pem_len_2, S2N_MAX_TEST_PEM_SIZE)); - - uint32_t cert_chain_len = pem_len_1 + pem_len_2; - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(connection, cert_chain_pem, cert_chain_len, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - EXPECT_TRUE(data.callback_invoked_count == 3); - }; - - /* s2n_x509_validator_validate_cert_chain blocks until all CRL callbacks respond */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_noop, NULL)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - /* Blocks if no response received from callbacks */ - for (int i = 0; i < 10; i++) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_ASYNC_BLOCKED); - } - - /* Continues to block if only one callback has sent a response */ - struct s2n_crl_lookup *lookup = NULL; - EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 0, (void **) &lookup)); - EXPECT_NOT_NULL(lookup); - EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, root_crl)); - for (int i = 0; i < 10; ++i) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_ASYNC_BLOCKED); - } - - /* Unblocks when all callbacks send a response */ - lookup = NULL; - EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 1, (void **) &lookup)); - EXPECT_NOT_NULL(lookup); - EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, intermediate_crl)); - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - }; - - /* CRL validation fails when a callback returns unsuccessfully */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_callback_fail, NULL)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CANCELLED); - }; - - /* CRL validation succeeds for a CRL with an invalid thisUpdate date */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_this_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid thisUpdate date */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_this_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation succeeds for a CRL with an invalid nextUpdate date */ - for (int disable_time_validation = 0; disable_time_validation <= 1; disable_time_validation += 1) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_next_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - /* Ensure that validation succeeds for a CRL with an invalid nextUpdate field when time - * validation is disabled. - */ - if (disable_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - } - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid nextUpdate date */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_next_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: server certificate is not revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: server certificate is revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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, config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_REVOKED); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: client certificate is not revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, - S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: client certificate is revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, - S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_REVOKED); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Calling s2n_crl_lookup return functions correctly set context fields */ - { - struct s2n_crl_lookup lookup = { 0 }; - - lookup.status = AWAITING_RESPONSE; - EXPECT_SUCCESS(s2n_crl_lookup_set(&lookup, root_crl)); - EXPECT_TRUE(lookup.status == FINISHED); - EXPECT_NOT_NULL(lookup.crl); - - lookup.status = AWAITING_RESPONSE; - EXPECT_SUCCESS(s2n_crl_lookup_ignore(&lookup)); - EXPECT_TRUE(lookup.status == FINISHED); - EXPECT_NULL(lookup.crl); - }; - - /* Certificate issuer hash is retrieved successfully */ - { - struct s2n_crl_lookup lookup = { 0 }; - EXPECT_NOT_NULL(received_lookup_data.certs[0]); - lookup.cert = received_lookup_data.certs[0]; - - uint64_t hash = 0; - EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&lookup, &hash)); - EXPECT_TRUE(hash != 0); - }; - - /* Retrieved hash values for certificates match CRL hashes */ - { - /* The hash of the leaf certificate matches the hash of the intermediate CRL */ - - struct s2n_crl_lookup leaf_lookup = { 0 }; - EXPECT_NOT_NULL(received_lookup_data.certs[0]); - leaf_lookup.cert = received_lookup_data.certs[0]; - - uint64_t leaf_cert_hash = 0; - EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&leaf_lookup, &leaf_cert_hash)); - EXPECT_TRUE(leaf_cert_hash != 0); - - uint64_t intermediate_crl_hash = 0; - EXPECT_SUCCESS(s2n_crl_get_issuer_hash(intermediate_crl, &intermediate_crl_hash)); - EXPECT_TRUE(intermediate_crl_hash != 0); - - EXPECT_TRUE(leaf_cert_hash == intermediate_crl_hash); - - /* The hash of the intermediate certificate matches the hash of the root CRL */ - - struct s2n_crl_lookup intermediate_lookup = { 0 }; - EXPECT_NOT_NULL(received_lookup_data.certs[1]); - intermediate_lookup.cert = received_lookup_data.certs[1]; - - uint64_t intermediate_cert_hash = 0; - EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&intermediate_lookup, &intermediate_cert_hash)); - EXPECT_TRUE(intermediate_cert_hash != 0); - - uint64_t root_crl_hash = 0; - EXPECT_SUCCESS(s2n_crl_get_issuer_hash(root_crl, &root_crl_hash)); - EXPECT_TRUE(root_crl_hash != 0); - - EXPECT_TRUE(intermediate_cert_hash == root_crl_hash); - - /* If the certificate and CRL were issued by different CAs, their hashes should not match */ - EXPECT_TRUE(leaf_cert_hash != root_crl_hash); - }; - - /* s2n_crl_validate_active tests */ - { - /* Succeeds for valid CRL */ - EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_crl)); - - /* Succeeds for expired CRL */ - EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_invalid_next_update_crl)); - - /* Fails for CRL that is not yet valid */ - EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_active(intermediate_invalid_this_update_crl), - S2N_ERR_CRL_NOT_YET_VALID); - }; - - /* s2n_crl_validate_not_expired tests */ - { - /* Succeeds for valid CRL */ - EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_crl)); - - /* Succeeds for CRL that is not yet valid */ - EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_invalid_this_update_crl)); - - /* Fails for expired CRL */ - EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_not_expired(intermediate_invalid_next_update_crl), - S2N_ERR_CRL_EXPIRED); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "tls/s2n_crl.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define S2N_CRL_ROOT_CERT "../pems/crl/root_cert.pem" +#define S2N_CRL_NONE_REVOKED_CERT_CHAIN "../pems/crl/none_revoked_cert_chain.pem" +#define S2N_CRL_NONE_REVOKED_KEY "../pems/crl/none_revoked_key.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN "../pems/crl/intermediate_revoked_cert_chain.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_KEY "../pems/crl/intermediate_revoked_key.pem" +#define S2N_CRL_LEAF_REVOKED_CERT_CHAIN "../pems/crl/leaf_revoked_cert_chain.pem" +#define S2N_CRL_LEAF_REVOKED_KEY "../pems/crl/leaf_revoked_key.pem" +#define S2N_CRL_ALL_REVOKED_CERT_CHAIN "../pems/crl/all_revoked_cert_chain.pem" +#define S2N_CRL_ALL_REVOKED_KEY "../pems/crl/all_revoked_key.pem" +#define S2N_CRL_ROOT_CRL "../pems/crl/root_crl.pem" +#define S2N_CRL_INTERMEDIATE_CRL "../pems/crl/intermediate_crl.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_CRL "../pems/crl/intermediate_revoked_crl.pem" +#define S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL "../pems/crl/intermediate_invalid_this_update_crl.pem" +#define S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL "../pems/crl/intermediate_invalid_next_update_crl.pem" + +#define CRL_TEST_CHAIN_LEN 2 + +struct crl_lookup_data { + struct s2n_crl *crls[5]; + X509 *certs[5]; + uint8_t callback_invoked_count; +}; + +static int crl_lookup_test_callback(struct s2n_crl_lookup *lookup, void *context) +{ + struct crl_lookup_data *crl_data = (struct crl_lookup_data *) context; + crl_data->callback_invoked_count += 1; + crl_data->certs[lookup->cert_idx] = lookup->cert; + + struct s2n_crl *crl = crl_data->crls[lookup->cert_idx]; + if (crl == NULL) { + POSIX_GUARD(s2n_crl_lookup_ignore(lookup)); + } else { + POSIX_GUARD(s2n_crl_lookup_set(lookup, crl)); + } + + return 0; +} + +static int crl_lookup_noop(struct s2n_crl_lookup *lookup, void *context) +{ + return 0; +} + +static int crl_lookup_callback_fail(struct s2n_crl_lookup *lookup, void *context) +{ + return 1; +} + +static uint8_t verify_host_always_allow(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +static struct s2n_crl *load_test_crl(const char *pem_path) +{ + uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t pem_len = 0; + PTR_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, crl_pem, &pem_len, S2N_MAX_TEST_PEM_SIZE)); + DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); + PTR_ENSURE_REF(crl); + PTR_GUARD_POSIX(s2n_crl_load_pem(crl, crl_pem, pem_len)); + + struct s2n_crl *crl_ret = crl; + ZERO_TO_DISABLE_DEFER_CLEANUP(crl); + + return crl_ret; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* s2n_crl_new allocates and frees a s2n_crl */ + { + struct s2n_crl *crl = s2n_crl_new(); + EXPECT_NOT_NULL(crl); + + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + + /* Multiple calls to free succeed */ + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + }; + + /* s2n_crl_new allocates and frees a s2n_crl with an internal X509_CRL set */ + { + struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL); + EXPECT_NOT_NULL(crl); + EXPECT_NOT_NULL(crl->crl); + + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + + /* Multiple calls to free succeed */ + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + }; + + /* Ensure s2n_crl_load_pem produces a valid X509_CRL internally */ + { + DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(crl); + EXPECT_NOT_NULL(crl->crl); + + /* Make sure an OpenSSL operation succeeds on the internal X509_CRL */ + X509_NAME *crl_name = X509_CRL_get_issuer(crl->crl); + POSIX_ENSURE_REF(crl_name); + }; + + /* s2n_crl_load_pem fails if provided a bad pem */ + { + uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t crl_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_ROOT_CRL, crl_pem, &crl_pem_len, S2N_MAX_TEST_PEM_SIZE)); + DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); + EXPECT_NOT_NULL(crl); + EXPECT_SUCCESS(s2n_crl_load_pem(crl, crl_pem, crl_pem_len)); + + /* Change a random byte in the pem to make it invalid */ + crl_pem[50] = 1; + + DEFER_CLEANUP(struct s2n_crl *invalid_crl = s2n_crl_new(), s2n_crl_free); + EXPECT_NOT_NULL(invalid_crl); + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_load_pem(invalid_crl, crl_pem, crl_pem_len), + S2N_ERR_INVALID_PEM); + }; + + /* CRL issuer hash is retrieved successfully */ + { + DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(crl); + + uint64_t hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(crl, &hash)); + EXPECT_TRUE(hash != 0); + }; + + DEFER_CLEANUP(struct s2n_crl *root_crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(root_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_crl = load_test_crl(S2N_CRL_INTERMEDIATE_CRL), s2n_crl_free); + EXPECT_NOT_NULL(intermediate_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_revoked_crl = load_test_crl(S2N_CRL_INTERMEDIATE_REVOKED_CRL), s2n_crl_free); + EXPECT_NOT_NULL(intermediate_revoked_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_this_update_crl = + load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL), + s2n_crl_free); + EXPECT_NOT_NULL(intermediate_invalid_this_update_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_next_update_crl = + load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL), + s2n_crl_free); + EXPECT_NOT_NULL(intermediate_invalid_next_update_crl); + + /* Save a list of received X509s for s2n_crl_lookup tests */ + struct crl_lookup_data received_lookup_data = { 0 }; + DEFER_CLEANUP(struct s2n_x509_validator received_lookup_data_validator, s2n_x509_validator_wipe); + + /* CRL validation succeeds for unrevoked certificate chain */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + EXPECT_SUCCESS(s2n_x509_validator_init(&received_lookup_data_validator, &trust_store, 0)); + + received_lookup_data.crls[0] = intermediate_crl; + received_lookup_data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &received_lookup_data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&received_lookup_data_validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out)); + EXPECT_TRUE(received_lookup_data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + + /* Ensure all certificates were received in the callback */ + for (int i = 0; i < CRL_TEST_CHAIN_LEN; i++) { + EXPECT_NOT_NULL(received_lookup_data.certs[i]); + } + }; + + /* CRL validation errors when a leaf certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation errors when an intermediate certificate is revoked */ + for (int i = 0; i < 2; i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_revoked_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + if (i == 0) { + /* Ensure CRL validation fails when only the intermediate certificate is revoked */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + } else if (i == 1) { + /* Ensure CRL validation fails when both the intermediate and leaf certificates are revoked */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_ALL_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + } + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + } + + /* CRL validation fails when a certificate is rejected from the callback */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CRL_LOOKUP_FAILED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation succeeds for unrevoked certificate chain when extraneous certificate is rejected */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + + /* Reject the extraneous cert */ + data.crls[2] = NULL; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE * 2]; + uint32_t pem_len_1 = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_NONE_REVOKED_CERT_CHAIN, cert_chain_pem, &pem_len_1, + S2N_MAX_TEST_PEM_SIZE)); + + /* Add an arbitrary cert to the chain that won't be included in the chain of trust */ + uint32_t pem_len_2 = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_SHA256_CLIENT_CERT, cert_chain_pem + pem_len_1, + &pem_len_2, S2N_MAX_TEST_PEM_SIZE)); + + uint32_t cert_chain_len = pem_len_1 + pem_len_2; + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(connection, cert_chain_pem, cert_chain_len, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == 3); + }; + + /* s2n_x509_validator_validate_cert_chain blocks until all CRL callbacks respond */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_noop, NULL)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + /* Blocks if no response received from callbacks */ + for (int i = 0; i < 10; i++) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_ASYNC_BLOCKED); + } + + /* Continues to block if only one callback has sent a response */ + struct s2n_crl_lookup *lookup = NULL; + EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 0, (void **) &lookup)); + EXPECT_NOT_NULL(lookup); + EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, root_crl)); + for (int i = 0; i < 10; ++i) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_ASYNC_BLOCKED); + } + + /* Unblocks when all callbacks send a response */ + lookup = NULL; + EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 1, (void **) &lookup)); + EXPECT_NOT_NULL(lookup); + EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, intermediate_crl)); + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + }; + + /* CRL validation fails when a callback returns unsuccessfully */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_callback_fail, NULL)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CANCELLED); + }; + + /* CRL validation succeeds for a CRL with an invalid thisUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_this_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid thisUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_this_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation succeeds for a CRL with an invalid nextUpdate date */ + for (int disable_time_validation = 0; disable_time_validation <= 1; disable_time_validation += 1) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_next_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + /* Ensure that validation succeeds for a CRL with an invalid nextUpdate field when time + * validation is disabled. + */ + if (disable_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid nextUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_next_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: server certificate is not revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: server certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_REVOKED); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: client certificate is not revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: client certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_REVOKED); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Calling s2n_crl_lookup return functions correctly set context fields */ + { + struct s2n_crl_lookup lookup = { 0 }; + + lookup.status = AWAITING_RESPONSE; + EXPECT_SUCCESS(s2n_crl_lookup_set(&lookup, root_crl)); + EXPECT_TRUE(lookup.status == FINISHED); + EXPECT_NOT_NULL(lookup.crl); + + lookup.status = AWAITING_RESPONSE; + EXPECT_SUCCESS(s2n_crl_lookup_ignore(&lookup)); + EXPECT_TRUE(lookup.status == FINISHED); + EXPECT_NULL(lookup.crl); + }; + + /* Certificate issuer hash is retrieved successfully */ + { + struct s2n_crl_lookup lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[0]); + lookup.cert = received_lookup_data.certs[0]; + + uint64_t hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&lookup, &hash)); + EXPECT_TRUE(hash != 0); + }; + + /* Retrieved hash values for certificates match CRL hashes */ + { + /* The hash of the leaf certificate matches the hash of the intermediate CRL */ + + struct s2n_crl_lookup leaf_lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[0]); + leaf_lookup.cert = received_lookup_data.certs[0]; + + uint64_t leaf_cert_hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&leaf_lookup, &leaf_cert_hash)); + EXPECT_TRUE(leaf_cert_hash != 0); + + uint64_t intermediate_crl_hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(intermediate_crl, &intermediate_crl_hash)); + EXPECT_TRUE(intermediate_crl_hash != 0); + + EXPECT_TRUE(leaf_cert_hash == intermediate_crl_hash); + + /* The hash of the intermediate certificate matches the hash of the root CRL */ + + struct s2n_crl_lookup intermediate_lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[1]); + intermediate_lookup.cert = received_lookup_data.certs[1]; + + uint64_t intermediate_cert_hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&intermediate_lookup, &intermediate_cert_hash)); + EXPECT_TRUE(intermediate_cert_hash != 0); + + uint64_t root_crl_hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(root_crl, &root_crl_hash)); + EXPECT_TRUE(root_crl_hash != 0); + + EXPECT_TRUE(intermediate_cert_hash == root_crl_hash); + + /* If the certificate and CRL were issued by different CAs, their hashes should not match */ + EXPECT_TRUE(leaf_cert_hash != root_crl_hash); + }; + + /* s2n_crl_validate_active tests */ + { + /* Succeeds for valid CRL */ + EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_crl)); + + /* Succeeds for expired CRL */ + EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_invalid_next_update_crl)); + + /* Fails for CRL that is not yet valid */ + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_active(intermediate_invalid_this_update_crl), + S2N_ERR_CRL_NOT_YET_VALID); + }; + + /* s2n_crl_validate_not_expired tests */ + { + /* Succeeds for valid CRL */ + EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_crl)); + + /* Succeeds for CRL that is not yet valid */ + EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_invalid_this_update_crl)); + + /* Fails for expired CRL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_not_expired(intermediate_invalid_next_update_crl), + S2N_ERR_CRL_EXPIRED); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_drain_alert_test.c b/tests/unit/s2n_drain_alert_test.c index 6b5ddb8145a..f56c46b221f 100644 --- a/tests/unit/s2n_drain_alert_test.c +++ b/tests/unit/s2n_drain_alert_test.c @@ -1,140 +1,141 @@ -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -#define INTERNAL_ERROR_ALERT_HEX 0x50 - -/* This test simulates a client sends a TLS alert record and closes its socket immediately after the ClientHello. - * We want to validate that s2n informs the caller of the alert instead of an I/O error. Both errors result - * in a failed handshake, but the alert is generally more useful. - */ - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - size_t body_len = sizeof(client_hello_message); - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - size_t message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - uint8_t alert_record[] = { - /* Record type ALERT */ - 0x15, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Length */ - 0x00, - 0x02, - /* Fatal alert "internal_error" */ - 0x02, - INTERNAL_ERROR_ALERT_HEX, - }; - - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - char *cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - char *private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - - /* Send an alert from client to server */ - EXPECT_EQUAL(write(io_pair.client, alert_record, sizeof(alert_record)), sizeof(alert_record)); - - /* Close the client read/write end */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - /* Expect the server to fail due to an incoming alert. We should not fail due to an I/O error(EPIPE). */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_ALERT); - EXPECT_EQUAL(s2n_connection_get_alert(server_conn), INTERNAL_ERROR_ALERT_HEX); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - free(cert_chain); - free(private_key); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +#define INTERNAL_ERROR_ALERT_HEX 0x50 + +/* This test simulates a client sends a TLS alert record and closes its socket immediately after the ClientHello. + * We want to validate that s2n informs the caller of the alert instead of an I/O error. Both errors result + * in a failed handshake, but the alert is generally more useful. + */ + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + size_t body_len = sizeof(client_hello_message); + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + size_t message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + uint8_t alert_record[] = { + /* Record type ALERT */ + 0x15, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Length */ + 0x00, + 0x02, + /* Fatal alert "internal_error" */ + 0x02, + INTERNAL_ERROR_ALERT_HEX, + }; + + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + char *cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + char *private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + + /* Send an alert from client to server */ + EXPECT_EQUAL(write(io_pair.client, alert_record, sizeof(alert_record)), sizeof(alert_record)); + + /* Close the client read/write end */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + /* Expect the server to fail due to an incoming alert. We should not fail due to an I/O error(EPIPE). */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_ALERT); + EXPECT_EQUAL(s2n_connection_get_alert(server_conn), INTERNAL_ERROR_ALERT_HEX); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + free(cert_chain); + free(private_key); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_invariant_test.c b/tests/unit/s2n_handshake_invariant_test.c index 15cb346bc43..a3de6282309 100644 --- a/tests/unit/s2n_handshake_invariant_test.c +++ b/tests/unit/s2n_handshake_invariant_test.c @@ -1,88 +1,89 @@ -/* - * 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 - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* A full record that has an "APPLICATION_DATA" inside according to s2n's - * handshake message_types. - */ -uint8_t record[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* record len */ - 0x00, 0x05, - /* Type(s2n has this as expected message type for APPLICATION_DATA handler. - * This is not a standardized value, just something s2n has hardcoded as a placeholder - * For the APPLICATON_DATA state in the state machine. - */ - 0x00, - /* Len */ - 0x00, 0x00, 0x01, - /* Data */ - 0x00 -}; -static int amt_written = 0; - -int s2n_app_data_in_handshake_record_recv_fn(void *io_context, uint8_t *buf, uint32_t len) -{ - int amt_left = sizeof(record) - amt_written; - int to_write = S2N_MIN(len, amt_left); - POSIX_CHECKED_MEMCPY(buf, record + amt_written, to_write); - amt_written += to_write; - return to_write; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - /* Initialize *some* handshake type. Not terribly relevant for this test. */ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; - /* Fast forward the handshake state machine to the end of this "handshake_type". - * APPLICATION_DATA is the 11th state for "NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY". - */ - conn->handshake.message_number = 10; - conn->actual_protocol_version = S2N_TLS12; - /* Provide the crafted record to s2n's I/O */ - s2n_connection_set_recv_cb(conn, s2n_app_data_in_handshake_record_recv_fn); - - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - s2n_connection_free(conn); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* A full record that has an "APPLICATION_DATA" inside according to s2n's + * handshake message_types. + */ +uint8_t record[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* record len */ + 0x00, 0x05, + /* Type(s2n has this as expected message type for APPLICATION_DATA handler. + * This is not a standardized value, just something s2n has hardcoded as a placeholder + * For the APPLICATON_DATA state in the state machine. + */ + 0x00, + /* Len */ + 0x00, 0x00, 0x01, + /* Data */ + 0x00 +}; +static int amt_written = 0; + +int s2n_app_data_in_handshake_record_recv_fn(void *io_context, uint8_t *buf, uint32_t len) +{ + int amt_left = sizeof(record) - amt_written; + int to_write = S2N_MIN(len, amt_left); + POSIX_CHECKED_MEMCPY(buf, record + amt_written, to_write); + amt_written += to_write; + return to_write; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Initialize *some* handshake type. Not terribly relevant for this test. */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; + /* Fast forward the handshake state machine to the end of this "handshake_type". + * APPLICATION_DATA is the 11th state for "NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY". + */ + conn->handshake.message_number = 10; + conn->actual_protocol_version = S2N_TLS12; + /* Provide the crafted record to s2n's I/O */ + s2n_connection_set_recv_cb(conn, s2n_app_data_in_handshake_record_recv_fn); + + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(conn); + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_test.c b/tests/unit/s2n_handshake_test.c index a51ae559aa5..966fa91b5ad 100644 --- a/tests/unit/s2n_handshake_test.c +++ b/tests/unit/s2n_handshake_test.c @@ -1,553 +1,554 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_handshake.h" - -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_mldsa.h" -#include "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -enum test_type { - TEST_TYPE_START, - TEST_TYPE_SYNC = TEST_TYPE_START, - TEST_TYPE_ASYNC, - TEST_TYPE_END -} test_type; - -struct s2n_async_pkey_op *pkey_op = NULL; -int async_pkey_op_called = 0; -int async_pkey_op_performed = 0; - -static uint8_t s2n_trust_all_verify_host(const char *host_name, size_t len, void *data) -{ - return 1; -} - -static int handle_async(struct s2n_connection *server_conn) -{ - s2n_blocked_status server_blocked; - - /* Test that handshake can't proceed until async pkey op is complete */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &server_blocked), S2N_ERR_ASYNC_BLOCKED); - - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Test that not performed pkey can't be applied */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), - S2N_ERR_ASYNC_NOT_PERFORMED); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(server_conn); - EXPECT_NOT_NULL(chain_and_key); - - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Test that we can perform pkey operation only once */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_perform(pkey_op, pkey), S2N_ERR_ASYNC_ALREADY_PERFORMED); - - /* Test that pkey op can't be applied to connection other than original one */ - struct s2n_connection *server_conn2 = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn2); - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn2), - S2N_ERR_ASYNC_WRONG_CONNECTION); - EXPECT_SUCCESS(s2n_connection_free(server_conn2)); - - /* Test that pkey op can be applied to original connection */ - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, server_conn)); - - /* Test that pkey op can't be applied to original connection more than once */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), - S2N_ERR_ASYNC_ALREADY_APPLIED); - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - async_pkey_op_performed++; - - return 0; -} - -static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn) -{ - s2n_blocked_status server_blocked; - s2n_blocked_status client_blocked; - - int tries = 0; - do { - int client_rc = s2n_negotiate(client_conn, &client_blocked); - if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return -1; - } - - int server_rc = s2n_negotiate(server_conn, &server_blocked); - if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return -1; - } - - if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { - /* Only can happen in async tests */ - EXPECT_EQUAL(test_type, TEST_TYPE_ASYNC); - EXPECT_SUCCESS(handle_async(server_conn)); - } - - EXPECT_NOT_EQUAL(++tries, 5); - } while (client_blocked || server_blocked); - - POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - return S2N_SUCCESS; -} - -int test_cipher_preferences(struct s2n_config *server_config, struct s2n_config *client_config, - struct s2n_cert_chain_and_key *expected_cert_chain, s2n_signature_algorithm sig_alg) -{ - const struct s2n_security_policy *security_policy = server_config->security_policy; - EXPECT_NOT_NULL(security_policy); - - if (s2n_is_in_fips_mode()) { - /* Override default client config ciphers when in FIPS mode to ensure all FIPS - * default ciphers are tested. - */ - client_config->security_policy = security_policy; - } - - const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; - EXPECT_NOT_NULL(cipher_preferences); - - /* Verify that a handshake succeeds for every available cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_suite *expected_cipher = cipher_preferences->suites[cipher_idx]; - uint8_t expect_failure = 0; - - /* Expect failure if the libcrypto we're building with can't support the cipher */ - if (!expected_cipher->available) { - expect_failure = 1; - } - - s2n_signature_algorithm expected_sig_alg = sig_alg; - /* Expect no server signature algorithm if RSA kex */ - if (expected_cipher->key_exchange_alg == &s2n_rsa) { - expected_sig_alg = S2N_SIGNATURE_ANONYMOUS; - } - - TEST_DEBUG_PRINT("Testing %s in %s mode, expect_failure=%d\n", expected_cipher->name, - test_type == TEST_TYPE_SYNC ? "synchronous" : "asynchronous", expect_failure); - - struct s2n_security_policy server_security_policy; - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(client_conn); - EXPECT_NOT_NULL(server_conn); - - /* Craft a cipher preference with a cipher_idx cipher and assign it to server */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - server_cipher_preferences.suites = &expected_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - server_conn->security_policy_override = &server_security_policy; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_config(client_conn, client_config)); - - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Reset counters */ - async_pkey_op_called = 0; - async_pkey_op_performed = 0; - - if (!expect_failure) { - POSIX_GUARD(try_handshake(server_conn, client_conn)); - - EXPECT_STRING_EQUAL(s2n_connection_get_cipher(server_conn), expected_cipher->name); - - EXPECT_EQUAL(server_conn->handshake_params.our_chain_and_key, expected_cert_chain); - EXPECT_NOT_NULL(server_conn->handshake_params.server_cert_sig_scheme); - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, expected_sig_alg); - - EXPECT_TRUE(IS_NEGOTIATED(server_conn)); - EXPECT_TRUE(IS_NEGOTIATED(client_conn)); - - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); - EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); - - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_EQUAL(async_pkey_op_called, 1); - EXPECT_EQUAL(async_pkey_op_performed, 1); - } else { - EXPECT_EQUAL(async_pkey_op_called, 0); - EXPECT_EQUAL(async_pkey_op_performed, 0); - } - } else { - POSIX_ENSURE_EQ(try_handshake(server_conn, client_conn), -1); - EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); - EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); - EXPECT_EQUAL(async_pkey_op_called, 0); - EXPECT_EQUAL(async_pkey_op_performed, 0); - } - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - return 0; -} - -int async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - /* Just store the op, we will process it later */ - pkey_op = op; - async_pkey_op_called++; - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - for (test_type = TEST_TYPE_START; test_type < TEST_TYPE_END; test_type++) { - /* Test: RSA cert */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* We need a security policy that only supports RSA certificates for auth */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20170210")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Test: RSA (TLS 1.2) key exchanges with TLS 1.3 client */ - { - if (!s2n_is_in_fips_mode()) { - /* Enable TLS 1.3 for the client */ - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Configures server with maximum version 1.2 with only RSA key exchange ciphersuites */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_rsa_kex")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* RSA encrypted premaster secret key exchange requires client versions - * to be set and read correctly, this test covers the behavior with a 1.3 client */ - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - }; - - /* Test: ECDSA cert */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_ecdsa")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_ecdsa")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_ECDSA)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Test: RSA cert with RSA PSS signatures */ - if (s2n_is_rsa_pss_signing_supported()) { - const struct s2n_signature_scheme *const rsa_pss_rsae_sig_schemes[] = { - /* RSA PSS */ - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_rsae_sha384, - &s2n_rsa_pss_rsae_sha512, - }; - - struct s2n_signature_preferences sig_prefs = { - .count = 3, - .signature_schemes = rsa_pss_rsae_sig_schemes, - }; - - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* We need a security policy that only supports RSA certificates for auth */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); - - struct s2n_security_policy security_policy = { - .minimum_protocol_version = server_config->security_policy->minimum_protocol_version, - .cipher_preferences = server_config->security_policy->cipher_preferences, - .kem_preferences = server_config->security_policy->kem_preferences, - .signature_preferences = &sig_prefs, - .ecc_preferences = server_config->security_policy->ecc_preferences, - }; - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - server_config->security_policy = &security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - client_config->check_ocsp = 0; - client_config->disable_x509_validation = 1; - client_config->security_policy = &security_policy; - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA_PSS_RSAE)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: RSA_PSS cert with RSA_PSS signatures */ - if (s2n_is_rsa_pss_certs_supported()) { - s2n_enable_tls13_in_test(); - - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20200207")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20200207")); - client_config->check_ocsp = 0; - client_config->disable_x509_validation = 1; - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA_PSS_PSS)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - s2n_disable_tls13_in_test(); - } - - s2n_reset_tls13_in_test(); - - /* Test: ML-DSA cert */ - if (s2n_mldsa_is_supported()) { - for (size_t verify = 0; verify <= 1; verify++) { - for (size_t client_auth = 0; client_auth <= 1; client_auth++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_MLDSA87_CERT, S2N_MLDSA87_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( - server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, - s2n_trust_all_verify_host, NULL)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, - S2N_MLDSA87_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( - client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(client_config, - s2n_trust_all_verify_host, NULL)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, - S2N_MLDSA87_CERT, NULL)); - - /* ML-DSA is only usable with TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, - "test_all_tls13")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, - "test_all_tls13")); - - /* The hashing path for verify_after_sign is subtly different. - * Make sure we test both paths. - */ - if (verify) { - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, - S2N_VERIFY_AFTER_SIGN_ENABLED)); - } - - /* Test both the async and sync paths */ - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback( - server_config, async_pkey_fn)); - } - - /* Also test the client side use of ML-DSA */ - if (client_auth) { - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, - S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, - S2N_CERT_AUTH_REQUIRED)); - } - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_MLDSA)); - } - } - } - } - - /* Ensure that a handshake can be performed after all file descriptors are closed */ - { - /* A fork is created to ensure that closing file descriptors (like stdout) won't impact - * other tests. - */ - pid_t pid = fork(); - if (pid == 0) { - long max_file_descriptors = sysconf(_SC_OPEN_MAX); - for (long fd = 0; fd < max_file_descriptors; fd++) { - EXPECT_TRUE(fd <= INT_MAX); - close((int) fd); - } - - /* use a nested scope to force the DEFER_CLEANUP statements to - * execute before the exit() call. - */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_NOT_NULL(chain_and_key); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - EXPECT_SUCCESS(test_cipher_preferences(config, config, chain_and_key, S2N_SIGNATURE_RSA)); - } - - exit(EXIT_SUCCESS); - } - - int status = 0; - EXPECT_EQUAL(waitpid(pid, &status, 0), pid); - EXPECT_EQUAL(status, EXIT_SUCCESS); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_handshake.h" + +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_mldsa.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +enum test_type { + TEST_TYPE_START, + TEST_TYPE_SYNC = TEST_TYPE_START, + TEST_TYPE_ASYNC, + TEST_TYPE_END +} test_type; + +struct s2n_async_pkey_op *pkey_op = NULL; +int async_pkey_op_called = 0; +int async_pkey_op_performed = 0; + +static uint8_t s2n_trust_all_verify_host(const char *host_name, size_t len, void *data) +{ + return 1; +} + +static int handle_async(struct s2n_connection *server_conn) +{ + s2n_blocked_status server_blocked; + + /* Test that handshake can't proceed until async pkey op is complete */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &server_blocked), S2N_ERR_ASYNC_BLOCKED); + + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Test that not performed pkey can't be applied */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), + S2N_ERR_ASYNC_NOT_PERFORMED); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(server_conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Test that we can perform pkey operation only once */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_perform(pkey_op, pkey), S2N_ERR_ASYNC_ALREADY_PERFORMED); + + /* Test that pkey op can't be applied to connection other than original one */ + struct s2n_connection *server_conn2 = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn2); + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn2), + S2N_ERR_ASYNC_WRONG_CONNECTION); + EXPECT_SUCCESS(s2n_connection_free(server_conn2)); + + /* Test that pkey op can be applied to original connection */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, server_conn)); + + /* Test that pkey op can't be applied to original connection more than once */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), + S2N_ERR_ASYNC_ALREADY_APPLIED); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + async_pkey_op_performed++; + + return 0; +} + +static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn) +{ + s2n_blocked_status server_blocked; + s2n_blocked_status client_blocked; + + int tries = 0; + do { + int client_rc = s2n_negotiate(client_conn, &client_blocked); + if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return -1; + } + + int server_rc = s2n_negotiate(server_conn, &server_blocked); + if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return -1; + } + + if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { + /* Only can happen in async tests */ + EXPECT_EQUAL(test_type, TEST_TYPE_ASYNC); + EXPECT_SUCCESS(handle_async(server_conn)); + } + + EXPECT_NOT_EQUAL(++tries, 5); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + return S2N_SUCCESS; +} + +int test_cipher_preferences(struct s2n_config *server_config, struct s2n_config *client_config, + struct s2n_cert_chain_and_key *expected_cert_chain, s2n_signature_algorithm sig_alg) +{ + const struct s2n_security_policy *security_policy = server_config->security_policy; + EXPECT_NOT_NULL(security_policy); + + if (s2n_is_in_fips_mode()) { + /* Override default client config ciphers when in FIPS mode to ensure all FIPS + * default ciphers are tested. + */ + client_config->security_policy = security_policy; + } + + const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; + EXPECT_NOT_NULL(cipher_preferences); + + /* Verify that a handshake succeeds for every available cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_suite *expected_cipher = cipher_preferences->suites[cipher_idx]; + uint8_t expect_failure = 0; + + /* Expect failure if the libcrypto we're building with can't support the cipher */ + if (!expected_cipher->available) { + expect_failure = 1; + } + + s2n_signature_algorithm expected_sig_alg = sig_alg; + /* Expect no server signature algorithm if RSA kex */ + if (expected_cipher->key_exchange_alg == &s2n_rsa) { + expected_sig_alg = S2N_SIGNATURE_ANONYMOUS; + } + + TEST_DEBUG_PRINT("Testing %s in %s mode, expect_failure=%d\n", expected_cipher->name, + test_type == TEST_TYPE_SYNC ? "synchronous" : "asynchronous", expect_failure); + + struct s2n_security_policy server_security_policy; + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + /* Craft a cipher preference with a cipher_idx cipher and assign it to server */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + server_cipher_preferences.suites = &expected_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + server_conn->security_policy_override = &server_security_policy; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_config(client_conn, client_config)); + + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Reset counters */ + async_pkey_op_called = 0; + async_pkey_op_performed = 0; + + if (!expect_failure) { + POSIX_GUARD(try_handshake(server_conn, client_conn)); + + EXPECT_STRING_EQUAL(s2n_connection_get_cipher(server_conn), expected_cipher->name); + + EXPECT_EQUAL(server_conn->handshake_params.our_chain_and_key, expected_cert_chain); + EXPECT_NOT_NULL(server_conn->handshake_params.server_cert_sig_scheme); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, expected_sig_alg); + + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); + EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); + + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_EQUAL(async_pkey_op_called, 1); + EXPECT_EQUAL(async_pkey_op_performed, 1); + } else { + EXPECT_EQUAL(async_pkey_op_called, 0); + EXPECT_EQUAL(async_pkey_op_performed, 0); + } + } else { + POSIX_ENSURE_EQ(try_handshake(server_conn, client_conn), -1); + EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); + EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); + EXPECT_EQUAL(async_pkey_op_called, 0); + EXPECT_EQUAL(async_pkey_op_performed, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + return 0; +} + +int async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + /* Just store the op, we will process it later */ + pkey_op = op; + async_pkey_op_called++; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + for (test_type = TEST_TYPE_START; test_type < TEST_TYPE_END; test_type++) { + /* Test: RSA cert */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* We need a security policy that only supports RSA certificates for auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20170210")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test: RSA (TLS 1.2) key exchanges with TLS 1.3 client */ + { + if (!s2n_is_in_fips_mode()) { + /* Enable TLS 1.3 for the client */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Configures server with maximum version 1.2 with only RSA key exchange ciphersuites */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_rsa_kex")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* RSA encrypted premaster secret key exchange requires client versions + * to be set and read correctly, this test covers the behavior with a 1.3 client */ + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test: ECDSA cert */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_ecdsa")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_ecdsa")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_ECDSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test: RSA cert with RSA PSS signatures */ + if (s2n_is_rsa_pss_signing_supported()) { + const struct s2n_signature_scheme *const rsa_pss_rsae_sig_schemes[] = { + /* RSA PSS */ + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + }; + + struct s2n_signature_preferences sig_prefs = { + .count = 3, + .signature_schemes = rsa_pss_rsae_sig_schemes, + }; + + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* We need a security policy that only supports RSA certificates for auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + + struct s2n_security_policy security_policy = { + .minimum_protocol_version = server_config->security_policy->minimum_protocol_version, + .cipher_preferences = server_config->security_policy->cipher_preferences, + .kem_preferences = server_config->security_policy->kem_preferences, + .signature_preferences = &sig_prefs, + .ecc_preferences = server_config->security_policy->ecc_preferences, + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + server_config->security_policy = &security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + client_config->check_ocsp = 0; + client_config->disable_x509_validation = 1; + client_config->security_policy = &security_policy; + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA_PSS_RSAE)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: RSA_PSS cert with RSA_PSS signatures */ + if (s2n_is_rsa_pss_certs_supported()) { + s2n_enable_tls13_in_test(); + + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20200207")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20200207")); + client_config->check_ocsp = 0; + client_config->disable_x509_validation = 1; + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA_PSS_PSS)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + s2n_disable_tls13_in_test(); + } + + s2n_reset_tls13_in_test(); + + /* Test: ML-DSA cert */ + if (s2n_mldsa_is_supported()) { + for (size_t verify = 0; verify <= 1; verify++) { + for (size_t client_auth = 0; client_auth <= 1; client_auth++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_MLDSA87_CERT, S2N_MLDSA87_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( + server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, + s2n_trust_all_verify_host, NULL)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, + S2N_MLDSA87_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( + client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(client_config, + s2n_trust_all_verify_host, NULL)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, + S2N_MLDSA87_CERT, NULL)); + + /* ML-DSA is only usable with TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, + "test_all_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, + "test_all_tls13")); + + /* The hashing path for verify_after_sign is subtly different. + * Make sure we test both paths. + */ + if (verify) { + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, + S2N_VERIFY_AFTER_SIGN_ENABLED)); + } + + /* Test both the async and sync paths */ + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback( + server_config, async_pkey_fn)); + } + + /* Also test the client side use of ML-DSA */ + if (client_auth) { + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, + S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, + S2N_CERT_AUTH_REQUIRED)); + } + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_MLDSA)); + } + } + } + } + + /* Ensure that a handshake can be performed after all file descriptors are closed */ + { + /* A fork is created to ensure that closing file descriptors (like stdout) won't impact + * other tests. + */ + pid_t pid = fork(); + if (pid == 0) { + long max_file_descriptors = sysconf(_SC_OPEN_MAX); + for (long fd = 0; fd < max_file_descriptors; fd++) { + EXPECT_TRUE(fd <= INT_MAX); + close((int) fd); + } + + /* use a nested scope to force the DEFER_CLEANUP statements to + * execute before the exit() call. + */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_NOT_NULL(chain_and_key); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + EXPECT_SUCCESS(test_cipher_preferences(config, config, chain_and_key, S2N_SIGNATURE_RSA)); + } + + exit(EXIT_SUCCESS); + } + + int status = 0; + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, EXIT_SUCCESS); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_type_test.c b/tests/unit/s2n_handshake_type_test.c index d555a4a30b4..a69a6e318b4 100644 --- a/tests/unit/s2n_handshake_type_test.c +++ b/tests/unit/s2n_handshake_type_test.c @@ -1,275 +1,276 @@ -/* - * 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 "s2n_test.h" -#include "tls/s2n_connection.h" - -#define S2N_FIRST_COMMON_HANDSHAKE_FLAG NEGOTIATED -#define S2N_LAST_COMMON_HANDSHAKE_FLAG NO_CLIENT_CERT -#define S2N_FIRST_TLS12_HANDSHAKE_FLAG TLS12_PERFECT_FORWARD_SECRECY -#define S2N_LAST_TLS12_HANDSHAKE_FLAG WITH_SESSION_TICKET -#define S2N_FIRST_TLS13_HANDSHAKE_FLAG HELLO_RETRY_REQUEST -#define S2N_LAST_TLS13_HANDSHAKE_FLAG EARLY_CLIENT_CCS - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* Sanity check test setup */ - EXPECT_EQUAL(S2N_FIRST_COMMON_HANDSHAKE_FLAG, 1); - EXPECT_TRUE(S2N_FIRST_COMMON_HANDSHAKE_FLAG < S2N_LAST_COMMON_HANDSHAKE_FLAG); - EXPECT_EQUAL(S2N_FIRST_TLS12_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); - EXPECT_TRUE(S2N_FIRST_TLS12_HANDSHAKE_FLAG < S2N_LAST_TLS12_HANDSHAKE_FLAG); - EXPECT_EQUAL(S2N_FIRST_TLS13_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); - EXPECT_TRUE(S2N_FIRST_TLS13_HANDSHAKE_FLAG < S2N_LAST_TLS13_HANDSHAKE_FLAG); - EXPECT_EQUAL(S2N_MAX((uint32_t) S2N_LAST_TLS12_HANDSHAKE_FLAG, (uint32_t) S2N_LAST_TLS13_HANDSHAKE_FLAG), S2N_HANDSHAKES_COUNT / 2); - - /* Test s2n_handshake_type_reset */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->handshake.handshake_type = 0x12AB; - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_OK(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_OK(s2n_handshake_type_set_flag(conn, 0xFFFF)); - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_OK(s2n_handshake_type_set_flag(conn, 0)); - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test s2n_handshake_type_set_flag */ - { - /* Safety */ - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_flag(NULL, 0), S2N_ERR_NULL); - - /* Sets all common flags */ - for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - EXPECT_OK(s2n_handshake_type_reset(conn)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_check_flag */ - { - /* Safety */ - EXPECT_FALSE(s2n_handshake_type_check_flag(NULL, 0)); - - /* Check when common flags set */ - for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - /* All flags set */ - { - conn->handshake.handshake_type = 0xFFFF; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - }; - - /* No flags set */ - { - conn->handshake.handshake_type = 0; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); - }; - - /* One flag set */ - { - conn->handshake.handshake_type = flag; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_set_tls12_flag */ - { - /* Safety */ - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(NULL, 0), S2N_ERR_NULL); - - /* Sets all TLS1.2 flags */ - for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_handshake_type_set_tls12_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - EXPECT_OK(s2n_handshake_type_reset(conn)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_check_tls12_flag */ - { - /* Safety */ - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(NULL, 0)); - - /* Check when common flags set */ - for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - /* All flags set */ - { - conn->handshake.handshake_type = 0xFFFF; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - }; - - /* No flags set */ - { - conn->handshake.handshake_type = 0; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - }; - - /* One flag set */ - { - conn->handshake.handshake_type = flag; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_set_tls13_flag */ - { - /* Safety */ - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(NULL, 0), S2N_ERR_NULL); - - /* Sets all TLS1.3 flags */ - for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_handshake_type_set_tls13_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_check_tls13_flag */ - { - /* Safety */ - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(NULL, 0)); - - /* Check when common flags set */ - for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - /* All flags set */ - { - conn->handshake.handshake_type = 0xFFFF; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); - }; - - /* No flags set */ - { - conn->handshake.handshake_type = 0; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - }; - - /* One flag set */ - { - conn->handshake.handshake_type = flag; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "s2n_test.h" +#include "tls/s2n_connection.h" + +#define S2N_FIRST_COMMON_HANDSHAKE_FLAG NEGOTIATED +#define S2N_LAST_COMMON_HANDSHAKE_FLAG NO_CLIENT_CERT +#define S2N_FIRST_TLS12_HANDSHAKE_FLAG TLS12_PERFECT_FORWARD_SECRECY +#define S2N_LAST_TLS12_HANDSHAKE_FLAG WITH_SESSION_TICKET +#define S2N_FIRST_TLS13_HANDSHAKE_FLAG HELLO_RETRY_REQUEST +#define S2N_LAST_TLS13_HANDSHAKE_FLAG EARLY_CLIENT_CCS + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Sanity check test setup */ + EXPECT_EQUAL(S2N_FIRST_COMMON_HANDSHAKE_FLAG, 1); + EXPECT_TRUE(S2N_FIRST_COMMON_HANDSHAKE_FLAG < S2N_LAST_COMMON_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_FIRST_TLS12_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); + EXPECT_TRUE(S2N_FIRST_TLS12_HANDSHAKE_FLAG < S2N_LAST_TLS12_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_FIRST_TLS13_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); + EXPECT_TRUE(S2N_FIRST_TLS13_HANDSHAKE_FLAG < S2N_LAST_TLS13_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_MAX((uint32_t) S2N_LAST_TLS12_HANDSHAKE_FLAG, (uint32_t) S2N_LAST_TLS13_HANDSHAKE_FLAG), S2N_HANDSHAKES_COUNT / 2); + + /* Test s2n_handshake_type_reset */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->handshake.handshake_type = 0x12AB; + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, 0xFFFF)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, 0)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_handshake_type_set_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all common flags */ + for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_OK(s2n_handshake_type_reset(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_set_tls12_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all TLS1.2 flags */ + for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_handshake_type_set_tls12_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_OK(s2n_handshake_type_reset(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_tls12_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_set_tls13_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all TLS1.3 flags */ + for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_tls13_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_tls13_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_key_update_threads_test.c b/tests/unit/s2n_key_update_threads_test.c index 42ca56cb5c9..b46e32e7cbb 100644 --- a/tests/unit/s2n_key_update_threads_test.c +++ b/tests/unit/s2n_key_update_threads_test.c @@ -1,268 +1,269 @@ -/* - * 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 "crypto/s2n_sequence.h" -#include "s2n_test.h" -#include "testlib/s2n_examples.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_random.h" - -#define S2N_TEST_BUFFER_SIZE 1000 -#define S2N_TEST_ENCRYPTION_LIMIT 3 -#define S2N_TEST_KEY_UPDATE_COUNT 25 -#define S2N_TEST_RECORD_COUNT (S2N_TEST_ENCRYPTION_LIMIT * S2N_TEST_KEY_UPDATE_COUNT) -#define S2N_TEST_BYTES_TO_SEND (S2N_DEFAULT_FRAGMENT_LENGTH * S2N_TEST_RECORD_COUNT) - -#define S2N_CIPHER_SUITE_WITH_LIMIT(name, source, limit) \ - struct s2n_cipher_suite name = *(source); \ - struct s2n_record_algorithm _##name##_record_alg = *name.record_alg; \ - _##name##_record_alg.encryption_limit = limit; \ - name.record_alg = &_##name##_record_alg; - -S2N_RESULT s2n_set_key_update_request_for_testing(s2n_peer_key_update request); - -static void *s2n_send_random_data(void *arg) -{ - struct s2n_connection *conn = (struct s2n_connection *) arg; - - uint8_t buffer[S2N_TEST_BUFFER_SIZE] = "hello world"; - - size_t bytes_to_send = S2N_TEST_BYTES_TO_SEND; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - while (bytes_to_send) { - int r = s2n_send(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_send), &blocked); - if (r >= 0) { - bytes_to_send -= r; - } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Send error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); - return NULL; - } - } - return conn; -} - -static void *s2n_recv_random_data(void *arg) -{ - struct s2n_connection *conn = (struct s2n_connection *) arg; - - uint8_t buffer[S2N_TEST_BUFFER_SIZE] = { 0 }; - - size_t bytes_to_read = S2N_TEST_BYTES_TO_SEND; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - while (bytes_to_read) { - int r = s2n_recv(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_read), &blocked); - if (r >= 0) { - bytes_to_read -= r; - } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Recv error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); - return NULL; - } - } - return conn; -} - -static S2N_RESULT s2n_send_and_recv_random_data(struct s2n_connection *conn) -{ - /* - * This test is intended to find concurrency issues when sending and receiving - * KeyUpdates, so we need to run the reader and writer in separate threads. - */ - - pthread_t reader = 0; - RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_recv_random_data, (void *) conn), 0); - - pthread_t writer = 0; - RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_send_random_data, (void *) conn), 0); - - void *reader_return = NULL; - RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); - RESULT_ENSURE_REF(reader_return); - - void *writer_return = NULL; - RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); - RESULT_ENSURE_REF(writer_return); - - RESULT_ENSURE_GT(conn->wire_bytes_out, S2N_TEST_BYTES_TO_SEND); - RESULT_ENSURE_GT(conn->wire_bytes_in, S2N_TEST_BYTES_TO_SEND); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_sanity_check_key_updates_sent(struct s2n_connection *conn) -{ - struct s2n_blob seq_num_blob = { 0 }; - RESULT_GUARD(s2n_connection_get_sequence_number(conn, conn->mode, &seq_num_blob)); - - uint64_t seq_num = 0; - RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &seq_num)); - RESULT_ENSURE_LTE(seq_num, conn->secure->cipher_suite->record_alg->encryption_limit); - - /* s2n-tls doesn't keep a running count of KeyUpdates, so to sanity check that - * at least one KeyUpdate occurred we have to rely on some math. - * - * wire_bytes_out represents the total bytes sent, and should therefore be - * less than or equal to (number of records sent) * (maximum size of a record). - * - * (maximum size of a record) can be calculated based on max_outgoing_fragment_length. - * We will call it max_record_size. - * - * (number of records sent) is seq_num, if no KeyUpdates were sent. seq_num - * starts at 0, is incremented by one for every record, and is reset to 0 by - * a KeyUpdate. So if no KeyUpdate occurs, seq_num represents the total number - * of records sent. - * - * If seq_num represents the total number of records sent, then wire_bytes_out - * must be less than or equal to (seq_num) * (max_record_size). - * If wire_bytes_out is instead greater than (seq_num) * (max_record_size), - * then more records were sent than seq_num accounts for. That means that seq_num - * must have been reset, which means that at least one KeyUpdate was sent. - */ - size_t max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); - RESULT_ENSURE_GT(conn->wire_bytes_out, max_record_size * seq_num); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_encryption_limits(struct s2n_connection *conn) -{ - RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); - - struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; - S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); - - conn->secure->cipher_suite = &key_limit_suite; - - RESULT_GUARD(s2n_send_and_recv_random_data(conn)); - RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); - - conn->secure->cipher_suite = original_suite; - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_peer_requests(struct s2n_connection *conn) -{ - RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); - - struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; - S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); - - conn->secure->cipher_suite = &key_limit_suite; - if (conn->mode == S2N_CLIENT) { - RESULT_GUARD(s2n_set_key_update_request_for_testing(S2N_KEY_UPDATE_REQUESTED)); - } - - RESULT_GUARD(s2n_send_and_recv_random_data(conn)); - RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); - - conn->secure->cipher_suite = original_suite; - return S2N_RESULT_OK; -} - -typedef S2N_RESULT (*s2n_test_scenario)(struct s2n_connection *conn); -static S2N_RESULT s2n_run_self_talk_test(s2n_test_scenario scenario_fn) -{ - struct s2n_cert_chain_and_key *chain_and_key = NULL; - RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - struct s2n_config *config = s2n_config_new(); - RESULT_ENSURE_REF(config); - RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); - RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); - RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); - - pid_t client_pid = fork(); - if (client_pid == 0) { - /* Suppress stdout. - * This only affects the new client process. - */ - fclose(stdout); - - struct s2n_connection *client = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); - - EXPECT_OK(scenario_fn(client)); - - EXPECT_SUCCESS(s2n_connection_free(client)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - exit(EXIT_SUCCESS); - } - - pid_t server_pid = fork(); - if (server_pid == 0) { - /* Suppress stdouts. - * This only affects the new server process. - */ - fclose(stdout); - - struct s2n_connection *server = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); - - EXPECT_OK(scenario_fn(server)); - - EXPECT_SUCCESS(s2n_connection_free(server)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - exit(EXIT_SUCCESS); - } - - int status = 0; - RESULT_ENSURE_EQ(waitpid(client_pid, &status, 0), client_pid); - RESULT_ENSURE_EQ(status, EXIT_SUCCESS); - RESULT_ENSURE_EQ(waitpid(server_pid, &status, 0), server_pid); - RESULT_ENSURE_EQ(status, EXIT_SUCCESS); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* KeyUpdate requires TLS1.3 */ - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* We're going to fork, so flush the initial test output first */ - EXPECT_EQUAL(fflush(stdout), 0); - - EXPECT_OK(s2n_run_self_talk_test(s2n_test_encryption_limits)); - EXPECT_OK(s2n_run_self_talk_test(s2n_test_peer_requests)); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_sequence.h" +#include "s2n_test.h" +#include "testlib/s2n_examples.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_BUFFER_SIZE 1000 +#define S2N_TEST_ENCRYPTION_LIMIT 3 +#define S2N_TEST_KEY_UPDATE_COUNT 25 +#define S2N_TEST_RECORD_COUNT (S2N_TEST_ENCRYPTION_LIMIT * S2N_TEST_KEY_UPDATE_COUNT) +#define S2N_TEST_BYTES_TO_SEND (S2N_DEFAULT_FRAGMENT_LENGTH * S2N_TEST_RECORD_COUNT) + +#define S2N_CIPHER_SUITE_WITH_LIMIT(name, source, limit) \ + struct s2n_cipher_suite name = *(source); \ + struct s2n_record_algorithm _##name##_record_alg = *name.record_alg; \ + _##name##_record_alg.encryption_limit = limit; \ + name.record_alg = &_##name##_record_alg; + +S2N_RESULT s2n_set_key_update_request_for_testing(s2n_peer_key_update request); + +static void *s2n_send_random_data(void *arg) +{ + struct s2n_connection *conn = (struct s2n_connection *) arg; + + uint8_t buffer[S2N_TEST_BUFFER_SIZE] = "hello world"; + + size_t bytes_to_send = S2N_TEST_BYTES_TO_SEND; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (bytes_to_send) { + int r = s2n_send(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_send), &blocked); + if (r >= 0) { + bytes_to_send -= r; + } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Send error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); + return NULL; + } + } + return conn; +} + +static void *s2n_recv_random_data(void *arg) +{ + struct s2n_connection *conn = (struct s2n_connection *) arg; + + uint8_t buffer[S2N_TEST_BUFFER_SIZE] = { 0 }; + + size_t bytes_to_read = S2N_TEST_BYTES_TO_SEND; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (bytes_to_read) { + int r = s2n_recv(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_read), &blocked); + if (r >= 0) { + bytes_to_read -= r; + } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Recv error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); + return NULL; + } + } + return conn; +} + +static S2N_RESULT s2n_send_and_recv_random_data(struct s2n_connection *conn) +{ + /* + * This test is intended to find concurrency issues when sending and receiving + * KeyUpdates, so we need to run the reader and writer in separate threads. + */ + + pthread_t reader = 0; + RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_recv_random_data, (void *) conn), 0); + + pthread_t writer = 0; + RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_send_random_data, (void *) conn), 0); + + void *reader_return = NULL; + RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); + RESULT_ENSURE_REF(reader_return); + + void *writer_return = NULL; + RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); + RESULT_ENSURE_REF(writer_return); + + RESULT_ENSURE_GT(conn->wire_bytes_out, S2N_TEST_BYTES_TO_SEND); + RESULT_ENSURE_GT(conn->wire_bytes_in, S2N_TEST_BYTES_TO_SEND); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_sanity_check_key_updates_sent(struct s2n_connection *conn) +{ + struct s2n_blob seq_num_blob = { 0 }; + RESULT_GUARD(s2n_connection_get_sequence_number(conn, conn->mode, &seq_num_blob)); + + uint64_t seq_num = 0; + RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &seq_num)); + RESULT_ENSURE_LTE(seq_num, conn->secure->cipher_suite->record_alg->encryption_limit); + + /* s2n-tls doesn't keep a running count of KeyUpdates, so to sanity check that + * at least one KeyUpdate occurred we have to rely on some math. + * + * wire_bytes_out represents the total bytes sent, and should therefore be + * less than or equal to (number of records sent) * (maximum size of a record). + * + * (maximum size of a record) can be calculated based on max_outgoing_fragment_length. + * We will call it max_record_size. + * + * (number of records sent) is seq_num, if no KeyUpdates were sent. seq_num + * starts at 0, is incremented by one for every record, and is reset to 0 by + * a KeyUpdate. So if no KeyUpdate occurs, seq_num represents the total number + * of records sent. + * + * If seq_num represents the total number of records sent, then wire_bytes_out + * must be less than or equal to (seq_num) * (max_record_size). + * If wire_bytes_out is instead greater than (seq_num) * (max_record_size), + * then more records were sent than seq_num accounts for. That means that seq_num + * must have been reset, which means that at least one KeyUpdate was sent. + */ + size_t max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); + RESULT_ENSURE_GT(conn->wire_bytes_out, max_record_size * seq_num); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_encryption_limits(struct s2n_connection *conn) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; + S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); + + conn->secure->cipher_suite = &key_limit_suite; + + RESULT_GUARD(s2n_send_and_recv_random_data(conn)); + RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); + + conn->secure->cipher_suite = original_suite; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_peer_requests(struct s2n_connection *conn) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; + S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); + + conn->secure->cipher_suite = &key_limit_suite; + if (conn->mode == S2N_CLIENT) { + RESULT_GUARD(s2n_set_key_update_request_for_testing(S2N_KEY_UPDATE_REQUESTED)); + } + + RESULT_GUARD(s2n_send_and_recv_random_data(conn)); + RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); + + conn->secure->cipher_suite = original_suite; + return S2N_RESULT_OK; +} + +typedef S2N_RESULT (*s2n_test_scenario)(struct s2n_connection *conn); +static S2N_RESULT s2n_run_self_talk_test(s2n_test_scenario scenario_fn) +{ + struct s2n_cert_chain_and_key *chain_and_key = NULL; + RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + RESULT_ENSURE_REF(config); + RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); + RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); + RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); + + pid_t client_pid = fork(); + if (client_pid == 0) { + /* Suppress stdout. + * This only affects the new client process. + */ + fclose(stdout); + + struct s2n_connection *client = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); + + EXPECT_OK(scenario_fn(client)); + + EXPECT_SUCCESS(s2n_connection_free(client)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + exit(EXIT_SUCCESS); + } + + pid_t server_pid = fork(); + if (server_pid == 0) { + /* Suppress stdouts. + * This only affects the new server process. + */ + fclose(stdout); + + struct s2n_connection *server = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + + EXPECT_OK(scenario_fn(server)); + + EXPECT_SUCCESS(s2n_connection_free(server)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + exit(EXIT_SUCCESS); + } + + int status = 0; + RESULT_ENSURE_EQ(waitpid(client_pid, &status, 0), client_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + RESULT_ENSURE_EQ(waitpid(server_pid, &status, 0), server_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* KeyUpdate requires TLS1.3 */ + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* We're going to fork, so flush the initial test output first */ + EXPECT_EQUAL(fflush(stdout), 0); + + EXPECT_OK(s2n_run_self_talk_test(s2n_test_encryption_limits)); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_peer_requests)); + + END_TEST(); +} diff --git a/tests/unit/s2n_mem_allocator_test.c b/tests/unit/s2n_mem_allocator_test.c index bd4bdf0f294..e4cf049abe2 100644 --- a/tests/unit/s2n_mem_allocator_test.c +++ b/tests/unit/s2n_mem_allocator_test.c @@ -1,305 +1,306 @@ -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -#define SUPPORTED_CERTIFICATE_FORMATS (2) - -static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; -static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; - -#define HISTOGRAM_SIZE 100 -static uint32_t histogram_values[HISTOGRAM_SIZE] = { 0 }; -static uint32_t histogram_counts[HISTOGRAM_SIZE] = { 0 }; - -static int custom_mem_init(void) -{ - return 0; -} - -static int custom_mem_cleanup(void) -{ - return 0; -} - -static int custom_mem_malloc(void **ptr, uint32_t requested, uint32_t *allocated) -{ - int i = 0; - for (i = 0; i < HISTOGRAM_SIZE; i++) { - if (histogram_values[i] == 0) { - histogram_values[i] = requested; - } - - if (histogram_values[i] == requested) { - break; - } - } - - if (i < HISTOGRAM_SIZE) { - histogram_counts[i] += 1; - } - - *ptr = malloc(requested); - *allocated = requested; - - /* Fill the memory with non-zeroes to check that s2n handles that fine */ - memset(*ptr, 'a', requested); - - return 0; -} - -static int custom_mem_free(void *ptr, uint32_t size) -{ - free(ptr); - return 0; -} - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - char buffer[0xffff]; - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - s2n_config_disable_x509_verification(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - s2n_connection_set_config(conn, config); - conn->server_protocol_version = S2N_TLS12; - conn->client_protocol_version = S2N_TLS12; - conn->actual_protocol_version = S2N_TLS12; - - s2n_connection_set_io_pair(conn, io_pair); - - s2n_negotiate(conn, &blocked); - - s2n_connection_free_handshake(conn); - - uint16_t timeout = 1; - s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); - int i = 0; - for (i = 1; i < 0xffff - 100; i += 100) { - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - s2n_send(conn, buffer, i, &blocked); - } - - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - - /* release the buffers here to validate we can continue IO after */ - s2n_connection_release_buffers(conn); - - /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ - struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; - int r = 0; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - /* Active application bytes consumed is reset to 0 in before writing data. */ - /* Its value should equal to bytes written after writing */ - ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); - if ((uint64_t) bytes_written != conn->active_application_bytes_consumed) { - exit(0); - } - - int shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - s2n_config_free(config); - - /* Give the server a chance to a void a sigpipe */ - sleep(1); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - s2n_blocked_status blocked; - int status = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - - /* We have to set the callback before BEGIN_TEST, because s2n_init() is called - * there. - */ - int rc = s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free); - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Can't add callbacks if s2n is initialized */ - EXPECT_FAILURE(s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free)); - - EXPECT_SUCCESS(rc); - - for (size_t is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { - struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - conn->server_protocol_version = S2N_TLS12; - conn->client_protocol_version = S2N_TLS12; - conn->actual_protocol_version = S2N_TLS12; - - for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); - } - - if (is_dh_key_exchange) { - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - } - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - char buffer[0xffff]; - for (int i = 1; i < 0xffff; i += 100) { - char *ptr = buffer; - int size = i; - - do { - int bytes_read = 0; - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); - - size -= bytes_read; - ptr += bytes_read; - } while (size); - - for (int j = 0; j < i; j++) { - EXPECT_EQUAL(buffer[j], 33); - } - - /* release the buffers here to validate we can continue IO after */ - EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); - } - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - } - -#if defined(S2N_TEST_DEBUG) - /* Sort our histogram */ - uint32_t spare_value, spare_count; - for (int i = 0; i < HISTOGRAM_SIZE; i++) { - if (histogram_counts[i] == 0) { - break; - } - - for (int j = i + 1; j < HISTOGRAM_SIZE; j++) { - if (histogram_counts[j] == 0) { - break; - } - - if (histogram_values[j] < histogram_values[i]) { - spare_value = histogram_values[i]; - spare_count = histogram_counts[i]; - - histogram_values[i] = histogram_values[j]; - histogram_counts[i] = histogram_counts[j]; - - histogram_values[j] = spare_value; - histogram_counts[j] = spare_count; - } - } - } - - /* Print the histogram values */ - TEST_DEBUG_PRINT("\n\n"); - for (int i = 0; i < HISTOGRAM_SIZE; i++) { - if (histogram_values[i] == 0) { - break; - } - TEST_DEBUG_PRINT("Allocated %d bytes %d times\n", histogram_values[i], histogram_counts[i]); - } - TEST_DEBUG_PRINT("\n"); -#endif - - END_TEST(); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +#define HISTOGRAM_SIZE 100 +static uint32_t histogram_values[HISTOGRAM_SIZE] = { 0 }; +static uint32_t histogram_counts[HISTOGRAM_SIZE] = { 0 }; + +static int custom_mem_init(void) +{ + return 0; +} + +static int custom_mem_cleanup(void) +{ + return 0; +} + +static int custom_mem_malloc(void **ptr, uint32_t requested, uint32_t *allocated) +{ + int i = 0; + for (i = 0; i < HISTOGRAM_SIZE; i++) { + if (histogram_values[i] == 0) { + histogram_values[i] = requested; + } + + if (histogram_values[i] == requested) { + break; + } + } + + if (i < HISTOGRAM_SIZE) { + histogram_counts[i] += 1; + } + + *ptr = malloc(requested); + *allocated = requested; + + /* Fill the memory with non-zeroes to check that s2n handles that fine */ + memset(*ptr, 'a', requested); + + return 0; +} + +static int custom_mem_free(void *ptr, uint32_t size) +{ + free(ptr); + return 0; +} + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + char buffer[0xffff]; + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + s2n_connection_set_config(conn, config); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + s2n_connection_set_io_pair(conn, io_pair); + + s2n_negotiate(conn, &blocked); + + s2n_connection_free_handshake(conn); + + uint16_t timeout = 1; + s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); + int i = 0; + for (i = 1; i < 0xffff - 100; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + s2n_send(conn, buffer, i, &blocked); + } + + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + /* release the buffers here to validate we can continue IO after */ + s2n_connection_release_buffers(conn); + + /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ + struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; + int r = 0; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + /* Active application bytes consumed is reset to 0 in before writing data. */ + /* Its value should equal to bytes written after writing */ + ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); + if ((uint64_t) bytes_written != conn->active_application_bytes_consumed) { + exit(0); + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + + /* We have to set the callback before BEGIN_TEST, because s2n_init() is called + * there. + */ + int rc = s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Can't add callbacks if s2n is initialized */ + EXPECT_FAILURE(s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free)); + + EXPECT_SUCCESS(rc); + + for (size_t is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + char buffer[0xffff]; + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + + /* release the buffers here to validate we can continue IO after */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + } + +#if defined(S2N_TEST_DEBUG) + /* Sort our histogram */ + uint32_t spare_value, spare_count; + for (int i = 0; i < HISTOGRAM_SIZE; i++) { + if (histogram_counts[i] == 0) { + break; + } + + for (int j = i + 1; j < HISTOGRAM_SIZE; j++) { + if (histogram_counts[j] == 0) { + break; + } + + if (histogram_values[j] < histogram_values[i]) { + spare_value = histogram_values[i]; + spare_count = histogram_counts[i]; + + histogram_values[i] = histogram_values[j]; + histogram_counts[i] = histogram_counts[j]; + + histogram_values[j] = spare_value; + histogram_counts[j] = spare_count; + } + } + } + + /* Print the histogram values */ + TEST_DEBUG_PRINT("\n\n"); + for (int i = 0; i < HISTOGRAM_SIZE; i++) { + if (histogram_values[i] == 0) { + break; + } + TEST_DEBUG_PRINT("Allocated %d bytes %d times\n", histogram_values[i], histogram_counts[i]); + } + TEST_DEBUG_PRINT("\n"); +#endif + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_mem_usage_test.c b/tests/unit/s2n_mem_usage_test.c index c8ea3af9464..78d73f70775 100644 --- a/tests/unit/s2n_mem_usage_test.c +++ b/tests/unit/s2n_mem_usage_test.c @@ -1,209 +1,210 @@ -/* - * 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. - */ - -#ifdef __FreeBSD__ - /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) - * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ - #undef _POSIX_C_SOURCE -/* clang-format off */ - #include - #include - /* clang-format on */ - #include -#elif defined(__OpenBSD__) - #undef _POSIX_C_SOURCE - #include -/* clang-format off */ - #include - #include - /* clang-format on */ - #include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -/* The number of connection pairs to allocate before measuring memory - * usage. The greater the value, the more accurate the end result. */ -#define MAX_CONNECTIONS 1000 - -ssize_t get_vm_data_size() -{ - long page_size = 0; - ssize_t size = 0, resident = 0, share = 0, text = 0, lib = 0, data = 0, dt = 0; - - page_size = sysconf(_SC_PAGESIZE); - if (page_size < 0) { - return -1; - } - - FILE *status_file = fopen("/proc/self/statm", "r"); - if (fscanf(status_file, "%zd %zd %zd %zd %zd %zd %zd", &size, &resident, &share, &text, &lib, &data, &dt) < 7) { - fclose(status_file); - return -1; - } - fclose(status_file); - - return data * page_size; -} - -int main(int argc, char **argv) -{ - size_t connectionsToUse = MAX_CONNECTIONS; - - char *cert_chain = NULL; - char *private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - /* Skip the test unless specifically enabled. - * This test is too unreliable to run in all customer environments. - * We should choose specific, known builds to run this test in. - */ - const char *env_var = getenv("S2N_EXPECTED_CONNECTION_MEMORY_KB"); - if (env_var == NULL) { - END_TEST(); - } - const int expected_kbs_per_conn = atoi(env_var); - EXPECT_TRUE(expected_kbs_per_conn > 1); - - struct rlimit file_limit; - EXPECT_SUCCESS(getrlimit(RLIMIT_NOFILE, &file_limit)); - /* 4 fds per connection: {client,server} {write,read} fd - * and reserve 16 fds for libraries, stdin/stdout/stderr and so on */ - if (4 * connectionsToUse + 16 > file_limit.rlim_cur) { - connectionsToUse = S2N_MAX(1, (file_limit.rlim_cur - 16) / 4); - } - - struct s2n_connection **clients = calloc(connectionsToUse, sizeof(struct s2n_connection *)); - struct s2n_connection **servers = calloc(connectionsToUse, sizeof(struct s2n_connection *)); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_config *server_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - ssize_t vm_data_initial = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_initial, -1); - - /* Allocate all connections */ - for (size_t i = 0; i < connectionsToUse; i++) { - struct s2n_connection *client_conn = NULL; - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - clients[i] = client_conn; - - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - servers[i] = server_conn; - } - - ssize_t vm_data_after_allocation = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_allocation, -1); - - for (size_t i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connections_set_io_pair(clients[i], servers[i], &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(servers[i], clients[i])); - } - - ssize_t vm_data_after_handshakes = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_handshakes, -1); - - for (int i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connection_free_handshake(servers[i])); - EXPECT_SUCCESS(s2n_connection_free_handshake(clients[i])); - } - ssize_t vm_data_after_free_handshake = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_free_handshake, -1); - - for (int i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connection_release_buffers(servers[i])); - EXPECT_SUCCESS(s2n_connection_release_buffers(clients[i])); - } - ssize_t vm_data_after_release_buffers = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_release_buffers, -1); - - for (int i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connection_free(clients[i])); - EXPECT_SUCCESS(s2n_connection_free(servers[i])); - } - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(cert_chain); - free(private_key); - free(clients); - free(servers); - - EXPECT_TRUE(vm_data_after_free_handshake <= vm_data_after_handshakes); - EXPECT_TRUE(vm_data_after_release_buffers <= vm_data_after_free_handshake); - - ssize_t handshake_diff = (vm_data_after_handshakes - vm_data_initial); - ssize_t allocation_diff = (vm_data_after_allocation - vm_data_initial); - EXPECT_TRUE(allocation_diff <= handshake_diff); - - ssize_t mem_per_conn = handshake_diff / (connectionsToUse * 2); - ssize_t kbs_per_conn = mem_per_conn / 1024; - - if (kbs_per_conn != expected_kbs_per_conn) { - printf("\nExpected KB per connection: %i\n", expected_kbs_per_conn); - printf("\nActual KB per connection: %zi\n", kbs_per_conn); - printf("This is a %.2f%% change\n", - (kbs_per_conn - expected_kbs_per_conn) * 100.0 / expected_kbs_per_conn); - - printf("\n"); - printf("VmData initial: %10zd\n", vm_data_initial); - printf("VmData after allocations: %10zd\n", vm_data_after_allocation); - printf("VmData after handshakes: %10zd\n", vm_data_after_handshakes); - printf("VmData after free handshake: %10zd\n", vm_data_after_free_handshake); - printf("VmData after release: %10zd\n", vm_data_after_release_buffers); - printf("Number of connections used: %10zu\n", connectionsToUse); - FAIL_MSG("Unexpected memory usage. If expected, update MEM_PER_CONNECTION."); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +#ifdef __FreeBSD__ + /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) + * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ + #undef _POSIX_C_SOURCE +/* clang-format off */ + #include + #include + /* clang-format on */ + #include +#elif defined(__OpenBSD__) + #undef _POSIX_C_SOURCE + #include +/* clang-format off */ + #include + #include + /* clang-format on */ + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* The number of connection pairs to allocate before measuring memory + * usage. The greater the value, the more accurate the end result. */ +#define MAX_CONNECTIONS 1000 + +ssize_t get_vm_data_size() +{ + long page_size = 0; + ssize_t size = 0, resident = 0, share = 0, text = 0, lib = 0, data = 0, dt = 0; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) { + return -1; + } + + FILE *status_file = fopen("/proc/self/statm", "r"); + if (fscanf(status_file, "%zd %zd %zd %zd %zd %zd %zd", &size, &resident, &share, &text, &lib, &data, &dt) < 7) { + fclose(status_file); + return -1; + } + fclose(status_file); + + return data * page_size; +} + +int main(int argc, char **argv) +{ + size_t connectionsToUse = MAX_CONNECTIONS; + + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Skip the test unless specifically enabled. + * This test is too unreliable to run in all customer environments. + * We should choose specific, known builds to run this test in. + */ + const char *env_var = getenv("S2N_EXPECTED_CONNECTION_MEMORY_KB"); + if (env_var == NULL) { + END_TEST(); + } + const int expected_kbs_per_conn = atoi(env_var); + EXPECT_TRUE(expected_kbs_per_conn > 1); + + struct rlimit file_limit; + EXPECT_SUCCESS(getrlimit(RLIMIT_NOFILE, &file_limit)); + /* 4 fds per connection: {client,server} {write,read} fd + * and reserve 16 fds for libraries, stdin/stdout/stderr and so on */ + if (4 * connectionsToUse + 16 > file_limit.rlim_cur) { + connectionsToUse = S2N_MAX(1, (file_limit.rlim_cur - 16) / 4); + } + + struct s2n_connection **clients = calloc(connectionsToUse, sizeof(struct s2n_connection *)); + struct s2n_connection **servers = calloc(connectionsToUse, sizeof(struct s2n_connection *)); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_config *server_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + ssize_t vm_data_initial = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_initial, -1); + + /* Allocate all connections */ + for (size_t i = 0; i < connectionsToUse; i++) { + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + clients[i] = client_conn; + + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + servers[i] = server_conn; + } + + ssize_t vm_data_after_allocation = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_allocation, -1); + + for (size_t i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connections_set_io_pair(clients[i], servers[i], &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(servers[i], clients[i])); + } + + ssize_t vm_data_after_handshakes = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_handshakes, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_free_handshake(servers[i])); + EXPECT_SUCCESS(s2n_connection_free_handshake(clients[i])); + } + ssize_t vm_data_after_free_handshake = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_free_handshake, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_release_buffers(servers[i])); + EXPECT_SUCCESS(s2n_connection_release_buffers(clients[i])); + } + ssize_t vm_data_after_release_buffers = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_release_buffers, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_free(clients[i])); + EXPECT_SUCCESS(s2n_connection_free(servers[i])); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(cert_chain); + free(private_key); + free(clients); + free(servers); + + EXPECT_TRUE(vm_data_after_free_handshake <= vm_data_after_handshakes); + EXPECT_TRUE(vm_data_after_release_buffers <= vm_data_after_free_handshake); + + ssize_t handshake_diff = (vm_data_after_handshakes - vm_data_initial); + ssize_t allocation_diff = (vm_data_after_allocation - vm_data_initial); + EXPECT_TRUE(allocation_diff <= handshake_diff); + + ssize_t mem_per_conn = handshake_diff / (connectionsToUse * 2); + ssize_t kbs_per_conn = mem_per_conn / 1024; + + if (kbs_per_conn != expected_kbs_per_conn) { + printf("\nExpected KB per connection: %i\n", expected_kbs_per_conn); + printf("\nActual KB per connection: %zi\n", kbs_per_conn); + printf("This is a %.2f%% change\n", + (kbs_per_conn - expected_kbs_per_conn) * 100.0 / expected_kbs_per_conn); + + printf("\n"); + printf("VmData initial: %10zd\n", vm_data_initial); + printf("VmData after allocations: %10zd\n", vm_data_after_allocation); + printf("VmData after handshakes: %10zd\n", vm_data_after_handshakes); + printf("VmData after free handshake: %10zd\n", vm_data_after_free_handshake); + printf("VmData after release: %10zd\n", vm_data_after_release_buffers); + printf("Number of connections used: %10zu\n", connectionsToUse); + FAIL_MSG("Unexpected memory usage. If expected, update MEM_PER_CONNECTION."); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_mutual_auth_test.c b/tests/unit/s2n_mutual_auth_test.c index 07ec723f447..b86fbd7c0b7 100644 --- a/tests/unit/s2n_mutual_auth_test.c +++ b/tests/unit/s2n_mutual_auth_test.c @@ -1,565 +1,566 @@ -/* - * 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 "api/s2n.h" -#include "api/unstable/cert_authorities.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_cert_authorities.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_security_policies.h" -#include "utils/s2n_safety.h" - -DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); -DEFINE_POINTER_CLEANUP_FUNC(X509_NAME *, X509_NAME_free); - -struct host_verify_data { - uint8_t callback_invoked; - uint8_t allow; -}; - -static uint8_t verify_host_fn(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return verify_data->allow; -} - -/** - * Pretty prints a DER-encoded Distinguished Name to an s2n_blob - * - * @param der_dn The DER-encoded Distinguished Name input - * @param der_dn_len Length of the DER-encoded Distinguished Name - * @param output_blob Pointer to an s2n_blob that will contain the pretty-printed output - */ -S2N_RESULT pretty_print_dn(const uint8_t *der_dn, size_t der_dn_len, struct s2n_blob *output_blob) -{ - RESULT_ENSURE_REF(der_dn); - RESULT_ENSURE_REF(output_blob); - - DEFER_CLEANUP(BIO *bio = BIO_new(BIO_s_mem()), BIO_free_pointer); - RESULT_ENSURE_REF(bio); - - /* Parse the DER-encoded DN into an X509_NAME structure */ - DEFER_CLEANUP(X509_NAME *name = d2i_X509_NAME(NULL, &der_dn, der_dn_len), X509_NAME_free_pointer); - RESULT_ENSURE_REF(name); - - /* Pretty print the X509_NAME to the BIO */ - X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE); - - BUF_MEM *bptr; - BIO_get_mem_ptr(bio, &bptr); - - RESULT_GUARD_POSIX(s2n_realloc(output_blob, bptr->length)); - RESULT_CHECKED_MEMCPY(output_blob->data, bptr->data, bptr->length); - - return S2N_RESULT_OK; -} - -static int cert_req_cb(struct s2n_connection *conn, void *ctx_in, struct s2n_certificate_request *req) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(ctx_in); - EXPECT_NOT_NULL(req); - - struct s2n_cert_chain_and_key **ctx = ctx_in; - - struct s2n_certificate_authority_list *list = s2n_certificate_request_get_ca_list(req); - EXPECT_NOT_NULL(list); - - uint8_t *name = NULL; - uint16_t length = 0; - - if (s2n_cert_authorities_supported_from_trust_store()) { - /* Confirm the list is re-readable (by reading it 3 times) */ - for (int i = 0; i < 3; i++) { - const char *expected_subjects[] = { - "C = US, CN = leaf", - "C = US, CN = branch", - "C = US, CN = root", - }; - - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(NULL, &name, &length), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, NULL, &length), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, NULL), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_reread(NULL), S2N_ERR_INVALID_ARGUMENT); - - for (int j = 0; j < s2n_array_len(expected_subjects); j++) { - EXPECT_TRUE(s2n_certificate_authority_list_has_next(list)); - EXPECT_SUCCESS(s2n_certificate_authority_list_next(list, &name, &length)); - - DEFER_CLEANUP(struct s2n_blob printed = { 0 }, s2n_free); - POSIX_GUARD_RESULT(pretty_print_dn(name, length, &printed)); - - const char *expected = expected_subjects[j]; - EXPECT_EQUAL(strlen(expected), printed.size); - EXPECT_EQUAL(memcmp(expected, printed.data, printed.size), 0); - } - - EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, &length), S2N_ERR_INVALID_ARGUMENT); - EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); - } - } else { - /* If certificate authorities weren't set, then there shouldn't be anything in the list. */ - EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); - EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); - EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); - } - - /* We set to null to test failure in which case setting will fail. */ - if (*ctx != NULL) { - EXPECT_SUCCESS(s2n_certificate_request_set_certificate(req, *ctx)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_request_set_certificate(req, *ctx), - S2N_ERR_INVALID_ARGUMENT); - } - - return 0; -} - -static bool s2n_should_skip_cipher(struct s2n_cipher_suite *suite) -{ - if (!suite->available) { - /* Skip Ciphers that aren't supported with the linked libcrypto */ - return true; - } - - /* Skip Ciphers that aren't supported with the certificate chain we use in this test */ - if (suite->auth_method == S2N_AUTHENTICATION_ECDSA) { - return true; - } - - if (suite->minimum_required_tls_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { - return true; - } - - return false; -} - -int main(int argc, char **argv) -{ - struct s2n_config *config = NULL; - const struct s2n_security_policy *default_security_policy = NULL; - const struct s2n_cipher_preferences *default_cipher_preferences = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* - * Test Mutual Auth using **s2n_connection_set_client_auth_type** - */ - - EXPECT_NOT_NULL(config = s2n_config_new_minimal()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - EXPECT_NOT_NULL(default_security_policy = config->security_policy); - EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); - - struct host_verify_data verify_data = { .allow = 1, .callback_invoked = 0 }; - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, verify_host_fn, &verify_data)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_cert_chain_and_key *cb_chain = NULL; - - /* If this isn't supported we can't really test the end-to-end flow. */ - if (s2n_cert_authorities_supported_from_trust_store()) { - EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config)); - } - - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_request_callback(NULL, cert_req_cb, (void *) &cb_chain), - S2N_ERR_INVALID_ARGUMENT); - - /** - * Test with 3 variants: - * - * * Callback not set - * * Callback set, returns NULL - * * Callback set, returns a valid cert chain - */ - for (int cert_req_callback_mode = 0; cert_req_callback_mode < 3; cert_req_callback_mode++) { - if (cert_req_callback_mode != 0) { - EXPECT_SUCCESS(s2n_config_set_cert_request_callback(config, cert_req_cb, (void *) &cb_chain)); - } - - bool saw_tls12 = false; - bool saw_tls13 = false; - const bool expected_success = cert_req_callback_mode != 1; - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - verify_data.callback_invoked = 0; - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - if (cert_req_callback_mode == 0) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else if (cert_req_callback_mode == 1) { - cb_chain = NULL; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_NO_CERT_FOUND); - } else if (cert_req_callback_mode == 2) { - cb_chain = chain_and_key; - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - - if (expected_success) { - switch (s2n_connection_get_actual_protocol_version(server_conn)) { - case S2N_TLS13: - saw_tls13 = true; - break; - case S2N_TLS12: - saw_tls12 = true; - break; - default: - /* no-op */ - break; - } - - /* Verify that both connections negotiated Mutual Auth */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - EXPECT_TRUE(verify_data.callback_invoked); - } - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - } - - if (expected_success) { - /* If not supported, don't expect to see TlS 1.3 */ - EXPECT_TRUE(saw_tls13 || !s2n_is_tls13_fully_supported()); - EXPECT_TRUE(saw_tls12); - } - - /* Reset the callback */ - config->cert_request_cb = NULL; - config->cert_request_cb_ctx = NULL; - } - - /* - * Test Mutual Auth using **s2n_config_set_client_auth_type** - */ - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated Mutual Auth */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - } - - /* - * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** - */ - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated Mutual Auth */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - } - - /* - * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** only on one side of the - * connection and verify that a connection is not established - */ - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* Only set S2N_CERT_AUTH_REQUIRED on the server and not the client so that the connection fails */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that NEITHER connections negotiated Mutual Auth */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - 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_ALERT(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - - /* 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); - free(private_key_pem); - free(dhparams_pem); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "api/unstable/cert_authorities.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_authorities.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); +DEFINE_POINTER_CLEANUP_FUNC(X509_NAME *, X509_NAME_free); + +struct host_verify_data { + uint8_t callback_invoked; + uint8_t allow; +}; + +static uint8_t verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return verify_data->allow; +} + +/** + * Pretty prints a DER-encoded Distinguished Name to an s2n_blob + * + * @param der_dn The DER-encoded Distinguished Name input + * @param der_dn_len Length of the DER-encoded Distinguished Name + * @param output_blob Pointer to an s2n_blob that will contain the pretty-printed output + */ +S2N_RESULT pretty_print_dn(const uint8_t *der_dn, size_t der_dn_len, struct s2n_blob *output_blob) +{ + RESULT_ENSURE_REF(der_dn); + RESULT_ENSURE_REF(output_blob); + + DEFER_CLEANUP(BIO *bio = BIO_new(BIO_s_mem()), BIO_free_pointer); + RESULT_ENSURE_REF(bio); + + /* Parse the DER-encoded DN into an X509_NAME structure */ + DEFER_CLEANUP(X509_NAME *name = d2i_X509_NAME(NULL, &der_dn, der_dn_len), X509_NAME_free_pointer); + RESULT_ENSURE_REF(name); + + /* Pretty print the X509_NAME to the BIO */ + X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE); + + BUF_MEM *bptr; + BIO_get_mem_ptr(bio, &bptr); + + RESULT_GUARD_POSIX(s2n_realloc(output_blob, bptr->length)); + RESULT_CHECKED_MEMCPY(output_blob->data, bptr->data, bptr->length); + + return S2N_RESULT_OK; +} + +static int cert_req_cb(struct s2n_connection *conn, void *ctx_in, struct s2n_certificate_request *req) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ctx_in); + EXPECT_NOT_NULL(req); + + struct s2n_cert_chain_and_key **ctx = ctx_in; + + struct s2n_certificate_authority_list *list = s2n_certificate_request_get_ca_list(req); + EXPECT_NOT_NULL(list); + + uint8_t *name = NULL; + uint16_t length = 0; + + if (s2n_cert_authorities_supported_from_trust_store()) { + /* Confirm the list is re-readable (by reading it 3 times) */ + for (int i = 0; i < 3; i++) { + const char *expected_subjects[] = { + "C = US, CN = leaf", + "C = US, CN = branch", + "C = US, CN = root", + }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(NULL, &name, &length), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, NULL, &length), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, NULL), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_reread(NULL), S2N_ERR_INVALID_ARGUMENT); + + for (int j = 0; j < s2n_array_len(expected_subjects); j++) { + EXPECT_TRUE(s2n_certificate_authority_list_has_next(list)); + EXPECT_SUCCESS(s2n_certificate_authority_list_next(list, &name, &length)); + + DEFER_CLEANUP(struct s2n_blob printed = { 0 }, s2n_free); + POSIX_GUARD_RESULT(pretty_print_dn(name, length, &printed)); + + const char *expected = expected_subjects[j]; + EXPECT_EQUAL(strlen(expected), printed.size); + EXPECT_EQUAL(memcmp(expected, printed.data, printed.size), 0); + } + + EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, &length), S2N_ERR_INVALID_ARGUMENT); + EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); + } + } else { + /* If certificate authorities weren't set, then there shouldn't be anything in the list. */ + EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); + EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); + EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); + } + + /* We set to null to test failure in which case setting will fail. */ + if (*ctx != NULL) { + EXPECT_SUCCESS(s2n_certificate_request_set_certificate(req, *ctx)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_request_set_certificate(req, *ctx), + S2N_ERR_INVALID_ARGUMENT); + } + + return 0; +} + +static bool s2n_should_skip_cipher(struct s2n_cipher_suite *suite) +{ + if (!suite->available) { + /* Skip Ciphers that aren't supported with the linked libcrypto */ + return true; + } + + /* Skip Ciphers that aren't supported with the certificate chain we use in this test */ + if (suite->auth_method == S2N_AUTHENTICATION_ECDSA) { + return true; + } + + if (suite->minimum_required_tls_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { + return true; + } + + return false; +} + +int main(int argc, char **argv) +{ + struct s2n_config *config = NULL; + const struct s2n_security_policy *default_security_policy = NULL; + const struct s2n_cipher_preferences *default_cipher_preferences = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* + * Test Mutual Auth using **s2n_connection_set_client_auth_type** + */ + + EXPECT_NOT_NULL(config = s2n_config_new_minimal()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = config->security_policy); + EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); + + struct host_verify_data verify_data = { .allow = 1, .callback_invoked = 0 }; + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, verify_host_fn, &verify_data)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_cert_chain_and_key *cb_chain = NULL; + + /* If this isn't supported we can't really test the end-to-end flow. */ + if (s2n_cert_authorities_supported_from_trust_store()) { + EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config)); + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_request_callback(NULL, cert_req_cb, (void *) &cb_chain), + S2N_ERR_INVALID_ARGUMENT); + + /** + * Test with 3 variants: + * + * * Callback not set + * * Callback set, returns NULL + * * Callback set, returns a valid cert chain + */ + for (int cert_req_callback_mode = 0; cert_req_callback_mode < 3; cert_req_callback_mode++) { + if (cert_req_callback_mode != 0) { + EXPECT_SUCCESS(s2n_config_set_cert_request_callback(config, cert_req_cb, (void *) &cb_chain)); + } + + bool saw_tls12 = false; + bool saw_tls13 = false; + const bool expected_success = cert_req_callback_mode != 1; + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + verify_data.callback_invoked = 0; + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + if (cert_req_callback_mode == 0) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else if (cert_req_callback_mode == 1) { + cb_chain = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_NO_CERT_FOUND); + } else if (cert_req_callback_mode == 2) { + cb_chain = chain_and_key; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + if (expected_success) { + switch (s2n_connection_get_actual_protocol_version(server_conn)) { + case S2N_TLS13: + saw_tls13 = true; + break; + case S2N_TLS12: + saw_tls12 = true; + break; + default: + /* no-op */ + break; + } + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + EXPECT_TRUE(verify_data.callback_invoked); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + if (expected_success) { + /* If not supported, don't expect to see TlS 1.3 */ + EXPECT_TRUE(saw_tls13 || !s2n_is_tls13_fully_supported()); + EXPECT_TRUE(saw_tls12); + } + + /* Reset the callback */ + config->cert_request_cb = NULL; + config->cert_request_cb_ctx = NULL; + } + + /* + * Test Mutual Auth using **s2n_config_set_client_auth_type** + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** only on one side of the + * connection and verify that a connection is not established + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Only set S2N_CERT_AUTH_REQUIRED on the server and not the client so that the connection fails */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that NEITHER connections negotiated Mutual Auth */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + 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_ALERT(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + + /* 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); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_openssl_x509_test.c b/tests/unit/s2n_openssl_x509_test.c index 85fa4500fd8..5a364cb4488 100644 --- a/tests/unit/s2n_openssl_x509_test.c +++ b/tests/unit/s2n_openssl_x509_test.c @@ -1,238 +1,239 @@ -/* - * 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 "crypto/s2n_openssl_x509.h" - -#include -#include - -#include "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, - struct s2n_blob *asn1_cert); - -static S2N_RESULT s2n_test_assert_s2n_cert_info_equality(const struct s2n_cert_info *info_a, - const struct s2n_cert_info *info_b) -{ - RESULT_ENSURE_EQ(info_a->public_key_bits, info_b->public_key_bits); - RESULT_ENSURE_EQ(info_a->public_key_nid, info_b->public_key_nid); - RESULT_ENSURE_EQ(info_a->signature_nid, info_b->signature_nid); - RESULT_ENSURE_EQ(info_a->signature_digest_nid, info_b->signature_digest_nid); - RESULT_ENSURE_EQ(info_a->self_signed, info_b->self_signed); - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* A certificate with one trailing byte is parsed successfully */ - { - uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ONE_TRAILING_BYTE_CERT_BIN, cert_chain_data, - &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_blob cert_chain_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); - - struct s2n_blob cert_asn1_der = { 0 }; - EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); - - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); - } - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); - } - } - - /* A certificate with too many trailing bytes errors */ - { - uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_FOUR_TRAILING_BYTE_CERT_BIN, cert_chain_data, - &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_blob cert_chain_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); - - struct s2n_blob cert_asn1_der = { 0 }; - EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); - - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_ERROR(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); - } - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); - } - } - - /* s2n_openssl_x509_get_cert_info */ - struct { - const char *key_type; - const char *signature; - const char *key_size; - const char *digest; - int expected_signature_nid; - int expected_digest_nid; - int expected_public_key_nid; - int expected_public_key_bits; - } test_cases[] = { - { - .key_type = "ec", - .signature = "ecdsa", - .key_size = "p384", - .digest = "sha256", - .expected_signature_nid = NID_ecdsa_with_SHA256, - .expected_digest_nid = NID_sha256, - .expected_public_key_nid = NID_secp384r1, - .expected_public_key_bits = 384, - }, - { - .key_type = "ec", - .signature = "ecdsa", - .key_size = "p256", - .digest = "sha384", - .expected_signature_nid = NID_ecdsa_with_SHA384, - .expected_digest_nid = NID_sha384, - .expected_public_key_nid = NID_X9_62_prime256v1, - .expected_public_key_bits = 256, - }, - { - .key_type = "ec", - .signature = "ecdsa", - .key_size = "p521", - .digest = "sha512", - .expected_signature_nid = NID_ecdsa_with_SHA512, - .expected_digest_nid = NID_sha512, - .expected_public_key_nid = NID_secp521r1, - .expected_public_key_bits = 521, - }, - { - .key_type = "rsae", - .signature = "pkcs", - .key_size = "2048", - .digest = "sha1", - .expected_signature_nid = NID_sha1WithRSAEncryption, - .expected_digest_nid = NID_sha1, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 2048, - }, - { - .key_type = "rsae", - .signature = "pkcs", - .key_size = "2048", - .digest = "sha224", - .expected_signature_nid = NID_sha224WithRSAEncryption, - .expected_digest_nid = NID_sha224, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 2048, - }, - { - .key_type = "rsae", - .signature = "pkcs", - .key_size = "3072", - .digest = "sha384", - .expected_signature_nid = NID_sha384WithRSAEncryption, - .expected_digest_nid = NID_sha384, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 3072, - }, -#if RSA_PSS_CERTS_SUPPORTED - { - .key_type = "rsae", - .signature = "pss", - .key_size = "4096", - .digest = "sha384", - .expected_signature_nid = NID_rsassaPss, - .expected_digest_nid = NID_undef, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 4096, - }, - { - .key_type = "rsapss", - .signature = "pss", - .key_size = "2048", - .digest = "sha256", - .expected_signature_nid = NID_rsassaPss, - .expected_digest_nid = NID_undef, - .expected_public_key_nid = NID_rsassaPss, - .expected_public_key_bits = 2048, - }, -#endif - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - /* initialize variables and read in certificates */ - char pathbuffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_OK(s2n_test_cert_permutation_get_server_chain_path(&pathbuffer[0], - test_cases[i].key_type, test_cases[i].signature, test_cases[i].key_size, - test_cases[i].digest)); - EXPECT_SUCCESS(s2n_read_test_pem(pathbuffer, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(X509 *leaf = NULL, X509_free_pointer); - DEFER_CLEANUP(X509 *intermediate = NULL, X509_free_pointer); - DEFER_CLEANUP(X509 *root = NULL, X509_free_pointer); - - /* read in cert chain */ - size_t chain_len = strlen((const char *) cert_file); - BIO *cert_bio = NULL; - EXPECT_NOT_NULL(cert_bio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(cert_bio, cert_file, chain_len) > 0); - EXPECT_NOT_NULL(leaf = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); - EXPECT_NOT_NULL(intermediate = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); - EXPECT_NOT_NULL(root = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); - EXPECT_SUCCESS(BIO_free(cert_bio)); - - /* retrieve cert info from test case certificates */ - struct s2n_cert_info leaf_info = { 0 }; - struct s2n_cert_info intermediate_info = { 0 }; - struct s2n_cert_info root_info = { 0 }; - - EXPECT_OK(s2n_openssl_x509_get_cert_info(leaf, &leaf_info)); - EXPECT_OK(s2n_openssl_x509_get_cert_info(intermediate, &intermediate_info)); - EXPECT_OK(s2n_openssl_x509_get_cert_info(root, &root_info)); - - struct s2n_cert_info expected_info = { - .signature_nid = test_cases[i].expected_signature_nid, - .signature_digest_nid = test_cases[i].expected_digest_nid, - .public_key_nid = test_cases[i].expected_public_key_nid, - .public_key_bits = test_cases[i].expected_public_key_bits, - .self_signed = false, - }; - - /* assert that cert info matches expected values */ - EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&leaf_info, &expected_info)); - EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&intermediate_info, &expected_info)); - /* root should be self-signed, but otherwise equal */ - expected_info.self_signed = true; - EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&root_info, &expected_info)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_openssl_x509.h" + +#include +#include + +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, + struct s2n_blob *asn1_cert); + +static S2N_RESULT s2n_test_assert_s2n_cert_info_equality(const struct s2n_cert_info *info_a, + const struct s2n_cert_info *info_b) +{ + RESULT_ENSURE_EQ(info_a->public_key_bits, info_b->public_key_bits); + RESULT_ENSURE_EQ(info_a->public_key_nid, info_b->public_key_nid); + RESULT_ENSURE_EQ(info_a->signature_nid, info_b->signature_nid); + RESULT_ENSURE_EQ(info_a->signature_digest_nid, info_b->signature_digest_nid); + RESULT_ENSURE_EQ(info_a->self_signed, info_b->self_signed); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* A certificate with one trailing byte is parsed successfully */ + { + uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ONE_TRAILING_BYTE_CERT_BIN, cert_chain_data, + &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_blob cert_chain_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); + + struct s2n_blob cert_asn1_der = { 0 }; + EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); + + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); + } + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); + } + } + + /* A certificate with too many trailing bytes errors */ + { + uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_FOUR_TRAILING_BYTE_CERT_BIN, cert_chain_data, + &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_blob cert_chain_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); + + struct s2n_blob cert_asn1_der = { 0 }; + EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); + + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_ERROR(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); + } + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); + } + } + + /* s2n_openssl_x509_get_cert_info */ + struct { + const char *key_type; + const char *signature; + const char *key_size; + const char *digest; + int expected_signature_nid; + int expected_digest_nid; + int expected_public_key_nid; + int expected_public_key_bits; + } test_cases[] = { + { + .key_type = "ec", + .signature = "ecdsa", + .key_size = "p384", + .digest = "sha256", + .expected_signature_nid = NID_ecdsa_with_SHA256, + .expected_digest_nid = NID_sha256, + .expected_public_key_nid = NID_secp384r1, + .expected_public_key_bits = 384, + }, + { + .key_type = "ec", + .signature = "ecdsa", + .key_size = "p256", + .digest = "sha384", + .expected_signature_nid = NID_ecdsa_with_SHA384, + .expected_digest_nid = NID_sha384, + .expected_public_key_nid = NID_X9_62_prime256v1, + .expected_public_key_bits = 256, + }, + { + .key_type = "ec", + .signature = "ecdsa", + .key_size = "p521", + .digest = "sha512", + .expected_signature_nid = NID_ecdsa_with_SHA512, + .expected_digest_nid = NID_sha512, + .expected_public_key_nid = NID_secp521r1, + .expected_public_key_bits = 521, + }, + { + .key_type = "rsae", + .signature = "pkcs", + .key_size = "2048", + .digest = "sha1", + .expected_signature_nid = NID_sha1WithRSAEncryption, + .expected_digest_nid = NID_sha1, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 2048, + }, + { + .key_type = "rsae", + .signature = "pkcs", + .key_size = "2048", + .digest = "sha224", + .expected_signature_nid = NID_sha224WithRSAEncryption, + .expected_digest_nid = NID_sha224, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 2048, + }, + { + .key_type = "rsae", + .signature = "pkcs", + .key_size = "3072", + .digest = "sha384", + .expected_signature_nid = NID_sha384WithRSAEncryption, + .expected_digest_nid = NID_sha384, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 3072, + }, +#if RSA_PSS_CERTS_SUPPORTED + { + .key_type = "rsae", + .signature = "pss", + .key_size = "4096", + .digest = "sha384", + .expected_signature_nid = NID_rsassaPss, + .expected_digest_nid = NID_undef, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 4096, + }, + { + .key_type = "rsapss", + .signature = "pss", + .key_size = "2048", + .digest = "sha256", + .expected_signature_nid = NID_rsassaPss, + .expected_digest_nid = NID_undef, + .expected_public_key_nid = NID_rsassaPss, + .expected_public_key_bits = 2048, + }, +#endif + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + /* initialize variables and read in certificates */ + char pathbuffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_OK(s2n_test_cert_permutation_get_server_chain_path(&pathbuffer[0], + test_cases[i].key_type, test_cases[i].signature, test_cases[i].key_size, + test_cases[i].digest)); + EXPECT_SUCCESS(s2n_read_test_pem(pathbuffer, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(X509 *leaf = NULL, X509_free_pointer); + DEFER_CLEANUP(X509 *intermediate = NULL, X509_free_pointer); + DEFER_CLEANUP(X509 *root = NULL, X509_free_pointer); + + /* read in cert chain */ + size_t chain_len = strlen((const char *) cert_file); + BIO *cert_bio = NULL; + EXPECT_NOT_NULL(cert_bio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(cert_bio, cert_file, chain_len) > 0); + EXPECT_NOT_NULL(leaf = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); + EXPECT_NOT_NULL(intermediate = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); + EXPECT_NOT_NULL(root = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); + EXPECT_SUCCESS(BIO_free(cert_bio)); + + /* retrieve cert info from test case certificates */ + struct s2n_cert_info leaf_info = { 0 }; + struct s2n_cert_info intermediate_info = { 0 }; + struct s2n_cert_info root_info = { 0 }; + + EXPECT_OK(s2n_openssl_x509_get_cert_info(leaf, &leaf_info)); + EXPECT_OK(s2n_openssl_x509_get_cert_info(intermediate, &intermediate_info)); + EXPECT_OK(s2n_openssl_x509_get_cert_info(root, &root_info)); + + struct s2n_cert_info expected_info = { + .signature_nid = test_cases[i].expected_signature_nid, + .signature_digest_nid = test_cases[i].expected_digest_nid, + .public_key_nid = test_cases[i].expected_public_key_nid, + .public_key_bits = test_cases[i].expected_public_key_bits, + .self_signed = false, + }; + + /* assert that cert info matches expected values */ + EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&leaf_info, &expected_info)); + EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&intermediate_info, &expected_info)); + /* root should be self-signed, but otherwise equal */ + expected_info.self_signed = true; + EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&root_info, &expected_info)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_optional_client_auth_test.c b/tests/unit/s2n_optional_client_auth_test.c index 953fc264b9e..a2bc6be9738 100644 --- a/tests/unit/s2n_optional_client_auth_test.c +++ b/tests/unit/s2n_optional_client_auth_test.c @@ -1,478 +1,479 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_security_policies.h" - -int main(int argc, char **argv) -{ - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - const struct s2n_security_policy *default_security_policy = NULL; - const struct s2n_cipher_preferences *default_cipher_preferences = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* Setup baseline server config and certs. */ - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); - EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with a valid client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server requires optional client auth and accepts the client cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_security_policy->cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated mutual auth. */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with S2N_CERT_AUTH_NONE for Server - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server does not request a Client Cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connections negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with no client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server requires optional client auth and accepts the client cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connection negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_connection_set_client_auth_type** with a valid client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - /* Server requires no client auth but the connection will. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated mutual auth. */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_connection_set_client_auth_type** with no client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - /* Server requires client auth but the connection will allow an empty client cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connection negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with an incorrect client - * cert provided fails negotiation, allowing the user to fatally kill the handshake if they want. - * https://tools.ietf.org/html/rfc5246#section-7.4.6 - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server requires optional client auth but will reject the client cert. We need to reset the config, to turn validation back on*/ - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Verify that a handshake fails for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake failed. Blinding is disabled for the failure case to speed up tests. */ - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connection negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_security_policies.h" + +int main(int argc, char **argv) +{ + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + const struct s2n_security_policy *default_security_policy = NULL; + const struct s2n_cipher_preferences *default_cipher_preferences = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* Setup baseline server config and certs. */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); + EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with a valid client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth and accepts the client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_security_policy->cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated mutual auth. */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with S2N_CERT_AUTH_NONE for Server + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server does not request a Client Cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connections negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with no client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth and accepts the client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_connection_set_client_auth_type** with a valid client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + /* Server requires no client auth but the connection will. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated mutual auth. */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_connection_set_client_auth_type** with no client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + /* Server requires client auth but the connection will allow an empty client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with an incorrect client + * cert provided fails negotiation, allowing the user to fatally kill the handshake if they want. + * https://tools.ietf.org/html/rfc5246#section-7.4.6 + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth but will reject the client cert. We need to reset the config, to turn validation back on*/ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify that a handshake fails for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake failed. Blinding is disabled for the failure case to speed up tests. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_pem_rsa_dhe_test.c b/tests/unit/s2n_pem_rsa_dhe_test.c index a9bf4306d5b..714bb2648f6 100644 --- a/tests/unit/s2n_pem_rsa_dhe_test.c +++ b/tests/unit/s2n_pem_rsa_dhe_test.c @@ -1,204 +1,205 @@ -/* - * 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 "crypto/s2n_dhe.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" - -static uint8_t unmatched_private_key[] = - "-----BEGIN RSA PRIVATE KEY-----\n" - "MIIEpQIBAAKCAQEA6ddvtO6DbX5GLrWeXD2jufmR6lvYuzwpIHbDf0RQr2AT50wf\n" - "vV+sMYVa/d4g68tqtihH691tqzKevVc25LXM0a0f0NfnrNibyA1/66CwFQsyy1j2\n" - "U90BdguI/ZxqEV58ee/PzerOoS5d/rgBALzGwwYwyvlzr35KGjfRZ4XOFoToKnkN\n" - "0mR44firCvb7dJ53QRXoPbkYpQQQ3vMWMOtDZoPsvP0dJJ50B7LaFL6nbOyJmZ2T\n" - "evaAov1Nuk5QSrZf2icpuQVXxuu2xLmTNL25OUiV3t7ZjiLtrOZrmrIzv8/sLcWp\n" - "VJcFWFoKizmjvpDfD086a9c81vo4kqgD/RZ9dQIDAQABAoIBAQDBNUzJ3MxgupW4\n" - "YD2BDzjpH2jNj6fKRBHjDd3HmKVl0eeAE2iiKpt2qy2cVl0zFfaMnUmXe3PyoLeB\n" - "z76+R+v8TqPcBZgZOzuzllvcTv9N09vbIh0c+50KcMt2aDdHNJ96jIdRJzIlAM+O\n" - "9290sYU0fDfybRuFo74MXZQ6idbWyNMjXEv2oPrmvMmOHm10sz9BCXWFUpDpDFKD\n" - "8xgw1GZ1QeHITWoQXMI2uJUFFj2hYbRAl6f8cMXVjUipmMNpfJk4CbtEdSMPkjrz\n" - "XgIVrMCorCiaLuVQgSQCHB01n8ETM6R9PYSgMSg3RYYqKq4N0nvREUf4VvKV8JOY\n" - "EGzNgq3BAoGBAPvHPoCKQawLEr4jE5ITMqhZXlBXh8aU7LetjSY1BfnsiH43eg1n\n" - "186lfOX0r1I2KkVZTehZohkEo8xeEpc+XMdux1z0mX2uz1tfmHl838sRaTBs1JZ0\n" - "slkBrASfDdOnLlHCMhRmFEEnA0eelNYCiy/Ak0UX7NpuhZ3bh2jGemixAoGBAO3D\n" - "MupFbCl64AFEuLCA3wSFRhG82AScYAF1txoTM01V/kqy66j2jJsqMXkVcyBcLBkJ\n" - "5ozW4kIKkh0dYtIjDpsnXKBK/kVcvJN+60hwVnAMFFJPlRCdejkKSsSe9xod+FzU\n" - "DvwWLpLaO3sFtykGHelFbzMDB6FaZQFSfSMsNhIFAoGBAKaNafor+z9s38wpdfPG\n" - "gVc+LxakoGur7l+fDeU9ZCOs5ang1vtxOyA29sVDtIqEzDet2MygJou4NwalIFUu\n" - "ar9+t6D1KWgrsH24YivTgFNbxCLFi2ev8J7SbVFtSf8983UgKnK2CCYFQbUp4Tkk\n" - "26AOGx20svjX7cm8A/o6eZUxAoGBANtedXSnNuOSpmklMc5QKPRvzrWA6kJe0Umn\n" - "hZf+TSA2jlf3eu07BYIITPst6jnaMSms89XQUZOjUyqfuVSu2cQXbiPK7Y2rwaXI\n" - "vWbplybsTjefi6Z31ZQZReDh1pV3P3bOhUDban898RFRtauZJDHdSXrkeb7Ku1Sb\n" - "+i9glEbNAoGAJngXJZ2djyzCxYtSNyUFP8AZfmSy+vmHVCCri1FCoFfj9D1HTw4h\n" - "G0guyN3/Qm51gJVeBTOu/dtoA5JAZi6CRyNuhClLCqV+jGsUr9xyMzA+t/JHVr+0\n" - "XQhx4wqVYQs2852qaJg+wYUi50FtRT6hLyDQmg9ttvS4YIwMTdVzl2Q=\n" - "-----END RSA PRIVATE KEY-----\n"; - -int main(int argc, char **argv) -{ - struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; - struct s2n_stuffer dhparams_in = { 0 }, dhparams_out = { 0 }; - struct s2n_stuffer rsa_key_in = { 0 }, rsa_key_out = { 0 }; - struct s2n_blob b = { 0 }; - char *leaf_cert_pem = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(leaf_cert_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_out, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_out, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_LEAF_CERT, leaf_cert_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) leaf_cert_pem, strlen(leaf_cert_pem) + 1)); - EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); - - EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) private_key_pem, strlen(private_key_pem) + 1)); - EXPECT_SUCCESS(s2n_stuffer_write(&rsa_key_in, &b)); - - EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) dhparams_pem, strlen(dhparams_pem) + 1)); - EXPECT_SUCCESS(s2n_stuffer_write(&dhparams_in, &b)); - - int type = 0; - EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); - EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&rsa_key_in, &rsa_key_out, &type)); - EXPECT_SUCCESS(s2n_stuffer_dhparams_from_pem(&dhparams_in, &dhparams_out)); - EXPECT_EQUAL(type, EVP_PKEY_RSA); - - struct s2n_pkey priv_key = { 0 }; - struct s2n_pkey pub_key = { 0 }; - s2n_pkey_type pkey_type = { 0 }; - - uint32_t available_size = 0; - available_size = s2n_stuffer_data_available(&certificate_out); - EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&pub_key, &pkey_type, &b)); - - /* Test without a type hint */ - int wrong_type = 0; - EXPECT_NOT_EQUAL(wrong_type, EVP_PKEY_RSA); - - available_size = s2n_stuffer_data_available(&rsa_key_out); - EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&rsa_key_out, available_size), available_size)); - EXPECT_OK(s2n_asn1der_to_private_key(&priv_key, &b, wrong_type)); - - EXPECT_SUCCESS(s2n_pkey_match(&pub_key, &priv_key)); - - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - struct s2n_dh_params dh_params = { 0 }; - available_size = s2n_stuffer_data_available(&dhparams_out); - EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&dhparams_out, available_size), available_size)); - EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - - /* Try signing and verification with RSA */ - uint8_t inputpad[] = "Hello world!"; - struct s2n_blob signature = { 0 }; - struct s2n_hash_state tls10_one = { 0 }; - struct s2n_hash_state tls10_two = { 0 }; - struct s2n_hash_state tls12_one = { 0 }; - struct s2n_hash_state tls12_two = { 0 }; - - uint32_t maximum_signature_length = 0; - EXPECT_OK(s2n_pkey_size(&pub_key, &maximum_signature_length)); - EXPECT_SUCCESS(s2n_alloc(&signature, maximum_signature_length)); - - if (s2n_hash_is_available(S2N_HASH_MD5_SHA1)) { - /* TLS 1.0 use of RSA with DHE is not permitted when FIPS mode is set */ - EXPECT_SUCCESS(s2n_hash_new(&tls10_one)); - EXPECT_SUCCESS(s2n_hash_new(&tls10_two)); - - EXPECT_SUCCESS(s2n_hash_init(&tls10_one, S2N_HASH_MD5_SHA1)); - EXPECT_SUCCESS(s2n_hash_init(&tls10_two, S2N_HASH_MD5_SHA1)); - - EXPECT_SUCCESS(s2n_hash_update(&tls10_one, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_hash_update(&tls10_two, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls10_one, &signature)); - EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls10_two, &signature)); - - EXPECT_SUCCESS(s2n_hash_free(&tls10_one)); - EXPECT_SUCCESS(s2n_hash_free(&tls10_two)); - } - - /* TLS 1.2 use of RSA with DHE is permitted for FIPS and non-FIPS */ - EXPECT_SUCCESS(s2n_hash_new(&tls12_one)); - EXPECT_SUCCESS(s2n_hash_new(&tls12_two)); - - EXPECT_SUCCESS(s2n_hash_init(&tls12_one, S2N_HASH_SHA1)); - EXPECT_SUCCESS(s2n_hash_init(&tls12_two, S2N_HASH_SHA1)); - - EXPECT_SUCCESS(s2n_hash_update(&tls12_one, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_hash_update(&tls12_two, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls12_one, &signature)); - EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls12_two, &signature)); - - EXPECT_SUCCESS(s2n_hash_free(&tls12_one)); - EXPECT_SUCCESS(s2n_hash_free(&tls12_two)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - - /* Mismatched public/private key should fail */ - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, (char *) unmatched_private_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - - EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); - EXPECT_SUCCESS(s2n_pkey_free(&priv_key)); - EXPECT_SUCCESS(s2n_pkey_free(&pub_key)); - EXPECT_SUCCESS(s2n_free(&signature)); - EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); - EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); - EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_in)); - EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_out)); - EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_in)); - EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_out)); - free(cert_chain_pem); - free(leaf_cert_pem); - free(private_key_pem); - free(dhparams_pem); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_dhe.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" + +static uint8_t unmatched_private_key[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpQIBAAKCAQEA6ddvtO6DbX5GLrWeXD2jufmR6lvYuzwpIHbDf0RQr2AT50wf\n" + "vV+sMYVa/d4g68tqtihH691tqzKevVc25LXM0a0f0NfnrNibyA1/66CwFQsyy1j2\n" + "U90BdguI/ZxqEV58ee/PzerOoS5d/rgBALzGwwYwyvlzr35KGjfRZ4XOFoToKnkN\n" + "0mR44firCvb7dJ53QRXoPbkYpQQQ3vMWMOtDZoPsvP0dJJ50B7LaFL6nbOyJmZ2T\n" + "evaAov1Nuk5QSrZf2icpuQVXxuu2xLmTNL25OUiV3t7ZjiLtrOZrmrIzv8/sLcWp\n" + "VJcFWFoKizmjvpDfD086a9c81vo4kqgD/RZ9dQIDAQABAoIBAQDBNUzJ3MxgupW4\n" + "YD2BDzjpH2jNj6fKRBHjDd3HmKVl0eeAE2iiKpt2qy2cVl0zFfaMnUmXe3PyoLeB\n" + "z76+R+v8TqPcBZgZOzuzllvcTv9N09vbIh0c+50KcMt2aDdHNJ96jIdRJzIlAM+O\n" + "9290sYU0fDfybRuFo74MXZQ6idbWyNMjXEv2oPrmvMmOHm10sz9BCXWFUpDpDFKD\n" + "8xgw1GZ1QeHITWoQXMI2uJUFFj2hYbRAl6f8cMXVjUipmMNpfJk4CbtEdSMPkjrz\n" + "XgIVrMCorCiaLuVQgSQCHB01n8ETM6R9PYSgMSg3RYYqKq4N0nvREUf4VvKV8JOY\n" + "EGzNgq3BAoGBAPvHPoCKQawLEr4jE5ITMqhZXlBXh8aU7LetjSY1BfnsiH43eg1n\n" + "186lfOX0r1I2KkVZTehZohkEo8xeEpc+XMdux1z0mX2uz1tfmHl838sRaTBs1JZ0\n" + "slkBrASfDdOnLlHCMhRmFEEnA0eelNYCiy/Ak0UX7NpuhZ3bh2jGemixAoGBAO3D\n" + "MupFbCl64AFEuLCA3wSFRhG82AScYAF1txoTM01V/kqy66j2jJsqMXkVcyBcLBkJ\n" + "5ozW4kIKkh0dYtIjDpsnXKBK/kVcvJN+60hwVnAMFFJPlRCdejkKSsSe9xod+FzU\n" + "DvwWLpLaO3sFtykGHelFbzMDB6FaZQFSfSMsNhIFAoGBAKaNafor+z9s38wpdfPG\n" + "gVc+LxakoGur7l+fDeU9ZCOs5ang1vtxOyA29sVDtIqEzDet2MygJou4NwalIFUu\n" + "ar9+t6D1KWgrsH24YivTgFNbxCLFi2ev8J7SbVFtSf8983UgKnK2CCYFQbUp4Tkk\n" + "26AOGx20svjX7cm8A/o6eZUxAoGBANtedXSnNuOSpmklMc5QKPRvzrWA6kJe0Umn\n" + "hZf+TSA2jlf3eu07BYIITPst6jnaMSms89XQUZOjUyqfuVSu2cQXbiPK7Y2rwaXI\n" + "vWbplybsTjefi6Z31ZQZReDh1pV3P3bOhUDban898RFRtauZJDHdSXrkeb7Ku1Sb\n" + "+i9glEbNAoGAJngXJZ2djyzCxYtSNyUFP8AZfmSy+vmHVCCri1FCoFfj9D1HTw4h\n" + "G0guyN3/Qm51gJVeBTOu/dtoA5JAZi6CRyNuhClLCqV+jGsUr9xyMzA+t/JHVr+0\n" + "XQhx4wqVYQs2852qaJg+wYUi50FtRT6hLyDQmg9ttvS4YIwMTdVzl2Q=\n" + "-----END RSA PRIVATE KEY-----\n"; + +int main(int argc, char **argv) +{ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_stuffer dhparams_in = { 0 }, dhparams_out = { 0 }; + struct s2n_stuffer rsa_key_in = { 0 }, rsa_key_out = { 0 }; + struct s2n_blob b = { 0 }; + char *leaf_cert_pem = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_cert_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_out, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_LEAF_CERT, leaf_cert_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) leaf_cert_pem, strlen(leaf_cert_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) private_key_pem, strlen(private_key_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&rsa_key_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) dhparams_pem, strlen(dhparams_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&dhparams_in, &b)); + + int type = 0; + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&rsa_key_in, &rsa_key_out, &type)); + EXPECT_SUCCESS(s2n_stuffer_dhparams_from_pem(&dhparams_in, &dhparams_out)); + EXPECT_EQUAL(type, EVP_PKEY_RSA); + + struct s2n_pkey priv_key = { 0 }; + struct s2n_pkey pub_key = { 0 }; + s2n_pkey_type pkey_type = { 0 }; + + uint32_t available_size = 0; + available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&pub_key, &pkey_type, &b)); + + /* Test without a type hint */ + int wrong_type = 0; + EXPECT_NOT_EQUAL(wrong_type, EVP_PKEY_RSA); + + available_size = s2n_stuffer_data_available(&rsa_key_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&rsa_key_out, available_size), available_size)); + EXPECT_OK(s2n_asn1der_to_private_key(&priv_key, &b, wrong_type)); + + EXPECT_SUCCESS(s2n_pkey_match(&pub_key, &priv_key)); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_dh_params dh_params = { 0 }; + available_size = s2n_stuffer_data_available(&dhparams_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&dhparams_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + /* Try signing and verification with RSA */ + uint8_t inputpad[] = "Hello world!"; + struct s2n_blob signature = { 0 }; + struct s2n_hash_state tls10_one = { 0 }; + struct s2n_hash_state tls10_two = { 0 }; + struct s2n_hash_state tls12_one = { 0 }; + struct s2n_hash_state tls12_two = { 0 }; + + uint32_t maximum_signature_length = 0; + EXPECT_OK(s2n_pkey_size(&pub_key, &maximum_signature_length)); + EXPECT_SUCCESS(s2n_alloc(&signature, maximum_signature_length)); + + if (s2n_hash_is_available(S2N_HASH_MD5_SHA1)) { + /* TLS 1.0 use of RSA with DHE is not permitted when FIPS mode is set */ + EXPECT_SUCCESS(s2n_hash_new(&tls10_one)); + EXPECT_SUCCESS(s2n_hash_new(&tls10_two)); + + EXPECT_SUCCESS(s2n_hash_init(&tls10_one, S2N_HASH_MD5_SHA1)); + EXPECT_SUCCESS(s2n_hash_init(&tls10_two, S2N_HASH_MD5_SHA1)); + + EXPECT_SUCCESS(s2n_hash_update(&tls10_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&tls10_two, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls10_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls10_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_free(&tls10_one)); + EXPECT_SUCCESS(s2n_hash_free(&tls10_two)); + } + + /* TLS 1.2 use of RSA with DHE is permitted for FIPS and non-FIPS */ + EXPECT_SUCCESS(s2n_hash_new(&tls12_one)); + EXPECT_SUCCESS(s2n_hash_new(&tls12_two)); + + EXPECT_SUCCESS(s2n_hash_init(&tls12_one, S2N_HASH_SHA1)); + EXPECT_SUCCESS(s2n_hash_init(&tls12_two, S2N_HASH_SHA1)); + + EXPECT_SUCCESS(s2n_hash_update(&tls12_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&tls12_two, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls12_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls12_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_free(&tls12_one)); + EXPECT_SUCCESS(s2n_hash_free(&tls12_two)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Mismatched public/private key should fail */ + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, (char *) unmatched_private_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); + EXPECT_SUCCESS(s2n_pkey_free(&priv_key)); + EXPECT_SUCCESS(s2n_pkey_free(&pub_key)); + EXPECT_SUCCESS(s2n_free(&signature)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_out)); + free(cert_chain_pem); + free(leaf_cert_pem); + free(private_key_pem); + free(dhparams_pem); + + END_TEST(); +} diff --git a/tests/unit/s2n_pem_test.c b/tests/unit/s2n_pem_test.c index e1589ca3604..a05d5e31293 100644 --- a/tests/unit/s2n_pem_test.c +++ b/tests/unit/s2n_pem_test.c @@ -1,123 +1,124 @@ -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -/* The ECDSA private key is missing the "publicKey" field, which is optional. - * The missing field makes the cert type difficult to detect via ASN1 parsing */ -#define S2N_MISSING_ECDSA_PUB_CERT_KEY "../pems/missing_public_key_ecdsa_key.pem" -#define S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN "../pems/missing_public_key_ecdsa_cert.pem" - -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 }, - { S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN, S2N_MISSING_ECDSA_PUB_CERT_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 }, -}; - -const struct { - const char *path; - uint16_t length; -} valid_cert_chains[] = { - { .path = S2N_TEST_TRUST_STORE, .length = 179 }, -}; - -int main(int argc, char **argv) -{ - struct s2n_config *config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - for (size_t i = 0; i < s2n_array_len(valid_pem_pairs); i++) { - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - } - - for (size_t i = 0; i < s2n_array_len(invalid_pem_pairs); i++) { - EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - - char large_cert_chain_pem[500000] = { 0 }; - for (size_t i = 0; i < s2n_array_len(valid_cert_chains); i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(chain); - EXPECT_SUCCESS(s2n_read_test_pem(valid_cert_chains[i].path, - large_cert_chain_pem, sizeof(large_cert_chain_pem))); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(chain, - (uint8_t *) large_cert_chain_pem, strlen(large_cert_chain_pem))); - EXPECT_SUCCESS(s2n_cert_chain_get_length(chain, &length)); - EXPECT_EQUAL(length, valid_cert_chains[i].length); - - DEFER_CLEANUP(struct s2n_config *test_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(test_config); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(test_config, large_cert_chain_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(test_config, chain)); - } - - free(cert_chain_pem); - free(private_key_pem); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +/* The ECDSA private key is missing the "publicKey" field, which is optional. + * The missing field makes the cert type difficult to detect via ASN1 parsing */ +#define S2N_MISSING_ECDSA_PUB_CERT_KEY "../pems/missing_public_key_ecdsa_key.pem" +#define S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN "../pems/missing_public_key_ecdsa_cert.pem" + +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 }, + { S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN, S2N_MISSING_ECDSA_PUB_CERT_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 }, +}; + +const struct { + const char *path; + uint16_t length; +} valid_cert_chains[] = { + { .path = S2N_TEST_TRUST_STORE, .length = 179 }, +}; + +int main(int argc, char **argv) +{ + struct s2n_config *config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + for (size_t i = 0; i < s2n_array_len(valid_pem_pairs); i++) { + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + for (size_t i = 0; i < s2n_array_len(invalid_pem_pairs); i++) { + EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + char large_cert_chain_pem[500000] = { 0 }; + for (size_t i = 0; i < s2n_array_len(valid_cert_chains); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(chain); + EXPECT_SUCCESS(s2n_read_test_pem(valid_cert_chains[i].path, + large_cert_chain_pem, sizeof(large_cert_chain_pem))); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(chain, + (uint8_t *) large_cert_chain_pem, strlen(large_cert_chain_pem))); + EXPECT_SUCCESS(s2n_cert_chain_get_length(chain, &length)); + EXPECT_EQUAL(length, valid_cert_chains[i].length); + + DEFER_CLEANUP(struct s2n_config *test_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(test_config); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(test_config, large_cert_chain_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(test_config, chain)); + } + + free(cert_chain_pem); + free(private_key_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_policy_defaults_test.c b/tests/unit/s2n_policy_defaults_test.c index 0fd65d53d9c..6dbdc4a86fd 100644 --- a/tests/unit/s2n_policy_defaults_test.c +++ b/tests/unit/s2n_policy_defaults_test.c @@ -1,135 +1,136 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/policy/s2n_policy_defaults.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -S2N_RESULT s2n_test_strict_compatibility(uint64_t start, uint64_t end, - struct s2n_test_cert_chain_list *cert_chains) -{ - for (size_t client_i = start; client_i <= end; client_i++) { - for (size_t server_i = start; server_i <= end; server_i++) { - for (size_t cert_i = 0; cert_i < cert_chains->count; cert_i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, - cert_chains->chains[cert_i].chain)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_security_policy(client, - s2n_security_policy_get(S2N_POLICY_STRICT, client_i))); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_security_policy(server, - s2n_security_policy_get(S2N_POLICY_STRICT, server_i))); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* For now, consider a HRR a potential breaking change. - * We can revisit later if necessary. - */ - EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server)); - } - } - } - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* Test: s2n_security_policy_get */ - { - /* Policies exist only for expected policy + version combinations */ - for (size_t policy_i = 0; policy_i < UINT8_MAX; policy_i++) { - bool first_null_found = false; - uint64_t versions_found = 0; - - for (size_t version_i = 0; version_i < UINT8_MAX; version_i++) { - const struct s2n_security_policy *policy = s2n_security_policy_get(policy_i, version_i); - - /* Invalid policy or version values should be NULL */ - if (version_i == 0 || policy_i == 0) { - /* Versioning starts at 1 instead of 0. - * We may want to later assign 0 a special meaning, like "none". - */ - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - continue; - } else if (policy_i >= S2N_MAX_DEFAULT_POLICIES) { - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - continue; - } else if (version_i >= S2N_MAX_POLICY_VERSIONS) { - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - continue; - } - - if (policy) { - /* The policy exists because the version is valid. - * Versions should be contiguous. No previous gaps. - */ - EXPECT_FALSE(first_null_found); - versions_found++; - } else if (first_null_found) { - /* If we've already found the first invalid version, all later - * versions should be invalid too. - */ - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - } else { - /* We have found the first invalid version */ - first_null_found = true; - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - } - } - - if (policy_i > 0 && policy_i < S2N_MAX_DEFAULT_POLICIES) { - /* Each valid policy should have at least one valid version */ - EXPECT_TRUE(versions_found > 0); - /* If we did't find the last valid version, we're not testing enough */ - EXPECT_TRUE(first_null_found); - } - }; - - /* Check known good values. - * Note: don't add EVERY new version to this test. We should only test - * an interesting selection of inputs. - */ - { - EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_STRICT, S2N_STRICT_2025_08_20)); - EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_COMPATIBLE, S2N_COMPAT_2025_08_20)); - } - }; - - /* Test: S2N_POLICY_STRICT backwards compatibility promises enforced */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_test_cert_chain_list cert_chains = { 0 }, s2n_test_cert_chains_free); - EXPECT_OK(s2n_test_cert_chains_init(&cert_chains)); - - EXPECT_OK(s2n_test_strict_compatibility(1, S2N_STRICT_LATEST_1, &cert_chains)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/policy/s2n_policy_defaults.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_test_strict_compatibility(uint64_t start, uint64_t end, + struct s2n_test_cert_chain_list *cert_chains) +{ + for (size_t client_i = start; client_i <= end; client_i++) { + for (size_t server_i = start; server_i <= end; server_i++) { + for (size_t cert_i = 0; cert_i < cert_chains->count; cert_i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, + cert_chains->chains[cert_i].chain)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_security_policy(client, + s2n_security_policy_get(S2N_POLICY_STRICT, client_i))); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_security_policy(server, + s2n_security_policy_get(S2N_POLICY_STRICT, server_i))); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* For now, consider a HRR a potential breaking change. + * We can revisit later if necessary. + */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server)); + } + } + } + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: s2n_security_policy_get */ + { + /* Policies exist only for expected policy + version combinations */ + for (size_t policy_i = 0; policy_i < UINT8_MAX; policy_i++) { + bool first_null_found = false; + uint64_t versions_found = 0; + + for (size_t version_i = 0; version_i < UINT8_MAX; version_i++) { + const struct s2n_security_policy *policy = s2n_security_policy_get(policy_i, version_i); + + /* Invalid policy or version values should be NULL */ + if (version_i == 0 || policy_i == 0) { + /* Versioning starts at 1 instead of 0. + * We may want to later assign 0 a special meaning, like "none". + */ + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + continue; + } else if (policy_i >= S2N_MAX_DEFAULT_POLICIES) { + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + continue; + } else if (version_i >= S2N_MAX_POLICY_VERSIONS) { + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + continue; + } + + if (policy) { + /* The policy exists because the version is valid. + * Versions should be contiguous. No previous gaps. + */ + EXPECT_FALSE(first_null_found); + versions_found++; + } else if (first_null_found) { + /* If we've already found the first invalid version, all later + * versions should be invalid too. + */ + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + } else { + /* We have found the first invalid version */ + first_null_found = true; + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + } + } + + if (policy_i > 0 && policy_i < S2N_MAX_DEFAULT_POLICIES) { + /* Each valid policy should have at least one valid version */ + EXPECT_TRUE(versions_found > 0); + /* If we did't find the last valid version, we're not testing enough */ + EXPECT_TRUE(first_null_found); + } + }; + + /* Check known good values. + * Note: don't add EVERY new version to this test. We should only test + * an interesting selection of inputs. + */ + { + EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_STRICT, S2N_STRICT_2025_08_20)); + EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_COMPATIBLE, S2N_COMPAT_2025_08_20)); + } + }; + + /* Test: S2N_POLICY_STRICT backwards compatibility promises enforced */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_test_cert_chain_list cert_chains = { 0 }, s2n_test_cert_chains_free); + EXPECT_OK(s2n_test_cert_chains_init(&cert_chains)); + + EXPECT_OK(s2n_test_strict_compatibility(1, S2N_STRICT_LATEST_1, &cert_chains)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_post_handshake_recv_test.c b/tests/unit/s2n_post_handshake_recv_test.c index 1ea2a50ef67..4113a82891a 100644 --- a/tests/unit/s2n_post_handshake_recv_test.c +++ b/tests/unit/s2n_post_handshake_recv_test.c @@ -1,515 +1,516 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/unstable/renegotiate.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_mem_testlib.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_key_update.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_safety.h" - -#define S2N_TEST_MESSAGE_COUNT 5 - -int s2n_key_update_write(struct s2n_blob *out); - -size_t tickets_count = 0; -static int s2n_ticket_count_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - tickets_count++; - return S2N_SUCCESS; -} - -size_t hello_request_count = 0; -static int s2n_hello_request_cb(struct s2n_connection *conn, void *ctx, s2n_renegotiate_response *response) -{ - hello_request_count++; - *response = S2N_RENEGOTIATE_IGNORE; - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_test_send_records(struct s2n_connection *conn, struct s2n_stuffer messages, uint32_t fragment_size) -{ - conn->max_outgoing_fragment_length = fragment_size; - - DEFER_CLEANUP(struct s2n_blob record_data = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_alloc(&record_data, fragment_size)); - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint32_t remaining = 0; - while ((remaining = s2n_stuffer_data_available(&messages)) > 0) { - record_data.size = S2N_MIN(record_data.size, remaining); - RESULT_GUARD_POSIX(s2n_stuffer_read(&messages, &record_data)); - RESULT_GUARD(s2n_record_write(conn, TLS_HANDSHAKE, &record_data)); - RESULT_GUARD_POSIX(s2n_flush(conn, &blocked)); - }; - - return S2N_RESULT_OK; -} - -/* - * Verify that the receiver can receive a byte sent by the sender. - * In the process, we also verify that the receiver can receive all previous - * data sent by the sender, since TCP / TLS messages have a guaranteed order. - */ -static S2N_RESULT s2n_test_basic_recv(struct s2n_connection *sender, struct s2n_connection *receiver) -{ - uint8_t app_data[1] = { 0 }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); - RESULT_GUARD_POSIX(send_ret); - RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); - - /* Reset all counters */ - RESULT_GUARD(s2n_mem_test_wipe_callbacks()); - tickets_count = 0; - hello_request_count = 0; - - int recv_ret = s2n_recv(receiver, app_data, sizeof(app_data), &blocked); - RESULT_GUARD_POSIX(recv_ret); - RESULT_ENSURE_EQ(recv_ret, sizeof(app_data)); - - return S2N_RESULT_OK; -} - -/* Like s2n_test_basic_recv, - * but we make only one byte of data available at a time. - * This forces us to call s2n_recv repeatedly and verifies that s2n_recv - * can resume across s2n_recv calls while handling fragmented post-handshake messages. - */ -static S2N_RESULT s2n_test_blocking_recv(struct s2n_connection *sender, struct s2n_connection *receiver, - struct s2n_test_io_stuffer_pair *io_pair) -{ - uint8_t app_data[1] = { 0 }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); - RESULT_GUARD_POSIX(send_ret); - RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); - - /* Reset all counters */ - RESULT_GUARD(s2n_mem_test_wipe_callbacks()); - tickets_count = 0; - hello_request_count = 0; - - /* Modify the stuffer's write_cursor to make only one byte - * of the socket / input data available at a time. - */ - struct s2n_stuffer *in = &io_pair->client_in; - if (receiver->mode == S2N_SERVER) { - in = &io_pair->server_in; - } - uint32_t *write_cursor = &in->write_cursor; - uint32_t *read_cursor = &in->read_cursor; - RESULT_ENSURE_GT(write_cursor, read_cursor); - uint32_t saved_write_cursor = *write_cursor; - RESULT_ENSURE_GT(saved_write_cursor, 0); - *write_cursor = *read_cursor + 1; - - while (s2n_recv(receiver, app_data, sizeof(app_data), &blocked) < 0) { - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - (*write_cursor)++; - RESULT_ENSURE_LTE(*write_cursor, saved_write_cursor); - } - RESULT_ENSURE_EQ(*write_cursor, saved_write_cursor); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_init_sender_and_receiver(struct s2n_config *config, - struct s2n_connection *sender, struct s2n_connection *receiver, - struct s2n_test_io_stuffer_pair *io_pair) -{ - RESULT_GUARD_POSIX(s2n_connection_set_config(sender, config)); - RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(sender, S2N_TLS13)); - RESULT_GUARD(s2n_connection_set_secrets(sender)); - RESULT_GUARD_POSIX(s2n_connection_set_blinding(sender, S2N_SELF_SERVICE_BLINDING)); - - RESULT_GUARD_POSIX(s2n_connection_set_config(receiver, config)); - RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(receiver, S2N_TLS13)); - RESULT_GUARD(s2n_connection_set_secrets(receiver)); - RESULT_GUARD_POSIX(s2n_connection_set_blinding(receiver, S2N_SELF_SERVICE_BLINDING)); - - RESULT_GUARD(s2n_io_stuffer_pair_init(io_pair)); - if (sender->mode == S2N_SERVER) { - RESULT_GUARD(s2n_connections_set_io_stuffer_pair(receiver, sender, io_pair)); - } else { - RESULT_GUARD(s2n_connections_set_io_stuffer_pair(sender, receiver, io_pair)); - } - - /* Send and receive to initialize io buffers */ - EXPECT_OK(s2n_test_basic_recv(sender, receiver)); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - const uint8_t unknown_message_type = UINT8_MAX; - const uint32_t test_large_message_size = 3001; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_ticket_count_cb, NULL)); - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_hello_request_cb, NULL)); - - /* Some tests require sending and receiving tickets. - * Setup the config to handle tickets, but don't send any by default. - */ - uint8_t ticket_key_name[16] = "key name"; - uint8_t ticket_key[] = "key data"; - uint64_t current_time = 0; - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, sizeof(ticket_key_name), - ticket_key, sizeof(ticket_key), current_time / ONE_SEC_IN_NANOS)); - config->initial_tickets_to_send = 0; - - const uint32_t fragment_sizes[] = { - 1, - 2, - S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE, - TLS_HANDSHAKE_HEADER_LENGTH, - TLS_HANDSHAKE_HEADER_LENGTH + 1, - S2N_DEFAULT_FRAGMENT_LENGTH, - S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, - }; - const uint8_t modes[] = { S2N_CLIENT, S2N_SERVER }; - - /* Test: client and server receive small post-handshake messages (KeyUpdates) */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Write KeyUpdate message */ - struct s2n_stuffer message = { 0 }; - DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&message_blob, S2N_KEY_UPDATE_MESSAGE_SIZE)); - for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { - EXPECT_SUCCESS(s2n_key_update_write(&message_blob)); - EXPECT_SUCCESS(s2n_stuffer_init(&message, &message_blob)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, S2N_KEY_UPDATE_MESSAGE_SIZE)); - - /* The TLS1.3 RFC says "Handshake messages MUST NOT span key changes". - * Because KeyUpdate messages trigger key changes, we cannot include multiple in one record. - * We must send individual KeyUpdate messages. - */ - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - - /* Update the traffic keys for the next records */ - EXPECT_SUCCESS(s2n_update_application_traffic_keys(sender, sender->mode, SENDING)); - } - - /* - * We have no mechanism to count KeyUpdates, but we can assume they are processed - * if we successfully decrypt all records. If they were not processed, - * then we would try to use the wrong key to decrypt the next record. - */ - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_OK(s2n_test_basic_recv(sender, receiver)); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - } - - /* Test: client receives large post-handshake messages (NewSessionTickets) - * - * There is no server version of this test because there are no large post-handshake messages - * valid for the server to accept. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); - - /* Write NewSessionTicket message */ - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { - server->tickets_to_send++; - EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); - } - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_basic_recv(server, client)); - EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); - EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); - } - - /* Test: client receives large post-handshake messages of different sizes (NewSessionTickets) - * - * There is no server version of this test because there are no large post-handshake messages - * valid for the server to accept. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); - server->server_max_early_data_size = 10; - - size_t total_size = 0; - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - for (size_t i = 0; i < 3; i++) { - /* Write a basic NewSessionTicket */ - server->server_max_early_data_size_overridden = false; - server->tickets_to_send++; - EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); - size_t min_length = s2n_stuffer_data_available(&messages) - total_size; - total_size += min_length; - - /* Write a NewSesionTicket with early data enabled - * so that the early_data_indication extension is included - * and the message is therefore longer. - */ - server->server_max_early_data_size_overridden = true; - server->tickets_to_send++; - EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); - size_t max_length = s2n_stuffer_data_available(&messages) - total_size; - EXPECT_TRUE(max_length > min_length); - total_size += max_length; - } - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_basic_recv(server, client)); - EXPECT_EQUAL(tickets_count, server->tickets_to_send); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); - EXPECT_EQUAL(tickets_count, server->tickets_to_send); - } - - /* Test: server rejects known, invalid post-handshake messages (NewSessionTickets) - * - * There is no client version of this test because the client accepts all supported - * post-handshake messages. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); - - /* Send NewSessionTicket message */ - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - client->tickets_to_send = 1; - EXPECT_OK(s2n_tls13_server_nst_write(client, &messages)); - EXPECT_OK(s2n_test_send_records(client, messages, fragment_size)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); - EXPECT_EQUAL(tickets_count, 0); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - - /* Test: server rejects fragmented post-handshake message (KeyUpdate) with an invalid size - * - * This response is unique to the server because we want to prevent a malicious - * client from forcing the server to allocate large amounts of memory. - * - * While we could extend the same validation to the client, the client accepts - * a variable-sized message (NewSessionTicket) so can't really be protected. - */ - { - /* This test is only interesting if the message is fragmented */ - uint32_t fragment_size = 2; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); - - /* Write large KeyUpdate messages */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); - EXPECT_OK(s2n_test_send_records(client, message, fragment_size)); - EXPECT_SUCCESS(s2n_update_application_traffic_keys(client, client->mode, SENDING)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); - - /* No post-handshake message should trigger the server to allocate memory */ - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - }; - - /* Test: client receives empty post-handshake messages (HelloRequests) - * - * There is no server version of this test because there are no empty post-handshake messages - * valid for the server to accept. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); - - /* HelloRequests are ignored if secure_renegotiation isn't set */ - client->secure_renegotiation = true; - - /* Write HelloRequest messages */ - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&messages, TLS_HELLO_REQUEST)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&messages, 0)); - } - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_basic_recv(server, client)); - EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); - EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - - /* Test: client and server reject known, invalid messages (ClientHellos) */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Send fake ClientHello messages */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, TLS_HANDSHAKE_HEADER_LENGTH)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); - - /* No post-handshake message should trigger the server to allocate memory */ - if (mode == S2N_SERVER) { - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - } - } - - /* Test: client and server reject unknown messages */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Send unknown message */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, unknown_message_type)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); - - /* No post-handshake message should trigger the server to allocate memory */ - if (mode == S2N_SERVER) { - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - } - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *= type=test - *# - Handshake messages MUST NOT be interleaved with other record - *# types. That is, if a handshake message is split over two or more - *# records, there MUST NOT be any other records between them. - */ - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - /* This test is only interesting if the message is fragmented */ - uint32_t fragment_size = 2; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Write a partial message */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, S2N_KEY_UPDATE_LENGTH)); - /* Don't write the actual message body -- we want the message to be incomplete */ - - /* Verify we can't receive the records: s2n_test_send_records does not send - * the complete handshake message, so we receive the application data sent by - * s2n_test_basic_recv in the middle of the handshake message. - */ - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/unstable/renegotiate.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_mem_testlib.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_key_update.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_safety.h" + +#define S2N_TEST_MESSAGE_COUNT 5 + +int s2n_key_update_write(struct s2n_blob *out); + +size_t tickets_count = 0; +static int s2n_ticket_count_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + tickets_count++; + return S2N_SUCCESS; +} + +size_t hello_request_count = 0; +static int s2n_hello_request_cb(struct s2n_connection *conn, void *ctx, s2n_renegotiate_response *response) +{ + hello_request_count++; + *response = S2N_RENEGOTIATE_IGNORE; + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_test_send_records(struct s2n_connection *conn, struct s2n_stuffer messages, uint32_t fragment_size) +{ + conn->max_outgoing_fragment_length = fragment_size; + + DEFER_CLEANUP(struct s2n_blob record_data = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&record_data, fragment_size)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint32_t remaining = 0; + while ((remaining = s2n_stuffer_data_available(&messages)) > 0) { + record_data.size = S2N_MIN(record_data.size, remaining); + RESULT_GUARD_POSIX(s2n_stuffer_read(&messages, &record_data)); + RESULT_GUARD(s2n_record_write(conn, TLS_HANDSHAKE, &record_data)); + RESULT_GUARD_POSIX(s2n_flush(conn, &blocked)); + }; + + return S2N_RESULT_OK; +} + +/* + * Verify that the receiver can receive a byte sent by the sender. + * In the process, we also verify that the receiver can receive all previous + * data sent by the sender, since TCP / TLS messages have a guaranteed order. + */ +static S2N_RESULT s2n_test_basic_recv(struct s2n_connection *sender, struct s2n_connection *receiver) +{ + uint8_t app_data[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(send_ret); + RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); + + /* Reset all counters */ + RESULT_GUARD(s2n_mem_test_wipe_callbacks()); + tickets_count = 0; + hello_request_count = 0; + + int recv_ret = s2n_recv(receiver, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(recv_ret); + RESULT_ENSURE_EQ(recv_ret, sizeof(app_data)); + + return S2N_RESULT_OK; +} + +/* Like s2n_test_basic_recv, + * but we make only one byte of data available at a time. + * This forces us to call s2n_recv repeatedly and verifies that s2n_recv + * can resume across s2n_recv calls while handling fragmented post-handshake messages. + */ +static S2N_RESULT s2n_test_blocking_recv(struct s2n_connection *sender, struct s2n_connection *receiver, + struct s2n_test_io_stuffer_pair *io_pair) +{ + uint8_t app_data[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(send_ret); + RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); + + /* Reset all counters */ + RESULT_GUARD(s2n_mem_test_wipe_callbacks()); + tickets_count = 0; + hello_request_count = 0; + + /* Modify the stuffer's write_cursor to make only one byte + * of the socket / input data available at a time. + */ + struct s2n_stuffer *in = &io_pair->client_in; + if (receiver->mode == S2N_SERVER) { + in = &io_pair->server_in; + } + uint32_t *write_cursor = &in->write_cursor; + uint32_t *read_cursor = &in->read_cursor; + RESULT_ENSURE_GT(write_cursor, read_cursor); + uint32_t saved_write_cursor = *write_cursor; + RESULT_ENSURE_GT(saved_write_cursor, 0); + *write_cursor = *read_cursor + 1; + + while (s2n_recv(receiver, app_data, sizeof(app_data), &blocked) < 0) { + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + (*write_cursor)++; + RESULT_ENSURE_LTE(*write_cursor, saved_write_cursor); + } + RESULT_ENSURE_EQ(*write_cursor, saved_write_cursor); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_init_sender_and_receiver(struct s2n_config *config, + struct s2n_connection *sender, struct s2n_connection *receiver, + struct s2n_test_io_stuffer_pair *io_pair) +{ + RESULT_GUARD_POSIX(s2n_connection_set_config(sender, config)); + RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(sender, S2N_TLS13)); + RESULT_GUARD(s2n_connection_set_secrets(sender)); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(sender, S2N_SELF_SERVICE_BLINDING)); + + RESULT_GUARD_POSIX(s2n_connection_set_config(receiver, config)); + RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(receiver, S2N_TLS13)); + RESULT_GUARD(s2n_connection_set_secrets(receiver)); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(receiver, S2N_SELF_SERVICE_BLINDING)); + + RESULT_GUARD(s2n_io_stuffer_pair_init(io_pair)); + if (sender->mode == S2N_SERVER) { + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(receiver, sender, io_pair)); + } else { + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(sender, receiver, io_pair)); + } + + /* Send and receive to initialize io buffers */ + EXPECT_OK(s2n_test_basic_recv(sender, receiver)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t unknown_message_type = UINT8_MAX; + const uint32_t test_large_message_size = 3001; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_ticket_count_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_hello_request_cb, NULL)); + + /* Some tests require sending and receiving tickets. + * Setup the config to handle tickets, but don't send any by default. + */ + uint8_t ticket_key_name[16] = "key name"; + uint8_t ticket_key[] = "key data"; + uint64_t current_time = 0; + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, sizeof(ticket_key_name), + ticket_key, sizeof(ticket_key), current_time / ONE_SEC_IN_NANOS)); + config->initial_tickets_to_send = 0; + + const uint32_t fragment_sizes[] = { + 1, + 2, + S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE, + TLS_HANDSHAKE_HEADER_LENGTH, + TLS_HANDSHAKE_HEADER_LENGTH + 1, + S2N_DEFAULT_FRAGMENT_LENGTH, + S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, + }; + const uint8_t modes[] = { S2N_CLIENT, S2N_SERVER }; + + /* Test: client and server receive small post-handshake messages (KeyUpdates) */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Write KeyUpdate message */ + struct s2n_stuffer message = { 0 }; + DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&message_blob, S2N_KEY_UPDATE_MESSAGE_SIZE)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + EXPECT_SUCCESS(s2n_key_update_write(&message_blob)); + EXPECT_SUCCESS(s2n_stuffer_init(&message, &message_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, S2N_KEY_UPDATE_MESSAGE_SIZE)); + + /* The TLS1.3 RFC says "Handshake messages MUST NOT span key changes". + * Because KeyUpdate messages trigger key changes, we cannot include multiple in one record. + * We must send individual KeyUpdate messages. + */ + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + /* Update the traffic keys for the next records */ + EXPECT_SUCCESS(s2n_update_application_traffic_keys(sender, sender->mode, SENDING)); + } + + /* + * We have no mechanism to count KeyUpdates, but we can assume they are processed + * if we successfully decrypt all records. If they were not processed, + * then we would try to use the wrong key to decrypt the next record. + */ + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_OK(s2n_test_basic_recv(sender, receiver)); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + + /* Test: client receives large post-handshake messages (NewSessionTickets) + * + * There is no server version of this test because there are no large post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + + /* Write NewSessionTicket message */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + } + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); + } + + /* Test: client receives large post-handshake messages of different sizes (NewSessionTickets) + * + * There is no server version of this test because there are no large post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + server->server_max_early_data_size = 10; + + size_t total_size = 0; + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < 3; i++) { + /* Write a basic NewSessionTicket */ + server->server_max_early_data_size_overridden = false; + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + size_t min_length = s2n_stuffer_data_available(&messages) - total_size; + total_size += min_length; + + /* Write a NewSesionTicket with early data enabled + * so that the early_data_indication extension is included + * and the message is therefore longer. + */ + server->server_max_early_data_size_overridden = true; + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + size_t max_length = s2n_stuffer_data_available(&messages) - total_size; + EXPECT_TRUE(max_length > min_length); + total_size += max_length; + } + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(tickets_count, server->tickets_to_send); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(tickets_count, server->tickets_to_send); + } + + /* Test: server rejects known, invalid post-handshake messages (NewSessionTickets) + * + * There is no client version of this test because the client accepts all supported + * post-handshake messages. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); + + /* Send NewSessionTicket message */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + client->tickets_to_send = 1; + EXPECT_OK(s2n_tls13_server_nst_write(client, &messages)); + EXPECT_OK(s2n_test_send_records(client, messages, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(tickets_count, 0); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + /* Test: server rejects fragmented post-handshake message (KeyUpdate) with an invalid size + * + * This response is unique to the server because we want to prevent a malicious + * client from forcing the server to allocate large amounts of memory. + * + * While we could extend the same validation to the client, the client accepts + * a variable-sized message (NewSessionTicket) so can't really be protected. + */ + { + /* This test is only interesting if the message is fragmented */ + uint32_t fragment_size = 2; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); + + /* Write large KeyUpdate messages */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(client, message, fragment_size)); + EXPECT_SUCCESS(s2n_update_application_traffic_keys(client, client->mode, SENDING)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + }; + + /* Test: client receives empty post-handshake messages (HelloRequests) + * + * There is no server version of this test because there are no empty post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + + /* HelloRequests are ignored if secure_renegotiation isn't set */ + client->secure_renegotiation = true; + + /* Write HelloRequest messages */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&messages, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&messages, 0)); + } + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + /* Test: client and server reject known, invalid messages (ClientHellos) */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Send fake ClientHello messages */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, TLS_HANDSHAKE_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + if (mode == S2N_SERVER) { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + } + + /* Test: client and server reject unknown messages */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Send unknown message */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, unknown_message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + if (mode == S2N_SERVER) { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *= type=test + *# - Handshake messages MUST NOT be interleaved with other record + *# types. That is, if a handshake message is split over two or more + *# records, there MUST NOT be any other records between them. + */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + /* This test is only interesting if the message is fragmented */ + uint32_t fragment_size = 2; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Write a partial message */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, S2N_KEY_UPDATE_LENGTH)); + /* Don't write the actual message body -- we want the message to be incomplete */ + + /* Verify we can't receive the records: s2n_test_send_records does not send + * the complete handshake message, so we receive the application data sent by + * s2n_test_basic_recv in the middle of the handshake message. + */ + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_prf_key_material_test.c b/tests/unit/s2n_prf_key_material_test.c index 2edbf61d431..dc95a83d041 100644 --- a/tests/unit/s2n_prf_key_material_test.c +++ b/tests/unit/s2n_prf_key_material_test.c @@ -1,235 +1,236 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_prf.h" -#include "utils/s2n_random.h" - -static S2N_RESULT s2n_test_validate_key_material(struct s2n_key_material *key_material, - struct s2n_blob *test_data_blob, uint8_t mac_size, uint8_t key_size, uint8_t iv_size) -{ - /* confirm that the data is copied to key_material */ - RESULT_ENSURE_EQ(test_data_blob->size, sizeof(key_material->key_block)); - RESULT_ENSURE_EQ(memcmp(test_data_blob->data, key_material->key_block, test_data_blob->size), 0); - - struct s2n_stuffer test_data_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&test_data_stuffer, test_data_blob)); - - /* test that its possible to access data from s2n_key_material */ - /* client MAC */ - uint8_t *test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_mac.data, mac_size), 0); - /* server MAC */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_mac.data, mac_size), 0); - - /* client KEY */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_key.data, key_size), 0); - /* server KEY */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_key.data, key_size), 0); - - /* client IV */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_iv.data, iv_size), 0); - /* server IV */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_iv.data, iv_size), 0); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* prepare test data */ - uint8_t test_data[S2N_MAX_KEY_BLOCK_LEN] = { 0 }; - struct s2n_blob test_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); - EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); - - /* fuzz s2n_key_material_init with different mac, key, iv sizes */ - { - for (uint8_t mac_size = 0; mac_size < SHA512_DIGEST_LENGTH; mac_size++) { - for (uint8_t key_size = 0; key_size < S2N_TLS_AES_256_GCM_KEY_LEN; key_size++) { - for (uint8_t iv_size = 0; iv_size < S2N_TLS_MAX_IV_LEN; iv_size++) { - if ((mac_size * 2 + key_size * 2 + iv_size * 2 > S2N_MAX_KEY_BLOCK_LEN)) { - continue; - } - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - /* test varying size of mac, key and iv */ - conn->actual_protocol_version = S2N_TLS10; - struct s2n_cipher temp_cipher = { - .type = S2N_COMPOSITE, - .key_material_size = key_size, - .io.comp = { - /* interpreted as iv size for composite ciphers.. which makes - * it easy for testing */ - .block_size = iv_size, - .mac_key_size = mac_size, - }, - }; - struct s2n_record_algorithm temp_record_alg = { - .cipher = &temp_cipher, - }; - struct s2n_cipher_suite temp_cipher_suite = { - .record_alg = &temp_record_alg, - }; - conn->secure->cipher_suite = &temp_cipher_suite; - - /* init s2n_key_material */ - struct s2n_key_material key_material = { 0 }; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - - /* assert that sizes match */ - EXPECT_EQUAL(key_material.client_mac.size, mac_size); - EXPECT_EQUAL(key_material.client_key.size, key_size); - EXPECT_EQUAL(key_material.client_iv.size, iv_size); - EXPECT_EQUAL(key_material.server_mac.size, mac_size); - EXPECT_EQUAL(key_material.server_key.size, key_size); - EXPECT_EQUAL(key_material.server_iv.size, iv_size); - - /* copy data into key_material and validate accessing key_material is sound */ - EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); - POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); - EXPECT_OK(s2n_test_validate_key_material(&key_material, &test_data_blob, mac_size, key_size, iv_size)); - } - } - } - } - - /* confirm that s2n_key_material can correctly handle all cipher suites */ - { - for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; - if (!cipher_suite->available) { - continue; - } - conn->secure->cipher_suite = cipher_suite; - - /* init s2n_key_material */ - struct s2n_key_material key_material = { 0 }; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - - /* copy data into key_material and validate accessing key_material is sound */ - EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); - POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); - /* test that its possible to access mac, key and iv correctly from s2n_key_material */ - EXPECT_OK(s2n_test_validate_key_material( - &key_material, - &test_data_blob, - key_material.client_mac.size, - key_material.client_key.size, - key_material.client_iv.size)); - } - } - - /* AEAD cipher - * IV size should be the same regardless of protocol version - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - EXPECT_EQUAL(conn->secure->cipher_suite->record_alg->cipher->type, S2N_AEAD); - - struct s2n_key_material key_material = { 0 }; - - uint32_t mac = 0; - uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; - uint32_t iv = S2N_TLS13_FIXED_IV_LEN; - - /* initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS10; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - - /* re-initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS11; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - /* assert same IV size regardless of protocol version */ - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - } - - /* NON AEAD cipher - * IV size depends on protocol version - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha256; - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - /* assert that the cipher chosen is non AEAD */ - EXPECT_TRUE(cipher->type == S2N_COMPOSITE || cipher->type == S2N_CBC); - - struct s2n_key_material key_material = { 0 }; - - uint32_t mac = SHA256_DIGEST_LENGTH; - uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; - uint32_t iv = 16; - - /* initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS10; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - /* assert IV of non 0 if protocol version <= S2N_TLS10 */ - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - - /* re-initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS11; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - /* assert IV of size == 0 if protocol version > S2N_TLS10 */ - iv = 0; - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_prf.h" +#include "utils/s2n_random.h" + +static S2N_RESULT s2n_test_validate_key_material(struct s2n_key_material *key_material, + struct s2n_blob *test_data_blob, uint8_t mac_size, uint8_t key_size, uint8_t iv_size) +{ + /* confirm that the data is copied to key_material */ + RESULT_ENSURE_EQ(test_data_blob->size, sizeof(key_material->key_block)); + RESULT_ENSURE_EQ(memcmp(test_data_blob->data, key_material->key_block, test_data_blob->size), 0); + + struct s2n_stuffer test_data_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&test_data_stuffer, test_data_blob)); + + /* test that its possible to access data from s2n_key_material */ + /* client MAC */ + uint8_t *test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_mac.data, mac_size), 0); + /* server MAC */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_mac.data, mac_size), 0); + + /* client KEY */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_key.data, key_size), 0); + /* server KEY */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_key.data, key_size), 0); + + /* client IV */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_iv.data, iv_size), 0); + /* server IV */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_iv.data, iv_size), 0); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* prepare test data */ + uint8_t test_data[S2N_MAX_KEY_BLOCK_LEN] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* fuzz s2n_key_material_init with different mac, key, iv sizes */ + { + for (uint8_t mac_size = 0; mac_size < SHA512_DIGEST_LENGTH; mac_size++) { + for (uint8_t key_size = 0; key_size < S2N_TLS_AES_256_GCM_KEY_LEN; key_size++) { + for (uint8_t iv_size = 0; iv_size < S2N_TLS_MAX_IV_LEN; iv_size++) { + if ((mac_size * 2 + key_size * 2 + iv_size * 2 > S2N_MAX_KEY_BLOCK_LEN)) { + continue; + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + /* test varying size of mac, key and iv */ + conn->actual_protocol_version = S2N_TLS10; + struct s2n_cipher temp_cipher = { + .type = S2N_COMPOSITE, + .key_material_size = key_size, + .io.comp = { + /* interpreted as iv size for composite ciphers.. which makes + * it easy for testing */ + .block_size = iv_size, + .mac_key_size = mac_size, + }, + }; + struct s2n_record_algorithm temp_record_alg = { + .cipher = &temp_cipher, + }; + struct s2n_cipher_suite temp_cipher_suite = { + .record_alg = &temp_record_alg, + }; + conn->secure->cipher_suite = &temp_cipher_suite; + + /* init s2n_key_material */ + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + /* assert that sizes match */ + EXPECT_EQUAL(key_material.client_mac.size, mac_size); + EXPECT_EQUAL(key_material.client_key.size, key_size); + EXPECT_EQUAL(key_material.client_iv.size, iv_size); + EXPECT_EQUAL(key_material.server_mac.size, mac_size); + EXPECT_EQUAL(key_material.server_key.size, key_size); + EXPECT_EQUAL(key_material.server_iv.size, iv_size); + + /* copy data into key_material and validate accessing key_material is sound */ + EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); + POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); + EXPECT_OK(s2n_test_validate_key_material(&key_material, &test_data_blob, mac_size, key_size, iv_size)); + } + } + } + } + + /* confirm that s2n_key_material can correctly handle all cipher suites */ + { + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + if (!cipher_suite->available) { + continue; + } + conn->secure->cipher_suite = cipher_suite; + + /* init s2n_key_material */ + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + /* copy data into key_material and validate accessing key_material is sound */ + EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); + POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); + /* test that its possible to access mac, key and iv correctly from s2n_key_material */ + EXPECT_OK(s2n_test_validate_key_material( + &key_material, + &test_data_blob, + key_material.client_mac.size, + key_material.client_key.size, + key_material.client_iv.size)); + } + } + + /* AEAD cipher + * IV size should be the same regardless of protocol version + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_EQUAL(conn->secure->cipher_suite->record_alg->cipher->type, S2N_AEAD); + + struct s2n_key_material key_material = { 0 }; + + uint32_t mac = 0; + uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; + uint32_t iv = S2N_TLS13_FIXED_IV_LEN; + + /* initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + + /* re-initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert same IV size regardless of protocol version */ + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + } + + /* NON AEAD cipher + * IV size depends on protocol version + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha256; + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + /* assert that the cipher chosen is non AEAD */ + EXPECT_TRUE(cipher->type == S2N_COMPOSITE || cipher->type == S2N_CBC); + + struct s2n_key_material key_material = { 0 }; + + uint32_t mac = SHA256_DIGEST_LENGTH; + uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; + uint32_t iv = 16; + + /* initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert IV of non 0 if protocol version <= S2N_TLS10 */ + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + + /* re-initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert IV of size == 0 if protocol version > S2N_TLS10 */ + iv = 0; + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_quic_support_io_test.c b/tests/unit/s2n_quic_support_io_test.c index 74eb2dad2fb..8ea9afa803f 100644 --- a/tests/unit/s2n_quic_support_io_test.c +++ b/tests/unit/s2n_quic_support_io_test.c @@ -1,608 +1,609 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_mem.h" - -/* We need access to some io logic */ -#include "tls/s2n_handshake_io.c" - -#define TEST_TICKET_AGE_ADD 0x01, 0x02, 0x03, 0x04 -#define TEST_LIFETIME 0x00, 0x01, 0x01, 0x01 -#define TEST_TICKET 0x01, 0xFF, 0x23 - -static const uint8_t TEST_DATA[] = "test"; -static const size_t TEST_DATA_SIZE = sizeof(TEST_DATA); - -struct s2n_stuffer input_stuffer, output_stuffer; - -static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - uint8_t *count = (uint8_t *) ctx; - (*count)++; - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_setup_conn(struct s2n_connection *conn) -{ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&input_stuffer)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&output_stuffer)); - RESULT_GUARD_POSIX(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_setup_conn_for_client_hello(struct s2n_connection *conn) -{ - RESULT_GUARD(s2n_setup_conn(conn)); - conn->handshake.handshake_type = INITIAL; - conn->handshake.message_number = 0; - RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_setup_conn_for_server_hello(struct s2n_connection *conn) -{ - RESULT_GUARD(s2n_setup_conn(conn)); - - /* Use arbitrary cipher suite */ - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - /* Setup secrets */ - const struct s2n_ecc_preferences *ecc_preferences = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - if (conn->kex_params.server_ecc_evp_params.evp_pkey == NULL) { - RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); - } - if (conn->kex_params.client_ecc_evp_params.evp_pkey == NULL) { - RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - } - - /* Set handshake to write message */ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - conn->handshake.message_number = 1; - RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_write_test_message(struct s2n_blob *out, message_type_t message_type) -{ - RESULT_GUARD_POSIX(s2n_alloc(out, TEST_DATA_SIZE + TLS_HANDSHAKE_HEADER_LENGTH)); - - struct s2n_stuffer stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, out)); - - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&stuffer, message_type)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - - return S2N_RESULT_OK; -} - -static int s2n_test_write_handler(struct s2n_connection *conn) -{ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, TEST_DATA, TEST_DATA_SIZE)); - return S2N_SUCCESS; -} - -static int s2n_test_read_handler(struct s2n_connection *conn) -{ - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->handshake.io, TEST_DATA_SIZE), - TEST_DATA, TEST_DATA_SIZE); - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* Test: s2n_quic_write_handshake_message */ - { - /* Safety checks */ - EXPECT_ERROR(s2n_quic_write_handshake_message(NULL)); - - /* Writes handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - uint8_t message_data[] = "The client says hello"; - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, message_data, sizeof(message_data))); - - EXPECT_OK(s2n_quic_write_handshake_message(conn)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), sizeof(message_data)); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->out, sizeof(message_data)), - message_data, sizeof(message_data)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test: s2n_quic_read_handshake_message */ - { - /* Safety checks */ - { - struct s2n_connection conn = { 0 }; - uint8_t message_type = 0; - - EXPECT_ERROR(s2n_quic_read_handshake_message(NULL, &message_type)); - EXPECT_ERROR(s2n_quic_read_handshake_message(&conn, NULL)); - }; - - /* Reads basic handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - uint8_t expected_message_type = 7; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, expected_message_type)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - - uint8_t actual_message_type = 0; - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - - EXPECT_EQUAL(actual_message_type, expected_message_type); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TLS_HANDSHAKE_HEADER_LENGTH); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), - TEST_DATA, sizeof(TEST_DATA)); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Blocks on insufficient data for handshake message header */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - - uint8_t actual_message_type = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), - S2N_ERR_IO_BLOCKED); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Blocks on insufficient data for handshake message data */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE - 1)); - - uint8_t actual_message_type = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), - S2N_ERR_IO_BLOCKED); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Fails for an impossibly large handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH + 1)); - - uint8_t actual_message_type = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), - S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Succeeds for a handshake message larger than the input buffer */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - uint8_t actual_message_type = 0; - - /* Read a small message to initialize the input buffer */ - const size_t small_message_size = 10; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, small_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, small_message_size)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_message_size); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - EXPECT_OK(s2n_record_wipe(conn)); - const size_t max_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); - EXPECT_TRUE(max_buffer_size > small_message_size); - - /* Read a large message to force the input buffer to resize */ - const size_t large_message_size = max_buffer_size + 10; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, large_message_size)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), large_message_size); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - EXPECT_OK(s2n_record_wipe(conn)); - const size_t resized_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); - EXPECT_TRUE(resized_buffer_size >= large_message_size); - - /* Read another message to check that the resize doesn't prevent future reads */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), - TEST_DATA, sizeof(TEST_DATA)); - }; - - /* Succeeds for multiple messages */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - uint8_t actual_message_type = 0; - size_t expected_buffer_size = 0; - for (size_t i = 0; i < 100; i++) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), - TEST_DATA, sizeof(TEST_DATA)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - EXPECT_OK(s2n_record_wipe(conn)); - - /* Ensure buffer size stays constant */ - const size_t buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); - if (i == 0) { - expected_buffer_size = buffer_size; - } - EXPECT_EQUAL(expected_buffer_size, buffer_size); - } - }; - }; - - /* Functional Tests */ - { - s2n_blocked_status blocked_status; - - /* Use handler stubs to avoid executing complicated handler implementations */ - for (size_t i = 0; i < s2n_array_len(tls13_state_machine); i++) { - tls13_state_machine[i].handler[S2N_SERVER] = s2n_test_read_handler; - tls13_state_machine[i].handler[S2N_CLIENT] = s2n_test_write_handler; - } - - /* Write test message */ - DEFER_CLEANUP(struct s2n_blob server_hello, s2n_free); - EXPECT_OK(s2n_write_test_message(&server_hello, TLS_SERVER_HELLO)); - - /* Setup IO buffers */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input_stuffer, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output_stuffer, 0)); - - /* Setup config */ - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_enable_quic(config)); - - /* Functional: successfully reads full handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Functional: successfully reads fragmented handshake message */ - for (size_t i = 1; i < server_hello.size - 1; i++) { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Write initial fragment */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, server_hello.data, i)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - /* Write rest of message */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, - server_hello.data + i, server_hello.size - i)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Functional: successfully reads multiple handshake messages */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - DEFER_CLEANUP(struct s2n_blob encrypted_extensions, s2n_free); - EXPECT_OK(s2n_write_test_message(&encrypted_extensions, TLS_ENCRYPTED_EXTENSIONS)); - - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &encrypted_extensions)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Function: fails to read record instead of handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Write the record: record type, protocol version, - * handshake message size, handshake message */ - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_HANDSHAKE)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, server_hello.size)); - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Function: fails to read Change Cipher Spec record */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Write the record: record type, protocol version, - * record data size, standard "0x01" record data */ - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_CHANGE_CIPHER_SPEC)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, 1)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 1)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - uint32_t client_hello_length = 0; - - /* Functional: successfully writes full handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - client_hello_length = s2n_stuffer_data_available(&output_stuffer); - - uint8_t actual_message_type = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output_stuffer, &actual_message_type)); - EXPECT_EQUAL(actual_message_type, TLS_CLIENT_HELLO); - - uint32_t actual_message_size = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint24(&output_stuffer, &actual_message_size)); - EXPECT_EQUAL(actual_message_size, TEST_DATA_SIZE); - - EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&output_stuffer, TEST_DATA_SIZE), - TEST_DATA, TEST_DATA_SIZE); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Functional: successfully retries after blocked write */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Sabotage the output stuffer to block writing */ - struct s2n_stuffer bad_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &bad_stuffer, conn)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); - EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), 0); - - /* Fix the output stuffer */ - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), client_hello_length); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_stuffer_free(&input_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_free(&output_stuffer)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test: s2n_recv_quic_post_handshake_message */ - { - /* Safety checks */ - s2n_blocked_status blocked = 0; - EXPECT_FAILURE(s2n_recv_quic_post_handshake_message(NULL, &blocked)); - - /* Parsable session ticket message */ - uint8_t ticket_message_bytes[] = { - TLS_SERVER_NEW_SESSION_TICKET, - 0x00, 0x00, 0x12, /* message size */ - TEST_LIFETIME, /* ticket lifetime */ - TEST_TICKET_AGE_ADD, /* ticket age add */ - 0x02, /* nonce len */ - 0x00, 0x00, /* nonce */ - 0x00, 0x03, /* ticket len */ - TEST_TICKET, /* ticket */ - 0x00, 0x00, /* extensions len */ - }; - - /* Test: fails to read post-handshake message that is not a ST */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - /* Create a post-handshake message that isn't supported by quic */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, TLS_KEY_UPDATE)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_UNSUPPORTED_WITH_QUIC); - }; - - /* Test: successfully reads and processes post-handshake message */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - uint8_t session_ticket_cb_count = 0; - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - /* Construct and process multiple ST handshake messages */ - for (size_t i = 1; i < 10; i++) { - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, sizeof(ticket_message_bytes))); - EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); - - /* Callback was triggered */ - EXPECT_EQUAL(session_ticket_cb_count, i); - } - }; - - /* Test: successfully reads and processes fragmented post-handshake message */ - for (size_t i = 1; i < sizeof(ticket_message_bytes); i++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - uint8_t session_ticket_cb_count = 0; - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - /* Mock receiving a fragmented handshake message */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, i)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Callback was not triggered */ - EXPECT_EQUAL(session_ticket_cb_count, 0); - - /* "Write" the rest of the message */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes + i, sizeof(ticket_message_bytes) - i)); - - EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); - - /* Callback was triggered */ - EXPECT_EQUAL(session_ticket_cb_count, 1); - }; - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_mem.h" + +/* We need access to some io logic */ +#include "tls/s2n_handshake_io.c" + +#define TEST_TICKET_AGE_ADD 0x01, 0x02, 0x03, 0x04 +#define TEST_LIFETIME 0x00, 0x01, 0x01, 0x01 +#define TEST_TICKET 0x01, 0xFF, 0x23 + +static const uint8_t TEST_DATA[] = "test"; +static const size_t TEST_DATA_SIZE = sizeof(TEST_DATA); + +struct s2n_stuffer input_stuffer, output_stuffer; + +static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + uint8_t *count = (uint8_t *) ctx; + (*count)++; + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_setup_conn(struct s2n_connection *conn) +{ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&input_stuffer)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&output_stuffer)); + RESULT_GUARD_POSIX(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_conn_for_client_hello(struct s2n_connection *conn) +{ + RESULT_GUARD(s2n_setup_conn(conn)); + conn->handshake.handshake_type = INITIAL; + conn->handshake.message_number = 0; + RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_conn_for_server_hello(struct s2n_connection *conn) +{ + RESULT_GUARD(s2n_setup_conn(conn)); + + /* Use arbitrary cipher suite */ + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Setup secrets */ + const struct s2n_ecc_preferences *ecc_preferences = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + if (conn->kex_params.server_ecc_evp_params.evp_pkey == NULL) { + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + } + if (conn->kex_params.client_ecc_evp_params.evp_pkey == NULL) { + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + } + + /* Set handshake to write message */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = 1; + RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_write_test_message(struct s2n_blob *out, message_type_t message_type) +{ + RESULT_GUARD_POSIX(s2n_alloc(out, TEST_DATA_SIZE + TLS_HANDSHAKE_HEADER_LENGTH)); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, out)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&stuffer, message_type)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + return S2N_RESULT_OK; +} + +static int s2n_test_write_handler(struct s2n_connection *conn) +{ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, TEST_DATA, TEST_DATA_SIZE)); + return S2N_SUCCESS; +} + +static int s2n_test_read_handler(struct s2n_connection *conn) +{ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->handshake.io, TEST_DATA_SIZE), + TEST_DATA, TEST_DATA_SIZE); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test: s2n_quic_write_handshake_message */ + { + /* Safety checks */ + EXPECT_ERROR(s2n_quic_write_handshake_message(NULL)); + + /* Writes handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + uint8_t message_data[] = "The client says hello"; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, message_data, sizeof(message_data))); + + EXPECT_OK(s2n_quic_write_handshake_message(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), sizeof(message_data)); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->out, sizeof(message_data)), + message_data, sizeof(message_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: s2n_quic_read_handshake_message */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + uint8_t message_type = 0; + + EXPECT_ERROR(s2n_quic_read_handshake_message(NULL, &message_type)); + EXPECT_ERROR(s2n_quic_read_handshake_message(&conn, NULL)); + }; + + /* Reads basic handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t expected_message_type = 7; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, expected_message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + uint8_t actual_message_type = 0; + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + + EXPECT_EQUAL(actual_message_type, expected_message_type); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TLS_HANDSHAKE_HEADER_LENGTH); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Blocks on insufficient data for handshake message header */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_IO_BLOCKED); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Blocks on insufficient data for handshake message data */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE - 1)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_IO_BLOCKED); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fails for an impossibly large handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH + 1)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Succeeds for a handshake message larger than the input buffer */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t actual_message_type = 0; + + /* Read a small message to initialize the input buffer */ + const size_t small_message_size = 10; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, small_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, small_message_size)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_message_size); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + const size_t max_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_TRUE(max_buffer_size > small_message_size); + + /* Read a large message to force the input buffer to resize */ + const size_t large_message_size = max_buffer_size + 10; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, large_message_size)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), large_message_size); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + const size_t resized_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_TRUE(resized_buffer_size >= large_message_size); + + /* Read another message to check that the resize doesn't prevent future reads */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + }; + + /* Succeeds for multiple messages */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t actual_message_type = 0; + size_t expected_buffer_size = 0; + for (size_t i = 0; i < 100; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + + /* Ensure buffer size stays constant */ + const size_t buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + if (i == 0) { + expected_buffer_size = buffer_size; + } + EXPECT_EQUAL(expected_buffer_size, buffer_size); + } + }; + }; + + /* Functional Tests */ + { + s2n_blocked_status blocked_status; + + /* Use handler stubs to avoid executing complicated handler implementations */ + for (size_t i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[S2N_SERVER] = s2n_test_read_handler; + tls13_state_machine[i].handler[S2N_CLIENT] = s2n_test_write_handler; + } + + /* Write test message */ + DEFER_CLEANUP(struct s2n_blob server_hello, s2n_free); + EXPECT_OK(s2n_write_test_message(&server_hello, TLS_SERVER_HELLO)); + + /* Setup IO buffers */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output_stuffer, 0)); + + /* Setup config */ + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + /* Functional: successfully reads full handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional: successfully reads fragmented handshake message */ + for (size_t i = 1; i < server_hello.size - 1; i++) { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write initial fragment */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, server_hello.data, i)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + /* Write rest of message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, + server_hello.data + i, server_hello.size - i)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Functional: successfully reads multiple handshake messages */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_blob encrypted_extensions, s2n_free); + EXPECT_OK(s2n_write_test_message(&encrypted_extensions, TLS_ENCRYPTED_EXTENSIONS)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &encrypted_extensions)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Function: fails to read record instead of handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write the record: record type, protocol version, + * handshake message size, handshake message */ + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_HANDSHAKE)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, server_hello.size)); + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Function: fails to read Change Cipher Spec record */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write the record: record type, protocol version, + * record data size, standard "0x01" record data */ + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_CHANGE_CIPHER_SPEC)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, 1)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + uint32_t client_hello_length = 0; + + /* Functional: successfully writes full handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + client_hello_length = s2n_stuffer_data_available(&output_stuffer); + + uint8_t actual_message_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output_stuffer, &actual_message_type)); + EXPECT_EQUAL(actual_message_type, TLS_CLIENT_HELLO); + + uint32_t actual_message_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&output_stuffer, &actual_message_size)); + EXPECT_EQUAL(actual_message_size, TEST_DATA_SIZE); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&output_stuffer, TEST_DATA_SIZE), + TEST_DATA, TEST_DATA_SIZE); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional: successfully retries after blocked write */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Sabotage the output stuffer to block writing */ + struct s2n_stuffer bad_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &bad_stuffer, conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), 0); + + /* Fix the output stuffer */ + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), client_hello_length); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&input_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_free(&output_stuffer)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test: s2n_recv_quic_post_handshake_message */ + { + /* Safety checks */ + s2n_blocked_status blocked = 0; + EXPECT_FAILURE(s2n_recv_quic_post_handshake_message(NULL, &blocked)); + + /* Parsable session ticket message */ + uint8_t ticket_message_bytes[] = { + TLS_SERVER_NEW_SESSION_TICKET, + 0x00, 0x00, 0x12, /* message size */ + TEST_LIFETIME, /* ticket lifetime */ + TEST_TICKET_AGE_ADD, /* ticket age add */ + 0x02, /* nonce len */ + 0x00, 0x00, /* nonce */ + 0x00, 0x03, /* ticket len */ + TEST_TICKET, /* ticket */ + 0x00, 0x00, /* extensions len */ + }; + + /* Test: fails to read post-handshake message that is not a ST */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Create a post-handshake message that isn't supported by quic */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_UNSUPPORTED_WITH_QUIC); + }; + + /* Test: successfully reads and processes post-handshake message */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + uint8_t session_ticket_cb_count = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Construct and process multiple ST handshake messages */ + for (size_t i = 1; i < 10; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, sizeof(ticket_message_bytes))); + EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); + + /* Callback was triggered */ + EXPECT_EQUAL(session_ticket_cb_count, i); + } + }; + + /* Test: successfully reads and processes fragmented post-handshake message */ + for (size_t i = 1; i < sizeof(ticket_message_bytes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + uint8_t session_ticket_cb_count = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Mock receiving a fragmented handshake message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, i)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Callback was not triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 0); + + /* "Write" the rest of the message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes + i, sizeof(ticket_message_bytes) - i)); + + EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); + + /* Callback was triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 1); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_record_size_test.c b/tests/unit/s2n_record_size_test.c index 79fba0ef5cc..a05e46b86ec 100644 --- a/tests/unit/s2n_record_size_test.c +++ b/tests/unit/s2n_record_size_test.c @@ -1,522 +1,523 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_prf.h" -#include "tls/s2n_record.h" -#include "utils/s2n_random.h" - -#define ONE_BLOCK 1024 -#define ONE_HUNDRED_K 100000 -#define RECORD_SIZE_HIGH_BYTE_ORDER 3 -#define RECORD_SIZE_LOW_BYTE_ORDER 4 -#define BYTE_SHIFT 8 -#define RECORD_SIZE(data) ((data[RECORD_SIZE_HIGH_BYTE_ORDER] << BYTE_SHIFT) | data[RECORD_SIZE_LOW_BYTE_ORDER]) - -#define EXPECT_LESS_THAN_EQUAL(p1, p2) EXPECT_TRUE((p1) <= (p2)) - -static int destroy_server_keys(struct s2n_connection *server_conn) -{ - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); - - return S2N_SUCCESS; -} - -static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) -{ - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); - - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - uint8_t mac_key[] = "sample mac key"; - - uint8_t random_data[S2N_LARGE_RECORD_LENGTH + 1]; - struct s2n_blob r = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); - EXPECT_OK(s2n_get_public_random_data(&r)); - - uint8_t aes128_key[] = "123456789012345"; - struct s2n_blob aes128 = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); - - /* Test record sizes with s2n_record_write */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - /* Client and server are in sync */ - conn->server = conn->secure; - conn->client = conn->secure; - - /* test the AES128 cipher with a SHA1 hash */ - conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes128_sha; - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes128)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes128)); - EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); - EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); - conn->actual_protocol_version = S2N_TLS11; - - /* Test that different modes allows for different fragment/payload sizes. - * Record overheads (IV, HMAC, padding) do not count towards these size */ - const int small_payload = S2N_SMALL_FRAGMENT_LENGTH; - const int large_payload = S2N_LARGE_FRAGMENT_LENGTH; - const int medium_payload = S2N_DEFAULT_FRAGMENT_LENGTH; - struct s2n_blob fragment = r; - - /* Check the default: medium records */ - fragment.size = medium_payload; - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); - EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); - - /* Check explicitly small records */ - fragment.size = small_payload; - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); - EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); - - /* Check explicitly large records */ - fragment.size = large_payload; - EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); - EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); - - /* Clean up */ - conn->secure->cipher_suite->record_alg = &s2n_record_alg_null; /* restore mutated null cipher suite */ - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); - }; - - /* Test s2n_record_max_write_payload_size() have proper checks in place */ - { - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - /* we deal with the default null cipher suite for now, as it makes reasoning - * about easier s2n_record_max_write_payload_size(), as it incur 0 overheads */ - uint16_t size = 0; - server_conn->max_outgoing_fragment_length = ONE_BLOCK; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, ONE_BLOCK); - - /* Trigger an overlarge payload by setting a maximum uint16_t value to max fragment length */ - server_conn->max_outgoing_fragment_length = UINT16_MAX; - /* Check that we are bound by S2N_TLS_MAXIMUM_FRAGMENT_LENGTH */ - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* trigger a payload that is under the limits */ - server_conn->max_outgoing_fragment_length = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_payload_size(server_conn, &size), S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - - /* Test boundary cases */ - - /* This is the theoretical maximum mfl allowed */ - server_conn->max_outgoing_fragment_length = S2N_TLS_MAXIMUM_FRAGMENT_LENGTH; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* MFL over limit is not allowed, but size is reduced to S2N_TLS_MAXIMUM_FRAGMENT_LENGTH*/ - server_conn->max_outgoing_fragment_length++; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* Test against different cipher suites */ - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->server->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - server_conn->max_outgoing_fragment_length = ONE_BLOCK; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, ONE_BLOCK); /* Verify size matches exactly specified max fragment length */ - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test s2n_record_max_write_payload_size with custom send buffer size */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Min buffer size */ - { - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); - }; - - /* Small buffer size */ - { - const uint32_t frag_len = 1000; - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, frag_len); - }; - - /* Buffer exactly fits one record */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - const uint32_t frag_len = conn->max_outgoing_fragment_length; - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, frag_len); - }; - - /* Buffer larger than one record */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - const uint32_t frag_len = conn->max_outgoing_fragment_length + 10; - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, conn->max_outgoing_fragment_length); - }; - }; - - /* Test s2n_record_min_write_payload_size() */ - { - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - uint16_t size = 0; - const int RECORD_SIZE_LESS_OVERHEADS = 1415; - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(RECORD_SIZE_LESS_OVERHEADS, size); - - const int MIN_SIZE = RECORD_SIZE_LESS_OVERHEADS + S2N_TLS_RECORD_HEADER_LENGTH; - - /* CBC */ - { - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - server_conn->actual_protocol_version = S2N_TLS11; - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_3des_sha; - uint8_t des3_key[] = "12345678901234567890123"; - struct s2n_blob des3 = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&des3, des3_key, sizeof(des3_key))); - server_conn->server = server_conn->secure; - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->server_key)); - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->client_key)); - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->secure->server_key, &des3)); - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->secure->client_key, &des3)); - EXPECT_SUCCESS(s2n_hmac_init(&server_conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - r.size = size; - const int after_overheads = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % 8; /* rounded down to cbc block size (8) */ - const uint16_t PADDING_LENGTH_BYTE = 1; - const uint16_t RECORD_IV_SIZE = 8; - const uint16_t HMAC_DIGEST = 20; - EXPECT_EQUAL(size, after_overheads - HMAC_DIGEST - RECORD_IV_SIZE - PADDING_LENGTH_BYTE); - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - }; - - /* AEAD */ - { - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; - EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - r.size = size; - const uint16_t IV = 8; - const uint16_t TAG = 16; - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG); - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - }; - - /* TLS1.3 AEAD */ - { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_aes128_gcm; - EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - r.size = size; - const uint16_t IV = 0; - const uint16_t TAG = 16; - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG - S2N_TLS_CONTENT_TYPE_LENGTH); - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - }; - - /* chacha20 */ - if (s2n_chacha20_poly1305.is_available()) { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_chacha20_poly1305; - uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; - struct s2n_blob chacha20_poly1305_key = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); - - EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN); - r.size = size; - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - } - - /* TLS1.3 chacha20 */ - if (s2n_chacha20_poly1305.is_available()) { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_chacha20_poly1305; - uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; - struct s2n_blob chacha20_poly1305_key = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); - - EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN - S2N_TLS_CONTENT_TYPE_LENGTH); - r.size = size; - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - } - - /* composite */ - if (s2n_aes128_sha.is_available() && s2n_aes128_sha256.is_available()) { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha_composite; - server_conn->actual_protocol_version = S2N_TLS11; - uint8_t mac_key_sha[20] = "server key shaserve"; - EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, &aes128)); - EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, &aes128)); - EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); - EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - const uint16_t COMPOSITE_BLOCK_SIZE = 16; - const uint16_t COMPOSITE_DIGEST_LENGTH = 20; - const uint16_t COMPOSITE_PADDING_LENGTH = 1; - const uint16_t size_aligned_to_block = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % COMPOSITE_BLOCK_SIZE - COMPOSITE_DIGEST_LENGTH - COMPOSITE_PADDING_LENGTH; - const uint16_t explicit_iv_len = 16; - const uint16_t size_after_overheads = size_aligned_to_block - explicit_iv_len; - EXPECT_EQUAL(size, size_after_overheads); - r.size = size; - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - } - - r.size = sizeof(random_data); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test large fragment/record sending for TLS 1.3 */ - { - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->server->cipher_suite = cipher_suite; - - struct s2n_session_key *session_key = &server_conn->server->server_key; - uint8_t *implicit_iv = server_conn->server->server_implicit_iv; - - /* init record algorithm */ - EXPECT_OK(cipher_suite->record_alg->cipher->init(session_key)); - S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); - EXPECT_OK(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); - EXPECT_OK(cipher_suite->record_alg->cipher->set_decryption_key(session_key, &key)); - - S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); - - /* copy iv bytes from input data */ - for (size_t i = 0; i < iv.size; i++) { - implicit_iv[i] = iv.data[i]; - } - - /* Configure to use s2n maximum fragment / record settings */ - EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); - - /* Testing with a small blob */ - s2n_stack_blob(small_blob, ONE_BLOCK, ONE_BLOCK); - struct iovec small_io_vec = { 0 }; - small_io_vec.iov_base = small_blob.data; - small_io_vec.iov_len = small_blob.size; - - int bytes_taken = 0; - - const uint16_t TLS13_RECORD_OVERHEAD = 22; - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &small_io_vec, 1, 0, small_blob.size)); - EXPECT_EQUAL(bytes_taken, ONE_BLOCK); /* we wrote the full blob size */ - EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), ONE_BLOCK + TLS13_RECORD_OVERHEAD); /* bytes on the wire */ - - /* Check we get a friendly error if we use s2n_record_write again */ - EXPECT_ERROR_WITH_ERRNO(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - /* Testing a big 100k blob to be written */ - s2n_stack_blob(big_blob, ONE_HUNDRED_K, ONE_HUNDRED_K); - struct iovec big_io_vec = { 0 }; - big_io_vec.iov_base = big_blob.data; - big_io_vec.iov_len = big_blob.size; - - /* Test that s2n_record_writev() doesn't error on writing large payloads. - * Also asserts the bytes written on the wire. - */ - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); - - /* We verify that s2n_record_writev() is able to send the maximum fragment length as specified by TLS RFCs */ - const uint16_t TLS_MAX_FRAG_LEN = 16384; - EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); /* plaintext bytes taken */ - EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), TLS_MAX_FRAG_LEN + TLS13_RECORD_OVERHEAD); /* bytes sent on the wire */ - - /* These are invariant regardless of s2n implementation */ - EXPECT_TRUE(bytes_taken <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); /* Plaintext max size - 2^14 = 16384 */ - EXPECT_TRUE(bytes_taken <= (S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 255)); /* Max record size for TLS 1.3 - 2^14 + 255 = 16639 */ - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS_MAXIMUM_RECORD_LENGTH); - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS13_MAXIMUM_RECORD_LENGTH); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - /* Now escape the sandbox and attempt to get record_write to use a larger plaintext bytes */ - /* However, the max fragment length should still be bounded based on the protocol specification */ - const uint16_t MAX_FORCED_OUTGOING_FRAGMENT_LENGTH = 16400; - - server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; /* Trigger fragment length bounding */ - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); - EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - /* Force a generous 100k resize on the outgoing record stuffer */ - EXPECT_SUCCESS(s2n_stuffer_resize(&server_conn->out, ONE_HUNDRED_K)); - server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); - EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* s2n_record_max_write_size */ - { - uint16_t result = 0; - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(NULL, 1, &result), S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(conn, 1, NULL), S2N_ERR_NULL); - - conn->actual_protocol_version = 0; - conn->handshake.handshake_type = INITIAL; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); - - conn->handshake.handshake_type = NEGOTIATED; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS12_MAXIMUM_RECORD_LENGTH); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH); - - uint16_t diff = 10; - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH - diff, &result)); - EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH - diff); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +#define ONE_BLOCK 1024 +#define ONE_HUNDRED_K 100000 +#define RECORD_SIZE_HIGH_BYTE_ORDER 3 +#define RECORD_SIZE_LOW_BYTE_ORDER 4 +#define BYTE_SHIFT 8 +#define RECORD_SIZE(data) ((data[RECORD_SIZE_HIGH_BYTE_ORDER] << BYTE_SHIFT) | data[RECORD_SIZE_LOW_BYTE_ORDER]) + +#define EXPECT_LESS_THAN_EQUAL(p1, p2) EXPECT_TRUE((p1) <= (p2)) + +static int destroy_server_keys(struct s2n_connection *server_conn) +{ + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); + + return S2N_SUCCESS; +} + +static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) +{ + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t mac_key[] = "sample mac key"; + + uint8_t random_data[S2N_LARGE_RECORD_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + EXPECT_OK(s2n_get_public_random_data(&r)); + + uint8_t aes128_key[] = "123456789012345"; + struct s2n_blob aes128 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); + + /* Test record sizes with s2n_record_write */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Client and server are in sync */ + conn->server = conn->secure; + conn->client = conn->secure; + + /* test the AES128 cipher with a SHA1 hash */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes128_sha; + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes128)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes128)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + /* Test that different modes allows for different fragment/payload sizes. + * Record overheads (IV, HMAC, padding) do not count towards these size */ + const int small_payload = S2N_SMALL_FRAGMENT_LENGTH; + const int large_payload = S2N_LARGE_FRAGMENT_LENGTH; + const int medium_payload = S2N_DEFAULT_FRAGMENT_LENGTH; + struct s2n_blob fragment = r; + + /* Check the default: medium records */ + fragment.size = medium_payload; + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Check explicitly small records */ + fragment.size = small_payload; + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Check explicitly large records */ + fragment.size = large_payload; + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Clean up */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_null; /* restore mutated null cipher suite */ + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + }; + + /* Test s2n_record_max_write_payload_size() have proper checks in place */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* we deal with the default null cipher suite for now, as it makes reasoning + * about easier s2n_record_max_write_payload_size(), as it incur 0 overheads */ + uint16_t size = 0; + server_conn->max_outgoing_fragment_length = ONE_BLOCK; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, ONE_BLOCK); + + /* Trigger an overlarge payload by setting a maximum uint16_t value to max fragment length */ + server_conn->max_outgoing_fragment_length = UINT16_MAX; + /* Check that we are bound by S2N_TLS_MAXIMUM_FRAGMENT_LENGTH */ + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* trigger a payload that is under the limits */ + server_conn->max_outgoing_fragment_length = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_payload_size(server_conn, &size), S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + + /* Test boundary cases */ + + /* This is the theoretical maximum mfl allowed */ + server_conn->max_outgoing_fragment_length = S2N_TLS_MAXIMUM_FRAGMENT_LENGTH; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* MFL over limit is not allowed, but size is reduced to S2N_TLS_MAXIMUM_FRAGMENT_LENGTH*/ + server_conn->max_outgoing_fragment_length++; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* Test against different cipher suites */ + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->max_outgoing_fragment_length = ONE_BLOCK; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, ONE_BLOCK); /* Verify size matches exactly specified max fragment length */ + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_record_max_write_payload_size with custom send buffer size */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Min buffer size */ + { + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); + }; + + /* Small buffer size */ + { + const uint32_t frag_len = 1000; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, frag_len); + }; + + /* Buffer exactly fits one record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + const uint32_t frag_len = conn->max_outgoing_fragment_length; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, frag_len); + }; + + /* Buffer larger than one record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + const uint32_t frag_len = conn->max_outgoing_fragment_length + 10; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, conn->max_outgoing_fragment_length); + }; + }; + + /* Test s2n_record_min_write_payload_size() */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + uint16_t size = 0; + const int RECORD_SIZE_LESS_OVERHEADS = 1415; + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(RECORD_SIZE_LESS_OVERHEADS, size); + + const int MIN_SIZE = RECORD_SIZE_LESS_OVERHEADS + S2N_TLS_RECORD_HEADER_LENGTH; + + /* CBC */ + { + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + server_conn->actual_protocol_version = S2N_TLS11; + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_3des_sha; + uint8_t des3_key[] = "12345678901234567890123"; + struct s2n_blob des3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&des3, des3_key, sizeof(des3_key))); + server_conn->server = server_conn->secure; + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->server_key)); + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->client_key)); + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->secure->server_key, &des3)); + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->secure->client_key, &des3)); + EXPECT_SUCCESS(s2n_hmac_init(&server_conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const int after_overheads = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % 8; /* rounded down to cbc block size (8) */ + const uint16_t PADDING_LENGTH_BYTE = 1; + const uint16_t RECORD_IV_SIZE = 8; + const uint16_t HMAC_DIGEST = 20; + EXPECT_EQUAL(size, after_overheads - HMAC_DIGEST - RECORD_IV_SIZE - PADDING_LENGTH_BYTE); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* AEAD */ + { + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const uint16_t IV = 8; + const uint16_t TAG = 16; + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* TLS1.3 AEAD */ + { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const uint16_t IV = 0; + const uint16_t TAG = 16; + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG - S2N_TLS_CONTENT_TYPE_LENGTH); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* chacha20 */ + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_chacha20_poly1305; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + + EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + /* TLS1.3 chacha20 */ + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_chacha20_poly1305; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + + EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN - S2N_TLS_CONTENT_TYPE_LENGTH); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + /* composite */ + if (s2n_aes128_sha.is_available() && s2n_aes128_sha256.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha_composite; + server_conn->actual_protocol_version = S2N_TLS11; + uint8_t mac_key_sha[20] = "server key shaserve"; + EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, &aes128)); + EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, &aes128)); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + const uint16_t COMPOSITE_BLOCK_SIZE = 16; + const uint16_t COMPOSITE_DIGEST_LENGTH = 20; + const uint16_t COMPOSITE_PADDING_LENGTH = 1; + const uint16_t size_aligned_to_block = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % COMPOSITE_BLOCK_SIZE - COMPOSITE_DIGEST_LENGTH - COMPOSITE_PADDING_LENGTH; + const uint16_t explicit_iv_len = 16; + const uint16_t size_after_overheads = size_aligned_to_block - explicit_iv_len; + EXPECT_EQUAL(size, size_after_overheads); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + r.size = sizeof(random_data); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test large fragment/record sending for TLS 1.3 */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server->cipher_suite = cipher_suite; + + struct s2n_session_key *session_key = &server_conn->server->server_key; + uint8_t *implicit_iv = server_conn->server->server_implicit_iv; + + /* init record algorithm */ + EXPECT_OK(cipher_suite->record_alg->cipher->init(session_key)); + S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); + EXPECT_OK(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); + EXPECT_OK(cipher_suite->record_alg->cipher->set_decryption_key(session_key, &key)); + + S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); + + /* copy iv bytes from input data */ + for (size_t i = 0; i < iv.size; i++) { + implicit_iv[i] = iv.data[i]; + } + + /* Configure to use s2n maximum fragment / record settings */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + /* Testing with a small blob */ + s2n_stack_blob(small_blob, ONE_BLOCK, ONE_BLOCK); + struct iovec small_io_vec = { 0 }; + small_io_vec.iov_base = small_blob.data; + small_io_vec.iov_len = small_blob.size; + + int bytes_taken = 0; + + const uint16_t TLS13_RECORD_OVERHEAD = 22; + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &small_io_vec, 1, 0, small_blob.size)); + EXPECT_EQUAL(bytes_taken, ONE_BLOCK); /* we wrote the full blob size */ + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), ONE_BLOCK + TLS13_RECORD_OVERHEAD); /* bytes on the wire */ + + /* Check we get a friendly error if we use s2n_record_write again */ + EXPECT_ERROR_WITH_ERRNO(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Testing a big 100k blob to be written */ + s2n_stack_blob(big_blob, ONE_HUNDRED_K, ONE_HUNDRED_K); + struct iovec big_io_vec = { 0 }; + big_io_vec.iov_base = big_blob.data; + big_io_vec.iov_len = big_blob.size; + + /* Test that s2n_record_writev() doesn't error on writing large payloads. + * Also asserts the bytes written on the wire. + */ + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + + /* We verify that s2n_record_writev() is able to send the maximum fragment length as specified by TLS RFCs */ + const uint16_t TLS_MAX_FRAG_LEN = 16384; + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); /* plaintext bytes taken */ + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), TLS_MAX_FRAG_LEN + TLS13_RECORD_OVERHEAD); /* bytes sent on the wire */ + + /* These are invariant regardless of s2n implementation */ + EXPECT_TRUE(bytes_taken <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); /* Plaintext max size - 2^14 = 16384 */ + EXPECT_TRUE(bytes_taken <= (S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 255)); /* Max record size for TLS 1.3 - 2^14 + 255 = 16639 */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS_MAXIMUM_RECORD_LENGTH); + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS13_MAXIMUM_RECORD_LENGTH); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Now escape the sandbox and attempt to get record_write to use a larger plaintext bytes */ + /* However, the max fragment length should still be bounded based on the protocol specification */ + const uint16_t MAX_FORCED_OUTGOING_FRAGMENT_LENGTH = 16400; + + server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; /* Trigger fragment length bounding */ + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Force a generous 100k resize on the outgoing record stuffer */ + EXPECT_SUCCESS(s2n_stuffer_resize(&server_conn->out, ONE_HUNDRED_K)); + server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* s2n_record_max_write_size */ + { + uint16_t result = 0; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(NULL, 1, &result), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(conn, 1, NULL), S2N_ERR_NULL); + + conn->actual_protocol_version = 0; + conn->handshake.handshake_type = INITIAL; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); + + conn->handshake.handshake_type = NEGOTIATED; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS12_MAXIMUM_RECORD_LENGTH); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH); + + uint16_t diff = 10; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH - diff, &result)); + EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH - diff); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_recv_test.c b/tests/unit/s2n_recv_test.c index ef8315f1686..3e4725713a1 100644 --- a/tests/unit/s2n_recv_test.c +++ b/tests/unit/s2n_recv_test.c @@ -1,670 +1,671 @@ -/* - * 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 "api/unstable/renegotiate.h" -#include "s2n_test.h" -#include "testlib/s2n_ktls_test_utils.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_random.h" - -bool s2n_custom_recv_fn_called = false; - -int s2n_expect_concurrent_error_recv_fn(void *io_context, uint8_t *buf, uint32_t len) -{ - struct s2n_connection *conn = (struct s2n_connection *) io_context; - s2n_custom_recv_fn_called = true; - - s2n_blocked_status blocked = 0; - ssize_t result = s2n_recv(conn, buf, len, &blocked); - EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); - return result; -} - -static ssize_t s2n_test_ktls_recvmsg_cb(void *io_context, struct msghdr *msg) -{ - POSIX_ENSURE_REF(io_context); - return *(ssize_t *) io_context; -} - -static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, - s2n_renegotiate_response *response) -{ - POSIX_ENSURE_REF(context); - size_t *count = (size_t *) context; - (*count)++; - *response = S2N_RENEGOTIATE_IGNORE; - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* s2n_peek */ - { - /* We do full handshakes and send with a real connection here instead of - * just calling s2n_connection_set_secrets because s2n_peek depends on details - * of how data is encrypted, and we don't want to make any incorrect assumptions. - */ - - /* Safety check */ - EXPECT_EQUAL(s2n_peek(NULL), 0); - - const uint8_t test_data[100] = "hello world"; - const size_t test_data_size = sizeof(test_data); - - /* s2n_peek reports available plaintext bytes */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Write some data */ - EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - /* Initially, no data reported as available */ - EXPECT_EQUAL(s2n_peek(server_conn), 0); - - /* Read some, but not all, of the data written */ - uint8_t output[sizeof(test_data)] = { 0 }; - const size_t expected_peek_size = 10; - const size_t recv_size = test_data_size - expected_peek_size; - EXPECT_EQUAL(s2n_recv(server_conn, output, recv_size, &blocked), recv_size); - - /* After a partial read, some data reported as available */ - EXPECT_EQUAL(s2n_peek(server_conn), expected_peek_size); - - /* Read the rest of the data */ - EXPECT_EQUAL(s2n_recv(server_conn, output, expected_peek_size, &blocked), expected_peek_size); - - /* After the complete read, no data reported as available */ - EXPECT_EQUAL(s2n_peek(server_conn), 0); - }; - - /* s2n_peek doesn't report bytes belonging to partially read, still encrypted records */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Use stuffers for IO so that we can trigger a block on a read */ - DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Write some data */ - EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - /* Drop some of the data */ - EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); - - /* Try to read the data, but block */ - uint8_t output[sizeof(test_data)] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(test_data), &blocked), - S2N_ERR_IO_BLOCKED); - - /* conn->in contains data, but s2n_peek reports no data available */ - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); - EXPECT_EQUAL(s2n_peek(server_conn), 0); - }; - - /* s2n_peek doesn't report bytes belonging to post-handshake messages */ - if (s2n_is_tls13_fully_supported()) { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Use stuffers for IO so that we can trigger a block on a read */ - DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Send a KeyUpdate message */ - s2n_atomic_flag_set(&client_conn->key_update_pending); - EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); - EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending)); - - /* Drop some of the data */ - EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); - - /* Try to read the KeyUpdate message, but block */ - uint8_t output[1] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(output), &blocked), - S2N_ERR_IO_BLOCKED); - - /* conn->in contains data, but s2n_peek reports no data available */ - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); - EXPECT_EQUAL(s2n_peek(server_conn), 0); - }; - }; - - /* s2n_recv cannot be called concurrently */ - { - /* Setup connection */ - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - /* Setup bad recv callback */ - EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_expect_concurrent_error_recv_fn)); - EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, (void *) conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - - uint8_t test_data[100] = { 0 }; - s2n_blocked_status blocked = 0; - s2n_custom_recv_fn_called = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, test_data, sizeof(test_data), &blocked), - S2N_ERR_IO); - EXPECT_TRUE(s2n_custom_recv_fn_called); - - /* Cleanup */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* s2n_config_set_recv_multi_record */ - { - const uint8_t test_data_size = 100; - DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); - - const size_t recv_size = test_data_size * 2; - DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&output, recv_size)); - - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Write some data, in three records */ - for (size_t i = 0; i < 3; i++) { - EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); - } - - /* Disable multi-record recv, set legacy behavior */ - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, false)); - - EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), test_data_size); - - /* Now enable multi record recv */ - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, true)); - - /* So we should be able to read the remaining two records in a single call */ - EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), recv_size); - } - } - - /* recv blocked status - * - * This test preserves the `blocked` parameter contract with various states of the connection - */ - { - const uint8_t test_data_size = 100; - const size_t record_count = 3; - DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); - - const size_t total_data_size = test_data_size * record_count; - DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&output, total_data_size)); - - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - for (size_t multi_record = 0; multi_record <= 1; multi_record++) { - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, multi_record)); - size_t max_recv_size = test_data_size; - - /* In multi-record, we can read all of the records in one go */ - if (multi_record) { - max_recv_size *= record_count; - } - - for (size_t read_size = 1; read_size <= total_data_size; read_size++) { - /* Write some data across multiple records */ - for (size_t send_count = 0; send_count < record_count; send_count++) { - EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); - } - - /* Call `s2n_recv` multiple times with an empty buffer to make sure that's handled correctly */ - for (size_t empty_count = 0; empty_count < 10; empty_count++) { - EXPECT_EQUAL(s2n_recv(server_conn, output.data, 0, &blocked), 0); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - } - - size_t recv_bytes = 0; - while (recv_bytes < total_data_size) { - size_t expected_recv_size = S2N_MIN(S2N_MIN(read_size, total_data_size - recv_bytes), max_recv_size); - - /* Perform the actual recv call */ - ssize_t actual_recv_size = s2n_recv(server_conn, output.data, read_size, &blocked); - - if (multi_record) { - /* In multi-record mode we should always read the size we expect */ - EXPECT_EQUAL(actual_recv_size, expected_recv_size); - } else { - /* In single-record mode, we could potentially get a smaller read than a full record due to - * random record boundaries so we can only assert it's within the range we expect. */ - EXPECT_NOT_EQUAL(actual_recv_size, 0); - EXPECT_TRUE(actual_recv_size <= expected_recv_size); - } - - /* Keep track of the total amount of bytes read */ - recv_bytes += actual_recv_size; - - /* Due to the history of this API, some applications depend on the blocked status to know if - * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. - * - * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing - * without conflating being blocked on reading from the OS socket vs blocked on the application's - * buffer size. - */ - if (s2n_peek(server_conn) == 0) { - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - } else { - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - } - } - - /* The final read should return blocked since we don't have any more data from the socket */ - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output.data, read_size, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - } - } - - EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client_conn, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Call `s2n_recv` multiple times at the end of the stream after receiving a shutdown */ - for (size_t eos_count = 0; eos_count < 10; eos_count++) { - EXPECT_EQUAL(s2n_recv(server_conn, output.data, output.size, &blocked), 0); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - } - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - }; - - /* Test with ktls */ - { - uint8_t test_data[100] = { 0 }; - struct s2n_blob test_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); - EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); - - const struct iovec test_iovec = { - .iov_base = test_data, - .iov_len = sizeof(test_data), - }; - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - /* Test: receive all requested application data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &test_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(test_data)); - - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, written); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, read); - }; - - /* Test: receive partial application data */ - { - const size_t partial_size = sizeof(test_data) / 2; - struct iovec partial_iovec = test_iovec; - partial_iovec.iov_len = partial_size; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &partial_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, partial_size); - - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, written); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, read); - }; - - /* Test: drain buffered application data */ - { - const size_t partial_size = sizeof(test_data) / 2; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &test_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(test_data)); - - /* The first read doesn't read all the available data */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, partial_size, &blocked); - EXPECT_EQUAL(read, partial_size); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); - EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); - - /* The second read drains the remaining data */ - const size_t remaining = sizeof(test_data) - partial_size; - read = s2n_recv(conn, output + read, remaining, &blocked); - EXPECT_EQUAL(read, remaining); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); - EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); - }; - - /* Test: receive blocks */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - }; - - /* Test: receive indicates end-of-data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - ssize_t ret_val = 0; - EXPECT_OK(s2n_ktls_set_recvmsg_cb(conn, s2n_test_ktls_recvmsg_cb, &ret_val)); - - uint8_t output[10] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Error fatal but not blinded */ - EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); - EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); - }; - - /* Test: receive alert */ - { - /* Use a specific alert -- if we just use random data, we might - * stumble into a close_notify or user_canceled. - */ - uint8_t alert_data[] = { - S2N_TLS_ALERT_LEVEL_FATAL, - S2N_TLS_ALERT_DECRYPT_ERROR, - }; - const struct iovec alert_iovec = { - .iov_base = alert_data, - .iov_len = sizeof(alert_data), - }; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &alert_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(alert_data)); - - uint8_t output[10] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_ALERT); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Error fatal but not blinded */ - EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); - EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); - }; - - /* Test: receive handshake message */ - { - DEFER_CLEANUP(struct s2n_config *reneg_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(reneg_config); - - size_t reneg_request_count = 0; - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(reneg_config, - s2n_test_reneg_req_cb, &reneg_request_count)); - - uint8_t hello_request[TLS_HANDSHAKE_HEADER_LENGTH] = { TLS_HELLO_REQUEST }; - const struct iovec hello_request_iovec = { - .iov_base = hello_request, - .iov_len = sizeof(hello_request), - }; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, reneg_config)); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - conn->secure_renegotiation = true; - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - - /* Send the handshake message */ - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_HANDSHAKE, - &hello_request_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(hello_request)); - - /* Also send some application data */ - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &test_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(test_data)); - - /* Verify that we received the application data */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, read); - - /* Verify that we received and processed the handshake message */ - EXPECT_EQUAL(reneg_request_count, 1); - }; - - /* Test: Multirecord mode */ - { - DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(multi_config); - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true)); - - /* Test: receive all requested application data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - /* Write a lot of very small records */ - struct iovec offset_iovec = { 0 }; - for (size_t offset = 0; offset < sizeof(test_data); offset++) { - offset_iovec.iov_base = test_data + offset; - offset_iovec.iov_len = 1; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &offset_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, 1); - } - - /* Receive all the data from the many small records */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); - }; - - /* Test: receive partial application data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - /* Write a lot of very small records, but don't write the full - * expected test data size. */ - const size_t partial_size = sizeof(test_data) / 2; - struct iovec offset_iovec = { 0 }; - for (size_t offset = 0; offset < partial_size; offset++) { - offset_iovec.iov_base = test_data + offset; - offset_iovec.iov_len = 1; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &offset_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, 1); - } - - /* Receive the partial data */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, partial_size); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); - }; - }; - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/unstable/renegotiate.h" +#include "s2n_test.h" +#include "testlib/s2n_ktls_test_utils.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +bool s2n_custom_recv_fn_called = false; + +int s2n_expect_concurrent_error_recv_fn(void *io_context, uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_recv_fn_called = true; + + s2n_blocked_status blocked = 0; + ssize_t result = s2n_recv(conn, buf, len, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +static ssize_t s2n_test_ktls_recvmsg_cb(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + return *(ssize_t *) io_context; +} + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, + s2n_renegotiate_response *response) +{ + POSIX_ENSURE_REF(context); + size_t *count = (size_t *) context; + (*count)++; + *response = S2N_RENEGOTIATE_IGNORE; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* s2n_peek */ + { + /* We do full handshakes and send with a real connection here instead of + * just calling s2n_connection_set_secrets because s2n_peek depends on details + * of how data is encrypted, and we don't want to make any incorrect assumptions. + */ + + /* Safety check */ + EXPECT_EQUAL(s2n_peek(NULL), 0); + + const uint8_t test_data[100] = "hello world"; + const size_t test_data_size = sizeof(test_data); + + /* s2n_peek reports available plaintext bytes */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data */ + EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Initially, no data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), 0); + + /* Read some, but not all, of the data written */ + uint8_t output[sizeof(test_data)] = { 0 }; + const size_t expected_peek_size = 10; + const size_t recv_size = test_data_size - expected_peek_size; + EXPECT_EQUAL(s2n_recv(server_conn, output, recv_size, &blocked), recv_size); + + /* After a partial read, some data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), expected_peek_size); + + /* Read the rest of the data */ + EXPECT_EQUAL(s2n_recv(server_conn, output, expected_peek_size, &blocked), expected_peek_size); + + /* After the complete read, no data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + + /* s2n_peek doesn't report bytes belonging to partially read, still encrypted records */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Use stuffers for IO so that we can trigger a block on a read */ + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data */ + EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Drop some of the data */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); + + /* Try to read the data, but block */ + uint8_t output[sizeof(test_data)] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(test_data), &blocked), + S2N_ERR_IO_BLOCKED); + + /* conn->in contains data, but s2n_peek reports no data available */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + + /* s2n_peek doesn't report bytes belonging to post-handshake messages */ + if (s2n_is_tls13_fully_supported()) { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Use stuffers for IO so that we can trigger a block on a read */ + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Send a KeyUpdate message */ + s2n_atomic_flag_set(&client_conn->key_update_pending); + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending)); + + /* Drop some of the data */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); + + /* Try to read the KeyUpdate message, but block */ + uint8_t output[1] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(output), &blocked), + S2N_ERR_IO_BLOCKED); + + /* conn->in contains data, but s2n_peek reports no data available */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + }; + + /* s2n_recv cannot be called concurrently */ + { + /* Setup connection */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Setup bad recv callback */ + EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_expect_concurrent_error_recv_fn)); + EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, (void *) conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + uint8_t test_data[100] = { 0 }; + s2n_blocked_status blocked = 0; + s2n_custom_recv_fn_called = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_recv_fn_called); + + /* Cleanup */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_config_set_recv_multi_record */ + { + const uint8_t test_data_size = 100; + DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); + + const size_t recv_size = test_data_size * 2; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&output, recv_size)); + + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data, in three records */ + for (size_t i = 0; i < 3; i++) { + EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); + } + + /* Disable multi-record recv, set legacy behavior */ + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, false)); + + EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), test_data_size); + + /* Now enable multi record recv */ + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, true)); + + /* So we should be able to read the remaining two records in a single call */ + EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), recv_size); + } + } + + /* recv blocked status + * + * This test preserves the `blocked` parameter contract with various states of the connection + */ + { + const uint8_t test_data_size = 100; + const size_t record_count = 3; + DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); + + const size_t total_data_size = test_data_size * record_count; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&output, total_data_size)); + + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + for (size_t multi_record = 0; multi_record <= 1; multi_record++) { + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, multi_record)); + size_t max_recv_size = test_data_size; + + /* In multi-record, we can read all of the records in one go */ + if (multi_record) { + max_recv_size *= record_count; + } + + for (size_t read_size = 1; read_size <= total_data_size; read_size++) { + /* Write some data across multiple records */ + for (size_t send_count = 0; send_count < record_count; send_count++) { + EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); + } + + /* Call `s2n_recv` multiple times with an empty buffer to make sure that's handled correctly */ + for (size_t empty_count = 0; empty_count < 10; empty_count++) { + EXPECT_EQUAL(s2n_recv(server_conn, output.data, 0, &blocked), 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + + size_t recv_bytes = 0; + while (recv_bytes < total_data_size) { + size_t expected_recv_size = S2N_MIN(S2N_MIN(read_size, total_data_size - recv_bytes), max_recv_size); + + /* Perform the actual recv call */ + ssize_t actual_recv_size = s2n_recv(server_conn, output.data, read_size, &blocked); + + if (multi_record) { + /* In multi-record mode we should always read the size we expect */ + EXPECT_EQUAL(actual_recv_size, expected_recv_size); + } else { + /* In single-record mode, we could potentially get a smaller read than a full record due to + * random record boundaries so we can only assert it's within the range we expect. */ + EXPECT_NOT_EQUAL(actual_recv_size, 0); + EXPECT_TRUE(actual_recv_size <= expected_recv_size); + } + + /* Keep track of the total amount of bytes read */ + recv_bytes += actual_recv_size; + + /* Due to the history of this API, some applications depend on the blocked status to know if + * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. + * + * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing + * without conflating being blocked on reading from the OS socket vs blocked on the application's + * buffer size. + */ + if (s2n_peek(server_conn) == 0) { + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } else { + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + } + + /* The final read should return blocked since we don't have any more data from the socket */ + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output.data, read_size, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Call `s2n_recv` multiple times at the end of the stream after receiving a shutdown */ + for (size_t eos_count = 0; eos_count < 10; eos_count++) { + EXPECT_EQUAL(s2n_recv(server_conn, output.data, output.size, &blocked), 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + }; + + /* Test with ktls */ + { + uint8_t test_data[100] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: receive all requested application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + }; + + /* Test: receive partial application data */ + { + const size_t partial_size = sizeof(test_data) / 2; + struct iovec partial_iovec = test_iovec; + partial_iovec.iov_len = partial_size; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &partial_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, partial_size); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + }; + + /* Test: drain buffered application data */ + { + const size_t partial_size = sizeof(test_data) / 2; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + /* The first read doesn't read all the available data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, partial_size, &blocked); + EXPECT_EQUAL(read, partial_size); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); + EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); + + /* The second read drains the remaining data */ + const size_t remaining = sizeof(test_data) - partial_size; + read = s2n_recv(conn, output + read, remaining, &blocked); + EXPECT_EQUAL(read, remaining); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); + EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); + }; + + /* Test: receive blocks */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + }; + + /* Test: receive indicates end-of-data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + ssize_t ret_val = 0; + EXPECT_OK(s2n_ktls_set_recvmsg_cb(conn, s2n_test_ktls_recvmsg_cb, &ret_val)); + + uint8_t output[10] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + }; + + /* Test: receive alert */ + { + /* Use a specific alert -- if we just use random data, we might + * stumble into a close_notify or user_canceled. + */ + uint8_t alert_data[] = { + S2N_TLS_ALERT_LEVEL_FATAL, + S2N_TLS_ALERT_DECRYPT_ERROR, + }; + const struct iovec alert_iovec = { + .iov_base = alert_data, + .iov_len = sizeof(alert_data), + }; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &alert_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(alert_data)); + + uint8_t output[10] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_ALERT); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + }; + + /* Test: receive handshake message */ + { + DEFER_CLEANUP(struct s2n_config *reneg_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(reneg_config); + + size_t reneg_request_count = 0; + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(reneg_config, + s2n_test_reneg_req_cb, &reneg_request_count)); + + uint8_t hello_request[TLS_HANDSHAKE_HEADER_LENGTH] = { TLS_HELLO_REQUEST }; + const struct iovec hello_request_iovec = { + .iov_base = hello_request, + .iov_len = sizeof(hello_request), + }; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, reneg_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + + /* Send the handshake message */ + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_HANDSHAKE, + &hello_request_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(hello_request)); + + /* Also send some application data */ + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + /* Verify that we received the application data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + + /* Verify that we received and processed the handshake message */ + EXPECT_EQUAL(reneg_request_count, 1); + }; + + /* Test: Multirecord mode */ + { + DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(multi_config); + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true)); + + /* Test: receive all requested application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write a lot of very small records */ + struct iovec offset_iovec = { 0 }; + for (size_t offset = 0; offset < sizeof(test_data); offset++) { + offset_iovec.iov_base = test_data + offset; + offset_iovec.iov_len = 1; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, 1); + } + + /* Receive all the data from the many small records */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); + }; + + /* Test: receive partial application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write a lot of very small records, but don't write the full + * expected test data size. */ + const size_t partial_size = sizeof(test_data) / 2; + struct iovec offset_iovec = { 0 }; + for (size_t offset = 0; offset < partial_size; offset++) { + offset_iovec.iov_base = test_data + offset; + offset_iovec.iov_len = 1; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, 1); + } + + /* Receive the partial data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, partial_size); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); + }; + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_release_non_empty_buffers_test.c b/tests/unit/s2n_release_non_empty_buffers_test.c index 65b8d221d32..9843c5c5d81 100644 --- a/tests/unit/s2n_release_non_empty_buffers_test.c +++ b/tests/unit/s2n_release_non_empty_buffers_test.c @@ -1,207 +1,208 @@ -/* - * 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 -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_random.h" - -#define MAX_BUF_SIZE 10000 - -static const uint8_t buf_to_send[1023] = { 27 }; - -int mock_client(struct s2n_test_io_pair *io_pair) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - - conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - s2n_connection_set_config(conn, client_config); - - /* Unlike the server, the client just passes ownership of I/O to s2n */ - s2n_connection_set_io_pair(conn, io_pair); - - result = s2n_negotiate(conn, &blocked); - if (result < 0) { - _exit(1); - } - - if (s2n_send(conn, buf_to_send, sizeof(buf_to_send), &blocked) != sizeof(buf_to_send)) { - _exit(2); - } - - s2n_shutdown(conn, &blocked); - s2n_connection_free(conn); - s2n_config_free(client_config); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_CLIENT)); - s2n_cleanup(); - - exit(0); -} - -/** - * This test ensures that we don't allow releasing connection buffers if they contain part - * of the unprocessed record, avoiding connection corruption. - */ -int main(int argc, char **argv) -{ - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - uint8_t buf[sizeof(buf_to_send)]; - uint32_t n = 0; - ssize_t ret = 0; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Create a pipe */ - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Run the client */ - mock_client(&io_pair); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Make pipes non-blocking */ - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); - - /* Negotiate the handshake. */ - do { - ret = s2n_negotiate(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - - /* check to see if we need to copy more over from the pipes to the buffers - * to continue the handshake */ - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (blocked); - - /* Receive only 100 bytes of the record and try to call s2n_recv */ - while (n < 100) { - ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, 100 - n, &n); - - if (errno == EAGAIN || errno == EWOULDBLOCK) { - continue; - } else { - POSIX_GUARD(ret); - } - } - - /* s2n_recv should fail as we received only part of the record */ - EXPECT_FAILURE(s2n_recv(conn, buf, sizeof(buf), &blocked)); - EXPECT_TRUE(blocked == S2N_BLOCKED_ON_READ); - - /* Now try to release the buffers and expect failure as buffers are not empty */ - EXPECT_FAILURE(s2n_connection_release_buffers(conn)); - - /* Read the rest of the buffer and expect s2n_recv to succeed */ - do { - ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - - if (errno == EAGAIN || errno == EWOULDBLOCK) { - continue; - } else { - POSIX_GUARD(ret); - } - - ret = s2n_recv(conn, buf, sizeof(buf), &blocked); - } while (ret < 0 && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED - && blocked == S2N_BLOCKED_ON_READ); - - /* Expect that we read the data client sent us */ - EXPECT_TRUE(ret == sizeof(buf_to_send)); - EXPECT_TRUE(memcmp(buf, buf_to_send, ret) == 0); - - /* Since full record was processed, we should be able to release buffers */ - EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); - - /* Shutdown after negotiating */ - uint8_t server_shutdown = 0; - do { - ret = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - if (ret == 0) { - server_shutdown = 1; - } - - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (!server_shutdown); - - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - - /* Clean up */ - free(cert_chain_pem); - free(private_key_pem); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - s2n_cleanup(); - - END_TEST(); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_random.h" + +#define MAX_BUF_SIZE 10000 + +static const uint8_t buf_to_send[1023] = { 27 }; + +int mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + + conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + s2n_connection_set_config(conn, client_config); + + /* Unlike the server, the client just passes ownership of I/O to s2n */ + s2n_connection_set_io_pair(conn, io_pair); + + result = s2n_negotiate(conn, &blocked); + if (result < 0) { + _exit(1); + } + + if (s2n_send(conn, buf_to_send, sizeof(buf_to_send), &blocked) != sizeof(buf_to_send)) { + _exit(2); + } + + s2n_shutdown(conn, &blocked); + s2n_connection_free(conn); + s2n_config_free(client_config); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_CLIENT)); + s2n_cleanup(); + + exit(0); +} + +/** + * This test ensures that we don't allow releasing connection buffers if they contain part + * of the unprocessed record, avoiding connection corruption. + */ +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + uint8_t buf[sizeof(buf_to_send)]; + uint32_t n = 0; + ssize_t ret = 0; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Create a pipe */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + mock_client(&io_pair); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Make pipes non-blocking */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); + + /* Negotiate the handshake. */ + do { + ret = s2n_negotiate(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + + /* check to see if we need to copy more over from the pipes to the buffers + * to continue the handshake */ + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (blocked); + + /* Receive only 100 bytes of the record and try to call s2n_recv */ + while (n < 100) { + ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, 100 - n, &n); + + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + POSIX_GUARD(ret); + } + } + + /* s2n_recv should fail as we received only part of the record */ + EXPECT_FAILURE(s2n_recv(conn, buf, sizeof(buf), &blocked)); + EXPECT_TRUE(blocked == S2N_BLOCKED_ON_READ); + + /* Now try to release the buffers and expect failure as buffers are not empty */ + EXPECT_FAILURE(s2n_connection_release_buffers(conn)); + + /* Read the rest of the buffer and expect s2n_recv to succeed */ + do { + ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + POSIX_GUARD(ret); + } + + ret = s2n_recv(conn, buf, sizeof(buf), &blocked); + } while (ret < 0 && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED + && blocked == S2N_BLOCKED_ON_READ); + + /* Expect that we read the data client sent us */ + EXPECT_TRUE(ret == sizeof(buf_to_send)); + EXPECT_TRUE(memcmp(buf, buf_to_send, ret) == 0); + + /* Since full record was processed, we should be able to release buffers */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + + /* Shutdown after negotiating */ + uint8_t server_shutdown = 0; + do { + ret = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + if (ret == 0) { + server_shutdown = 1; + } + + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (!server_shutdown); + + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Clean up */ + free(cert_chain_pem); + free(private_key_pem); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + s2n_cleanup(); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_renegotiate_test.c b/tests/unit/s2n_renegotiate_test.c index b85c9599054..5d5afa64e08 100644 --- a/tests/unit/s2n_renegotiate_test.c +++ b/tests/unit/s2n_renegotiate_test.c @@ -1,620 +1,621 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_renegotiate.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" - -struct s2n_reneg_test_case { - uint8_t protocol_version; - struct s2n_cipher_suite *cipher_suite; - uint8_t max_frag_code; -}; - -const struct s2n_reneg_test_case dhe_test_cases[] = { - { - .protocol_version = S2N_SSLv3, - .cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha, - .max_frag_code = 0, - }, - { - .protocol_version = S2N_TLS10, - .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_512, - }, - { - .protocol_version = S2N_TLS11, - .cipher_suite = &s2n_dhe_rsa_with_aes_256_cbc_sha, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_1024, - }, - { - .protocol_version = S2N_TLS12, - .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha256, - .max_frag_code = 0, - }, - { - .protocol_version = S2N_TLS12, - .cipher_suite = &s2n_dhe_rsa_with_aes_256_gcm_sha384, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_2048, - }, - { - .protocol_version = S2N_TLS12, - .cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_4096, - }, -}; - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - char dh_params[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dh_params, sizeof(dh_params))); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - uint8_t app_data[] = "smaller hello world"; - uint8_t large_app_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = "hello world and a lot of zeroes"; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - /* Test s2n_renegotiate_wipe */ - { - /* Default IO unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* "io_pair" just uses file descriptors and the default io callbacks */ - 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_EQUAL(client_conn->send, s2n_socket_write); - EXPECT_TRUE(client_conn->managed_send_io); - EXPECT_EQUAL(client_conn->recv, s2n_socket_read); - EXPECT_TRUE(client_conn->managed_recv_io); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - }; - - /* Custom IO callbacks unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* "io_stuffers" use custom IO callbacks written for tests */ - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - EXPECT_NOT_EQUAL(client_conn->send, s2n_socket_write); - EXPECT_FALSE(client_conn->managed_send_io); - EXPECT_NOT_EQUAL(client_conn->recv, s2n_socket_read); - EXPECT_FALSE(client_conn->managed_recv_io); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - }; - - /* Fragment size unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(client_conn)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t original_out_size = s2n_stuffer_data_available(&out); - EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t wiped_out_size = s2n_stuffer_data_available(&out); - EXPECT_EQUAL(original_out_size, wiped_out_size); - }; - - /* Forced very small fragment size unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - const size_t small_frag_len = S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE; - client_conn->max_outgoing_fragment_length = small_frag_len; - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t original_out_size = s2n_stuffer_data_available(&out); - EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, small_frag_len); - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t wiped_out_size = s2n_stuffer_data_available(&out); - EXPECT_EQUAL(original_out_size, wiped_out_size); - }; - - /* Handshake succeeds after wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - }; - - /* Handshake with added client auth succeeds after wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(client_conn)); - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(server_conn)); - }; - - /* Handshake with different fragment length succeeds after wipe */ - { - DEFER_CLEANUP(struct s2n_config *small_frag_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(small_frag_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(small_frag_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(small_frag_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(small_frag_config, "20240501")); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(small_frag_config)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(small_frag_config, S2N_TLS_MAX_FRAG_LEN_512)); - - DEFER_CLEANUP(struct s2n_config *larger_frag_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(larger_frag_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(larger_frag_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(larger_frag_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(larger_frag_config, "20240501")); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(larger_frag_config)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(larger_frag_config, S2N_TLS_MAX_FRAG_LEN_4096)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, small_frag_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, small_frag_config)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - }; - - /* renegotiation_info is non-empty after wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the renegotiation_info was empty / missing */ - ssize_t renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, - S2N_EXTENSION_RENEGOTIATION_INFO); - EXPECT_EQUAL(renegotiation_info_len, 0); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_TRUE(client_conn->handshake.finished_len > 0); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the renegotiation_info was not empty / missing */ - renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, - S2N_EXTENSION_RENEGOTIATION_INFO); - EXPECT_TRUE(renegotiation_info_len > sizeof(uint8_t)); - }; - - /* Wipe of insecure connection not allowed */ - { - 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, config)); - client_conn->actual_protocol_version = S2N_TLS12; - - EXPECT_FALSE(client_conn->secure_renegotiation); - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_NO_RENEGOTIATION); - client_conn->secure_renegotiation = true; - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe of TLS1.3 connection not allowed */ - { - 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, config)); - client_conn->secure_renegotiation = true; - - EXPECT_TRUE(client_conn->actual_protocol_version > S2N_TLS12); - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - client_conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe mid-write not allowed */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Initiate a partial send */ - uint16_t partial_send_len = client_conn->max_outgoing_fragment_length / 2; - DEFER_CLEANUP(struct s2n_stuffer small_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_alloc(&small_out, partial_send_len)); - EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&small_out, client_conn)); - EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); - - /* Finish the send */ - EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, client_conn)); - EXPECT_EQUAL(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), sizeof(large_app_data)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe mid-read not allowed */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Initiate a partial recv */ - uint16_t partial_recv_len = sizeof(app_data) / 2; - uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, partial_recv_len, &blocked), partial_recv_len); - EXPECT_BYTEARRAY_EQUAL(app_data, recv_buffer, partial_recv_len); - EXPECT_TRUE(s2n_peek(client_conn) > 0); - - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); - - /* Finish the recv */ - size_t remaining_recv_len = sizeof(app_data) - partial_recv_len; - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, remaining_recv_len, &blocked), remaining_recv_len); - EXPECT_BYTEARRAY_EQUAL(app_data + partial_recv_len, recv_buffer, remaining_recv_len); - EXPECT_EQUAL(s2n_peek(client_conn), 0); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe with next record buffered allowed, and data preserved */ - { - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* Write two records, but only receive one. - * Due to recv buffering, the second record will be read and buffered - * at the same time as the first record, but not processed yet. - */ - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_peek(client), 0); - EXPECT_TRUE(s2n_stuffer_data_available(&client->buffer_in)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client)); - - /* The second record is still available to read after the wipe */ - EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - }; - }; - - /* Test the basic renegotiation mechanism with a variety of connection parameters. - * A client should always be able to receive and negotiate after wiping a connection for renegotiation. - */ - { - /* Setup a security policy that only contains one cipher */ - struct s2n_cipher_preferences one_cipher_preference = { .count = 1, .suites = NULL }; - struct s2n_security_policy one_cipher_policy = security_policy_test_all; - one_cipher_policy.cipher_preferences = &one_cipher_preference; - - /* This config can only be used for servers, because currently only servers can have multiple certs */ - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dh_params)); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); - server_config->security_policy = &one_cipher_policy; - - /* Setting the max fragment length will require modifying the client config */ - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - /* The oldest version s2n-tls supports is SSLv3. - * However, SSLv3 requires MD5 for its PRF. - */ - uint8_t oldest_tested_version = S2N_SSLv3; - if (!s2n_hash_is_available(S2N_HASH_MD5)) { - oldest_tested_version = S2N_TLS10; - } - - struct s2n_reneg_test_case test_cases[2000] = { 0 }; - size_t test_cases_count = 0; - - /* FFDHE is very, VERY slow. - * To avoid this test taking multiple minutes, - * we choose a limited number of dhe test cases. - */ - for (size_t i = 0; i < s2n_array_len(dhe_test_cases); i++) { - if (!dhe_test_cases[i].cipher_suite->available) { - continue; - } - if (dhe_test_cases[i].protocol_version < oldest_tested_version) { - continue; - } - test_cases[test_cases_count] = dhe_test_cases[i]; - test_cases_count++; - EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); - } - EXPECT_TRUE(test_cases_count > 0); - - const struct s2n_cipher_preferences *ciphers = security_policy_test_all.cipher_preferences; - for (uint8_t version = oldest_tested_version; version < S2N_TLS13; version++) { - for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { - struct s2n_cipher_suite *cipher = ciphers->suites[cipher_i]; - - if (!cipher->available) { - continue; - } - - if (version < cipher->minimum_required_tls_version) { - continue; - } - - if (cipher->key_exchange_alg == &s2n_dhe) { - /* See dhe_test_cases */ - continue; - } - - for (size_t max_frag_i = 0; max_frag_i < s2n_array_len(mfl_code_to_length); max_frag_i++) { - test_cases[test_cases_count] = (struct s2n_reneg_test_case){ - .protocol_version = version, - .cipher_suite = ciphers->suites[cipher_i], - .max_frag_code = max_frag_i, - }; - test_cases_count++; - EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); - } - } - } - - for (size_t i = 0; i < test_cases_count; i++) { - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - 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)); - - /* Setup test case */ - server_conn->server_protocol_version = test_cases[i].protocol_version; - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, test_cases[i].max_frag_code)); - one_cipher_preference.suites = &test_cases[i].cipher_suite; - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify test case setup */ - EXPECT_EQUAL(client_conn->actual_protocol_version, test_cases[i].protocol_version); - EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, mfl_code_to_length[test_cases[i].max_frag_code]); - if (test_cases[i].protocol_version > S2N_SSLv3) { - EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite); - } else { - EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite->sslv3_cipher_suite); - } - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - /* Test that the client can still receive application data */ - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - - /* Test that a second handshake can occur. */ - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_renegotiate.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +struct s2n_reneg_test_case { + uint8_t protocol_version; + struct s2n_cipher_suite *cipher_suite; + uint8_t max_frag_code; +}; + +const struct s2n_reneg_test_case dhe_test_cases[] = { + { + .protocol_version = S2N_SSLv3, + .cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha, + .max_frag_code = 0, + }, + { + .protocol_version = S2N_TLS10, + .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_512, + }, + { + .protocol_version = S2N_TLS11, + .cipher_suite = &s2n_dhe_rsa_with_aes_256_cbc_sha, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_1024, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha256, + .max_frag_code = 0, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_aes_256_gcm_sha384, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_2048, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_4096, + }, +}; + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + char dh_params[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dh_params, sizeof(dh_params))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + uint8_t app_data[] = "smaller hello world"; + uint8_t large_app_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = "hello world and a lot of zeroes"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test s2n_renegotiate_wipe */ + { + /* Default IO unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* "io_pair" just uses file descriptors and the default io callbacks */ + 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_EQUAL(client_conn->send, s2n_socket_write); + EXPECT_TRUE(client_conn->managed_send_io); + EXPECT_EQUAL(client_conn->recv, s2n_socket_read); + EXPECT_TRUE(client_conn->managed_recv_io); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + + /* Custom IO callbacks unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* "io_stuffers" use custom IO callbacks written for tests */ + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + EXPECT_NOT_EQUAL(client_conn->send, s2n_socket_write); + EXPECT_FALSE(client_conn->managed_send_io); + EXPECT_NOT_EQUAL(client_conn->recv, s2n_socket_read); + EXPECT_FALSE(client_conn->managed_recv_io); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + + /* Fragment size unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t original_out_size = s2n_stuffer_data_available(&out); + EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t wiped_out_size = s2n_stuffer_data_available(&out); + EXPECT_EQUAL(original_out_size, wiped_out_size); + }; + + /* Forced very small fragment size unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + const size_t small_frag_len = S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE; + client_conn->max_outgoing_fragment_length = small_frag_len; + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t original_out_size = s2n_stuffer_data_available(&out); + EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, small_frag_len); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t wiped_out_size = s2n_stuffer_data_available(&out); + EXPECT_EQUAL(original_out_size, wiped_out_size); + }; + + /* Handshake succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Handshake with added client auth succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(client_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(server_conn)); + }; + + /* Handshake with different fragment length succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_config *small_frag_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(small_frag_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(small_frag_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(small_frag_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(small_frag_config, "20240501")); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(small_frag_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(small_frag_config, S2N_TLS_MAX_FRAG_LEN_512)); + + DEFER_CLEANUP(struct s2n_config *larger_frag_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(larger_frag_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(larger_frag_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(larger_frag_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(larger_frag_config, "20240501")); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(larger_frag_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(larger_frag_config, S2N_TLS_MAX_FRAG_LEN_4096)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, small_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, small_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* renegotiation_info is non-empty after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the renegotiation_info was empty / missing */ + ssize_t renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, + S2N_EXTENSION_RENEGOTIATION_INFO); + EXPECT_EQUAL(renegotiation_info_len, 0); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_TRUE(client_conn->handshake.finished_len > 0); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the renegotiation_info was not empty / missing */ + renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, + S2N_EXTENSION_RENEGOTIATION_INFO); + EXPECT_TRUE(renegotiation_info_len > sizeof(uint8_t)); + }; + + /* Wipe of insecure connection not allowed */ + { + 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, config)); + client_conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FALSE(client_conn->secure_renegotiation); + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_NO_RENEGOTIATION); + client_conn->secure_renegotiation = true; + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe of TLS1.3 connection not allowed */ + { + 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, config)); + client_conn->secure_renegotiation = true; + + EXPECT_TRUE(client_conn->actual_protocol_version > S2N_TLS12); + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + client_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe mid-write not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Initiate a partial send */ + uint16_t partial_send_len = client_conn->max_outgoing_fragment_length / 2; + DEFER_CLEANUP(struct s2n_stuffer small_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&small_out, partial_send_len)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&small_out, client_conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); + + /* Finish the send */ + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, client_conn)); + EXPECT_EQUAL(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), sizeof(large_app_data)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe mid-read not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Initiate a partial recv */ + uint16_t partial_recv_len = sizeof(app_data) / 2; + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, partial_recv_len, &blocked), partial_recv_len); + EXPECT_BYTEARRAY_EQUAL(app_data, recv_buffer, partial_recv_len); + EXPECT_TRUE(s2n_peek(client_conn) > 0); + + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); + + /* Finish the recv */ + size_t remaining_recv_len = sizeof(app_data) - partial_recv_len; + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, remaining_recv_len, &blocked), remaining_recv_len); + EXPECT_BYTEARRAY_EQUAL(app_data + partial_recv_len, recv_buffer, remaining_recv_len); + EXPECT_EQUAL(s2n_peek(client_conn), 0); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe with next record buffered allowed, and data preserved */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Write two records, but only receive one. + * Due to recv buffering, the second record will be read and buffered + * at the same time as the first record, but not processed yet. + */ + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_peek(client), 0); + EXPECT_TRUE(s2n_stuffer_data_available(&client->buffer_in)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client)); + + /* The second record is still available to read after the wipe */ + EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + }; + + /* Test the basic renegotiation mechanism with a variety of connection parameters. + * A client should always be able to receive and negotiate after wiping a connection for renegotiation. + */ + { + /* Setup a security policy that only contains one cipher */ + struct s2n_cipher_preferences one_cipher_preference = { .count = 1, .suites = NULL }; + struct s2n_security_policy one_cipher_policy = security_policy_test_all; + one_cipher_policy.cipher_preferences = &one_cipher_preference; + + /* This config can only be used for servers, because currently only servers can have multiple certs */ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dh_params)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + server_config->security_policy = &one_cipher_policy; + + /* Setting the max fragment length will require modifying the client config */ + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + /* The oldest version s2n-tls supports is SSLv3. + * However, SSLv3 requires MD5 for its PRF. + */ + uint8_t oldest_tested_version = S2N_SSLv3; + if (!s2n_hash_is_available(S2N_HASH_MD5)) { + oldest_tested_version = S2N_TLS10; + } + + struct s2n_reneg_test_case test_cases[2000] = { 0 }; + size_t test_cases_count = 0; + + /* FFDHE is very, VERY slow. + * To avoid this test taking multiple minutes, + * we choose a limited number of dhe test cases. + */ + for (size_t i = 0; i < s2n_array_len(dhe_test_cases); i++) { + if (!dhe_test_cases[i].cipher_suite->available) { + continue; + } + if (dhe_test_cases[i].protocol_version < oldest_tested_version) { + continue; + } + test_cases[test_cases_count] = dhe_test_cases[i]; + test_cases_count++; + EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); + } + EXPECT_TRUE(test_cases_count > 0); + + const struct s2n_cipher_preferences *ciphers = security_policy_test_all.cipher_preferences; + for (uint8_t version = oldest_tested_version; version < S2N_TLS13; version++) { + for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { + struct s2n_cipher_suite *cipher = ciphers->suites[cipher_i]; + + if (!cipher->available) { + continue; + } + + if (version < cipher->minimum_required_tls_version) { + continue; + } + + if (cipher->key_exchange_alg == &s2n_dhe) { + /* See dhe_test_cases */ + continue; + } + + for (size_t max_frag_i = 0; max_frag_i < s2n_array_len(mfl_code_to_length); max_frag_i++) { + test_cases[test_cases_count] = (struct s2n_reneg_test_case){ + .protocol_version = version, + .cipher_suite = ciphers->suites[cipher_i], + .max_frag_code = max_frag_i, + }; + test_cases_count++; + EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); + } + } + } + + for (size_t i = 0; i < test_cases_count; i++) { + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + 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)); + + /* Setup test case */ + server_conn->server_protocol_version = test_cases[i].protocol_version; + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, test_cases[i].max_frag_code)); + one_cipher_preference.suites = &test_cases[i].cipher_suite; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify test case setup */ + EXPECT_EQUAL(client_conn->actual_protocol_version, test_cases[i].protocol_version); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, mfl_code_to_length[test_cases[i].max_frag_code]); + if (test_cases[i].protocol_version > S2N_SSLv3) { + EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite); + } else { + EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite->sslv3_cipher_suite); + } + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + /* Test that the client can still receive application data */ + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + + /* Test that a second handshake can occur. */ + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_rsa_pss_test.c b/tests/unit/s2n_rsa_pss_test.c index 4f6b7be6bcb..d8ec97cd4ce 100644 --- a/tests/unit/s2n_rsa_pss_test.c +++ b/tests/unit/s2n_rsa_pss_test.c @@ -1,350 +1,351 @@ -/* - * 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 "crypto/s2n_rsa_pss.h" - -#include "crypto/s2n_certificate.h" -#include "crypto/s2n_dhe.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "utils/s2n_random.h" - -int s2n_flip_random_bit(struct s2n_blob *blob) -{ - /* Flip a random bit in the blob */ - uint64_t byte_flip_pos = 0; - POSIX_GUARD_RESULT(s2n_public_random(blob->size, &byte_flip_pos)); - uint64_t bit_flip_pos = 0; - POSIX_GUARD_RESULT(s2n_public_random(8, &bit_flip_pos)); - - uint8_t mask = 0x01 << (uint8_t) bit_flip_pos; - blob->data[byte_flip_pos] ^= mask; - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Don't use RSA-PSS certs if unsupported */ -#if !RSA_PSS_CERTS_SUPPORTED - EXPECT_FALSE(s2n_is_rsa_pss_certs_supported()); - END_TEST(); -#endif - EXPECT_TRUE(s2n_is_rsa_pss_certs_supported()); - - /* Check that s2n_is_rsa_pss_certs_supported() is a superset of s2n_is_rsa_pss_signing_supported() */ - EXPECT_TRUE(s2n_is_rsa_pss_signing_supported()); - - /* Positive Test: Ensure we can sign and verify a randomly generated signature. - * Pseudocode: assert(SUCCESS == verify(Key1_public, message, sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - - /* Load the Private Key */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - /* Load the Public Key */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - EXPECT_EQUAL(pkey_type, S2N_PKEY_TYPE_RSA_PSS); - - /* Sign and Verify a Random Value to ensure that Public and Private Key Matches */ - EXPECT_SUCCESS(s2n_pkey_match(&public_key, chain_and_key->private_key)); - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(cert_chain_pem); - free(private_key_pem); - - /* Verify repeated key frees. - * (Later calls should be a no-op) */ - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - }; - - /* Negative Test: Loading mismatching RSA PSS Public/Private Keys will fail. - * Pseudocode: assert(FAILURE == load_pem_pair(Key1_public, Key2_private)) - */ - { - struct s2n_config *server_config = NULL; - char *leaf_cert_chain_pem = NULL; - char *root_private_key_pem = NULL; - struct s2n_cert_chain_and_key *misconfigured_chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - - EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Incorrectly reading the CA's Private Key from disk, not the Leaf's Private Key */ - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(misconfigured_chain_and_key = s2n_cert_chain_and_key_new()); - - /* Attempting to Load RSA_PSS Certificate with wrong RSA_PSS Key should fail */ - EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(misconfigured_chain_and_key, leaf_cert_chain_pem, root_private_key_pem)); - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(misconfigured_chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(leaf_cert_chain_pem); - free(root_private_key_pem); - }; - - /* Negative Test: Ensure flipping a bit in the signature is rejected - * Pseudocode: assert(FAILURE == verify(Key1_public, message, bitflip(sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - /* Parse the leaf cert for the public key and certificate type */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); - EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); - - struct s2n_pkey *private_key = chain_and_key->private_key; - { - EXPECT_NOT_NULL(public_key.pkey); - EXPECT_NOT_NULL(private_key); - EXPECT_NOT_NULL(private_key->pkey); - - /* Generate a random blob to sign and verify */ - s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); - EXPECT_OK(s2n_get_private_random_data(&random_msg)); - - /* Sign/Verify API's only accept Hashes, so hash our Random Data */ - DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); - EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); - - DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); - EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); - - /* Sign and Verify the Hash of the Random Blob */ - s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); - EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); - - /* Flip a random bit in the signature */ - EXPECT_SUCCESS(s2n_flip_random_bit(&signature_data)); - EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); - }; - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(cert_chain_pem); - free(private_key_pem); - }; - - /* Negative Test: Ensure Verification with wrong key fails - * Pseudocode: assert(FAILURE == verify(Key2_public, message, sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *root_cert_chain_pem = NULL; - char *root_private_key_pem = NULL; - char *leaf_cert_chain_pem = NULL; - char *leaf_private_key_pem = NULL; - struct s2n_cert_chain_and_key *root_chain_and_key = NULL; - struct s2n_cert_chain_and_key *leaf_chain_and_key = NULL; - struct s2n_pkey root_public_key = { 0 }; - struct s2n_pkey leaf_public_key = { 0 }; - s2n_pkey_type root_pkey_type = S2N_PKEY_TYPE_UNKNOWN; - s2n_pkey_type leaf_pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(root_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(leaf_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, root_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, leaf_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(root_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_NOT_NULL(leaf_chain_and_key = s2n_cert_chain_and_key_new()); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(root_chain_and_key, root_cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(root_chain_and_key, root_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(leaf_chain_and_key, leaf_cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(leaf_chain_and_key, leaf_private_key_pem)); - - /* Parse the cert for the public key and certificate type */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&root_public_key, &root_pkey_type, &root_chain_and_key->cert_chain->head->raw)); - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&leaf_public_key, &leaf_pkey_type, &leaf_chain_and_key->cert_chain->head->raw)); - EXPECT_NOT_EQUAL(root_pkey_type, S2N_PKEY_TYPE_UNKNOWN); - EXPECT_NOT_EQUAL(leaf_pkey_type, S2N_PKEY_TYPE_UNKNOWN); - - EXPECT_SUCCESS(s2n_cert_set_cert_type(root_chain_and_key->cert_chain->head, root_pkey_type)); - EXPECT_SUCCESS(s2n_cert_set_cert_type(leaf_chain_and_key->cert_chain->head, leaf_pkey_type)); - - struct s2n_pkey *root_private_key = root_chain_and_key->private_key; - struct s2n_pkey *leaf_private_key = leaf_chain_and_key->private_key; - { - EXPECT_NOT_NULL(root_public_key.pkey); - EXPECT_NOT_NULL(leaf_public_key.pkey); - - EXPECT_NOT_NULL(root_private_key); - EXPECT_NOT_NULL(root_private_key->pkey); - EXPECT_NOT_NULL(leaf_private_key); - EXPECT_NOT_NULL(leaf_private_key->pkey); - - /* Generate a random blob to sign and verify */ - s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); - EXPECT_OK(s2n_get_private_random_data(&random_msg)); - - /* Sign/Verify API's only accept Hashes, so hash our Random Data */ - DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); - EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); - - DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); - EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); - - /* Sign and Verify the Hash of the Random Blob */ - s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); - - /* Sign with Root's Key, but verify with Leaf's Key. This should fail. */ - EXPECT_SUCCESS(s2n_pkey_sign(root_private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); - EXPECT_FAILURE(s2n_pkey_verify(&leaf_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); - }; - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(root_chain_and_key)); - EXPECT_SUCCESS(s2n_pkey_free(&root_public_key)); - free(root_cert_chain_pem); - free(root_private_key_pem); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(leaf_chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&leaf_public_key)); - free(leaf_cert_chain_pem); - free(leaf_private_key_pem); - }; - - /* Negative Test: Ensure flipping a bit in message given to verification fails - * Pseudocode: assert(FAILURE == verify(Key1_public, bitflip(message), sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - /* Parse the leaf cert for the public key and certificate type */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); - EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); - - struct s2n_pkey *private_key = chain_and_key->private_key; - { - EXPECT_NOT_NULL(public_key.pkey); - EXPECT_NOT_NULL(private_key); - EXPECT_NOT_NULL(private_key->pkey); - - /* Generate a random blob to sign and verify */ - s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); - EXPECT_OK(s2n_get_private_random_data(&random_msg)); - - /* Sign/Verify API's only accept Hashes, so hash our Random Data */ - DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); - EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); - - /* Flip a random bit in the message before verification */ - EXPECT_SUCCESS(s2n_flip_random_bit(&random_msg)); - - DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); - EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); - - /* Sign and Verify the Hash of the Random Blob */ - s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); - EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); - EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); - }; - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(cert_chain_pem); - free(private_key_pem); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_rsa_pss.h" + +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_dhe.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_random.h" + +int s2n_flip_random_bit(struct s2n_blob *blob) +{ + /* Flip a random bit in the blob */ + uint64_t byte_flip_pos = 0; + POSIX_GUARD_RESULT(s2n_public_random(blob->size, &byte_flip_pos)); + uint64_t bit_flip_pos = 0; + POSIX_GUARD_RESULT(s2n_public_random(8, &bit_flip_pos)); + + uint8_t mask = 0x01 << (uint8_t) bit_flip_pos; + blob->data[byte_flip_pos] ^= mask; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Don't use RSA-PSS certs if unsupported */ +#if !RSA_PSS_CERTS_SUPPORTED + EXPECT_FALSE(s2n_is_rsa_pss_certs_supported()); + END_TEST(); +#endif + EXPECT_TRUE(s2n_is_rsa_pss_certs_supported()); + + /* Check that s2n_is_rsa_pss_certs_supported() is a superset of s2n_is_rsa_pss_signing_supported() */ + EXPECT_TRUE(s2n_is_rsa_pss_signing_supported()); + + /* Positive Test: Ensure we can sign and verify a randomly generated signature. + * Pseudocode: assert(SUCCESS == verify(Key1_public, message, sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + /* Load the Private Key */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + /* Load the Public Key */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_EQUAL(pkey_type, S2N_PKEY_TYPE_RSA_PSS); + + /* Sign and Verify a Random Value to ensure that Public and Private Key Matches */ + EXPECT_SUCCESS(s2n_pkey_match(&public_key, chain_and_key->private_key)); + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + + /* Verify repeated key frees. + * (Later calls should be a no-op) */ + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + }; + + /* Negative Test: Loading mismatching RSA PSS Public/Private Keys will fail. + * Pseudocode: assert(FAILURE == load_pem_pair(Key1_public, Key2_private)) + */ + { + struct s2n_config *server_config = NULL; + char *leaf_cert_chain_pem = NULL; + char *root_private_key_pem = NULL; + struct s2n_cert_chain_and_key *misconfigured_chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + + EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Incorrectly reading the CA's Private Key from disk, not the Leaf's Private Key */ + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(misconfigured_chain_and_key = s2n_cert_chain_and_key_new()); + + /* Attempting to Load RSA_PSS Certificate with wrong RSA_PSS Key should fail */ + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(misconfigured_chain_and_key, leaf_cert_chain_pem, root_private_key_pem)); + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(misconfigured_chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(leaf_cert_chain_pem); + free(root_private_key_pem); + }; + + /* Negative Test: Ensure flipping a bit in the signature is rejected + * Pseudocode: assert(FAILURE == verify(Key1_public, message, bitflip(sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + struct s2n_pkey *private_key = chain_and_key->private_key; + { + EXPECT_NOT_NULL(public_key.pkey); + EXPECT_NOT_NULL(private_key); + EXPECT_NOT_NULL(private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + + /* Flip a random bit in the signature */ + EXPECT_SUCCESS(s2n_flip_random_bit(&signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + }; + + /* Negative Test: Ensure Verification with wrong key fails + * Pseudocode: assert(FAILURE == verify(Key2_public, message, sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *root_cert_chain_pem = NULL; + char *root_private_key_pem = NULL; + char *leaf_cert_chain_pem = NULL; + char *leaf_private_key_pem = NULL; + struct s2n_cert_chain_and_key *root_chain_and_key = NULL; + struct s2n_cert_chain_and_key *leaf_chain_and_key = NULL; + struct s2n_pkey root_public_key = { 0 }; + struct s2n_pkey leaf_public_key = { 0 }; + s2n_pkey_type root_pkey_type = S2N_PKEY_TYPE_UNKNOWN; + s2n_pkey_type leaf_pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(root_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, root_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, leaf_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(leaf_chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(root_chain_and_key, root_cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(root_chain_and_key, root_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(leaf_chain_and_key, leaf_cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(leaf_chain_and_key, leaf_private_key_pem)); + + /* Parse the cert for the public key and certificate type */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&root_public_key, &root_pkey_type, &root_chain_and_key->cert_chain->head->raw)); + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&leaf_public_key, &leaf_pkey_type, &leaf_chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(root_pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_NOT_EQUAL(leaf_pkey_type, S2N_PKEY_TYPE_UNKNOWN); + + EXPECT_SUCCESS(s2n_cert_set_cert_type(root_chain_and_key->cert_chain->head, root_pkey_type)); + EXPECT_SUCCESS(s2n_cert_set_cert_type(leaf_chain_and_key->cert_chain->head, leaf_pkey_type)); + + struct s2n_pkey *root_private_key = root_chain_and_key->private_key; + struct s2n_pkey *leaf_private_key = leaf_chain_and_key->private_key; + { + EXPECT_NOT_NULL(root_public_key.pkey); + EXPECT_NOT_NULL(leaf_public_key.pkey); + + EXPECT_NOT_NULL(root_private_key); + EXPECT_NOT_NULL(root_private_key->pkey); + EXPECT_NOT_NULL(leaf_private_key); + EXPECT_NOT_NULL(leaf_private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + + /* Sign with Root's Key, but verify with Leaf's Key. This should fail. */ + EXPECT_SUCCESS(s2n_pkey_sign(root_private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&leaf_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(root_chain_and_key)); + EXPECT_SUCCESS(s2n_pkey_free(&root_public_key)); + free(root_cert_chain_pem); + free(root_private_key_pem); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(leaf_chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&leaf_public_key)); + free(leaf_cert_chain_pem); + free(leaf_private_key_pem); + }; + + /* Negative Test: Ensure flipping a bit in message given to verification fails + * Pseudocode: assert(FAILURE == verify(Key1_public, bitflip(message), sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + struct s2n_pkey *private_key = chain_and_key->private_key; + { + EXPECT_NOT_NULL(public_key.pkey); + EXPECT_NOT_NULL(private_key); + EXPECT_NOT_NULL(private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + /* Flip a random bit in the message before verification */ + EXPECT_SUCCESS(s2n_flip_random_bit(&random_msg)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_seccomp_handshake_test.c b/tests/unit/s2n_seccomp_handshake_test.c index 75bf7b93495..f1afa81e0d4 100644 --- a/tests/unit/s2n_seccomp_handshake_test.c +++ b/tests/unit/s2n_seccomp_handshake_test.c @@ -1,85 +1,86 @@ -/* - * 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 "s2n_test.h" -#include "testlib/s2n_testlib.h" - -int main(int argc, char **argv) -{ - /* We need to execute s2n_init before the seccomp filter is applied. - * Some one-time initialization involves opening files, like "dev/urandom". - * If built with aws-lc, s2n-tls also needs to call CRYPTO_pre_sandbox_init() - * before seccomp starts sandboxing. - * - * An application using s2n-tls with seccomp would need to do the same. - */ - BEGIN_TEST(); - - /* One of the primary purposes of seccomp is to block opening new files. - * So before we enable seccomp, we need to open any files that the test would - * need. In this case, we need to load certificate pems from files. - * - * An application using s2n-tls with seccomp would need to do the same. - */ - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, - private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* No unexpected syscalls allowed beyond this point */ - EXPECT_OK(s2n_seccomp_init()); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, cert_chain_pem)); - - const char *security_policies[] = { "test_all_tls12", "default_tls13" }; - - for (size_t i = 0; i < s2n_array_len(security_policies); i++) { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policies[i])); - EXPECT_SUCCESS(s2n_set_server_name(client, "127.0.0.1")); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, security_policies[i])); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - const uint8_t data[] = "hello world"; - uint8_t buffer[100] = { 0 }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_EQUAL(s2n_send(client, data, sizeof(data), &blocked), sizeof(data)); - EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(data)); - EXPECT_BYTEARRAY_EQUAL(buffer, data, sizeof(data)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + /* We need to execute s2n_init before the seccomp filter is applied. + * Some one-time initialization involves opening files, like "dev/urandom". + * If built with aws-lc, s2n-tls also needs to call CRYPTO_pre_sandbox_init() + * before seccomp starts sandboxing. + * + * An application using s2n-tls with seccomp would need to do the same. + */ + BEGIN_TEST(); + + /* One of the primary purposes of seccomp is to block opening new files. + * So before we enable seccomp, we need to open any files that the test would + * need. In this case, we need to load certificate pems from files. + * + * An application using s2n-tls with seccomp would need to do the same. + */ + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, + private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* No unexpected syscalls allowed beyond this point */ + EXPECT_OK(s2n_seccomp_init()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, cert_chain_pem)); + + const char *security_policies[] = { "test_all_tls12", "default_tls13" }; + + for (size_t i = 0; i < s2n_array_len(security_policies); i++) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policies[i])); + EXPECT_SUCCESS(s2n_set_server_name(client, "127.0.0.1")); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, security_policies[i])); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + const uint8_t data[] = "hello world"; + uint8_t buffer[100] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(client, data, sizeof(data), &blocked), sizeof(data)); + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(data)); + EXPECT_BYTEARRAY_EQUAL(buffer, data, sizeof(data)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_alerts_test.c b/tests/unit/s2n_self_talk_alerts_test.c index cf596ba5d5c..fc68a4808ed 100644 --- a/tests/unit/s2n_self_talk_alerts_test.c +++ b/tests/unit/s2n_self_talk_alerts_test.c @@ -1,335 +1,336 @@ -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -#define TLS_ALERT 21 -#define TLS_ALERT_VERSION 0x03, 0x03 -#define TLS_ALERT_LENGTH 0x00, 0x02 - -#define TLS_ALERT_LEVEL_WARNING 1 -#define TLS_ALERT_LEVEL_FATAL 2 - -#define TLS_ALERT_CLOSE_NOTIFY 0 -#define TLS_ALERT_UNRECOGNIZED_NAME 122 - -struct alert_ctx { - int write_fd; - int invoked; - int count; - - uint8_t level; - uint8_t code; -}; - -int mock_client(struct s2n_test_io_pair *io_pair, s2n_alert_behavior alert_behavior, int expect_failure) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int result = 0; - int rc = 0; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - s2n_config_disable_x509_verification(config); - s2n_config_set_alert_behavior(config, alert_behavior); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - rc = s2n_negotiate(conn, &blocked); - if (expect_failure) { - if (!rc) { - result = 1; - } - } else { - char buffer[0xffff]; - if (rc < 0) { - result = 1; - } - - for (size_t i = 1; i < 0xffff; i += 100) { - memset(buffer, 33, sizeof(char) * i); - s2n_send(conn, buffer, i, &blocked); - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - } while (shutdown_rc != 0); - } - - s2n_connection_free(conn); - s2n_config_free(config); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - s2n_cleanup(); - - exit(result); -} - -int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) -{ - static int called = 0; - - /* When first called return 0 seconds */ - *nanoseconds = 0; - - /* When next called return 31 seconds */ - if (called) { - *nanoseconds += (uint64_t) 31 * 1000000000; - } - - called = 1; - - return 0; -} - -int client_hello_send_alerts(struct s2n_connection *conn, void *ctx) -{ - struct alert_ctx *alert = ctx; - uint8_t alert_msg[] = { TLS_ALERT, TLS_ALERT_VERSION, TLS_ALERT_LENGTH, alert->level, alert->code }; - - for (int i = 0; i < alert->count; i++) { - if (write(alert->write_fd, alert_msg, sizeof(alert_msg)) != sizeof(alert_msg)) { - exit(100); - } - - alert->invoked++; - } - - return 0; -} - -S2N_RESULT cleanup(char **cert_chain_pem, char **private_key_pem, - struct s2n_cert_chain_and_key **chain_and_key) -{ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(*chain_and_key)); - free(*cert_chain_pem); - free(*private_key_pem); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - char buffer[0xffff]; - struct s2n_connection *conn = NULL; - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - BEGIN_TEST(); - - /* Ignore SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - /* Test that we ignore Warning Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ - { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 0); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - /* Set up the callback to send an alert after receiving ClientHello */ - struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 2, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); - - /* This is the parent */ - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure that callback was invoked */ - EXPECT_EQUAL(warning_alert.invoked, 2); - - for (size_t i = 1; i < 0xffff; i += 100) { - char *ptr = buffer; - int size = i; - - do { - int bytes_read = 0; - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); - - size -= bytes_read; - ptr += bytes_read; - } while (size); - - for (int j = 0; j < i; j++) { - EXPECT_EQUAL(buffer[j], 33); - } - } - - EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - }; - - /* Test that we don't ignore Fatal Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ - { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 1); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - /* Set up the callback to send an alert after receiving ClientHello */ - struct alert_ctx fatal_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_FATAL, .code = TLS_ALERT_UNRECOGNIZED_NAME }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &fatal_alert)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure that callback was invoked */ - EXPECT_EQUAL(fatal_alert.invoked, 1); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - }; - - /* Test that we don't ignore Warning Alerts in S2N_ALERT_FAIL_ON_WARNINGS mode in TLS1.2 */ - { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - mock_client(&io_pair, S2N_ALERT_FAIL_ON_WARNINGS, 1); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - - /* Set up the callback to send an alert after receiving ClientHello */ - struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure that callback was invoked */ - EXPECT_EQUAL(warning_alert.invoked, 1); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - }; - - /* Shutdown */ - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - - END_TEST(); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define TLS_ALERT 21 +#define TLS_ALERT_VERSION 0x03, 0x03 +#define TLS_ALERT_LENGTH 0x00, 0x02 + +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNRECOGNIZED_NAME 122 + +struct alert_ctx { + int write_fd; + int invoked; + int count; + + uint8_t level; + uint8_t code; +}; + +int mock_client(struct s2n_test_io_pair *io_pair, s2n_alert_behavior alert_behavior, int expect_failure) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int result = 0; + int rc = 0; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + s2n_config_disable_x509_verification(config); + s2n_config_set_alert_behavior(config, alert_behavior); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + rc = s2n_negotiate(conn, &blocked); + if (expect_failure) { + if (!rc) { + result = 1; + } + } else { + char buffer[0xffff]; + if (rc < 0) { + result = 1; + } + + for (size_t i = 1; i < 0xffff; i += 100) { + memset(buffer, 33, sizeof(char) * i); + s2n_send(conn, buffer, i, &blocked); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + } while (shutdown_rc != 0); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + s2n_cleanup(); + + exit(result); +} + +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + static int called = 0; + + /* When first called return 0 seconds */ + *nanoseconds = 0; + + /* When next called return 31 seconds */ + if (called) { + *nanoseconds += (uint64_t) 31 * 1000000000; + } + + called = 1; + + return 0; +} + +int client_hello_send_alerts(struct s2n_connection *conn, void *ctx) +{ + struct alert_ctx *alert = ctx; + uint8_t alert_msg[] = { TLS_ALERT, TLS_ALERT_VERSION, TLS_ALERT_LENGTH, alert->level, alert->code }; + + for (int i = 0; i < alert->count; i++) { + if (write(alert->write_fd, alert_msg, sizeof(alert_msg)) != sizeof(alert_msg)) { + exit(100); + } + + alert->invoked++; + } + + return 0; +} + +S2N_RESULT cleanup(char **cert_chain_pem, char **private_key_pem, + struct s2n_cert_chain_and_key **chain_and_key) +{ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(*chain_and_key)); + free(*cert_chain_pem); + free(*private_key_pem); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + char buffer[0xffff]; + struct s2n_connection *conn = NULL; + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + BEGIN_TEST(); + + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + /* Test that we ignore Warning Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 0); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 2, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); + + /* This is the parent */ + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(warning_alert.invoked, 2); + + for (size_t i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Test that we don't ignore Fatal Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 1); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx fatal_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_FATAL, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &fatal_alert)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(fatal_alert.invoked, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Test that we don't ignore Warning Alerts in S2N_ALERT_FAIL_ON_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_FAIL_ON_WARNINGS, 1); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(warning_alert.invoked, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Shutdown */ + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_self_talk_broken_pipe_test.c b/tests/unit/s2n_self_talk_broken_pipe_test.c index 87b237d11d2..8d47563c303 100644 --- a/tests/unit/s2n_self_talk_broken_pipe_test.c +++ b/tests/unit/s2n_self_talk_broken_pipe_test.c @@ -1,179 +1,180 @@ -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -#define SUPPORTED_CERTIFICATE_FORMATS (2) - -static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; -static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - s2n_config_disable_x509_verification(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - int result = s2n_negotiate(conn, &blocked); - if (result < 0) { - exit(1); - } - - result = s2n_connection_free_handshake(conn); - if (result < 0) { - exit(1); - } - -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) - /* On FreeBSD shutdown from one end of the socket pair does not give EPIPE. Must use close. */ - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); -#else - /* Close client read fd to mock half closed pipe at server side */ - s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_RD); -#endif - /* Give server a chance to send data on broken pipe */ - sleep(2); - - s2n_shutdown(conn, &blocked); - - result = s2n_connection_free(conn); - if (result < 0) { - exit(1); - } - - result = s2n_config_free(config); - if (result < 0) { - exit(1); - } - - /* Give the server a chance to avoid a sigpipe */ - sleep(1); - - s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_WR); - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int status = 0; - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - - BEGIN_TEST(); - - for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { - struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; - - /* Create a pipe */ - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); - } - - if (is_dh_key_exchange) { - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - } - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Give client a chance to close pipe at the receiving end */ - sleep(1); - char buffer[1]; - /* Fist flush on half closed pipe should get EPIPE */ - ssize_t w = s2n_send(conn, buffer, 1, &blocked); - EXPECT_EQUAL(w, -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); - EXPECT_EQUAL(errno, EPIPE); - - /* Second flush on half closed pipe should not get EPIPE as we write is skipped */ - w = s2n_shutdown(conn, &blocked); - EXPECT_EQUAL(w, -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); - EXPECT_EQUAL(errno, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); - } - EXPECT_SUCCESS(s2n_config_free(config)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - if (getenv("S2N_VALGRIND") == NULL) { - EXPECT_EQUAL(status, 0); - } - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + int result = s2n_negotiate(conn, &blocked); + if (result < 0) { + exit(1); + } + + result = s2n_connection_free_handshake(conn); + if (result < 0) { + exit(1); + } + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) + /* On FreeBSD shutdown from one end of the socket pair does not give EPIPE. Must use close. */ + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); +#else + /* Close client read fd to mock half closed pipe at server side */ + s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_RD); +#endif + /* Give server a chance to send data on broken pipe */ + sleep(2); + + s2n_shutdown(conn, &blocked); + + result = s2n_connection_free(conn); + if (result < 0) { + exit(1); + } + + result = s2n_config_free(config); + if (result < 0) { + exit(1); + } + + /* Give the server a chance to avoid a sigpipe */ + sleep(1); + + s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_WR); + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status = 0; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + + BEGIN_TEST(); + + for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Give client a chance to close pipe at the receiving end */ + sleep(1); + char buffer[1]; + /* Fist flush on half closed pipe should get EPIPE */ + ssize_t w = s2n_send(conn, buffer, 1, &blocked); + EXPECT_EQUAL(w, -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + EXPECT_EQUAL(errno, EPIPE); + + /* Second flush on half closed pipe should not get EPIPE as we write is skipped */ + w = s2n_shutdown(conn, &blocked); + EXPECT_EQUAL(w, -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + EXPECT_EQUAL(errno, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + if (getenv("S2N_VALGRIND") == NULL) { + EXPECT_EQUAL(status, 0); + } + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_certificates_test.c b/tests/unit/s2n_self_talk_certificates_test.c index 5c2a1f1769d..369361e8a20 100644 --- a/tests/unit/s2n_self_talk_certificates_test.c +++ b/tests/unit/s2n_self_talk_certificates_test.c @@ -1,139 +1,140 @@ -/* - * 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 "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" - -static uint8_t s2n_noop_verify_host_fn(const char *name, size_t len, void *data) -{ - return 1; -} - -static S2N_RESULT s2n_test_load_certificate(struct s2n_cert_chain_and_key **chain_out, - char *chain_pem_out, const char *chain_pem_path, const char *key_pem_path) -{ - RESULT_ENSURE_REF(chain_out); - - char key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - RESULT_GUARD_POSIX(s2n_read_test_pem(chain_pem_path, chain_pem_out, S2N_MAX_TEST_PEM_SIZE)); - RESULT_GUARD_POSIX(s2n_read_test_pem(key_pem_path, key_pem, S2N_MAX_TEST_PEM_SIZE)); - - *chain_out = s2n_cert_chain_and_key_new(); - RESULT_ENSURE_REF(*chain_out); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_out, chain_pem_out, key_pem)); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_rsa_pss_certs_supported() || !s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - const uint8_t test_versions[] = { S2N_TLS13, S2N_TLS12, S2N_TLS11, S2N_TLS10 }; - struct { - const char *cert; - const char *key; - uint8_t min_version; - } test_certs[] = { - { - .cert = S2N_RSA_2048_PKCS1_SHA256_CERT_CHAIN, - .key = S2N_RSA_2048_PKCS1_SHA256_CERT_KEY, - }, - { - .cert = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, - .key = S2N_ECDSA_P384_PKCS1_KEY, - }, - { - .cert = S2N_ECDSA_P512_CERT_CHAIN, - .key = S2N_ECDSA_P512_KEY, - }, - { - .cert = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, - .key = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, - .min_version = S2N_TLS12, - }, - }; - - for (size_t cert_i = 0; cert_i < s2n_array_len(test_certs); cert_i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - char pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_OK(s2n_test_load_certificate(&chain, pem, - test_certs[cert_i].cert, test_certs[cert_i].key)); - - for (size_t version_i = 0; version_i < s2n_array_len(test_versions); version_i++) { - uint8_t version = test_versions[version_i]; - bool expect_success = (version >= test_certs[cert_i].min_version); - - /* 20240501 only supports up to TLS1.2 */ - const char *security_policy = "20240501"; - if (version >= S2N_TLS13) { - security_policy = "default_tls13"; - } else if (version < S2N_TLS12) { - /* The default policies don't support legacy versions */ - security_policy = "test_all"; - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, pem)); - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, s2n_noop_verify_host_fn, NULL)); - EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(config, 0)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, security_policy)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - server->server_protocol_version = version; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - client->client_protocol_version = version; - - 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_connections_set_io_pair(client, server, &io_pair)); - - bool handshake_success = - (s2n_negotiate_test_server_and_client(server, client) == S2N_SUCCESS); - if (handshake_success) { - EXPECT_EQUAL(server->actual_protocol_version, version); - EXPECT_EQUAL(client->actual_protocol_version, version); - } - - const char *error_message = "Handshake failed"; - if (!expect_success) { - error_message = "Handshake unexpectedly succeeded"; - } - if (handshake_success != expect_success) { - fprintf(stderr, "%s version=%i cert=%s\n", - error_message, version, test_certs[cert_i].cert); - FAIL_MSG(error_message); - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static uint8_t s2n_noop_verify_host_fn(const char *name, size_t len, void *data) +{ + return 1; +} + +static S2N_RESULT s2n_test_load_certificate(struct s2n_cert_chain_and_key **chain_out, + char *chain_pem_out, const char *chain_pem_path, const char *key_pem_path) +{ + RESULT_ENSURE_REF(chain_out); + + char key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + RESULT_GUARD_POSIX(s2n_read_test_pem(chain_pem_path, chain_pem_out, S2N_MAX_TEST_PEM_SIZE)); + RESULT_GUARD_POSIX(s2n_read_test_pem(key_pem_path, key_pem, S2N_MAX_TEST_PEM_SIZE)); + + *chain_out = s2n_cert_chain_and_key_new(); + RESULT_ENSURE_REF(*chain_out); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_out, chain_pem_out, key_pem)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_rsa_pss_certs_supported() || !s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint8_t test_versions[] = { S2N_TLS13, S2N_TLS12, S2N_TLS11, S2N_TLS10 }; + struct { + const char *cert; + const char *key; + uint8_t min_version; + } test_certs[] = { + { + .cert = S2N_RSA_2048_PKCS1_SHA256_CERT_CHAIN, + .key = S2N_RSA_2048_PKCS1_SHA256_CERT_KEY, + }, + { + .cert = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, + .key = S2N_ECDSA_P384_PKCS1_KEY, + }, + { + .cert = S2N_ECDSA_P512_CERT_CHAIN, + .key = S2N_ECDSA_P512_KEY, + }, + { + .cert = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .min_version = S2N_TLS12, + }, + }; + + for (size_t cert_i = 0; cert_i < s2n_array_len(test_certs); cert_i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + char pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_OK(s2n_test_load_certificate(&chain, pem, + test_certs[cert_i].cert, test_certs[cert_i].key)); + + for (size_t version_i = 0; version_i < s2n_array_len(test_versions); version_i++) { + uint8_t version = test_versions[version_i]; + bool expect_success = (version >= test_certs[cert_i].min_version); + + /* 20240501 only supports up to TLS1.2 */ + const char *security_policy = "20240501"; + if (version >= S2N_TLS13) { + security_policy = "default_tls13"; + } else if (version < S2N_TLS12) { + /* The default policies don't support legacy versions */ + security_policy = "test_all"; + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, pem)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, s2n_noop_verify_host_fn, NULL)); + EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(config, 0)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, security_policy)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + server->server_protocol_version = version; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + client->client_protocol_version = version; + + 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_connections_set_io_pair(client, server, &io_pair)); + + bool handshake_success = + (s2n_negotiate_test_server_and_client(server, client) == S2N_SUCCESS); + if (handshake_success) { + EXPECT_EQUAL(server->actual_protocol_version, version); + EXPECT_EQUAL(client->actual_protocol_version, version); + } + + const char *error_message = "Handshake failed"; + if (!expect_success) { + error_message = "Handshake unexpectedly succeeded"; + } + if (handshake_success != expect_success) { + fprintf(stderr, "%s version=%i cert=%s\n", + error_message, version, test_certs[cert_i].cert); + FAIL_MSG(error_message); + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_custom_io_test.c b/tests/unit/s2n_self_talk_custom_io_test.c index e0b02452c4c..67841cacb99 100644 --- a/tests/unit/s2n_self_talk_custom_io_test.c +++ b/tests/unit/s2n_self_talk_custom_io_test.c @@ -1,220 +1,221 @@ -/* - * 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 -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_random.h" - -#define MAX_BUF_SIZE 10000 - -int mock_client(struct s2n_test_io_pair *io_pair) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - - conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - s2n_connection_set_config(conn, client_config); - - /* Unlike the server, the client just passes ownership of I/O to s2n */ - s2n_connection_set_io_pair(conn, io_pair); - - result = s2n_negotiate(conn, &blocked); - if (result < 0) { - exit(1); - } - - s2n_shutdown(conn, &blocked); - s2n_connection_free(conn); - s2n_config_free(client_config); - s2n_cleanup(); - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - /** - * This test creates a server, client, and a pair of pipes. The client uses the - * pipes directly for I/O in s2n. The server copies data from the pipes into - * stuffers and manages s2n I/O with a set of I/O callbacks that read and write - * from the stuffers. - */ - { - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - - /* For convenience, this test will intentionally try to write to closed pipes during shutdown. Ignore the signal to - * avoid exiting the process on SIGPIPE. - */ - signal(SIGPIPE, SIG_IGN); - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Run the client */ - mock_client(&io_pair); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FAILURE(s2n_connection_use_corked_io(conn)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); - - /* Make our pipes non-blocking */ - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - - /* Negotiate the handshake. */ - do { - int ret = 0; - - ret = s2n_negotiate(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - - /* check to see if we need to copy more over from the pipes to the buffers - * to continue the handshake - */ - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (blocked); - - /* Shutdown after negotiating */ - uint8_t server_shutdown = 0; - do { - int ret = 0; - - ret = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - if (ret == 0) { - server_shutdown = 1; - } - - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (!server_shutdown); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - }; - - /* Clients and servers can utilize both custom IO and default IO for their sending and receiving */ - { - /* Setup connections */ - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - /* Setup config */ - struct s2n_config *config_with_certs = NULL; - EXPECT_NOT_NULL(config_with_certs = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_certs, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_certs)); - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_certs, chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_certs)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_certs)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - /* Server writes to fd and client reads from fd */ - EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, io_pair.server)); - EXPECT_SUCCESS(s2n_connection_set_read_fd(client_conn, io_pair.client)); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, S2N_DEFAULT_RECORD_LENGTH)); - - /* Client writes to stuffer and server reads from stuffer */ - EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&stuffer, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&stuffer, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Clean-up */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(config_with_certs)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_random.h" + +#define MAX_BUF_SIZE 10000 + +int mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + + conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + s2n_connection_set_config(conn, client_config); + + /* Unlike the server, the client just passes ownership of I/O to s2n */ + s2n_connection_set_io_pair(conn, io_pair); + + result = s2n_negotiate(conn, &blocked); + if (result < 0) { + exit(1); + } + + s2n_shutdown(conn, &blocked); + s2n_connection_free(conn); + s2n_config_free(client_config); + s2n_cleanup(); + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + /** + * This test creates a server, client, and a pair of pipes. The client uses the + * pipes directly for I/O in s2n. The server copies data from the pipes into + * stuffers and manages s2n I/O with a set of I/O callbacks that read and write + * from the stuffers. + */ + { + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + + /* For convenience, this test will intentionally try to write to closed pipes during shutdown. Ignore the signal to + * avoid exiting the process on SIGPIPE. + */ + signal(SIGPIPE, SIG_IGN); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + mock_client(&io_pair); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FAILURE(s2n_connection_use_corked_io(conn)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); + + /* Make our pipes non-blocking */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + + /* Negotiate the handshake. */ + do { + int ret = 0; + + ret = s2n_negotiate(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + + /* check to see if we need to copy more over from the pipes to the buffers + * to continue the handshake + */ + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (blocked); + + /* Shutdown after negotiating */ + uint8_t server_shutdown = 0; + do { + int ret = 0; + + ret = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + if (ret == 0) { + server_shutdown = 1; + } + + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (!server_shutdown); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + }; + + /* Clients and servers can utilize both custom IO and default IO for their sending and receiving */ + { + /* Setup connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_config *config_with_certs = NULL; + EXPECT_NOT_NULL(config_with_certs = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_certs, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_certs)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_certs, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_certs)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_certs)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Server writes to fd and client reads from fd */ + EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, io_pair.server)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(client_conn, io_pair.client)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, S2N_DEFAULT_RECORD_LENGTH)); + + /* Client writes to stuffer and server reads from stuffer */ + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&stuffer, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config_with_certs)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_min_protocol_version_test.c b/tests/unit/s2n_self_talk_min_protocol_version_test.c index 4e0f2ecfa38..bab61a42af9 100644 --- a/tests/unit/s2n_self_talk_min_protocol_version_test.c +++ b/tests/unit/s2n_self_talk_min_protocol_version_test.c @@ -1,129 +1,130 @@ -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -int mock_client(struct s2n_test_io_pair *io_pair, uint8_t version) -{ - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - - client_conn = s2n_connection_new(S2N_CLIENT); - s2n_connection_set_config(client_conn, client_config); - s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING); - - /* Force TLSv1 on a client so that server will fail handshake */ - client_conn->client_protocol_version = S2N_TLS10; - if (version >= S2N_TLS13) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - } else { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); - } - - s2n_connection_set_io_pair(client_conn, io_pair); - - result = s2n_negotiate(client_conn, &blocked); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - s2n_connection_free(client_conn); - s2n_config_free(client_config); - - s2n_cleanup(); - - /* Expect failure of handshake */ - exit(result == 0 ? 1 : 0); -} - -int main(int argc, char **argv) -{ - s2n_blocked_status blocked; - int status = 0; - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - - BEGIN_TEST(); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* TLS1.2 and TLS1.3 have different version negotiation mechanisms. - * We should test both. - */ - for (uint8_t version = S2N_TLS12; version <= S2N_TLS13; version++) { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Send the client hello with TLSv1 and validate that we failed handshake */ - mock_client(&io_pair, version); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - /* Pick cipher preference with TLSv1.2 as a minimum version */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "CloudFront-TLS-1-2-2019")); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - /* Check that blinding was not invoked */ - EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); - - /* Close the pipes */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +int mock_client(struct s2n_test_io_pair *io_pair, uint8_t version) +{ + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + + client_conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_config(client_conn, client_config); + s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING); + + /* Force TLSv1 on a client so that server will fail handshake */ + client_conn->client_protocol_version = S2N_TLS10; + if (version >= S2N_TLS13) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + } else { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + } + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + s2n_cleanup(); + + /* Expect failure of handshake */ + exit(result == 0 ? 1 : 0); +} + +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status = 0; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* TLS1.2 and TLS1.3 have different version negotiation mechanisms. + * We should test both. + */ + for (uint8_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Send the client hello with TLSv1 and validate that we failed handshake */ + mock_client(&io_pair, version); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + /* Pick cipher preference with TLSv1.2 as a minimum version */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "CloudFront-TLS-1-2-2019")); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /* Check that blinding was not invoked */ + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + + /* Close the pipes */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_nonblocking_test.c b/tests/unit/s2n_self_talk_nonblocking_test.c index 427d03e46eb..d0882a11b12 100644 --- a/tests/unit/s2n_self_talk_nonblocking_test.c +++ b/tests/unit/s2n_self_talk_nonblocking_test.c @@ -1,392 +1,393 @@ -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -static const float minimum_send_percent = 5.0; - -#define MIN_PERCENT_COMPLETE(remaining, total) ((((total - remaining) / (total * 1.0)) * 100.0) > minimum_send_percent) - -int mock_client(struct s2n_test_io_pair *io_pair, uint8_t *expected_data, uint32_t size) -{ - uint8_t *buffer = malloc(size); - uint8_t *ptr = buffer; - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - /* If something goes wrong, and the server never finishes sending, - * we'll want to have the child process die eventually, or certain - * CI/CD pipelines might never complete */ - int should_block = 1; - - /* Give the server a chance to listen */ - sleep(1); - - client_conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - s2n_connection_set_config(client_conn, client_config); - POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); - - s2n_connection_set_io_pair(client_conn, io_pair); - - result = s2n_negotiate(client_conn, &blocked); - if (result < 0) { - return 1; - } - - /* Receive 10MB of data */ - uint32_t remaining = size; - while (remaining) { - int r = s2n_recv(client_conn, ptr, remaining, &blocked); - if (r < 0) { - return 1; - } - remaining -= r; - ptr += r; - if (should_block && MIN_PERCENT_COMPLETE(remaining, size)) { - raise(SIGSTOP); - should_block = 0; - } - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(client_conn, &blocked); - } while (shutdown_rc != 0); - - for (size_t i = 0; i < size; i++) { - if (buffer[i] != expected_data[i]) { - return 1; - } - } - - free(buffer); - s2n_connection_free(client_conn); - s2n_config_free(client_config); - - s2n_cleanup(); - - return 0; -} - -int mock_client_iov(struct s2n_test_io_pair *io_pair, struct iovec *iov, uint32_t iov_size) -{ - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - int total_size = 0, i = 0; - int should_block = 1; - - for (i = 0; i < iov_size; i++) { - total_size += iov[i].iov_len; - } - uint8_t *buffer = malloc(total_size + iov[0].iov_len); - int buffer_offs = 0; - - /* Give the server a chance to listen */ - sleep(1); - - client_conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - s2n_connection_set_config(client_conn, client_config); - POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); - - s2n_connection_set_io_pair(client_conn, io_pair); - - result = s2n_negotiate(client_conn, &blocked); - if (result < 0) { - return 1; - } - - uint32_t remaining = total_size; - while (remaining) { - int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); - if (r < 0) { - return 1; - } - remaining -= r; - buffer_offs += r; - if (should_block && MIN_PERCENT_COMPLETE(remaining, total_size)) { - raise(SIGSTOP); - should_block = 0; - } - } - - remaining = iov[0].iov_len; - while (remaining) { - int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); - if (r < 0) { - return 1; - } - remaining -= r; - buffer_offs += r; - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(client_conn, &blocked); - } while (shutdown_rc != 0); - - for (i = 0, buffer_offs = 0; i < iov_size; i++) { - if (memcmp(iov[i].iov_base, &buffer[buffer_offs], iov[i].iov_len)) { - return 1; - } - buffer_offs += iov[i].iov_len; - } - - if (memcmp(iov[0].iov_base, &buffer[buffer_offs], iov[0].iov_len)) { - return 1; - } - - free(buffer); - s2n_connection_free(client_conn); - s2n_config_free(client_config); - - return 0; -} - -S2N_RESULT cleanup_io_data(struct iovec **iov, int iov_size, struct s2n_blob *blob) -{ - if (*iov) { - for (int i = 0; i < iov_size; i++) { - free((*iov)[i].iov_base); - } - free(*iov); - } else { - s2n_free(blob); - } - - return S2N_RESULT_OK; -} - -int test_send(int use_tls13, int use_iov, int prefer_throughput) -{ - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - - /* Get some random data to send/receive */ - uint32_t data_size = 0; - struct s2n_blob blob = { 0 }; - - /* These numbers are chosen so that some of the payload is bigger - * than max TLS1.3 record size (2**14 + 1), which is needed to validate - * that we handle record sizing correctly. - * (see https://github.com/awslabs/s2n/pull/1780). - * - * Note that for each iov in the list, the payload size is doubled - * to ensure the implementation handles various lengths. - * - * With the current values, it will include - * * 8192 bytes - * * 16384 bytes - * * 32768 bytes - * * 65536 bytes - * * 131072 bytes - * * 262144 bytes - * * 524288 bytes */ - int iov_payload_size = 8192, iov_size = 7; - - struct iovec *iov = NULL; - if (!use_iov) { - data_size = 10000000; - EXPECT_SUCCESS(s2n_alloc(&blob, data_size)); - EXPECT_OK(s2n_get_public_random_data(&blob)); - } else { - iov = malloc(sizeof(*iov) * iov_size); - data_size = 0; - for (int i = 0; i < iov_size; i++, iov_payload_size *= 2) { - struct s2n_blob blob_local = { 0 }; - iov[i].iov_base = blob_local.data = malloc(iov_payload_size); - iov[i].iov_len = blob_local.size = iov_payload_size; - EXPECT_NOT_NULL(blob_local.data); - EXPECT_OK(s2n_get_public_random_data(&blob_local)); - data_size += iov_payload_size; - } - } - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Run the client */ - const int client_rc = !use_iov ? mock_client(&io_pair, blob.data, data_size) : mock_client_iov(&io_pair, iov, iov_size); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); - exit(client_rc); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), s2n_cert_chain_and_key_ptr_free); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - - if (use_tls13) { - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all")); - } else { - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - } - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - if (prefer_throughput) { - EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); - } else { - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); - } - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - EXPECT_SUCCESS(s2n_connection_use_corked_io(conn)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - /* Make sure we negotiated the expected version */ - if (use_tls13) { - EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - } else { - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - } - - /* Make our pipes non-blocking */ - s2n_fd_set_non_blocking(io_pair.server); - - /* Try to all 10MB of data, should be enough to fill PIPEBUF, so - we'll get blocked at some point */ - uint32_t remaining = data_size; - uint8_t *ptr = blob.data; - uint32_t iov_offs = 0; - - while (remaining) { - int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : - s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); - /* We will send up to minimum_send_percent, after which the client will automatically block itself. - * This allows us to cover the case where s2n_send gets EAGAIN on the very first call - * which can happen on certain platforms. By making sure we've successfully sent something - * we can ensure write -> block -> client drain -> write ordering.*/ - if (r < 0 && !MIN_PERCENT_COMPLETE(remaining, data_size)) { - continue; - } - - if (r < 0 && blocked == S2N_BLOCKED_ON_WRITE) { - /* We reached a blocked state and made no forward progress last call */ - break; - } - - EXPECT_TRUE(r > 0); - remaining -= r; - if (!use_iov) { - ptr += r; - } else { - iov_offs += r; - } - } - - /* Remaining should be between data_size and 0 */ - EXPECT_TRUE(remaining < data_size); - EXPECT_TRUE(remaining > 0); - - /* Wait for the child process to read some bytes and block itself*/ - sleep(1); - /* Wake the child process by sending it SIGCONT */ - EXPECT_SUCCESS(kill(pid, SIGCONT)); - - /* Make our sockets blocking again */ - s2n_fd_set_blocking(io_pair.server); - - /* Actually send the remaining data */ - while (remaining) { - int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : - s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); - EXPECT_TRUE(r > 0); - remaining -= r; - if (!use_iov) { - ptr += r; - } else { - iov_offs += r; - } - } - - if (use_iov) { - int r = s2n_sendv(conn, iov, 1, &blocked); - EXPECT_TRUE(r > 0); - } - - EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); - - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - - /* Clean up */ - EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - return 0; -} - -int main(int argc, char **argv) -{ - /* Ignore SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - - BEGIN_TEST(); - - for (int use_tls13 = 0; use_tls13 < 2; use_tls13++) { - for (int use_iovec = 0; use_iovec < 2; use_iovec++) { - for (int use_throughput = 0; use_throughput < 2; use_throughput++) { - test_send(use_tls13, use_iovec, use_throughput); - } - } - } - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static const float minimum_send_percent = 5.0; + +#define MIN_PERCENT_COMPLETE(remaining, total) ((((total - remaining) / (total * 1.0)) * 100.0) > minimum_send_percent) + +int mock_client(struct s2n_test_io_pair *io_pair, uint8_t *expected_data, uint32_t size) +{ + uint8_t *buffer = malloc(size); + uint8_t *ptr = buffer; + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + /* If something goes wrong, and the server never finishes sending, + * we'll want to have the child process die eventually, or certain + * CI/CD pipelines might never complete */ + int should_block = 1; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + return 1; + } + + /* Receive 10MB of data */ + uint32_t remaining = size; + while (remaining) { + int r = s2n_recv(client_conn, ptr, remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + ptr += r; + if (should_block && MIN_PERCENT_COMPLETE(remaining, size)) { + raise(SIGSTOP); + should_block = 0; + } + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + + for (size_t i = 0; i < size; i++) { + if (buffer[i] != expected_data[i]) { + return 1; + } + } + + free(buffer); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + s2n_cleanup(); + + return 0; +} + +int mock_client_iov(struct s2n_test_io_pair *io_pair, struct iovec *iov, uint32_t iov_size) +{ + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + int total_size = 0, i = 0; + int should_block = 1; + + for (i = 0; i < iov_size; i++) { + total_size += iov[i].iov_len; + } + uint8_t *buffer = malloc(total_size + iov[0].iov_len); + int buffer_offs = 0; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + return 1; + } + + uint32_t remaining = total_size; + while (remaining) { + int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + buffer_offs += r; + if (should_block && MIN_PERCENT_COMPLETE(remaining, total_size)) { + raise(SIGSTOP); + should_block = 0; + } + } + + remaining = iov[0].iov_len; + while (remaining) { + int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + buffer_offs += r; + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + + for (i = 0, buffer_offs = 0; i < iov_size; i++) { + if (memcmp(iov[i].iov_base, &buffer[buffer_offs], iov[i].iov_len)) { + return 1; + } + buffer_offs += iov[i].iov_len; + } + + if (memcmp(iov[0].iov_base, &buffer[buffer_offs], iov[0].iov_len)) { + return 1; + } + + free(buffer); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + return 0; +} + +S2N_RESULT cleanup_io_data(struct iovec **iov, int iov_size, struct s2n_blob *blob) +{ + if (*iov) { + for (int i = 0; i < iov_size; i++) { + free((*iov)[i].iov_base); + } + free(*iov); + } else { + s2n_free(blob); + } + + return S2N_RESULT_OK; +} + +int test_send(int use_tls13, int use_iov, int prefer_throughput) +{ + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + + /* Get some random data to send/receive */ + uint32_t data_size = 0; + struct s2n_blob blob = { 0 }; + + /* These numbers are chosen so that some of the payload is bigger + * than max TLS1.3 record size (2**14 + 1), which is needed to validate + * that we handle record sizing correctly. + * (see https://github.com/awslabs/s2n/pull/1780). + * + * Note that for each iov in the list, the payload size is doubled + * to ensure the implementation handles various lengths. + * + * With the current values, it will include + * * 8192 bytes + * * 16384 bytes + * * 32768 bytes + * * 65536 bytes + * * 131072 bytes + * * 262144 bytes + * * 524288 bytes */ + int iov_payload_size = 8192, iov_size = 7; + + struct iovec *iov = NULL; + if (!use_iov) { + data_size = 10000000; + EXPECT_SUCCESS(s2n_alloc(&blob, data_size)); + EXPECT_OK(s2n_get_public_random_data(&blob)); + } else { + iov = malloc(sizeof(*iov) * iov_size); + data_size = 0; + for (int i = 0; i < iov_size; i++, iov_payload_size *= 2) { + struct s2n_blob blob_local = { 0 }; + iov[i].iov_base = blob_local.data = malloc(iov_payload_size); + iov[i].iov_len = blob_local.size = iov_payload_size; + EXPECT_NOT_NULL(blob_local.data); + EXPECT_OK(s2n_get_public_random_data(&blob_local)); + data_size += iov_payload_size; + } + } + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + const int client_rc = !use_iov ? mock_client(&io_pair, blob.data, data_size) : mock_client_iov(&io_pair, iov, iov_size); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); + exit(client_rc); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), s2n_cert_chain_and_key_ptr_free); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + if (use_tls13) { + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + if (prefer_throughput) { + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + } else { + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + } + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_use_corked_io(conn)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure we negotiated the expected version */ + if (use_tls13) { + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + } else { + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + } + + /* Make our pipes non-blocking */ + s2n_fd_set_non_blocking(io_pair.server); + + /* Try to all 10MB of data, should be enough to fill PIPEBUF, so + we'll get blocked at some point */ + uint32_t remaining = data_size; + uint8_t *ptr = blob.data; + uint32_t iov_offs = 0; + + while (remaining) { + int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : + s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); + /* We will send up to minimum_send_percent, after which the client will automatically block itself. + * This allows us to cover the case where s2n_send gets EAGAIN on the very first call + * which can happen on certain platforms. By making sure we've successfully sent something + * we can ensure write -> block -> client drain -> write ordering.*/ + if (r < 0 && !MIN_PERCENT_COMPLETE(remaining, data_size)) { + continue; + } + + if (r < 0 && blocked == S2N_BLOCKED_ON_WRITE) { + /* We reached a blocked state and made no forward progress last call */ + break; + } + + EXPECT_TRUE(r > 0); + remaining -= r; + if (!use_iov) { + ptr += r; + } else { + iov_offs += r; + } + } + + /* Remaining should be between data_size and 0 */ + EXPECT_TRUE(remaining < data_size); + EXPECT_TRUE(remaining > 0); + + /* Wait for the child process to read some bytes and block itself*/ + sleep(1); + /* Wake the child process by sending it SIGCONT */ + EXPECT_SUCCESS(kill(pid, SIGCONT)); + + /* Make our sockets blocking again */ + s2n_fd_set_blocking(io_pair.server); + + /* Actually send the remaining data */ + while (remaining) { + int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : + s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); + EXPECT_TRUE(r > 0); + remaining -= r; + if (!use_iov) { + ptr += r; + } else { + iov_offs += r; + } + } + + if (use_iov) { + int r = s2n_sendv(conn, iov, 1, &blocked); + EXPECT_TRUE(r > 0); + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Clean up */ + EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + return 0; +} + +int main(int argc, char **argv) +{ + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + BEGIN_TEST(); + + for (int use_tls13 = 0; use_tls13 < 2; use_tls13++) { + for (int use_iovec = 0; use_iovec < 2; use_iovec++) { + for (int use_throughput = 0; use_throughput < 2; use_throughput++) { + test_send(use_tls13, use_iovec, use_throughput); + } + } + } + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_session_id_test.c b/tests/unit/s2n_self_talk_session_id_test.c index dbef1031b01..fd46110dad1 100644 --- a/tests/unit/s2n_self_talk_session_id_test.c +++ b/tests/unit/s2n_self_talk_session_id_test.c @@ -1,672 +1,673 @@ -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_bitmap.h" - -#define MAX_KEY_LEN 32 -#define MAX_VAL_LEN 255 - -static const char SESSION_ID[] = "0123456789abcdef0123456789abcdef"; -static const char MSG[] = "Test"; - -struct session_cache_entry { - uint8_t key[MAX_KEY_LEN]; - uint8_t key_len; - uint8_t value[MAX_VAL_LEN]; - uint8_t value_len; - uint8_t lock; -}; - -struct session_cache_entry session_cache[256]; - -int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) -{ - struct session_cache_entry *cache = ctx; - - if (key_size == 0 || key_size > MAX_KEY_LEN) { - return -1; - } - if (value_size == 0 || value_size > MAX_VAL_LEN) { - return -1; - } - - uint8_t idx = ((const uint8_t *) key)[0]; - - EXPECT_MEMCPY_SUCCESS(cache[idx].key, key, key_size); - EXPECT_MEMCPY_SUCCESS(cache[idx].value, value, value_size); - - cache[idx].key_len = key_size; - cache[idx].value_len = value_size; - - return 0; -} - -int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) -{ - struct session_cache_entry *cache = ctx; - - if (key_size == 0 || key_size > MAX_KEY_LEN) { - return -1; - } - - uint8_t idx = ((const uint8_t *) key)[0]; - - if (cache[idx].lock) { - /* here we mock a remote connection/event blocking the handshake - * state machine, until lock is free - */ - cache[idx].lock = 0; - return S2N_CALLBACK_BLOCKED; - } - - if (cache[idx].key_len != key_size) { - return -1; - } - - if (memcmp(cache[idx].key, key, key_size)) { - return -1; - } - - if (*value_size < cache[idx].value_len) { - return -1; - } - - *value_size = cache[idx].value_len; - EXPECT_MEMCPY_SUCCESS(value, cache[idx].value, cache[idx].value_len); - - return 0; -} - -int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) -{ - struct session_cache_entry *cache = ctx; - - if (key_size == 0 || key_size > MAX_KEY_LEN) { - return -1; - } - - uint8_t idx = ((const uint8_t *) key)[0]; - - if (cache[idx].key_len == 0) { - return 0; - } - - if (cache[idx].key_len != key_size) { - return -1; - } - - if (memcmp(cache[idx].key, key, key_size)) { - return -1; - } - - cache[idx].key_len = 0; - cache[idx].value_len = 0; - - return 0; -} - -/* init session cache lock field, which is used to mock a remote - * connection/event block - */ -static void initialize_cache() -{ - for (int i = 0; i < 256; i++) { - session_cache[i].lock = 1; - } -} - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - size_t serialized_session_state_length = 0; - uint8_t serialized_session_state[256] = { 0 }; - - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int result = 0; - - /* Give the server a chance to listen */ - sleep(1); - - /* Initial handshake */ - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - s2n_config_set_cipher_preferences(config, "20240501"); - s2n_config_disable_x509_verification(config); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - /* Set the session id to ensure we're able to fallback to full handshake if session is not in server cache */ - EXPECT_MEMCPY_SUCCESS(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN); - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - - if (s2n_negotiate(conn, &blocked) != 0) { - result = 1; - } - - /* Make sure we did a full handshake */ - if (!IS_FULL_HANDSHAKE(conn)) { - result = 2; - } - - /* Save session state from the connection */ - memset(serialized_session_state, 0, sizeof(serialized_session_state)); - serialized_session_state_length = s2n_connection_get_session_length(conn); - if (serialized_session_state_length > sizeof(serialized_session_state)) { - result = 3; - } - - /* Send very low session buffer size and see that you can get an error */ - if (s2n_connection_get_session(conn, serialized_session_state, 1) == 0) { - result = 4; - } - - if (s2n_errno != S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG) { - result = 5; - } - - if ((size_t) s2n_connection_get_session(conn, serialized_session_state, serialized_session_state_length) != serialized_session_state_length) { - result = 6; - } - - /* server would choose a session ID for client */ - if (memcmp(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN) == 0) { - result = 7; - } - - if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { - result = 8; - } - - int shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - - /* Give the server a chance to avoid sigpipe */ - sleep(1); - - /* Session resumption */ - conn = s2n_connection_new(S2N_CLIENT); - s2n_connection_set_io_pair(conn, io_pair); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - - /* Set session state on client connection */ - if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { - result = 9; - } - - if (s2n_negotiate(conn, &blocked) != 0) { - result = 10; - } - - /* Make sure we did a abbreviated handshake */ - if (!IS_RESUMPTION_HANDSHAKE(conn)) { - result = 11; - } - - if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { - result = 12; - } - - shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - - /* Give the server a chance to avoid sigpipe */ - sleep(1); - - /* Session resumption with bad session state */ - conn = s2n_connection_new(S2N_CLIENT); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - s2n_connection_set_io_pair(conn, io_pair); - - /* Change the format of the session state and check we cannot deserialize it */ - serialized_session_state[0] = 3; - if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) == 0) { - result = 13; - } - - if (s2n_errno != S2N_ERR_INVALID_SERIALIZED_SESSION_STATE) { - result = 14; - } - - serialized_session_state[0] = 0; - /* Change the protocol version (36th byte) in session state */ - if (serialized_session_state_length < 36) { - result = 15; - } - - serialized_session_state[35] = 30; - if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { - result = 16; - } - - if (s2n_negotiate(conn, &blocked) == 0) { - result = 17; - } - - if (s2n_errno != S2N_ERR_BAD_MESSAGE) { - result = 18; - } - - s2n_connection_free(conn); - s2n_config_free(config); - - /* Give the server a chance to avoid sigpipe */ - sleep(1); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - exit(result); -} - -int main(int argc, char **argv) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - char buffer[256]; - int bytes_read = 0; - int shutdown_rc = -1; - uint64_t now = 0; - uint8_t session_id_from_server[MAX_KEY_LEN]; - uint8_t session_id_from_client[MAX_KEY_LEN]; - - /* aes keys. Used for session ticket/session data encryption. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ - uint8_t ticket_key_name[16] = "2018.07.26.15\0"; - uint8_t ticket_key[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, - 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, - 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, - 0xcb, 0x04 }; - - BEGIN_TEST(); - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - /* Initial handshake */ - { - /* Initialize the cache so the client and server start off on the same page */ - initialize_cache(); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache)); - EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache)); - EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache)); - - /* Although we disable session ticket, as long as session cache - * callbacks are binded, session ticket key storage would be initialized - */ - POSIX_GUARD(s2n_config_set_session_cache_onoff(config, 1)); - POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), ticket_key, sizeof(ticket_key), now / ONE_SEC_IN_NANOS)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - int r = s2n_negotiate(conn, &blocked); - /* first time it always blocks the handshake, as we mock a remote - * connection/event from the lock - */ - EXPECT_EQUAL(r, -1); - EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - /* Make sure the get_session_id and get_session_id_length APIs are - * working as expected */ - EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); - EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_server, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); - - /* Make sure we did a full TLS1.2 handshake */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(conn)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure the message was delivered */ - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); - EXPECT_EQUAL(bytes_read, sizeof(MSG)); - EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); - - /* Shutdown handshake */ - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - /* Clean up */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Session resumption */ - { - initialize_cache(); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - int r = s2n_negotiate(conn, &blocked); - /* first time it always blocks the handshake, as we mock a remote - * connection/event from the lock - */ - EXPECT_EQUAL(r, -1); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - /* Make sure the get_session_id and get_session_id_length APIs are - * working as expected */ - EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); - EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_client, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); - EXPECT_EQUAL(0, memcmp(session_id_from_client, session_id_from_server, MAX_KEY_LEN)); - - /* Make sure we did a abbreviated handshake */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(conn)); - - /* Ensure the message was delivered */ - memset(buffer, 0, sizeof(buffer)); - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); - EXPECT_EQUAL(bytes_read, sizeof(MSG)); - EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); - - /* Shutdown handshake */ - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - /* Clean up */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Session resumption with bad session state on client side */ - { - initialize_cache(); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - int r = s2n_negotiate(conn, &blocked); - EXPECT_EQUAL(r, -1); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); - /* Verify we failed to negotiate */ - EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); - - /* Clean up */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Close the pipes */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Session caching with a server that does not support EMS */ - { - initialize_cache(); - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - s2n_config_disable_x509_verification(config); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate until server has read the Client Hello message but hasn't written the server hello message */ - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); - - /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false - * and removing the EMS extension from our received extensions. */ - server_conn->ems_negotiated = false; - s2n_extension_type_id ems_ext_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); - S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); - - /* Connection is successful and EMS is not negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_FALSE(server_conn->ems_negotiated); - EXPECT_FALSE(client_conn->ems_negotiated); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); - uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Wipe connections and set up new handshake */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Server will block the first time cache is accessed */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); - - /* Resumed connection is successful and EMS is not negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 0); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_FALSE(server_conn->ems_negotiated); - EXPECT_FALSE(client_conn->ems_negotiated); - EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *= type=test - *# If the original session used the "extended_master_secret" - *# extension but the new ClientHello does not contain it, the server - *# MUST abort the abbreviated handshake. - **/ - { - initialize_cache(); - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - s2n_config_disable_x509_verification(config); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Connection is successful and EMS is negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(server_conn->ems_negotiated); - EXPECT_TRUE(client_conn->ems_negotiated); - - size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); - uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Wipe connections and set up new handshake */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the client to not send the EMS extension */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - client_conn->ems_negotiated = false; - - /* Server will block the first time cache is accessed */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); - - /* Server did not receive the EMS extension from client */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_MISSING_EXTENSION); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *= type=test - *# If the original session did not use the "extended_master_secret" - *# extension but the new ClientHello contains the extension, then the - *# server MUST NOT perform the abbreviated handshake. Instead, it - *# SHOULD continue with a full handshake (as described in - *# Section 5.2) to negotiate a new session. - **/ - { - initialize_cache(); - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - s2n_config_disable_x509_verification(config); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate until server has read the Client Hello message but hasn't written the Server Hello message */ - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); - - /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false - * and removing the EMS extension from our received extensions. */ - server_conn->ems_negotiated = false; - s2n_extension_type_id ems_ext_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); - S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); - - /* Connection is successful and EMS is not negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_FALSE(server_conn->ems_negotiated); - EXPECT_FALSE(client_conn->ems_negotiated); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); - uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Wipe connections and set up new handshake */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the client to send the EMS extension even though the original session did not negotiate EMS */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - client_conn->ems_negotiated = true; - - /* Server will block the first time cache is accessed */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); - - /* Fallback to full handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 1); - EXPECT_TRUE(server_conn->ems_negotiated); - EXPECT_TRUE(client_conn->ems_negotiated); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Clean up */ - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - free(cert_chain_pem); - free(private_key_pem); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_bitmap.h" + +#define MAX_KEY_LEN 32 +#define MAX_VAL_LEN 255 + +static const char SESSION_ID[] = "0123456789abcdef0123456789abcdef"; +static const char MSG[] = "Test"; + +struct session_cache_entry { + uint8_t key[MAX_KEY_LEN]; + uint8_t key_len; + uint8_t value[MAX_VAL_LEN]; + uint8_t value_len; + uint8_t lock; +}; + +struct session_cache_entry session_cache[256]; + +int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + if (value_size == 0 || value_size > MAX_VAL_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + EXPECT_MEMCPY_SUCCESS(cache[idx].key, key, key_size); + EXPECT_MEMCPY_SUCCESS(cache[idx].value, value, value_size); + + cache[idx].key_len = key_size; + cache[idx].value_len = value_size; + + return 0; +} + +int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].lock) { + /* here we mock a remote connection/event blocking the handshake + * state machine, until lock is free + */ + cache[idx].lock = 0; + return S2N_CALLBACK_BLOCKED; + } + + if (cache[idx].key_len != key_size) { + return -1; + } + + if (memcmp(cache[idx].key, key, key_size)) { + return -1; + } + + if (*value_size < cache[idx].value_len) { + return -1; + } + + *value_size = cache[idx].value_len; + EXPECT_MEMCPY_SUCCESS(value, cache[idx].value, cache[idx].value_len); + + return 0; +} + +int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].key_len == 0) { + return 0; + } + + if (cache[idx].key_len != key_size) { + return -1; + } + + if (memcmp(cache[idx].key, key, key_size)) { + return -1; + } + + cache[idx].key_len = 0; + cache[idx].value_len = 0; + + return 0; +} + +/* init session cache lock field, which is used to mock a remote + * connection/event block + */ +static void initialize_cache() +{ + for (int i = 0; i < 256; i++) { + session_cache[i].lock = 1; + } +} + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + size_t serialized_session_state_length = 0; + uint8_t serialized_session_state[256] = { 0 }; + + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int result = 0; + + /* Give the server a chance to listen */ + sleep(1); + + /* Initial handshake */ + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_set_cipher_preferences(config, "20240501"); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + /* Set the session id to ensure we're able to fallback to full handshake if session is not in server cache */ + EXPECT_MEMCPY_SUCCESS(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + + if (s2n_negotiate(conn, &blocked) != 0) { + result = 1; + } + + /* Make sure we did a full handshake */ + if (!IS_FULL_HANDSHAKE(conn)) { + result = 2; + } + + /* Save session state from the connection */ + memset(serialized_session_state, 0, sizeof(serialized_session_state)); + serialized_session_state_length = s2n_connection_get_session_length(conn); + if (serialized_session_state_length > sizeof(serialized_session_state)) { + result = 3; + } + + /* Send very low session buffer size and see that you can get an error */ + if (s2n_connection_get_session(conn, serialized_session_state, 1) == 0) { + result = 4; + } + + if (s2n_errno != S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG) { + result = 5; + } + + if ((size_t) s2n_connection_get_session(conn, serialized_session_state, serialized_session_state_length) != serialized_session_state_length) { + result = 6; + } + + /* server would choose a session ID for client */ + if (memcmp(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN) == 0) { + result = 7; + } + + if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { + result = 8; + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + /* Session resumption */ + conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_io_pair(conn, io_pair); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + + /* Set session state on client connection */ + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { + result = 9; + } + + if (s2n_negotiate(conn, &blocked) != 0) { + result = 10; + } + + /* Make sure we did a abbreviated handshake */ + if (!IS_RESUMPTION_HANDSHAKE(conn)) { + result = 11; + } + + if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { + result = 12; + } + + shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + /* Session resumption with bad session state */ + conn = s2n_connection_new(S2N_CLIENT); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + s2n_connection_set_io_pair(conn, io_pair); + + /* Change the format of the session state and check we cannot deserialize it */ + serialized_session_state[0] = 3; + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) == 0) { + result = 13; + } + + if (s2n_errno != S2N_ERR_INVALID_SERIALIZED_SESSION_STATE) { + result = 14; + } + + serialized_session_state[0] = 0; + /* Change the protocol version (36th byte) in session state */ + if (serialized_session_state_length < 36) { + result = 15; + } + + serialized_session_state[35] = 30; + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { + result = 16; + } + + if (s2n_negotiate(conn, &blocked) == 0) { + result = 17; + } + + if (s2n_errno != S2N_ERR_BAD_MESSAGE) { + result = 18; + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + exit(result); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + char buffer[256]; + int bytes_read = 0; + int shutdown_rc = -1; + uint64_t now = 0; + uint8_t session_id_from_server[MAX_KEY_LEN]; + uint8_t session_id_from_client[MAX_KEY_LEN]; + + /* aes keys. Used for session ticket/session data encryption. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name[16] = "2018.07.26.15\0"; + uint8_t ticket_key[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }; + + BEGIN_TEST(); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + /* Initial handshake */ + { + /* Initialize the cache so the client and server start off on the same page */ + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache)); + EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache)); + EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache)); + + /* Although we disable session ticket, as long as session cache + * callbacks are binded, session ticket key storage would be initialized + */ + POSIX_GUARD(s2n_config_set_session_cache_onoff(config, 1)); + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), ticket_key, sizeof(ticket_key), now / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + int r = s2n_negotiate(conn, &blocked); + /* first time it always blocks the handshake, as we mock a remote + * connection/event from the lock + */ + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure the get_session_id and get_session_id_length APIs are + * working as expected */ + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); + EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_server, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); + + /* Make sure we did a full TLS1.2 handshake */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure the message was delivered */ + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); + EXPECT_EQUAL(bytes_read, sizeof(MSG)); + EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); + + /* Shutdown handshake */ + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session resumption */ + { + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + int r = s2n_negotiate(conn, &blocked); + /* first time it always blocks the handshake, as we mock a remote + * connection/event from the lock + */ + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure the get_session_id and get_session_id_length APIs are + * working as expected */ + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); + EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_client, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); + EXPECT_EQUAL(0, memcmp(session_id_from_client, session_id_from_server, MAX_KEY_LEN)); + + /* Make sure we did a abbreviated handshake */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(conn)); + + /* Ensure the message was delivered */ + memset(buffer, 0, sizeof(buffer)); + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); + EXPECT_EQUAL(bytes_read, sizeof(MSG)); + EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); + + /* Shutdown handshake */ + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session resumption with bad session state on client side */ + { + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + int r = s2n_negotiate(conn, &blocked); + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + /* Verify we failed to negotiate */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Close the pipes */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Session caching with a server that does not support EMS */ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message but hasn't written the server hello message */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Resumed connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 0); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session used the "extended_master_secret" + *# extension but the new ClientHello does not contain it, the server + *# MUST abort the abbreviated handshake. + **/ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Connection is successful and EMS is negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the client to not send the EMS extension */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + client_conn->ems_negotiated = false; + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Server did not receive the EMS extension from client */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + **/ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message but hasn't written the Server Hello message */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the client to send the EMS extension even though the original session did not negotiate EMS */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + client_conn->ems_negotiated = true; + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Fallback to full handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 1); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Clean up */ + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_tls12_test.c b/tests/unit/s2n_self_talk_tls12_test.c index e3713e30fad..90df3c3e6dc 100644 --- a/tests/unit/s2n_self_talk_tls12_test.c +++ b/tests/unit/s2n_self_talk_tls12_test.c @@ -1,206 +1,207 @@ -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -#define SUPPORTED_CERTIFICATE_FORMATS (2) - -static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; -static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - char buffer[0xffff]; - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - s2n_config_disable_x509_verification(config); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - s2n_negotiate(conn, &blocked); - - s2n_connection_free_handshake(conn); - - uint16_t timeout = 1; - s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); - int i = 0; - for (i = 1; i < 0xffff - 100; i += 100) { - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - s2n_send(conn, buffer, i, &blocked); - } - - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - - /* release the buffers here to validate we can continue IO after */ - s2n_connection_release_buffers(conn); - - /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ - struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; - int r = 0; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - /* Active application bytes consumed is reset to 0 in before writing data. */ - /* Its value should equal to bytes written after writing */ - ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); - if (bytes_written != conn->active_application_bytes_consumed) { - exit(0); - } - - int shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - s2n_config_free(config); - - /* Give the server a chance to a void a sigpipe */ - sleep(1); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int status = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { - struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); - } - - if (is_dh_key_exchange) { - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - } - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - char buffer[0xffff]; - for (int i = 1; i < 0xffff; i += 100) { - char *ptr = buffer; - int size = i; - - do { - int bytes_read = 0; - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); - - size -= bytes_read; - ptr += bytes_read; - } while (size); - - for (int j = 0; j < i; j++) { - EXPECT_EQUAL(buffer[j], 33); - } - - /* release the buffers here to validate we can continue IO after */ - EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); - } - EXPECT_SUCCESS(s2n_config_free(config)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + char buffer[0xffff]; + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + s2n_negotiate(conn, &blocked); + + s2n_connection_free_handshake(conn); + + uint16_t timeout = 1; + s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); + int i = 0; + for (i = 1; i < 0xffff - 100; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + s2n_send(conn, buffer, i, &blocked); + } + + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + /* release the buffers here to validate we can continue IO after */ + s2n_connection_release_buffers(conn); + + /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ + struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; + int r = 0; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + /* Active application bytes consumed is reset to 0 in before writing data. */ + /* Its value should equal to bytes written after writing */ + ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); + if (bytes_written != conn->active_application_bytes_consumed) { + exit(0); + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + char buffer[0xffff]; + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + + /* release the buffers here to validate we can continue IO after */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_send_key_update_test.c b/tests/unit/s2n_send_key_update_test.c index c8ecadb6bac..f7f217f7731 100644 --- a/tests/unit/s2n_send_key_update_test.c +++ b/tests/unit/s2n_send_key_update_test.c @@ -1,197 +1,198 @@ -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" - -int s2n_key_update_write(struct s2n_blob *out); - -static int s2n_test_init_encryption(struct s2n_connection *conn) -{ - struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - conn->server->cipher_suite = cipher_suite; - conn->client->cipher_suite = cipher_suite; - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - /* Just some data that's the right length */ - S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); - S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); - S2N_BLOB_FROM_HEX(application_secret, - "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" - "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); - - struct s2n_session_key *server_session_key = &conn->server->server_key; - struct s2n_session_key *client_session_key = &conn->server->server_key; - uint8_t *server_implicit_iv = conn->server->server_implicit_iv; - uint8_t *client_implicit_iv = conn->client->client_implicit_iv; - - /* Initialize record algorithm */ - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(server_session_key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(client_session_key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(server_session_key, &key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(client_session_key, &key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(server_session_key, &key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(client_session_key, &key)); - - /* Initialized secrets */ - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, application_secret.data, application_secret.size); - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); - - /* Copy iv bytes from input data */ - POSIX_CHECKED_MEMCPY(server_implicit_iv, iv.data, iv.size); - POSIX_CHECKED_MEMCPY(client_implicit_iv, iv.data, iv.size); - - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* The maximum record number converted to base 256 */ - uint8_t max_record_limit[S2N_TLS_SEQUENCE_NUM_LEN] = { 0, 0, 0, 0, 1, 106, 9, 229 }; - - /* s2n_send sends key update if necessary */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - server_conn->actual_protocol_version = S2N_TLS13; - client_conn->actual_protocol_version = S2N_TLS13; - - uint8_t zero_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; - - EXPECT_SUCCESS(s2n_test_init_encryption(server_conn)); - EXPECT_SUCCESS(s2n_test_init_encryption(client_conn)); - - DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); - - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); - - /* Mimic key update send conditions */ - for (size_t i = 0; i < S2N_TLS_SEQUENCE_NUM_LEN; i++) { - server_conn->secure->server_sequence_number[i] = max_record_limit[i]; - } - - /* Next message to send will trigger key update message*/ - s2n_blocked_status blocked; - char message[] = "sent message"; - EXPECT_SUCCESS(s2n_send(server_conn, message, sizeof(message), &blocked)); - - /* Verify key update happened */ - EXPECT_BYTEARRAY_NOT_EQUAL(server_conn->secrets.version.tls13.server_app_secret, client_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); - EXPECT_BYTEARRAY_EQUAL(server_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); - - /* Receive keyupdate message */ - uint8_t data[100]; - EXPECT_SUCCESS(s2n_recv(client_conn, data, sizeof(message), &blocked)); - EXPECT_BYTEARRAY_EQUAL(data, message, sizeof(message)); - EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.server_app_secret, server_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); - EXPECT_BYTEARRAY_EQUAL(client_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* TLS 1.2 Server that receives TLS 1.3 KeyUpdate from Client should close connection */ - { - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char *cert_chain = NULL; - char *private_key = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Force Client to send a TLS 1.3 KeyUpdate Message over TLS 1.2 connection */ - uint8_t key_update_data[S2N_KEY_UPDATE_MESSAGE_SIZE] = { 0 }; - struct s2n_blob key_update_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&key_update_blob, key_update_data, sizeof(key_update_data))); - EXPECT_SUCCESS(s2n_key_update_write(&key_update_blob)); - EXPECT_OK(s2n_record_write(client_conn, TLS_HANDSHAKE, &key_update_blob)); - EXPECT_SUCCESS(s2n_flush(client_conn, &blocked)); - - /* Attempt to recv on Server conn, see KeyUpdate Message, and confirm connection is closed. */ - uint8_t server_message[128]; - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, server_message, sizeof(server_message), &blocked), S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - free(cert_chain); - free(private_key); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +int s2n_key_update_write(struct s2n_blob *out); + +static int s2n_test_init_encryption(struct s2n_connection *conn) +{ + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->server->cipher_suite = cipher_suite; + conn->client->cipher_suite = cipher_suite; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Just some data that's the right length */ + S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); + S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); + S2N_BLOB_FROM_HEX(application_secret, + "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" + "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); + + struct s2n_session_key *server_session_key = &conn->server->server_key; + struct s2n_session_key *client_session_key = &conn->server->server_key; + uint8_t *server_implicit_iv = conn->server->server_implicit_iv; + uint8_t *client_implicit_iv = conn->client->client_implicit_iv; + + /* Initialize record algorithm */ + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(server_session_key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(client_session_key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(server_session_key, &key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(client_session_key, &key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(server_session_key, &key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(client_session_key, &key)); + + /* Initialized secrets */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, application_secret.data, application_secret.size); + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Copy iv bytes from input data */ + POSIX_CHECKED_MEMCPY(server_implicit_iv, iv.data, iv.size); + POSIX_CHECKED_MEMCPY(client_implicit_iv, iv.data, iv.size); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* The maximum record number converted to base 256 */ + uint8_t max_record_limit[S2N_TLS_SEQUENCE_NUM_LEN] = { 0, 0, 0, 0, 1, 106, 9, 229 }; + + /* s2n_send sends key update if necessary */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + + uint8_t zero_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + + EXPECT_SUCCESS(s2n_test_init_encryption(server_conn)); + EXPECT_SUCCESS(s2n_test_init_encryption(client_conn)); + + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + /* Mimic key update send conditions */ + for (size_t i = 0; i < S2N_TLS_SEQUENCE_NUM_LEN; i++) { + server_conn->secure->server_sequence_number[i] = max_record_limit[i]; + } + + /* Next message to send will trigger key update message*/ + s2n_blocked_status blocked; + char message[] = "sent message"; + EXPECT_SUCCESS(s2n_send(server_conn, message, sizeof(message), &blocked)); + + /* Verify key update happened */ + EXPECT_BYTEARRAY_NOT_EQUAL(server_conn->secrets.version.tls13.server_app_secret, client_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(server_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + + /* Receive keyupdate message */ + uint8_t data[100]; + EXPECT_SUCCESS(s2n_recv(client_conn, data, sizeof(message), &blocked)); + EXPECT_BYTEARRAY_EQUAL(data, message, sizeof(message)); + EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.server_app_secret, server_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* TLS 1.2 Server that receives TLS 1.3 KeyUpdate from Client should close connection */ + { + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char *cert_chain = NULL; + char *private_key = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Force Client to send a TLS 1.3 KeyUpdate Message over TLS 1.2 connection */ + uint8_t key_update_data[S2N_KEY_UPDATE_MESSAGE_SIZE] = { 0 }; + struct s2n_blob key_update_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&key_update_blob, key_update_data, sizeof(key_update_data))); + EXPECT_SUCCESS(s2n_key_update_write(&key_update_blob)); + EXPECT_OK(s2n_record_write(client_conn, TLS_HANDSHAKE, &key_update_blob)); + EXPECT_SUCCESS(s2n_flush(client_conn, &blocked)); + + /* Attempt to recv on Server conn, see KeyUpdate Message, and confirm connection is closed. */ + uint8_t server_message[128]; + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, server_message, sizeof(server_message), &blocked), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + free(cert_chain); + free(private_key); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_send_multirecord_test.c b/tests/unit/s2n_send_multirecord_test.c index 4ff94234ef0..f97ee5a063d 100644 --- a/tests/unit/s2n_send_multirecord_test.c +++ b/tests/unit/s2n_send_multirecord_test.c @@ -1,582 +1,583 @@ -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_random.h" - -/* clang-format off */ -#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } -#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } -#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } -#define EXPECTED_SEND_RESULT(bytes) { .result = bytes, .assert_result = true } -#define OK_SEND_RESULT { .result = INT_MAX } -/* clang-format on */ - -int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); -bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size); - -struct s2n_send_result { - int result; - int error; - bool assert_result; -}; - -struct s2n_send_context { - size_t calls; - size_t bytes_sent; - const struct s2n_send_result *results; - const size_t results_len; -}; - -static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) -{ - struct s2n_send_context *context = (struct s2n_send_context *) io_context; - POSIX_ENSURE_REF(context); - - POSIX_ENSURE_LT(context->calls, context->results_len); - const struct s2n_send_result *result = &context->results[context->calls]; - - int retval = S2N_MIN((int) len, result->result); - if (result->assert_result) { - POSIX_ENSURE_EQ(retval, len); - } - - context->calls++; - if (retval > 0) { - context->bytes_sent += retval; - } - - errno = result->error; - return retval; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - uint8_t test_data[] = "hello world"; - - uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; - struct s2n_blob large_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); - EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); - - /* Small record sizes will require a LOT of calls to s2n_send. - * Use this context when they should all succeed. - */ - struct s2n_send_result results_all_ok[50] = { 0 }; - for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { - results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; - } - const struct s2n_send_context context_all_ok = { - .results = results_all_ok, - .results_len = s2n_array_len(results_all_ok) - }; - - /* Setup a large output buffer that can contain all of large_test_data */ - const size_t buffer_size = 20000; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - - /* Setup an output buffer that is slightly too small for large_test_data */ - const uint32_t smaller_buffer_size = 17300; - DEFER_CLEANUP(struct s2n_config *smaller_buffer_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(smaller_buffer_config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(smaller_buffer_config, smaller_buffer_size)); - - /* Test s2n_should_flush */ - { - /* Flush if multirecord send not enabled */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - /* Multirecord send not enabled */ - EXPECT_FALSE(conn->multirecord_send); - EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); - - /* Multirecord send enabled */ - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_TRUE(conn->multirecord_send); - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - }; - - /* Flush if all data sent */ - { - ssize_t send_size = 100; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* No data sent */ - EXPECT_FALSE(s2n_should_flush(conn, send_size)); - - /* Some data sent */ - conn->current_user_data_consumed = send_size / 2; - EXPECT_FALSE(s2n_should_flush(conn, send_size)); - - /* All data sent */ - conn->current_user_data_consumed = send_size; - EXPECT_TRUE(s2n_should_flush(conn, send_size)); - }; - - /* Flush if buffer can't hold max size record */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Uninitialized buffer */ - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - - /* Empty buffer */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - - /* Buffer not empty, but sufficient space remains */ - size_t max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, buffer_size - max_record_size)); - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - - /* Insufficient space in buffer */ - EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, 1)); - EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); - }; - }; - - /* Total data fits in a single record. - * Equivalent to not using multirecord. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_TRUE(context.bytes_sent > sizeof(test_data)); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Send buffer was configured too small for even a single record. - * Send smaller records. - * - * The minimum buffer size we allow generates a fragment size of 5, to prevent - * fragmenting KeyUpdate messages, which are always 5 bytes. At this minimum size, - * application data is also fragmented into 5 byte chunks, which is pretty silly, - * but is an edge case. - */ - { - DEFER_CLEANUP(struct s2n_config *min_buffer_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(min_buffer_config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(min_buffer_config, S2N_MIN_SEND_BUFFER_SIZE)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, min_buffer_config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - ssize_t send_size = sizeof(test_data); - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, send_size, &blocked), send_size); - - /* Since each record only contains two bytes of payload, - * we need to send a number of records equal to our total send ceil(size / 2). - */ - uint8_t remainder = (send_size % S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) ? 1 : 0; - EXPECT_EQUAL(context.calls, (send_size / S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) + remainder); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_TRUE(context.bytes_sent > send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, S2N_MIN_SEND_BUFFER_SIZE); - }; - - /* Total data fits in multiple records. - * Without multirecord, this would result in multiple calls to send. - */ - uint16_t large_test_data_send_size = 0; - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_TRUE(context.bytes_sent > sizeof(large_test_data)); - large_test_data_send_size = context.bytes_sent; - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Total data with multiple records too large for the send buffer. - * Call send multiple times. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - - EXPECT_EQUAL(context.calls, 2); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - /* Even though it took more send calls, - * we still sent the same number of records with the same overhead. - */ - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); - }; - - /* Block while buffering multiple records. - * Send blocks until all buffered data is sent. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - const uint32_t partial_send = 10; - const uint32_t at_least_one_record = large_test_data_send_size - partial_send - 1; - const struct s2n_send_result results[] = { - /* First send writes less than one record before blocking */ - PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, - /* Second send writes at least one record before blocking */ - PARTIAL_SEND_RESULT(at_least_one_record), BLOCK_SEND_RESULT, - /* Third send completes */ - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send); - - /* Unlike when we buffer a single record at a time, s2n_send does not report each fragment / record flushed. - * Instead, it won't report any data as sent until all buffered data is flushed. - */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, at_least_one_record + partial_send); - - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Block while buffering multiple records across multiple send calls. - * Each send blocks until all buffered data is flushed. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); - - const uint32_t partial_send = 10; - const uint32_t at_least_one_flush = smaller_buffer_size - partial_send; - const struct s2n_send_result results[] = { - /* First send writes less than one record before blocking */ - PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, - /* Second send flushes the output buffer before blocking */ - PARTIAL_SEND_RESULT(at_least_one_flush), BLOCK_SEND_RESULT, - /* Third send completes */ - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send); - - /* Write the buffer, which contains two records. */ - ssize_t expected_sent = S2N_DEFAULT_FRAGMENT_LENGTH * 2; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_sent); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - - size_t offset = expected_sent; - expected_sent = sizeof(large_test_data) - offset; - EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), expected_sent); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); - }; - - /* Send a post-handshake message when records are buffered. - * - * We only test the KeyUpdate post-handshake message. That can trigger - * part way through a call to s2n_send if the encryption limit is reached. - * - * We can't reliably test NewSessionTicket post-handshake messages. - * Those could only trigger if an application called s2n_connection_add_new_tickets_to_send - * part way through a call to s2n_send, which requires calling from another thread - * at just the right (wrong?) time. - */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - /* Find size of a KeyUpdate record */ - size_t key_update_size = 0; - { - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_atomic_flag_set(&conn->key_update_pending); - EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - key_update_size = context.bytes_sent; - }; - EXPECT_TRUE(key_update_size > 0); - - const struct s2n_send_result results[] = { - /* We expect the buffer to be flushed before the post handshake message */ - OK_SEND_RESULT, - /* We expect the buffer to be flushed again after the post handshake message */ - EXPECTED_SEND_RESULT(key_update_size), - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - /* Find record limit */ - uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; - EXPECT_TRUE(limit > 0); - - /* Initialize sequence number */ - struct s2n_blob seq_num_blob = { 0 }; - struct s2n_stuffer seq_num_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); - - /* Set the sequence number so that a KeyUpdate triggers after one more record. */ - uint64_t initial_seq_num = limit - 1; - EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); - EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - - /* Send */ - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - - /* Verify KeyUpdate happened: the sequence number was reset */ - uint64_t final_seq_num = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); - EXPECT_TRUE(final_seq_num < initial_seq_num); - - /* Verify expected send behavior */ - size_t expected_calls = 1 /* first record */ + 1 /* KeyUpdate */ + 1 /* remaining records */; - EXPECT_EQUAL(context.calls, expected_calls); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Test: Alert records are not buffered with ApplicationData records - * - * We flush before sending an alert, even if there is sufficient - * space for the alert record in the send buffer. - * - * If this behavior changed, then s2n_should_flush would need to consider - * the size of a possible alert. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - const uint32_t send_size = 10; - const uint32_t max_app_data_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(send_size); - const uint32_t max_alert_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_ALERT_LENGTH); - const uint32_t min_send_buffer_size = max_app_data_record_size + max_alert_record_size; - EXPECT_TRUE(min_send_buffer_size <= buffer_size); - - /* Queue the alert */ - EXPECT_OK(s2n_queue_reader_no_renegotiation_alert(conn)); - - /* Send the Application Data and Alert */ - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, send_size, &blocked), send_size); - - /* We expect two separate send calls: one for the application data record - * and one for the alert record. - */ - EXPECT_EQUAL(context.calls, 2); - - /* We expect that the output buffer never contained all data sent, - * since that data was split between two records. - */ - EXPECT_TRUE(conn->out.high_water_mark < context.bytes_sent); - }; - - /* Send a post-handshake message when records are buffered, and IO blocks */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - /* Find size of a KeyUpdate record */ - size_t key_update_size = 0; - { - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_atomic_flag_set(&conn->key_update_pending); - EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - key_update_size = context.bytes_sent; - } - EXPECT_TRUE(key_update_size > 0); - - /* Block the first two calls to send, only allowing the third to succeed. */ - const struct s2n_send_result results[] = { - /* Initial ApplicationData records */ - BLOCK_SEND_RESULT, - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - /* KeyUpdate record */ - BLOCK_SEND_RESULT, - BLOCK_SEND_RESULT, - EXPECTED_SEND_RESULT(key_update_size), - /* Remaining ApplicationData records */ - BLOCK_SEND_RESULT, - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - /* Find record limit */ - uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; - EXPECT_TRUE(limit > 0); - - /* Initialize sequence number */ - struct s2n_blob seq_num_blob = { 0 }; - struct s2n_stuffer seq_num_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); - - /* Set the sequence number so that a KeyUpdate triggers after one more record. */ - uint64_t initial_seq_num = limit - 1; - EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); - EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - - /* Send until all data written */ - size_t total = 0; - while (total < sizeof(large_test_data)) { - ssize_t sent = s2n_send(conn, large_test_data + total, sizeof(large_test_data) - total, &blocked); - if (sent >= S2N_SUCCESS) { - total += sent; - } else { - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - } - } - - /* Verify KeyUpdate happened: the sequence number was reset */ - uint64_t final_seq_num = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); - EXPECT_TRUE(final_seq_num < initial_seq_num); - - /* Verify expected send behavior */ - size_t expected_calls = s2n_array_len(results); - EXPECT_EQUAL(context.calls, expected_calls); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* clang-format off */ +#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } +#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } +#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } +#define EXPECTED_SEND_RESULT(bytes) { .result = bytes, .assert_result = true } +#define OK_SEND_RESULT { .result = INT_MAX } +/* clang-format on */ + +int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); +bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size); + +struct s2n_send_result { + int result; + int error; + bool assert_result; +}; + +struct s2n_send_context { + size_t calls; + size_t bytes_sent; + const struct s2n_send_result *results; + const size_t results_len; +}; + +static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_send_context *context = (struct s2n_send_context *) io_context; + POSIX_ENSURE_REF(context); + + POSIX_ENSURE_LT(context->calls, context->results_len); + const struct s2n_send_result *result = &context->results[context->calls]; + + int retval = S2N_MIN((int) len, result->result); + if (result->assert_result) { + POSIX_ENSURE_EQ(retval, len); + } + + context->calls++; + if (retval > 0) { + context->bytes_sent += retval; + } + + errno = result->error; + return retval; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_data[] = "hello world"; + + uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; + struct s2n_blob large_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); + EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); + + /* Small record sizes will require a LOT of calls to s2n_send. + * Use this context when they should all succeed. + */ + struct s2n_send_result results_all_ok[50] = { 0 }; + for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { + results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; + } + const struct s2n_send_context context_all_ok = { + .results = results_all_ok, + .results_len = s2n_array_len(results_all_ok) + }; + + /* Setup a large output buffer that can contain all of large_test_data */ + const size_t buffer_size = 20000; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + /* Setup an output buffer that is slightly too small for large_test_data */ + const uint32_t smaller_buffer_size = 17300; + DEFER_CLEANUP(struct s2n_config *smaller_buffer_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(smaller_buffer_config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(smaller_buffer_config, smaller_buffer_size)); + + /* Test s2n_should_flush */ + { + /* Flush if multirecord send not enabled */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + /* Multirecord send not enabled */ + EXPECT_FALSE(conn->multirecord_send); + EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); + + /* Multirecord send enabled */ + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_TRUE(conn->multirecord_send); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + }; + + /* Flush if all data sent */ + { + ssize_t send_size = 100; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* No data sent */ + EXPECT_FALSE(s2n_should_flush(conn, send_size)); + + /* Some data sent */ + conn->current_user_data_consumed = send_size / 2; + EXPECT_FALSE(s2n_should_flush(conn, send_size)); + + /* All data sent */ + conn->current_user_data_consumed = send_size; + EXPECT_TRUE(s2n_should_flush(conn, send_size)); + }; + + /* Flush if buffer can't hold max size record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Uninitialized buffer */ + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Empty buffer */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Buffer not empty, but sufficient space remains */ + size_t max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, buffer_size - max_record_size)); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Insufficient space in buffer */ + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, 1)); + EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); + }; + }; + + /* Total data fits in a single record. + * Equivalent to not using multirecord. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > sizeof(test_data)); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Send buffer was configured too small for even a single record. + * Send smaller records. + * + * The minimum buffer size we allow generates a fragment size of 5, to prevent + * fragmenting KeyUpdate messages, which are always 5 bytes. At this minimum size, + * application data is also fragmented into 5 byte chunks, which is pretty silly, + * but is an edge case. + */ + { + DEFER_CLEANUP(struct s2n_config *min_buffer_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(min_buffer_config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(min_buffer_config, S2N_MIN_SEND_BUFFER_SIZE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, min_buffer_config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + ssize_t send_size = sizeof(test_data); + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, send_size, &blocked), send_size); + + /* Since each record only contains two bytes of payload, + * we need to send a number of records equal to our total send ceil(size / 2). + */ + uint8_t remainder = (send_size % S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) ? 1 : 0; + EXPECT_EQUAL(context.calls, (send_size / S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) + remainder); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, S2N_MIN_SEND_BUFFER_SIZE); + }; + + /* Total data fits in multiple records. + * Without multirecord, this would result in multiple calls to send. + */ + uint16_t large_test_data_send_size = 0; + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > sizeof(large_test_data)); + large_test_data_send_size = context.bytes_sent; + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Total data with multiple records too large for the send buffer. + * Call send multiple times. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + EXPECT_EQUAL(context.calls, 2); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + /* Even though it took more send calls, + * we still sent the same number of records with the same overhead. + */ + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); + }; + + /* Block while buffering multiple records. + * Send blocks until all buffered data is sent. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const uint32_t partial_send = 10; + const uint32_t at_least_one_record = large_test_data_send_size - partial_send - 1; + const struct s2n_send_result results[] = { + /* First send writes less than one record before blocking */ + PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, + /* Second send writes at least one record before blocking */ + PARTIAL_SEND_RESULT(at_least_one_record), BLOCK_SEND_RESULT, + /* Third send completes */ + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Unlike when we buffer a single record at a time, s2n_send does not report each fragment / record flushed. + * Instead, it won't report any data as sent until all buffered data is flushed. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, at_least_one_record + partial_send); + + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Block while buffering multiple records across multiple send calls. + * Each send blocks until all buffered data is flushed. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); + + const uint32_t partial_send = 10; + const uint32_t at_least_one_flush = smaller_buffer_size - partial_send; + const struct s2n_send_result results[] = { + /* First send writes less than one record before blocking */ + PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, + /* Second send flushes the output buffer before blocking */ + PARTIAL_SEND_RESULT(at_least_one_flush), BLOCK_SEND_RESULT, + /* Third send completes */ + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Write the buffer, which contains two records. */ + ssize_t expected_sent = S2N_DEFAULT_FRAGMENT_LENGTH * 2; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_sent); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + size_t offset = expected_sent; + expected_sent = sizeof(large_test_data) - offset; + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), expected_sent); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); + }; + + /* Send a post-handshake message when records are buffered. + * + * We only test the KeyUpdate post-handshake message. That can trigger + * part way through a call to s2n_send if the encryption limit is reached. + * + * We can't reliably test NewSessionTicket post-handshake messages. + * Those could only trigger if an application called s2n_connection_add_new_tickets_to_send + * part way through a call to s2n_send, which requires calling from another thread + * at just the right (wrong?) time. + */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + /* Find size of a KeyUpdate record */ + size_t key_update_size = 0; + { + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + key_update_size = context.bytes_sent; + }; + EXPECT_TRUE(key_update_size > 0); + + const struct s2n_send_result results[] = { + /* We expect the buffer to be flushed before the post handshake message */ + OK_SEND_RESULT, + /* We expect the buffer to be flushed again after the post handshake message */ + EXPECTED_SEND_RESULT(key_update_size), + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + /* Find record limit */ + uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; + EXPECT_TRUE(limit > 0); + + /* Initialize sequence number */ + struct s2n_blob seq_num_blob = { 0 }; + struct s2n_stuffer seq_num_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); + + /* Set the sequence number so that a KeyUpdate triggers after one more record. */ + uint64_t initial_seq_num = limit - 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Send */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + /* Verify KeyUpdate happened: the sequence number was reset */ + uint64_t final_seq_num = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); + EXPECT_TRUE(final_seq_num < initial_seq_num); + + /* Verify expected send behavior */ + size_t expected_calls = 1 /* first record */ + 1 /* KeyUpdate */ + 1 /* remaining records */; + EXPECT_EQUAL(context.calls, expected_calls); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Test: Alert records are not buffered with ApplicationData records + * + * We flush before sending an alert, even if there is sufficient + * space for the alert record in the send buffer. + * + * If this behavior changed, then s2n_should_flush would need to consider + * the size of a possible alert. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + const uint32_t send_size = 10; + const uint32_t max_app_data_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(send_size); + const uint32_t max_alert_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_ALERT_LENGTH); + const uint32_t min_send_buffer_size = max_app_data_record_size + max_alert_record_size; + EXPECT_TRUE(min_send_buffer_size <= buffer_size); + + /* Queue the alert */ + EXPECT_OK(s2n_queue_reader_no_renegotiation_alert(conn)); + + /* Send the Application Data and Alert */ + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, send_size, &blocked), send_size); + + /* We expect two separate send calls: one for the application data record + * and one for the alert record. + */ + EXPECT_EQUAL(context.calls, 2); + + /* We expect that the output buffer never contained all data sent, + * since that data was split between two records. + */ + EXPECT_TRUE(conn->out.high_water_mark < context.bytes_sent); + }; + + /* Send a post-handshake message when records are buffered, and IO blocks */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + /* Find size of a KeyUpdate record */ + size_t key_update_size = 0; + { + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + key_update_size = context.bytes_sent; + } + EXPECT_TRUE(key_update_size > 0); + + /* Block the first two calls to send, only allowing the third to succeed. */ + const struct s2n_send_result results[] = { + /* Initial ApplicationData records */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + /* KeyUpdate record */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + EXPECTED_SEND_RESULT(key_update_size), + /* Remaining ApplicationData records */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + /* Find record limit */ + uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; + EXPECT_TRUE(limit > 0); + + /* Initialize sequence number */ + struct s2n_blob seq_num_blob = { 0 }; + struct s2n_stuffer seq_num_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); + + /* Set the sequence number so that a KeyUpdate triggers after one more record. */ + uint64_t initial_seq_num = limit - 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Send until all data written */ + size_t total = 0; + while (total < sizeof(large_test_data)) { + ssize_t sent = s2n_send(conn, large_test_data + total, sizeof(large_test_data) - total, &blocked); + if (sent >= S2N_SUCCESS) { + total += sent; + } else { + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + } + } + + /* Verify KeyUpdate happened: the sequence number was reset */ + uint64_t final_seq_num = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); + EXPECT_TRUE(final_seq_num < initial_seq_num); + + /* Verify expected send behavior */ + size_t expected_calls = s2n_array_len(results); + EXPECT_EQUAL(context.calls, expected_calls); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_send_test.c b/tests/unit/s2n_send_test.c index 840b9a83c7f..5387f06e069 100644 --- a/tests/unit/s2n_send_test.c +++ b/tests/unit/s2n_send_test.c @@ -1,808 +1,809 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_random.h" - -/* clang-format off */ -#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } -#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } -#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } -#define OK_SEND_RESULT { .result = INT_MAX } -/* clang-format on */ - -enum s2n_test_mfl { - S2N_MFL_DEFAULT = 0, - S2N_MFL_LARGE, - S2N_MFL_SMALL, - S2N_MFL_MINIMUM, - S2N_MFL_COUNT, -}; - -static S2N_RESULT s2n_set_test_max_fragment_len(struct s2n_connection *conn, enum s2n_test_mfl mfl) -{ - switch (mfl) { - case S2N_MFL_DEFAULT: - break; - case S2N_MFL_LARGE: - EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); - break; - case S2N_MFL_SMALL: - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); - break; - case S2N_MFL_MINIMUM: - conn->max_outgoing_fragment_length = mfl_code_to_length[1]; - break; - case S2N_MFL_COUNT: - RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); - break; - } - return S2N_RESULT_OK; -} - -struct s2n_send_result { - int result; - int error; -}; - -struct s2n_send_context { - size_t calls; - size_t bytes_sent; - const struct s2n_send_result *results; - const size_t results_len; -}; - -bool s2n_custom_send_fn_called = false; -int s2n_expect_concurrent_error_send_fn(void *io_context, const uint8_t *buf, uint32_t len) -{ - struct s2n_connection *conn = (struct s2n_connection *) io_context; - s2n_custom_send_fn_called = true; - - s2n_blocked_status blocked = 0; - ssize_t result = s2n_send(conn, buf, len, &blocked); - EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); - return result; -} - -static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) -{ - struct s2n_send_context *context = (struct s2n_send_context *) io_context; - POSIX_ENSURE_REF(context); - - POSIX_ENSURE_LT(context->calls, context->results_len); - const struct s2n_send_result *result = &context->results[context->calls]; - - int retval = S2N_MIN((int) len, result->result); - - context->calls++; - if (retval > 0) { - context->bytes_sent += retval; - } - - errno = result->error; - return retval; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - uint8_t test_data[] = "hello world"; - - uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; - struct s2n_blob large_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); - EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); - - /* Small record sizes will require a LOT of calls to s2n_send. - * Use this context when they should all succeed. - */ - struct s2n_send_result results_all_ok[50] = { 0 }; - for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { - results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; - } - const struct s2n_send_context context_all_ok = { - .results = results_all_ok, - .results_len = s2n_array_len(results_all_ok) - }; - - /* Calculating the record size for given data can be tricky. - * Instead, let's set the values based on the results of tests. - */ - ssize_t test_data_bytes_sent = 0; - - /* s2n_send */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - - /* Set the expected record size for future tests */ - test_data_bytes_sent = context.bytes_sent; - EXPECT_TRUE(test_data_bytes_sent > sizeof(test_data)); - }; - - /* Calculating the max record size for a given max fragment length can be tricky. - * Instead, let's set the values based on the results of tests. - */ - ssize_t max_frag_bytes_sent[S2N_MFL_COUNT] = { 0 }; - - /* Track the size of the output buffer. - * It should be constant across all tests with the same max fragment length. - */ - uint32_t out_size[S2N_MFL_COUNT] = { 0 }; - - /* Send exactly the maximum fragment size */ - for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); - uint32_t fragment_len = conn->max_outgoing_fragment_length; - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - /* Send exactly the fragment length */ - EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len, &blocked), fragment_len); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, 1); - - /* Set the expected record size for future tests */ - max_frag_bytes_sent[mfl] = context.bytes_sent; - EXPECT_TRUE(max_frag_bytes_sent[mfl] > 0); - - /* Set the expected output buffer size for future tests */ - out_size[mfl] = conn->out.blob.size; - EXPECT_TRUE(out_size[mfl] > 0); - - /* Sanity check: Send one byte more than the fragment length. - * If this is actually the maximum fragment length, one extra byte will - * lead to an extra record / extra call to send. - */ - context.calls = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len + 1, &blocked), fragment_len + 1); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, 2); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); - } - - /* s2n_send cannot be called concurrently */ - { - /* Setup connections */ - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - /* Setup bad send callback */ - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_expect_concurrent_error_send_fn)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - - s2n_blocked_status blocked = 0; - s2n_custom_send_fn_called = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), - S2N_ERR_IO); - EXPECT_TRUE(s2n_custom_send_fn_called); - EXPECT_EQUAL(0, conn->wire_bytes_out); - }; - - /* s2n_send tracks conn->wire_bytes_out on send */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_EQUAL(0, conn->wire_bytes_out); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); - }; - - /* s2n_send tracks conn->wire_bytes_out on partial send */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_EQUAL(0, conn->wire_bytes_out); - - const uint32_t partial_send = 10; - const struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(partial_send), - CLOSED_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO); - - EXPECT_EQUAL(context.calls, 2); - EXPECT_EQUAL(context.bytes_sent, partial_send); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); - }; - - /* s2n_send sends all data, despite partial writes */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - const struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(1), - PARTIAL_SEND_RESULT(5), - PARTIAL_SEND_RESULT(2), - OK_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, s2n_array_len(results)); - EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send would block and must be retried */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - const uint32_t partial_send = 10; - const struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - /* First attempt blocks */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send); - - /* Second attempt blocks */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send * 2); - - /* Third attempt completes */ - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* Calculating the record size for given data can be tricky. - * Instead, let's set the values based on the results of tests. - */ - ssize_t large_test_data_bytes_sent = 0; - - /* s2n_send sends multiple records worth of data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_result results[] = { OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, s2n_array_len(results)); - - large_test_data_bytes_sent = context.bytes_sent; - EXPECT_TRUE(large_test_data_bytes_sent > sizeof(large_test_data)); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send sends all records and data, despite partial writes */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(10), OK_SEND_RESULT, - OK_SEND_RESULT, - PARTIAL_SEND_RESULT(5), PARTIAL_SEND_RESULT(1), OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, s2n_array_len(results)); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send would block while sending multiple records */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - const uint32_t partial_send = 10; - struct s2n_send_result results[] = { - OK_SEND_RESULT, - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; - - /* First attempt blocks after writing one record */ - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), - S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, record_size + partial_send); - - /* Don't re-send the data already sent. */ - const uint32_t offset = S2N_DEFAULT_FRAGMENT_LENGTH; - - /* Second attempt blocks without writing another record */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, record_size + partial_send + partial_send); - - /* Third attempt completes */ - EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), - sizeof(large_test_data) - S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send would block after sending multiple records. - * ALL flushed records must be reported to the caller. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_result results[] = { - OK_SEND_RESULT, - OK_SEND_RESULT, - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; - - /* First attempt blocks after writing two records */ - ssize_t expected_send = S2N_DEFAULT_FRAGMENT_LENGTH * 2; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_send); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, record_size * 2); - - /* Don't re-send the data already sent. */ - const uint32_t offset = expected_send; - - /* Second attempt completes */ - EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), - sizeof(large_test_data) - offset); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* Sending multiple records supports different maximum fragment lengths */ - for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - - /* We expect enough calls to send to split the payload into records */ - size_t expected_calls = ceil(sizeof(large_test_data) / (double) conn->max_outgoing_fragment_length); - EXPECT_EQUAL(context.calls, expected_calls); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); - } - - /* Test dynamic record threshold record fragmentation */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - /* Retrieve the fragment size to expect */ - uint16_t single_mtu_mfl = 0; - EXPECT_OK(s2n_record_min_write_payload_size(conn, &single_mtu_mfl)); - - /* Set the dynamic record threshold large enough for two small records */ - const uint32_t resize_threshold = single_mtu_mfl * 2; - EXPECT_SUCCESS(s2n_connection_set_dynamic_record_threshold(conn, resize_threshold, UINT16_MAX)); - - struct s2n_send_result results[] = { - /* Block before sending the first record so that we can examine - * the connection state after buffering the first record. - */ - BLOCK_SEND_RESULT, OK_SEND_RESULT, - /* Send the second record */ - BLOCK_SEND_RESULT, OK_SEND_RESULT, - /* Send the third record */ - OK_SEND_RESULT, - BLOCK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - s2n_blocked_status blocked = 0; - const size_t send_size = single_mtu_mfl * 2; - - /* The first call to s2n_send blocks before sending the first record. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, send_size, &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - /* No records have been sent yet. */ - EXPECT_EQUAL(context.bytes_sent, 0); - /* The first record is buffered, - * so its bytes still count towards the resize_threshold. - * We have NOT passed the threshold. - */ - EXPECT_EQUAL(conn->active_application_bytes_consumed, single_mtu_mfl); - EXPECT_TRUE(conn->active_application_bytes_consumed < resize_threshold); - /* Output buffer should be able to handle the default size, not the single MTU size. - * Otherwise, the output buffer would need to resize later. - */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - - /* The second call to s2n_send flushes the buffered first record, - * but blocks before sending the second record. - */ - ssize_t result = s2n_send(conn, large_test_data, send_size, &blocked); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - /* First small, single-MTU record was sent. */ - EXPECT_EQUAL(result, single_mtu_mfl); - EXPECT_TRUE(context.bytes_sent < ETH_MTU); - /* The second record is buffered, - * so its bytes count towards the resize_threshold. - * We have therefore hit the threshold. - */ - EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); - /* Output buffer should be able to handle the default size, not the single MTU size. - * Otherwise, the output buffer would need to resize later. - */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - - /* The third call to s2n_send flushes the second record. */ - result = s2n_send(conn, large_test_data, send_size - single_mtu_mfl, &blocked); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - /* Second small, single-MTU record was sent. */ - EXPECT_EQUAL(result, single_mtu_mfl); - /* There should be no change regarding the resize_threshold, - * since we did not construct any new records. - */ - EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); - /* Output buffer should be able to handle the default size, not the single MTU size. - * Otherwise, the output buffer would need to resize later. - */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - - /* The fourth call to s2n_send sends the third record. */ - result = s2n_send(conn, large_test_data, conn->max_outgoing_fragment_length * 2, &blocked); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - /* We have passed the resize_threshold, so records are no longer small. - * Instead they use the standard connection fragment length. - */ - EXPECT_TRUE(result > single_mtu_mfl); - EXPECT_EQUAL(result, conn->max_outgoing_fragment_length); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* Test: s2n_sendv_with_offset_total_size */ - { - const struct iovec test_multiple_bufs[] = { - { .iov_len = 0 }, - { .iov_len = 1 }, - { .iov_len = 2 }, - { .iov_len = 0 }, - { .iov_len = 14 }, - { .iov_len = 0 }, - { .iov_len = 3 }, - { .iov_len = 0 }, - }; - const ssize_t test_multiple_bufs_total_size = 20; - - /* Safety */ - { - const struct iovec test_buf = { 0 }; - ssize_t out = 0; - - /* Check null safety */ - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(&test_buf, 1, 0, NULL), - S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(NULL, 1, 0, &out), - S2N_ERR_NULL); - - /* Check negative safety */ - EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, -1, 0, &out)); - EXPECT_EQUAL(out, 0); - EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, -1, 0, &out)); - EXPECT_EQUAL(out, 0); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(NULL, 0, -1, &out), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(&test_buf, 1, -1, &out), - S2N_ERR_INVALID_ARGUMENT); - } - - /* No iovecs */ - { - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, 0, 0, &out)); - EXPECT_EQUAL(out, 0); - } - - /* Array of zero-length iovecs */ - { - const struct iovec test_bufs[10] = { 0 }; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size( - test_bufs, s2n_array_len(test_bufs), 0, &out)); - EXPECT_EQUAL(out, 0); - } - - /* Single iovec */ - { - const ssize_t expected_size = 10; - const struct iovec test_buf = { .iov_len = expected_size }; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, 0, &out)); - EXPECT_EQUAL(out, expected_size); - } - - /* Single iovec with offset */ - { - const struct iovec test_buf = { .iov_len = 10 }; - const ssize_t offset = 5; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, offset, &out)); - EXPECT_EQUAL(out, test_buf.iov_len - offset); - } - - /* Multiple iovecs */ - { - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size( - test_multiple_bufs, s2n_array_len(test_multiple_bufs), 0, &out)); - EXPECT_EQUAL(out, test_multiple_bufs_total_size); - } - - /* Multiple iovecs with offset */ - { - const ssize_t offset = 10; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size( - test_multiple_bufs, s2n_array_len(test_multiple_bufs), offset, &out)); - EXPECT_EQUAL(out, test_multiple_bufs_total_size - offset); - } - - /* Offset with no data */ - { - const struct iovec test_bufs[10] = { 0 }; - ssize_t out = 0; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(NULL, 0, 1, &out), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs, 0, 1, &out), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs, s2n_array_len(test_bufs), 1, &out), - S2N_ERR_INVALID_ARGUMENT); - } - - /* Offset larger than available data */ - { - const struct iovec test_buf = { .iov_len = 10 }; - ssize_t out = 0; - - ssize_t test_buf_offset = test_buf.iov_len + 1; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(&test_buf, 1, test_buf_offset, &out), - S2N_ERR_INVALID_ARGUMENT); - - ssize_t test_multiple_bufs_offset = test_multiple_bufs_total_size + 1; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_multiple_bufs, - s2n_array_len(test_multiple_bufs), test_multiple_bufs_offset, &out), - S2N_ERR_INVALID_ARGUMENT); - } - - /* Too much data to count - * - * This isn't really practically possible since an application would need - * to allocate more than SIZE_MAX memory for the iovec buffers, but we - * should ensure that the inputs don't cause unexpected behavior. - */ - { - ssize_t out = 0; - - const struct iovec test_bufs_ssize[] = { - { .iov_len = SSIZE_MAX }, - { .iov_len = 1 }, - }; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs_ssize, s2n_array_len(test_bufs_ssize), 0, &out), - S2N_ERR_INVALID_ARGUMENT); - - const struct iovec test_bufs_size[] = { - { .iov_len = SIZE_MAX }, - { .iov_len = 1 }, - }; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs_size, s2n_array_len(test_bufs_size), 0, &out), - S2N_ERR_INVALID_ARGUMENT); - } - }; - - /* Test: s2n_flush is necessary and not achievable with s2n_send */ - { - bool use_send[] = { true, false }; - - /* To reproduce the problematic scenario, we need to block on - * sending a record during a multi-record write. - */ - struct s2n_send_result results[] = { - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - OK_SEND_RESULT, - OK_SEND_RESULT, - }; - - for (size_t i = 0; i < s2n_array_len(use_send); i++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_context context = { - .results = results, - .results_len = s2n_array_len(results) - }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - /* First attempt blocks */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, 0); - - /* For our control case, we attempt to use a zero-length send as flush */ - if (use_send[i]) { - EXPECT_FAILURE_WITH_ERRNO( - s2n_send(conn, large_test_data, 0, &blocked), - S2N_ERR_SEND_SIZE); - continue; - } - - /* Unlike the zero-length send, s2n_flush succeeds */ - EXPECT_SUCCESS(s2n_flush(conn, &blocked)); - EXPECT_EQUAL(context.bytes_sent, max_frag_bytes_sent[S2N_MFL_DEFAULT]); - - /* We can also successfully finish sending */ - EXPECT_EQUAL( - s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), - sizeof(large_test_data)); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* clang-format off */ +#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } +#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } +#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } +#define OK_SEND_RESULT { .result = INT_MAX } +/* clang-format on */ + +enum s2n_test_mfl { + S2N_MFL_DEFAULT = 0, + S2N_MFL_LARGE, + S2N_MFL_SMALL, + S2N_MFL_MINIMUM, + S2N_MFL_COUNT, +}; + +static S2N_RESULT s2n_set_test_max_fragment_len(struct s2n_connection *conn, enum s2n_test_mfl mfl) +{ + switch (mfl) { + case S2N_MFL_DEFAULT: + break; + case S2N_MFL_LARGE: + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + break; + case S2N_MFL_SMALL: + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + break; + case S2N_MFL_MINIMUM: + conn->max_outgoing_fragment_length = mfl_code_to_length[1]; + break; + case S2N_MFL_COUNT: + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); + break; + } + return S2N_RESULT_OK; +} + +struct s2n_send_result { + int result; + int error; +}; + +struct s2n_send_context { + size_t calls; + size_t bytes_sent; + const struct s2n_send_result *results; + const size_t results_len; +}; + +bool s2n_custom_send_fn_called = false; +int s2n_expect_concurrent_error_send_fn(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_send_fn_called = true; + + s2n_blocked_status blocked = 0; + ssize_t result = s2n_send(conn, buf, len, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_send_context *context = (struct s2n_send_context *) io_context; + POSIX_ENSURE_REF(context); + + POSIX_ENSURE_LT(context->calls, context->results_len); + const struct s2n_send_result *result = &context->results[context->calls]; + + int retval = S2N_MIN((int) len, result->result); + + context->calls++; + if (retval > 0) { + context->bytes_sent += retval; + } + + errno = result->error; + return retval; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_data[] = "hello world"; + + uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; + struct s2n_blob large_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); + EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); + + /* Small record sizes will require a LOT of calls to s2n_send. + * Use this context when they should all succeed. + */ + struct s2n_send_result results_all_ok[50] = { 0 }; + for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { + results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; + } + const struct s2n_send_context context_all_ok = { + .results = results_all_ok, + .results_len = s2n_array_len(results_all_ok) + }; + + /* Calculating the record size for given data can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t test_data_bytes_sent = 0; + + /* s2n_send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + + /* Set the expected record size for future tests */ + test_data_bytes_sent = context.bytes_sent; + EXPECT_TRUE(test_data_bytes_sent > sizeof(test_data)); + }; + + /* Calculating the max record size for a given max fragment length can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t max_frag_bytes_sent[S2N_MFL_COUNT] = { 0 }; + + /* Track the size of the output buffer. + * It should be constant across all tests with the same max fragment length. + */ + uint32_t out_size[S2N_MFL_COUNT] = { 0 }; + + /* Send exactly the maximum fragment size */ + for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); + uint32_t fragment_len = conn->max_outgoing_fragment_length; + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* Send exactly the fragment length */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len, &blocked), fragment_len); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, 1); + + /* Set the expected record size for future tests */ + max_frag_bytes_sent[mfl] = context.bytes_sent; + EXPECT_TRUE(max_frag_bytes_sent[mfl] > 0); + + /* Set the expected output buffer size for future tests */ + out_size[mfl] = conn->out.blob.size; + EXPECT_TRUE(out_size[mfl] > 0); + + /* Sanity check: Send one byte more than the fragment length. + * If this is actually the maximum fragment length, one extra byte will + * lead to an extra record / extra call to send. + */ + context.calls = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len + 1, &blocked), fragment_len + 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, 2); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); + } + + /* s2n_send cannot be called concurrently */ + { + /* Setup connections */ + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Setup bad send callback */ + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_expect_concurrent_error_send_fn)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + s2n_blocked_status blocked = 0; + s2n_custom_send_fn_called = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_send_fn_called); + EXPECT_EQUAL(0, conn->wire_bytes_out); + }; + + /* s2n_send tracks conn->wire_bytes_out on send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_EQUAL(0, conn->wire_bytes_out); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* s2n_send tracks conn->wire_bytes_out on partial send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_EQUAL(0, conn->wire_bytes_out); + + const uint32_t partial_send = 10; + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(partial_send), + CLOSED_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO); + + EXPECT_EQUAL(context.calls, 2); + EXPECT_EQUAL(context.bytes_sent, partial_send); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* s2n_send sends all data, despite partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(1), + PARTIAL_SEND_RESULT(5), + PARTIAL_SEND_RESULT(2), + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block and must be retried */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const uint32_t partial_send = 10; + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* First attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Second attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send * 2); + + /* Third attempt completes */ + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Calculating the record size for given data can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t large_test_data_bytes_sent = 0; + + /* s2n_send sends multiple records worth of data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + + large_test_data_bytes_sent = context.bytes_sent; + EXPECT_TRUE(large_test_data_bytes_sent > sizeof(large_test_data)); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send sends all records and data, despite partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(10), OK_SEND_RESULT, + OK_SEND_RESULT, + PARTIAL_SEND_RESULT(5), PARTIAL_SEND_RESULT(1), OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block while sending multiple records */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const uint32_t partial_send = 10; + struct s2n_send_result results[] = { + OK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; + + /* First attempt blocks after writing one record */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size + partial_send); + + /* Don't re-send the data already sent. */ + const uint32_t offset = S2N_DEFAULT_FRAGMENT_LENGTH; + + /* Second attempt blocks without writing another record */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size + partial_send + partial_send); + + /* Third attempt completes */ + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + sizeof(large_test_data) - S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block after sending multiple records. + * ALL flushed records must be reported to the caller. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { + OK_SEND_RESULT, + OK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; + + /* First attempt blocks after writing two records */ + ssize_t expected_send = S2N_DEFAULT_FRAGMENT_LENGTH * 2; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_send); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size * 2); + + /* Don't re-send the data already sent. */ + const uint32_t offset = expected_send; + + /* Second attempt completes */ + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + sizeof(large_test_data) - offset); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Sending multiple records supports different maximum fragment lengths */ + for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* We expect enough calls to send to split the payload into records */ + size_t expected_calls = ceil(sizeof(large_test_data) / (double) conn->max_outgoing_fragment_length); + EXPECT_EQUAL(context.calls, expected_calls); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); + } + + /* Test dynamic record threshold record fragmentation */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Retrieve the fragment size to expect */ + uint16_t single_mtu_mfl = 0; + EXPECT_OK(s2n_record_min_write_payload_size(conn, &single_mtu_mfl)); + + /* Set the dynamic record threshold large enough for two small records */ + const uint32_t resize_threshold = single_mtu_mfl * 2; + EXPECT_SUCCESS(s2n_connection_set_dynamic_record_threshold(conn, resize_threshold, UINT16_MAX)); + + struct s2n_send_result results[] = { + /* Block before sending the first record so that we can examine + * the connection state after buffering the first record. + */ + BLOCK_SEND_RESULT, OK_SEND_RESULT, + /* Send the second record */ + BLOCK_SEND_RESULT, OK_SEND_RESULT, + /* Send the third record */ + OK_SEND_RESULT, + BLOCK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + s2n_blocked_status blocked = 0; + const size_t send_size = single_mtu_mfl * 2; + + /* The first call to s2n_send blocks before sending the first record. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, send_size, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* No records have been sent yet. */ + EXPECT_EQUAL(context.bytes_sent, 0); + /* The first record is buffered, + * so its bytes still count towards the resize_threshold. + * We have NOT passed the threshold. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, single_mtu_mfl); + EXPECT_TRUE(conn->active_application_bytes_consumed < resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The second call to s2n_send flushes the buffered first record, + * but blocks before sending the second record. + */ + ssize_t result = s2n_send(conn, large_test_data, send_size, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* First small, single-MTU record was sent. */ + EXPECT_EQUAL(result, single_mtu_mfl); + EXPECT_TRUE(context.bytes_sent < ETH_MTU); + /* The second record is buffered, + * so its bytes count towards the resize_threshold. + * We have therefore hit the threshold. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The third call to s2n_send flushes the second record. */ + result = s2n_send(conn, large_test_data, send_size - single_mtu_mfl, &blocked); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + /* Second small, single-MTU record was sent. */ + EXPECT_EQUAL(result, single_mtu_mfl); + /* There should be no change regarding the resize_threshold, + * since we did not construct any new records. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The fourth call to s2n_send sends the third record. */ + result = s2n_send(conn, large_test_data, conn->max_outgoing_fragment_length * 2, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* We have passed the resize_threshold, so records are no longer small. + * Instead they use the standard connection fragment length. + */ + EXPECT_TRUE(result > single_mtu_mfl); + EXPECT_EQUAL(result, conn->max_outgoing_fragment_length); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Test: s2n_sendv_with_offset_total_size */ + { + const struct iovec test_multiple_bufs[] = { + { .iov_len = 0 }, + { .iov_len = 1 }, + { .iov_len = 2 }, + { .iov_len = 0 }, + { .iov_len = 14 }, + { .iov_len = 0 }, + { .iov_len = 3 }, + { .iov_len = 0 }, + }; + const ssize_t test_multiple_bufs_total_size = 20; + + /* Safety */ + { + const struct iovec test_buf = { 0 }; + ssize_t out = 0; + + /* Check null safety */ + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(&test_buf, 1, 0, NULL), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(NULL, 1, 0, &out), + S2N_ERR_NULL); + + /* Check negative safety */ + EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, -1, 0, &out)); + EXPECT_EQUAL(out, 0); + EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, -1, 0, &out)); + EXPECT_EQUAL(out, 0); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(NULL, 0, -1, &out), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(&test_buf, 1, -1, &out), + S2N_ERR_INVALID_ARGUMENT); + } + + /* No iovecs */ + { + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, 0, 0, &out)); + EXPECT_EQUAL(out, 0); + } + + /* Array of zero-length iovecs */ + { + const struct iovec test_bufs[10] = { 0 }; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size( + test_bufs, s2n_array_len(test_bufs), 0, &out)); + EXPECT_EQUAL(out, 0); + } + + /* Single iovec */ + { + const ssize_t expected_size = 10; + const struct iovec test_buf = { .iov_len = expected_size }; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, 0, &out)); + EXPECT_EQUAL(out, expected_size); + } + + /* Single iovec with offset */ + { + const struct iovec test_buf = { .iov_len = 10 }; + const ssize_t offset = 5; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, offset, &out)); + EXPECT_EQUAL(out, test_buf.iov_len - offset); + } + + /* Multiple iovecs */ + { + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size( + test_multiple_bufs, s2n_array_len(test_multiple_bufs), 0, &out)); + EXPECT_EQUAL(out, test_multiple_bufs_total_size); + } + + /* Multiple iovecs with offset */ + { + const ssize_t offset = 10; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size( + test_multiple_bufs, s2n_array_len(test_multiple_bufs), offset, &out)); + EXPECT_EQUAL(out, test_multiple_bufs_total_size - offset); + } + + /* Offset with no data */ + { + const struct iovec test_bufs[10] = { 0 }; + ssize_t out = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(NULL, 0, 1, &out), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs, 0, 1, &out), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs, s2n_array_len(test_bufs), 1, &out), + S2N_ERR_INVALID_ARGUMENT); + } + + /* Offset larger than available data */ + { + const struct iovec test_buf = { .iov_len = 10 }; + ssize_t out = 0; + + ssize_t test_buf_offset = test_buf.iov_len + 1; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(&test_buf, 1, test_buf_offset, &out), + S2N_ERR_INVALID_ARGUMENT); + + ssize_t test_multiple_bufs_offset = test_multiple_bufs_total_size + 1; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_multiple_bufs, + s2n_array_len(test_multiple_bufs), test_multiple_bufs_offset, &out), + S2N_ERR_INVALID_ARGUMENT); + } + + /* Too much data to count + * + * This isn't really practically possible since an application would need + * to allocate more than SIZE_MAX memory for the iovec buffers, but we + * should ensure that the inputs don't cause unexpected behavior. + */ + { + ssize_t out = 0; + + const struct iovec test_bufs_ssize[] = { + { .iov_len = SSIZE_MAX }, + { .iov_len = 1 }, + }; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs_ssize, s2n_array_len(test_bufs_ssize), 0, &out), + S2N_ERR_INVALID_ARGUMENT); + + const struct iovec test_bufs_size[] = { + { .iov_len = SIZE_MAX }, + { .iov_len = 1 }, + }; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs_size, s2n_array_len(test_bufs_size), 0, &out), + S2N_ERR_INVALID_ARGUMENT); + } + }; + + /* Test: s2n_flush is necessary and not achievable with s2n_send */ + { + bool use_send[] = { true, false }; + + /* To reproduce the problematic scenario, we need to block on + * sending a record during a multi-record write. + */ + struct s2n_send_result results[] = { + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + OK_SEND_RESULT, + OK_SEND_RESULT, + }; + + for (size_t i = 0; i < s2n_array_len(use_send); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_context context = { + .results = results, + .results_len = s2n_array_len(results) + }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* First attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, 0); + + /* For our control case, we attempt to use a zero-length send as flush */ + if (use_send[i]) { + EXPECT_FAILURE_WITH_ERRNO( + s2n_send(conn, large_test_data, 0, &blocked), + S2N_ERR_SEND_SIZE); + continue; + } + + /* Unlike the zero-length send, s2n_flush succeeds */ + EXPECT_SUCCESS(s2n_flush(conn, &blocked)); + EXPECT_EQUAL(context.bytes_sent, max_frag_bytes_sent[S2N_MFL_DEFAULT]); + + /* We can also successfully finish sending */ + EXPECT_EQUAL( + s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + sizeof(large_test_data)); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_server_hello_retry_test.c b/tests/unit/s2n_server_hello_retry_test.c index 9338e0b0eea..fc2aa2ec32f 100644 --- a/tests/unit/s2n_server_hello_retry_test.c +++ b/tests/unit/s2n_server_hello_retry_test.c @@ -1,626 +1,627 @@ -/* - * 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 "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_key_share.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/extensions/s2n_server_supported_versions.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_result.h" - -#define HELLO_RETRY_MSG_NO 1 - -const uint8_t SESSION_ID_SIZE = 1; -const uint8_t COMPRESSION_METHOD_SIZE = 1; - -struct client_hello_context { - int invocations; - s2n_client_hello_cb_mode mode; - bool mark_done; -}; - -int s2n_negotiate_poll_hello_retry(struct s2n_connection *server_conn, - struct s2n_connection *client_conn, - struct client_hello_context *client_hello_ctx) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_IO_BLOCKED); - - /* complete the callback on the next call */ - client_hello_ctx->mark_done = true; - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* - * hello retry will invoke the s2n_negotiate twice but the callback should - * be called once regardless of polling - */ - EXPECT_EQUAL(client_hello_ctx->invocations, 1); - - return S2N_SUCCESS; -} - -static int client_hello_detect_duplicate_calls(struct s2n_connection *conn, void *ctx) -{ - if (ctx == NULL) { - return -1; - } - - struct client_hello_context *client_hello_ctx = ctx; - - /* Incremet counter */ - client_hello_ctx->invocations++; - EXPECT_EQUAL(client_hello_ctx->invocations, 1); - if (client_hello_ctx->mode == S2N_CLIENT_HELLO_CB_NONBLOCKING) { - EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); - } - return 0; -} - -int s2n_client_hello_poll_cb(struct s2n_connection *conn, void *ctx) -{ - struct client_hello_context *client_hello_ctx = NULL; - if (ctx == NULL) { - return -1; - } - client_hello_ctx = ctx; - /* Increment counter to ensure that callback was invoked */ - client_hello_ctx->invocations++; - - if (client_hello_ctx->mark_done) { - EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); - return S2N_SUCCESS; - } - - return S2N_SUCCESS; -} - -S2N_RESULT hello_retry_client_hello_cb_test() -{ - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - EXPECT_NOT_NULL(tls13_chain_and_key); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* setup the client hello callback */ - struct client_hello_context client_hello_ctx = { .invocations = 0, - .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING, - .mark_done = false }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, - s2n_client_hello_poll_cb, &client_hello_ctx)); - EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, - S2N_CLIENT_HELLO_CB_NONBLOCKING)); - - /* negotiate and make assertions */ - EXPECT_SUCCESS(s2n_negotiate_poll_hello_retry(server_conn, client_conn, &client_hello_ctx)); - - /* check hello retry state */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - - /* cleanup */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Send Hello Retry Request messages */ - { - struct s2n_config *server_config = NULL; - struct s2n_connection *server_conn = NULL; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - struct s2n_stuffer *server_stuffer = &server_conn->handshake.io; - - uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN - + S2N_TLS_RANDOM_DATA_LEN - + SESSION_ID_SIZE - + server_conn->session_id_len - + S2N_TLS_CIPHER_SUITE_LEN - + COMPRESSION_METHOD_SIZE; - - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); - server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); - - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); - - /* The client will need a key share extension to properly parse the hello */ - /* Total extension size + size of each extension */ - total += 2 + s2n_extensions_server_supported_versions_size(server_conn) - + s2n_extensions_server_key_share_send_size(server_conn); - - EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_EQUAL(s2n_stuffer_data_available(server_stuffer), total); - - EXPECT_NOT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.evp_pkey); - EXPECT_TRUE(memcmp(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN) == 0); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Verify the requires_retry flag causes a retry to be sent */ - { - struct s2n_config *conf = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(conf = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); - - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); - - EXPECT_TRUE(s2n_is_hello_retry_message(conn)); - EXPECT_SUCCESS(s2n_server_hello_retry_send(conn)); - - EXPECT_SUCCESS(s2n_config_free(conf)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Retry requests with incorrect random data are not accepted */ - { - struct s2n_config *conf = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(conf = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); - - struct s2n_stuffer *io = &conn->handshake.io; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); - - /* protocol version */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); - - /* random data */ - uint8_t bad_retry_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, bad_retry_random, S2N_TLS_RANDOM_DATA_LEN)); - - /* session id */ - uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); - - /* cipher suites */ - EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, 0x1301)); - - /* no compression */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_FALSE(s2n_is_hello_retry_message(conn)); - - EXPECT_SUCCESS(s2n_config_free(conf)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Verify the client key share extension properly handles HelloRetryRequests */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer *extension_stuffer = &server_conn->handshake.io; - - EXPECT_SUCCESS(s2n_connection_allow_response_extension(client_conn, s2n_server_key_share_extension.iana_value)); - EXPECT_SUCCESS(s2n_connection_allow_response_extension(server_conn, s2n_server_key_share_extension.iana_value)); - - POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); - EXPECT_SUCCESS(s2n_extensions_server_key_share_send(server_conn, extension_stuffer)); - - S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, TLS_EXTENSION_KEY_SHARE, uint16); - /* 4 = S2N_SIZE_OF_EXTENSION_TYPE + S2N_SIZE_OF_EXTENSION_DATA_SIZE */ - S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, s2n_extensions_server_key_share_send_size(server_conn) - 4, uint16); - - client_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); - - /* Setup the client to receive a HelloRetryRequest */ - POSIX_CHECKED_MEMCPY(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); - - /* Setup the handshake type and message number to simulate a condition where a HelloRetry should be sent */ - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); - EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); - - /* Parse the key share */ - EXPECT_SUCCESS(s2n_extensions_server_key_share_recv(client_conn, extension_stuffer)); - EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); - - /* Server negotiated curve value will be non-null, if the extension succeeded */ - EXPECT_NOT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* Verify that the hash transcript recreation function correctly takes the existing ClientHello1 - * hash, and generates a synthetic message. */ - { - struct s2n_config *conf = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(conf = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); - - conn->server_protocol_version = S2N_TLS13; - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - /* This blob is taken from the functional test RFC. That RFC does not actually provide hash transcript - * values, so the expected hashes are taken from what our hash functions generated and the hash - * generated from the transcript recreation. - * https://tools.ietf.org/html/rfc8448#section-5 */ - S2N_BLOB_FROM_HEX(client_hello1, - "010000c00303cb34ecb1e78163" - "ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283" - "024dece7000006130113031302010000910000000b" - "0009000006736572766572ff01000100000a001400" - "12001d001700180019010001010102010301040023" - "0000003300260024001d002099381de560e4bd43d2" - "3d8e435a7dbafeb3c06e51c13cae4d5413691e529a" - "af2c002b0003020304000d0020001e040305030603" - "020308040805080604010501060102010402050206" - "020202002d00020101001c00024001"); - - S2N_BLOB_FROM_HEX(client_hello1_expected_hash, - "4db255f30da09a407c841720be831a06a5aa9b3662a5f44267d37706b73c2b8c"); - - S2N_BLOB_FROM_HEX(synthetic_message_with_ch1_expected_hash, - "ff1135ed878322e29699da3e451d2f08bf11fc693038769978e75bb63304a225"); - - EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(conn, &client_hello1)); - - s2n_tls13_connection_keys(keys, conn); - uint8_t hash_digest_length = keys.size; - struct s2n_blob compare_blob = { 0 }; - - DEFER_CLEANUP(struct s2n_hash_state client_hello1_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&client_hello1_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &client_hello1_hash)); - - uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); - - EXPECT_SUCCESS(s2n_blob_init(&compare_blob, client_hello1_digest_out, hash_digest_length)); - S2N_BLOB_EXPECT_EQUAL(client_hello1_expected_hash, compare_blob); - - EXPECT_SUCCESS(s2n_server_hello_retry_recreate_transcript(conn)); - - DEFER_CLEANUP(struct s2n_hash_state recreated_hash = { 0 }, s2n_hash_free); - uint8_t recreated_transcript_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_hash_new(&recreated_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &recreated_hash)); - EXPECT_SUCCESS(s2n_hash_digest(&recreated_hash, recreated_transcript_digest_out, hash_digest_length)); - - EXPECT_SUCCESS(s2n_blob_init(&compare_blob, recreated_transcript_digest_out, hash_digest_length)); - S2N_BLOB_EXPECT_EQUAL(synthetic_message_with_ch1_expected_hash, compare_blob); - - EXPECT_SUCCESS(s2n_config_free(conf)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Send and receive Hello Retry Request messages */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - struct client_hello_context client_hello_ctx = { .invocations = 0, .mode = S2N_CLIENT_HELLO_CB_BLOCKING }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, client_hello_detect_duplicate_calls, &client_hello_ctx)); - - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); - - /* Force HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send the first CH message */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Receive the CH and send an HRR, which will execute the HRR code paths */ - EXPECT_EQUAL(client_hello_ctx.invocations, 0); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(client_hello_ctx.invocations, 1); - - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); - - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); - - /* Send the second CH message */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Verify that receiving the second CH message does not execute the callback */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(client_hello_ctx.invocations, 1); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - /* Send and receive Hello Retry Request messages, test for non blocking client hello callback */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* setup the client hello callback */ - struct client_hello_context client_hello_ctx = { .invocations = 0, - .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, - client_hello_detect_duplicate_calls, &client_hello_ctx)); - EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(server_config, - S2N_CLIENT_HELLO_CB_NONBLOCKING)); - - /* ensure that handshake succeeds via HRR path using non_blocking CH */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(server_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - EXPECT_EQUAL(client_hello_ctx.invocations, 1); - - EXPECT_NOT_NULL(s2n_connection_get_client_hello(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Hello Retry Request + (poll and no-poll) client hello callback */ - { - EXPECT_OK(hello_retry_client_hello_cb_test()); - }; - - /* Test s2n_set_hello_retry_required correctly sets the handshake type to HELLO_RETRY_REQUEST, - * when conn->actual_protocol_version is set to TLS1.3 version */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); - - EXPECT_SUCCESS(s2n_set_hello_retry_required(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test s2n_set_hello_retry_required raises a S2N_ERR_INVALID_HELLO_RETRY error - * when conn->actual_protocol_version is less than TLS1.3 */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - conn->actual_protocol_version = S2N_TLS12; - - EXPECT_FAILURE_WITH_ERRNO(s2n_set_hello_retry_required(conn), S2N_ERR_INVALID_HELLO_RETRY); - EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 - *= type=test - *# Clients MUST abort the handshake with an - *# "illegal_parameter" alert if the HelloRetryRequest would not result - *# in any change in the ClientHello. - */ - { - const struct s2n_security_policy *security_policy = NULL; - EXPECT_SUCCESS(s2n_find_security_policy_from_version("20201021", &security_policy)); - EXPECT_NOT_NULL(security_policy); - const struct s2n_ecc_named_curve *test_curve = security_policy->ecc_preferences->ecc_curves[0]; - - /** - * Retry for key share is valid - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 - *= type=test - *# and (2) the selected_group field does not - *# correspond to a group which was provided in the "key_share" extension - *# in the original ClientHello. - **/ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); - conn->actual_protocol_version = S2N_TLS13; - conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - - conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; - - /* Server requested key share is NOT present: allow retry */ - EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); - - /* Server requested key share is present: do NOT allow retry */ - conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; - conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); - EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), - S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Retry for multiple reasons is valid */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); - conn->actual_protocol_version = S2N_TLS13; - conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - - conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; - - /* All retry conditions met: allow retry */ - conn->early_data_state = S2N_EARLY_DATA_REQUESTED; - EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); - - /* No retry conditions met: do NOT allow retry */ - conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; - conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; - conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); - EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), - S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_result.h" + +#define HELLO_RETRY_MSG_NO 1 + +const uint8_t SESSION_ID_SIZE = 1; +const uint8_t COMPRESSION_METHOD_SIZE = 1; + +struct client_hello_context { + int invocations; + s2n_client_hello_cb_mode mode; + bool mark_done; +}; + +int s2n_negotiate_poll_hello_retry(struct s2n_connection *server_conn, + struct s2n_connection *client_conn, + struct client_hello_context *client_hello_ctx) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + + /* complete the callback on the next call */ + client_hello_ctx->mark_done = true; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* + * hello retry will invoke the s2n_negotiate twice but the callback should + * be called once regardless of polling + */ + EXPECT_EQUAL(client_hello_ctx->invocations, 1); + + return S2N_SUCCESS; +} + +static int client_hello_detect_duplicate_calls(struct s2n_connection *conn, void *ctx) +{ + if (ctx == NULL) { + return -1; + } + + struct client_hello_context *client_hello_ctx = ctx; + + /* Incremet counter */ + client_hello_ctx->invocations++; + EXPECT_EQUAL(client_hello_ctx->invocations, 1); + if (client_hello_ctx->mode == S2N_CLIENT_HELLO_CB_NONBLOCKING) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + } + return 0; +} + +int s2n_client_hello_poll_cb(struct s2n_connection *conn, void *ctx) +{ + struct client_hello_context *client_hello_ctx = NULL; + if (ctx == NULL) { + return -1; + } + client_hello_ctx = ctx; + /* Increment counter to ensure that callback was invoked */ + client_hello_ctx->invocations++; + + if (client_hello_ctx->mark_done) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + return S2N_SUCCESS; + } + + return S2N_SUCCESS; +} + +S2N_RESULT hello_retry_client_hello_cb_test() +{ + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_NOT_NULL(tls13_chain_and_key); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* setup the client hello callback */ + struct client_hello_context client_hello_ctx = { .invocations = 0, + .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING, + .mark_done = false }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, + s2n_client_hello_poll_cb, &client_hello_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, + S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* negotiate and make assertions */ + EXPECT_SUCCESS(s2n_negotiate_poll_hello_retry(server_conn, client_conn, &client_hello_ctx)); + + /* check hello retry state */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* cleanup */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Send Hello Retry Request messages */ + { + struct s2n_config *server_config = NULL; + struct s2n_connection *server_conn = NULL; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + struct s2n_stuffer *server_stuffer = &server_conn->handshake.io; + + uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN + + S2N_TLS_RANDOM_DATA_LEN + + SESSION_ID_SIZE + + server_conn->session_id_len + + S2N_TLS_CIPHER_SUITE_LEN + + COMPRESSION_METHOD_SIZE; + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + + /* The client will need a key share extension to properly parse the hello */ + /* Total extension size + size of each extension */ + total += 2 + s2n_extensions_server_supported_versions_size(server_conn) + + s2n_extensions_server_key_share_send_size(server_conn); + + EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(server_stuffer), total); + + EXPECT_NOT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.evp_pkey); + EXPECT_TRUE(memcmp(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN) == 0); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Verify the requires_retry flag causes a retry to be sent */ + { + struct s2n_config *conf = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + + EXPECT_TRUE(s2n_is_hello_retry_message(conn)); + EXPECT_SUCCESS(s2n_server_hello_retry_send(conn)); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Retry requests with incorrect random data are not accepted */ + { + struct s2n_config *conf = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + struct s2n_stuffer *io = &conn->handshake.io; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + /* random data */ + uint8_t bad_retry_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, bad_retry_random, S2N_TLS_RANDOM_DATA_LEN)); + + /* session id */ + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, 0x1301)); + + /* no compression */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_FALSE(s2n_is_hello_retry_message(conn)); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Verify the client key share extension properly handles HelloRetryRequests */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer *extension_stuffer = &server_conn->handshake.io; + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(client_conn, s2n_server_key_share_extension.iana_value)); + EXPECT_SUCCESS(s2n_connection_allow_response_extension(server_conn, s2n_server_key_share_extension.iana_value)); + + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_extensions_server_key_share_send(server_conn, extension_stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, TLS_EXTENSION_KEY_SHARE, uint16); + /* 4 = S2N_SIZE_OF_EXTENSION_TYPE + S2N_SIZE_OF_EXTENSION_DATA_SIZE */ + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, s2n_extensions_server_key_share_send_size(server_conn) - 4, uint16); + + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + /* Setup the client to receive a HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + + /* Setup the handshake type and message number to simulate a condition where a HelloRetry should be sent */ + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + + /* Parse the key share */ + EXPECT_SUCCESS(s2n_extensions_server_key_share_recv(client_conn, extension_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); + + /* Server negotiated curve value will be non-null, if the extension succeeded */ + EXPECT_NOT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Verify that the hash transcript recreation function correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. */ + { + struct s2n_config *conf = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + conn->server_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + /* This blob is taken from the functional test RFC. That RFC does not actually provide hash transcript + * values, so the expected hashes are taken from what our hash functions generated and the hash + * generated from the transcript recreation. + * https://tools.ietf.org/html/rfc8448#section-5 */ + S2N_BLOB_FROM_HEX(client_hello1, + "010000c00303cb34ecb1e78163" + "ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283" + "024dece7000006130113031302010000910000000b" + "0009000006736572766572ff01000100000a001400" + "12001d001700180019010001010102010301040023" + "0000003300260024001d002099381de560e4bd43d2" + "3d8e435a7dbafeb3c06e51c13cae4d5413691e529a" + "af2c002b0003020304000d0020001e040305030603" + "020308040805080604010501060102010402050206" + "020202002d00020101001c00024001"); + + S2N_BLOB_FROM_HEX(client_hello1_expected_hash, + "4db255f30da09a407c841720be831a06a5aa9b3662a5f44267d37706b73c2b8c"); + + S2N_BLOB_FROM_HEX(synthetic_message_with_ch1_expected_hash, + "ff1135ed878322e29699da3e451d2f08bf11fc693038769978e75bb63304a225"); + + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(conn, &client_hello1)); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + struct s2n_blob compare_blob = { 0 }; + + DEFER_CLEANUP(struct s2n_hash_state client_hello1_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&client_hello1_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &client_hello1_hash)); + + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, client_hello1_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(client_hello1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_server_hello_retry_recreate_transcript(conn)); + + DEFER_CLEANUP(struct s2n_hash_state recreated_hash = { 0 }, s2n_hash_free); + uint8_t recreated_transcript_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_hash_new(&recreated_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &recreated_hash)); + EXPECT_SUCCESS(s2n_hash_digest(&recreated_hash, recreated_transcript_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, recreated_transcript_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(synthetic_message_with_ch1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Send and receive Hello Retry Request messages */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + struct client_hello_context client_hello_ctx = { .invocations = 0, .mode = S2N_CLIENT_HELLO_CB_BLOCKING }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, client_hello_detect_duplicate_calls, &client_hello_ctx)); + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send the first CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Receive the CH and send an HRR, which will execute the HRR code paths */ + EXPECT_EQUAL(client_hello_ctx.invocations, 0); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* Send the second CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Verify that receiving the second CH message does not execute the callback */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /* Send and receive Hello Retry Request messages, test for non blocking client hello callback */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* setup the client hello callback */ + struct client_hello_context client_hello_ctx = { .invocations = 0, + .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, + client_hello_detect_duplicate_calls, &client_hello_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(server_config, + S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* ensure that handshake succeeds via HRR path using non_blocking CH */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_NOT_NULL(s2n_connection_get_client_hello(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Hello Retry Request + (poll and no-poll) client hello callback */ + { + EXPECT_OK(hello_retry_client_hello_cb_test()); + }; + + /* Test s2n_set_hello_retry_required correctly sets the handshake type to HELLO_RETRY_REQUEST, + * when conn->actual_protocol_version is set to TLS1.3 version */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + + EXPECT_SUCCESS(s2n_set_hello_retry_required(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_set_hello_retry_required raises a S2N_ERR_INVALID_HELLO_RETRY error + * when conn->actual_protocol_version is less than TLS1.3 */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_set_hello_retry_required(conn), S2N_ERR_INVALID_HELLO_RETRY); + EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 + *= type=test + *# Clients MUST abort the handshake with an + *# "illegal_parameter" alert if the HelloRetryRequest would not result + *# in any change in the ClientHello. + */ + { + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20201021", &security_policy)); + EXPECT_NOT_NULL(security_policy); + const struct s2n_ecc_named_curve *test_curve = security_policy->ecc_preferences->ecc_curves[0]; + + /** + * Retry for key share is valid + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 + *= type=test + *# and (2) the selected_group field does not + *# correspond to a group which was provided in the "key_share" extension + *# in the original ClientHello. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + + /* Server requested key share is NOT present: allow retry */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + /* Server requested key share is present: do NOT allow retry */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), + S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Retry for multiple reasons is valid */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + + /* All retry conditions met: allow retry */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + /* No retry conditions met: do NOT allow retry */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), + S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_session_ticket_test.c b/tests/unit/s2n_session_ticket_test.c index 4f3ccab1d23..5eac57836e5 100644 --- a/tests/unit/s2n_session_ticket_test.c +++ b/tests/unit/s2n_session_ticket_test.c @@ -1,1794 +1,1795 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_NANOS (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) -#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS S2N_SESSION_STATE_CONFIGURABLE_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 - -/** - * This function is used to "skip" time in unit tests. It will mock the system - * time to be current_time (ns) + data (ns). The "data" parameter is a uint64_t - * passed in as a void*. - */ -int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) -{ - struct timespec current_time; - - clock_gettime(S2N_CLOCK_SYS, ¤t_time); - - /** - * current_time fields are represented as time_t, and time_t has a platform - * dependent size. On 32 bit platforms, attempting to convert the current - * system time to nanoseconds will overflow, causing odd failures in unit - * tests. We upcast current_time fields to uint64_t before multiplying to - * avoid this. - */ - *nanoseconds = 0; - *nanoseconds += (uint64_t) current_time.tv_sec * ONE_SEC_IN_NANOS; - *nanoseconds += (uint64_t) current_time.tv_nsec; - *nanoseconds += *(uint64_t *) data; - - return 0; -} - -static int mock_time(void *data, uint64_t *nanoseconds) -{ - if (data) { - *nanoseconds = *((uint64_t *) data); - } else { - *nanoseconds = 1000000000; - } - return S2N_SUCCESS; -} - -uint8_t cb_session_data[S2N_TLS12_SESSION_SIZE * 2] = { 0 }; -size_t cb_session_data_len = 0; -uint32_t cb_session_lifetime = 0; -static int s2n_test_session_ticket_callback(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(ticket); - - /* Store the callback data for comparison at the end of the connection. */ - EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); - EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, sizeof(cb_session_data), cb_session_data)); - EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); - - return S2N_SUCCESS; -} - -/* make a struct with a ticket name and key adjacent in memory */ -struct small_name_ticket { - uint8_t name[1]; - uint8_t key[32]; -}; - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - uint64_t now = 0; - struct s2n_ticket_key *ticket_key = NULL; - uint32_t ticket_keys_len = 0; - - size_t serialized_session_state_length = 0; - uint8_t s2n_state_with_session_id = S2N_STATE_WITH_SESSION_ID; - uint8_t serialized_session_state[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES + S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; - - /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ - uint8_t ticket_key_name1[1] = "A"; - uint8_t ticket_key_name2[4] = "BBBB"; - uint8_t ticket_key_name3[16] = "CCCCCCCCCCCCCCCC"; - uint8_t ticket_key1[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, - 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, - 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, - 0xb3, 0xe5 }; - uint8_t ticket_key2[32] = { 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, - 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, - 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, - 0xc2, 0x44 }; - uint8_t ticket_key3[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, - 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, - 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, - 0xcb, 0x04 }; - - /* Testcases: - * 1) Client sends empty ST extension. Server issues NST. - * 2) Client sends empty ST extension. Server does a full handshake, but is unable to issue NST due to absence of an encrypt-decrypt key. - * 3) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. - * 4) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST even though the key is in decrypt-only state. - * 5) Client sends non-empty ST extension. Server does an abbreviated handshake, but does not issue a NST even though the key is in - * decrypt-only state due to absence of encrypt-decrypt key. - * 6) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key is not found. - * 7) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key has expired. - * 8) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. - * 9) Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. - * 10) Client sets corrupted ST extension. - * 11) User tries adding a duplicate key to the server. - * 12) Testing expired keys are removed from the server config while adding new keys. - * 13) Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. - * 14) Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. - * 15) Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and s2n_config_set_ticket_decrypt_key_lifetime calls. - * 16) Add keys out of order and pre-emptively add a key. - * 17) Handshake with client auth and session ticket enabled. - * 18) Session resumption APIs and session_ticket_cb return the same values when receiving a new ticket in TLS1.2 - * 19) Session resumption APIs and session_ticket_cb return sane values when receiving a new ticket in TLS1.3 - * 20) Client has TLS1.3 ticket but negotiates TLS1.2, so does full handshake - */ - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - DEFER_CLEANUP(struct s2n_stuffer tls13_serialized_session_state = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_serialized_session_state, 0)); - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - - struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Test ticket name handling */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Enable session tickets */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - - /* The name should be greater than 0 and less than or equal to 16 */ - uint8_t too_large_name[17] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 0, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 17, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Add a ticket with a single-byte name */ - struct small_name_ticket small = { .name = { 0xAA }, .key = { 0xBB, 0xBB, 0xBB, 0xBB } }; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, small.name, s2n_array_len(small.name), small.key, s2n_array_len(small.key), 0)); - - /* Ensure a ticket with the same name is not added */ - struct small_name_ticket small2 = { .name = { 0xAA }, .key = { 0xCC, 0xCC, 0xCC, 0xCC } }; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, small2.name, s2n_array_len(small2.name), small2.key, s2n_array_len(small2.key), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Ensure a ticket with a zero-padded name is not added */ - uint8_t padded_name[16] = { 0xAA, 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, padded_name, s2n_array_len(padded_name), ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - }; - - /* Client sends empty ST extension. Server issues NST. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* A newly created connection should not be considered resumed */ - EXPECT_FALSE(s2n_connection_is_session_resumed(server_conn)); - EXPECT_FALSE(s2n_connection_is_session_resumed(client_conn)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends empty ST extension. Server does a full handshake, but is unable - * to issue NST due to absence of an encrypt-decrypt key. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and did not issue NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did an abbreviated handshake and not issue NST */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); - EXPECT_TRUE(s2n_connection_is_session_resumed(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that client_ticket is same as before because server didn't issue a NST */ - uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; - EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - EXPECT_TRUE(s2n_connection_is_session_resumed(client_conn)); - s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - /* Verify that the server lifetime hint is 0 because server didn't issue a NST */ - EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing a NST - * even though the key is in decrypt-only state. - */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add a mock delay such that key 1 moves to decrypt-only state */ - mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; - - uint32_t key_intro_time = mock_current_time / ONE_SEC_IN_NANOS; - 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), key_intro_time)); - - /* Verify there is an encrypt key available */ - EXPECT_OK(s2n_config_is_encrypt_key_available(server_config)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did an abbreviated handshake without issuing NST */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that client_ticket is the same as before because server didn't issue a NST */ - uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; - EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - /* Verify the lifetime hint from the server */ - EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does an abbreviated handshake, - * but does not issue a NST even though the key is in decrypt-only state due to - * the absence of encrypt-decrypt key. - */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add a mock delay such that key 1 moves to decrypt-only state */ - mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did an abbreviated handshake and did not issue a NST */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that client_ticket is same as before because server did not issue a NST */ - uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; - EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); - EXPECT_FALSE(memcmp(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does a full handshake and issues - * a NST because the key used to encrypt the session ticket is not found. - */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - 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)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_TICKET_KEY_NAME_LOCATION, - ticket_key_name1, s2n_array_len(ticket_key_name1)); - - /* Verify the lifetime hint from the server */ - uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); - EXPECT_EQUAL(session_ticket_lifetime, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does a full handshake and issues a NST - * because the key has expired. - */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - 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)); - - /* Add a mock delay such that the key used to encrypt ST expires */ - mock_current_time += server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that the server has only the unexpired key */ - EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); - EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name2, s2n_array_len(ticket_key_name2)); - EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); - EXPECT_EQUAL(ticket_keys_len, 1); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Tamper session state to make session ticket size smaller than what we expect */ - /* Verify that client_ticket is same as before because server did not issue a NST */ - uint8_t tampered_session_state[sizeof(serialized_session_state) - 1]; - /* Copy session format */ - tampered_session_state[0] = serialized_session_state[0]; - /* Copy and reduce by 1 the session ticket length */ - tampered_session_state[1] = serialized_session_state[1]; - tampered_session_state[2] = serialized_session_state[2] - 1; - /* Skip 1 byte of the session ticket and copy the rest */ - EXPECT_MEMCPY_SUCCESS(tampered_session_state + 3, serialized_session_state + 4, sizeof(tampered_session_state) - 4); - - /* Set client tampered ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tampered_session_state, serialized_session_state_length - 1)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - 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)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Not enabling resumption using ST */ - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and did not issue NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sets corrupted ST extension. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - memset(serialized_session_state, 0, serialized_session_state_length); - - /* Set client ST and session state */ - EXPECT_FAILURE(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* User tries adding a duplicate key to the server */ - { - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - - 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)); - - /* Try adding the same key, but with a different name */ - EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key1, s2n_array_len(ticket_key1), 0)); - - /* Try adding a different key, but with the same name */ - EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key2, s2n_array_len(ticket_key2), 0)); - EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Try adding a key with invalid key length */ - EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, 0, 0)); - EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_LENGTH); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Testing expired keys are removed from the server config while adding new keys. */ - { - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - - /* Add 2 ST keys */ - 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_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add a mock delay such that the first two keys expire */ - uint64_t mock_delay = server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - - /* Add a third ST key */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); - - /* Try adding the expired keys */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - 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)); - - /* Verify that the config has three unexpired keys */ - EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); - /* ticket_key3 should have "rotated" to the first index as other keys expired */ - EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name3, s2n_array_len(ticket_key_name3)); - EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); - EXPECT_EQUAL(ticket_keys_len, 3); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Attempting to add more than S2N_MAX_TICKET_KEYS causes failures. */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - uint8_t id = 0; - uint8_t ticket_key_buf[32] = { 0 }; - - for (uint8_t i = 0; i < S2N_MAX_TICKET_KEYS; i++) { - id = i; - ticket_key_buf[0] = i; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, - &id, sizeof(id), ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); - } - - id = S2N_MAX_TICKET_KEYS; - ticket_key_buf[0] = S2N_MAX_TICKET_KEYS; - EXPECT_FAILURE(s2n_config_add_ticket_crypto_key(config, &id, sizeof(id), - ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); - }; - - /* Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - 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)); - - /* Add a mock delay such that the first key is close to it's encryption peak */ - uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; - mock_current_time += delay_in_nanos; - - /* Add two more ST keys */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_TICKET_KEY_NAME_LOCATION, - ticket_key_name1, s2n_array_len(ticket_key_name1)); - - /* Verify the lifetime hint from the server */ - uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); - uint32_t first_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - delay_in_nanos / ONE_SEC_IN_NANOS; - EXPECT_EQUAL(session_ticket_lifetime, first_key_remaining_lifetime); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST */ - { - const size_t allowed_failures = 1; - size_t failures = 0; - bool expected_key_chosen = false; - - /* This test sets up three different ticket encryption keys at various times in their encryption lifetime. The test - * is meant to check that the weighted random selection algorithm correctly selects the key that is at its - * encryption peak. However the test will sometimes pick a key that is not at its encryption peak because the - * selection function uses a weighted random selection algorithm. Here we retry the test once if the key chosen - * is not the expected key. - * - * The wrong key will be chosen 0.02% of the time. This value is drawn from the weight of the expected key, - * which does not change per test run. Therefore, the probability that the test chooses the wrong key - * more than allowed_failures times is 0.0002 ^ 2 = 0.00000004, which is extremely unlikely to occur. If - * the logic changes to chose the wrong key at a higher rate, say 50% of the time, this test would fail at a - * 0.5 ^ 2 = 0.25 or 25% of the time. This rate is high enough for us to notice and investigate. - */ - while (expected_key_chosen == false) { - EXPECT_TRUE(failures <= allowed_failures); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - 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)); - - /* Add second key when the first key is very close to it's encryption peak */ - uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; - mock_current_time += delay_in_nanos; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add third key when the second key is very close to it's encryption peak and - * the first key is about to transition from encrypt-decrypt state to decrypt-only state - */ - mock_current_time += delay_in_nanos; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), - ticket_key3, s2n_array_len(ticket_key3), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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); - 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; - } else { - failures += 1; - } - - /* Verify the lifetime hint from the server */ - uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); - uint32_t second_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - (delay_in_nanos / ONE_SEC_IN_NANOS); - EXPECT_EQUAL(session_ticket_lifetime, second_key_remaining_lifetime); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - }; - - /* Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and - * s2n_config_set_ticket_decrypt_key_lifetime calls */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - /* Set encrypt-decrypt key expire time to 24 hours */ - EXPECT_SUCCESS(s2n_config_set_ticket_encrypt_decrypt_key_lifetime(server_config, 86400)); - - /* Set decrypt-only key expire time to 5 hours */ - EXPECT_SUCCESS(s2n_config_set_ticket_decrypt_key_lifetime(server_config, 18000)); - - /* Add one ST key */ - 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)); - - /* Add second key when the first key is very close to it's encryption peak */ - uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add third key when the second key is very close to it's encryption peak and - * the first key is about to transition from encrypt-decrypt state to decrypt-only state - */ - mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos - ONE_SEC_IN_NANOS; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Add keys out of order and pre-emptively add a key */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - /* Add a key. After 1 hour it will be considered an encrypt-decrypt key. */ - POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); - 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), (now / ONE_SEC_IN_NANOS) + 3600)); - - /* Add a key. After 1 hour it will reach it's peak */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), now / ONE_SEC_IN_NANOS)); - - /* Add a key pre-emptively. It can be used only after 10 hours */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), now / ONE_SEC_IN_NANOS + 36000)); - - /* Add a mock delay such that negotiation happens after 1 hour */ - uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Handshake with client auth and session ticket enabled */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - /* Client has session ticket and mutual auth enabled */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - /* Server has session ticket and mutual auth enabled */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and did not issue NST since client - * auth is enabled in server mode */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* s2n_resume_decrypt_session fails to decrypt when presented with a valid ticket_key, valid iv and invalid encrypted blob */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - - /* Add Session Ticket key on the server config */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - 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 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))); - - uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; - POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); - - server_conn->session_ticket_status = S2N_DECRYPT_TICKET; - EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_DECRYPT); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* s2n_resume_decrypt_session fails with a key not found error when presented with an invalid ticket_key, valid iv and invalid encrypted blob */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - /* Add Session Ticket key on the server config */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - 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 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))); - - uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; - POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); - - server_conn->session_ticket_status = S2N_DECRYPT_TICKET; - EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Test s2n_connection_is_session_resumed */ - { - /* TLS1.2 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - - conn->handshake.handshake_type = INITIAL; - EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); - - conn->handshake.handshake_type = NEGOTIATED | WITH_SESSION_TICKET; - EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); - - /* Ignores PSK mode */ - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS13; - - conn->handshake.handshake_type = INITIAL; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); - - conn->handshake.handshake_type = NEGOTIATED; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); - - conn->handshake.handshake_type = NEGOTIATED; - conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; - EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Session resumption APIs and session_ticket_cb return the same values - * when receiving a new ticket in TLS1.2 - */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_wall_clock(client_config, mock_time, NULL)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - /* Client will use callback when server nst is received */ - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_callback, NULL)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - /* Add one ST key */ - POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); - 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), now / ONE_SEC_IN_NANOS)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Expect values from the session_ticket_cb are equivalent to values from the APIs */ - EXPECT_EQUAL(cb_session_data_len, s2n_connection_get_session_length(client_conn)); - uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, session_data, cb_session_data_len)); - EXPECT_BYTEARRAY_EQUAL(cb_session_data, session_data, cb_session_data_len); - - EXPECT_EQUAL(cb_session_lifetime, s2n_connection_get_session_ticket_lifetime_hint(client_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - /* Session resumption APIs and session_ticket_cb return the same values - * when receiving a new ticket in TLS1.3 - */ - if (s2n_is_tls13_fully_supported()) { - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - /* Freeze time */ - POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); - EXPECT_OK(s2n_config_mock_wall_clock(config, &now)); - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), - ticket_key1, s2n_array_len(ticket_key1), 0)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - /* Send one NewSessionTicket */ - cb_session_data_len = 0; - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_callback, NULL)); - EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 1)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that TLS1.3 was negotiated */ - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - - /* Old TLS1.2 customer code will likely attempt to read the ticket here -- ensure we indicate no ticket yet */ - EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); - - /* Receive and save the issued session ticket for the next test */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint8_t out = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, &out, 1, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_NOT_EQUAL(cb_session_data_len, 0); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&tls13_serialized_session_state, cb_session_data, cb_session_data_len)); - - /* Verify correct session ticket lifetime "hint" */ - EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), cb_session_lifetime); - - /* Verify the session ticket APIs produce the same results as the callback */ - DEFER_CLEANUP(struct s2n_blob legacy_api_ticket = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_realloc(&legacy_api_ticket, cb_session_data_len)); - EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), cb_session_data_len); - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, legacy_api_ticket.data, legacy_api_ticket.size)); - EXPECT_BYTEARRAY_EQUAL(cb_session_data, legacy_api_ticket.data, legacy_api_ticket.size); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - } - - /* Client has TLS1.3 ticket but negotiates TLS1.2 */ - if (s2n_is_tls13_fully_supported()) { - s2n_extension_type_id client_session_ticket_ext_id = 0, psk_ext_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &client_session_ticket_ext_id)); - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), - ticket_key1, s2n_array_len(ticket_key1), 0)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_serialized_session_state.blob.data, - s2n_stuffer_data_available(&tls13_serialized_session_state))); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "20240501")); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that TLS1.2 was negotiated */ - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - - /* Verify that the client did NOT try to use TLS1.2 tickets */ - EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); - EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); - - /* Verify that the client tried to use TLS1.3 tickets, but the server ignored them */ - EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, psk_ext_id)); - EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, psk_ext_id)); - - /* Verify that a full handshake occurred instead */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - } - - /* Test: TLS1.3 resumption is successful when key used to encrypt ticket is in decrypt-only state */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(client_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); - - DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(server_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_configuration)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, - chain_and_key)); - - /* Add the key that encrypted the session ticket so that the server will be able to decrypt - * the ticket successfully. - */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - /* Add a mock delay such that key 1 moves to decrypt-only state */ - uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, - &mock_delay)); - - /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ - POSIX_GUARD(server_configuration->wall_clock(server_configuration->sys_clock_ctx, &now)); - uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name2, - s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), key_intro_time)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_session(client, tls13_serialized_session_state.blob.data, - s2n_stuffer_data_available(&tls13_serialized_session_state))); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); - - /* Create nonblocking pipes */ - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* Verify that TLS1.3 was negotiated */ - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); - - /* Expect a resumption handshake because the session ticket is valid. - * If a full handshake is performed instead, then the session ticket is incorrectly - * being evaluated as invalid. This was previously known to happen with a decrypt-only - * key because we'd incorrectly try to set a TLS1.2-only handshake type flag, - * triggering an error while decrypting the session ticket. - */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server)); - } - - /* Test TLS 1.2 Server sends a zero-length ticket in the NewSessionTicket handshake - * if the ticket key was expired after SERVER_HELLO - */ - { - DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(client_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "20240501")); - - DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(server_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "20240501")); - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - /* Stop the handshake after the peers have established that a ticket - * will be sent in this handshake. - */ - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, - CLIENT_FINISHED)); - - /* Expire current session ticket key so that server no longer holds a valid key */ - uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, - &mock_delay)); - - /* Attempt to send a NewSessionTicket. This should send a zero-length NST message */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* Verify that TLS1.2 was negotiated */ - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - /* Verify that the server issued zero-length session ticket */ - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server)); - - /* Client does not have a session ticket since it received zero-length NST message */ - EXPECT_EQUAL(client->client_ticket.size, 0); - EXPECT_EQUAL(client->ticket_lifetime_hint, 0); - } - - /* Test: Server disables tls12 tickets */ - { - DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(forward_secret_config); - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(forward_secret_config, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(forward_secret_config, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_client_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_client_config, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_client_config)); - /* Security policy that does not support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); - - DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_client_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_client_config, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls13_client_config)); - /* Security policy that does support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "default_tls13")); - - /* Server does not send ticket when forward secrecy is enforced and TLS1.2 is negotiated */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - uint16_t tickets_sent = 0; - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 0); - } - - /* Server does send tickets when forward secrecy is enforced and TLS1.3 is negotiated */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls13_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); - - uint16_t tickets_sent = 0; - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 1); - } - - /* Server does not accept valid TLS1.2 ticket when forward secrecy is enforced */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - /* Disable forward secrecy for the first handshake */ - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, false)); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - uint16_t tickets_sent = 0; - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 1); - - uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client, session_data, sizeof(session_data))); - - /* Enable forward secrecy for the second handshake */ - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); - - EXPECT_SUCCESS(s2n_connection_wipe(client)); - EXPECT_SUCCESS(s2n_connection_wipe(server)); - - EXPECT_SUCCESS(s2n_connection_set_session(client, session_data, sizeof(session_data))); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - /* Session ticket not accepted */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(client)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server)); - - /* No ticket issued */ - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 0); - } - } - - /* Test: Client disables tls12 tickets */ - { - DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(forward_secret_config); - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(forward_secret_config)); - - DEFER_CLEANUP(struct s2n_config *tls12_server_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_server_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, 1)); - /* Security policy that does not support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls12_server_config, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - DEFER_CLEANUP(struct s2n_config *tls13_server_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls13_server_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_server_config, 1)); - /* Security policy that does support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_server_config, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls13_server_config, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - /* No ticket is received when forward secrecy is enforced and TLS1.2 is negotiated */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_server_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - EXPECT_EQUAL(client->client_ticket.size, 0); - } - - /* A ticket is received when forward secrecy is enforced and TLS1.3 is negotiated */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, tls13_server_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); - - /* Do a recv call to pick up TLS1.3 ticket */ - uint8_t data = 1; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client, &data, 1, &blocked), S2N_ERR_IO_BLOCKED); - - EXPECT_NOT_EQUAL(client->client_ticket.size, 0); - } - } - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_NANOS (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) +#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS S2N_SESSION_STATE_CONFIGURABLE_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 + +/** + * This function is used to "skip" time in unit tests. It will mock the system + * time to be current_time (ns) + data (ns). The "data" parameter is a uint64_t + * passed in as a void*. + */ +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + struct timespec current_time; + + clock_gettime(S2N_CLOCK_SYS, ¤t_time); + + /** + * current_time fields are represented as time_t, and time_t has a platform + * dependent size. On 32 bit platforms, attempting to convert the current + * system time to nanoseconds will overflow, causing odd failures in unit + * tests. We upcast current_time fields to uint64_t before multiplying to + * avoid this. + */ + *nanoseconds = 0; + *nanoseconds += (uint64_t) current_time.tv_sec * ONE_SEC_IN_NANOS; + *nanoseconds += (uint64_t) current_time.tv_nsec; + *nanoseconds += *(uint64_t *) data; + + return 0; +} + +static int mock_time(void *data, uint64_t *nanoseconds) +{ + if (data) { + *nanoseconds = *((uint64_t *) data); + } else { + *nanoseconds = 1000000000; + } + return S2N_SUCCESS; +} + +uint8_t cb_session_data[S2N_TLS12_SESSION_SIZE * 2] = { 0 }; +size_t cb_session_data_len = 0; +uint32_t cb_session_lifetime = 0; +static int s2n_test_session_ticket_callback(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ticket); + + /* Store the callback data for comparison at the end of the connection. */ + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); + EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, sizeof(cb_session_data), cb_session_data)); + EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); + + return S2N_SUCCESS; +} + +/* make a struct with a ticket name and key adjacent in memory */ +struct small_name_ticket { + uint8_t name[1]; + uint8_t key[32]; +}; + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + uint64_t now = 0; + struct s2n_ticket_key *ticket_key = NULL; + uint32_t ticket_keys_len = 0; + + size_t serialized_session_state_length = 0; + uint8_t s2n_state_with_session_id = S2N_STATE_WITH_SESSION_ID; + uint8_t serialized_session_state[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES + S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + + /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name1[1] = "A"; + uint8_t ticket_key_name2[4] = "BBBB"; + uint8_t ticket_key_name3[16] = "CCCCCCCCCCCCCCCC"; + uint8_t ticket_key1[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, + 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, + 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, + 0xb3, 0xe5 }; + uint8_t ticket_key2[32] = { 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, + 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, + 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, + 0xc2, 0x44 }; + uint8_t ticket_key3[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }; + + /* Testcases: + * 1) Client sends empty ST extension. Server issues NST. + * 2) Client sends empty ST extension. Server does a full handshake, but is unable to issue NST due to absence of an encrypt-decrypt key. + * 3) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. + * 4) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST even though the key is in decrypt-only state. + * 5) Client sends non-empty ST extension. Server does an abbreviated handshake, but does not issue a NST even though the key is in + * decrypt-only state due to absence of encrypt-decrypt key. + * 6) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key is not found. + * 7) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key has expired. + * 8) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. + * 9) Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. + * 10) Client sets corrupted ST extension. + * 11) User tries adding a duplicate key to the server. + * 12) Testing expired keys are removed from the server config while adding new keys. + * 13) Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. + * 14) Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. + * 15) Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and s2n_config_set_ticket_decrypt_key_lifetime calls. + * 16) Add keys out of order and pre-emptively add a key. + * 17) Handshake with client auth and session ticket enabled. + * 18) Session resumption APIs and session_ticket_cb return the same values when receiving a new ticket in TLS1.2 + * 19) Session resumption APIs and session_ticket_cb return sane values when receiving a new ticket in TLS1.3 + * 20) Client has TLS1.3 ticket but negotiates TLS1.2, so does full handshake + */ + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_stuffer tls13_serialized_session_state = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_serialized_session_state, 0)); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + + struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Test ticket name handling */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Enable session tickets */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + + /* The name should be greater than 0 and less than or equal to 16 */ + uint8_t too_large_name[17] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 0, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 17, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Add a ticket with a single-byte name */ + struct small_name_ticket small = { .name = { 0xAA }, .key = { 0xBB, 0xBB, 0xBB, 0xBB } }; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, small.name, s2n_array_len(small.name), small.key, s2n_array_len(small.key), 0)); + + /* Ensure a ticket with the same name is not added */ + struct small_name_ticket small2 = { .name = { 0xAA }, .key = { 0xCC, 0xCC, 0xCC, 0xCC } }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, small2.name, s2n_array_len(small2.name), small2.key, s2n_array_len(small2.key), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Ensure a ticket with a zero-padded name is not added */ + uint8_t padded_name[16] = { 0xAA, 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, padded_name, s2n_array_len(padded_name), ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + }; + + /* Client sends empty ST extension. Server issues NST. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* A newly created connection should not be considered resumed */ + EXPECT_FALSE(s2n_connection_is_session_resumed(server_conn)); + EXPECT_FALSE(s2n_connection_is_session_resumed(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends empty ST extension. Server does a full handshake, but is unable + * to issue NST due to absence of an encrypt-decrypt key. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and not issue NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_TRUE(s2n_connection_is_session_resumed(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is same as before because server didn't issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + EXPECT_TRUE(s2n_connection_is_session_resumed(client_conn)); + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + /* Verify that the server lifetime hint is 0 because server didn't issue a NST */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing a NST + * even though the key is in decrypt-only state. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; + + uint32_t key_intro_time = mock_current_time / ONE_SEC_IN_NANOS; + 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), key_intro_time)); + + /* Verify there is an encrypt key available */ + EXPECT_OK(s2n_config_is_encrypt_key_available(server_config)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake without issuing NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is the same as before because server didn't issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake, + * but does not issue a NST even though the key is in decrypt-only state due to + * the absence of encrypt-decrypt key. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and did not issue a NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is same as before because server did not issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_FALSE(memcmp(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues + * a NST because the key used to encrypt the session ticket is not found. + */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + 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)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); + EXPECT_EQUAL(session_ticket_lifetime, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues a NST + * because the key has expired. + */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + 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)); + + /* Add a mock delay such that the key used to encrypt ST expires */ + mock_current_time += server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the server has only the unexpired key */ + EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 1); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Tamper session state to make session ticket size smaller than what we expect */ + /* Verify that client_ticket is same as before because server did not issue a NST */ + uint8_t tampered_session_state[sizeof(serialized_session_state) - 1]; + /* Copy session format */ + tampered_session_state[0] = serialized_session_state[0]; + /* Copy and reduce by 1 the session ticket length */ + tampered_session_state[1] = serialized_session_state[1]; + tampered_session_state[2] = serialized_session_state[2] - 1; + /* Skip 1 byte of the session ticket and copy the rest */ + EXPECT_MEMCPY_SUCCESS(tampered_session_state + 3, serialized_session_state + 4, sizeof(tampered_session_state) - 4); + + /* Set client tampered ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tampered_session_state, serialized_session_state_length - 1)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + 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)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Not enabling resumption using ST */ + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sets corrupted ST extension. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + memset(serialized_session_state, 0, serialized_session_state_length); + + /* Set client ST and session state */ + EXPECT_FAILURE(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* User tries adding a duplicate key to the server */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + + 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)); + + /* Try adding the same key, but with a different name */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Try adding a different key, but with the same name */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key2, s2n_array_len(ticket_key2), 0)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Try adding a key with invalid key length */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, 0, 0)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_LENGTH); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Testing expired keys are removed from the server config while adding new keys. */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + + /* Add 2 ST keys */ + 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_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that the first two keys expire */ + uint64_t mock_delay = server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + /* Add a third ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + /* Try adding the expired keys */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + 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)); + + /* Verify that the config has three unexpired keys */ + EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + /* ticket_key3 should have "rotated" to the first index as other keys expired */ + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name3, s2n_array_len(ticket_key_name3)); + EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 3); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Attempting to add more than S2N_MAX_TICKET_KEYS causes failures. */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + uint8_t id = 0; + uint8_t ticket_key_buf[32] = { 0 }; + + for (uint8_t i = 0; i < S2N_MAX_TICKET_KEYS; i++) { + id = i; + ticket_key_buf[0] = i; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, + &id, sizeof(id), ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); + } + + id = S2N_MAX_TICKET_KEYS; + ticket_key_buf[0] = S2N_MAX_TICKET_KEYS; + EXPECT_FAILURE(s2n_config_add_ticket_crypto_key(config, &id, sizeof(id), + ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); + }; + + /* Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + 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)); + + /* Add a mock delay such that the first key is close to it's encryption peak */ + uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; + mock_current_time += delay_in_nanos; + + /* Add two more ST keys */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); + uint32_t first_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - delay_in_nanos / ONE_SEC_IN_NANOS; + EXPECT_EQUAL(session_ticket_lifetime, first_key_remaining_lifetime); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST */ + { + const size_t allowed_failures = 1; + size_t failures = 0; + bool expected_key_chosen = false; + + /* This test sets up three different ticket encryption keys at various times in their encryption lifetime. The test + * is meant to check that the weighted random selection algorithm correctly selects the key that is at its + * encryption peak. However the test will sometimes pick a key that is not at its encryption peak because the + * selection function uses a weighted random selection algorithm. Here we retry the test once if the key chosen + * is not the expected key. + * + * The wrong key will be chosen 0.02% of the time. This value is drawn from the weight of the expected key, + * which does not change per test run. Therefore, the probability that the test chooses the wrong key + * more than allowed_failures times is 0.0002 ^ 2 = 0.00000004, which is extremely unlikely to occur. If + * the logic changes to chose the wrong key at a higher rate, say 50% of the time, this test would fail at a + * 0.5 ^ 2 = 0.25 or 25% of the time. This rate is high enough for us to notice and investigate. + */ + while (expected_key_chosen == false) { + EXPECT_TRUE(failures <= allowed_failures); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + 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)); + + /* Add second key when the first key is very close to it's encryption peak */ + uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; + mock_current_time += delay_in_nanos; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add third key when the second key is very close to it's encryption peak and + * the first key is about to transition from encrypt-decrypt state to decrypt-only state + */ + mock_current_time += delay_in_nanos; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), + ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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); + 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; + } else { + failures += 1; + } + + /* Verify the lifetime hint from the server */ + uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); + uint32_t second_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - (delay_in_nanos / ONE_SEC_IN_NANOS); + EXPECT_EQUAL(session_ticket_lifetime, second_key_remaining_lifetime); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + }; + + /* Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and + * s2n_config_set_ticket_decrypt_key_lifetime calls */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Set encrypt-decrypt key expire time to 24 hours */ + EXPECT_SUCCESS(s2n_config_set_ticket_encrypt_decrypt_key_lifetime(server_config, 86400)); + + /* Set decrypt-only key expire time to 5 hours */ + EXPECT_SUCCESS(s2n_config_set_ticket_decrypt_key_lifetime(server_config, 18000)); + + /* Add one ST key */ + 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)); + + /* Add second key when the first key is very close to it's encryption peak */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add third key when the second key is very close to it's encryption peak and + * the first key is about to transition from encrypt-decrypt state to decrypt-only state + */ + mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Add keys out of order and pre-emptively add a key */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add a key. After 1 hour it will be considered an encrypt-decrypt key. */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + 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), (now / ONE_SEC_IN_NANOS) + 3600)); + + /* Add a key. After 1 hour it will reach it's peak */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), now / ONE_SEC_IN_NANOS)); + + /* Add a key pre-emptively. It can be used only after 10 hours */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), now / ONE_SEC_IN_NANOS + 36000)); + + /* Add a mock delay such that negotiation happens after 1 hour */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Handshake with client auth and session ticket enabled */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + /* Client has session ticket and mutual auth enabled */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Server has session ticket and mutual auth enabled */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST since client + * auth is enabled in server mode */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* s2n_resume_decrypt_session fails to decrypt when presented with a valid ticket_key, valid iv and invalid encrypted blob */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + + /* Add Session Ticket key on the server config */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + 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 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))); + + uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); + + server_conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_DECRYPT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* s2n_resume_decrypt_session fails with a key not found error when presented with an invalid ticket_key, valid iv and invalid encrypted blob */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + /* Add Session Ticket key on the server config */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + 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 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))); + + uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); + + server_conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Test s2n_connection_is_session_resumed */ + { + /* TLS1.2 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + conn->handshake.handshake_type = INITIAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED | WITH_SESSION_TICKET; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + /* Ignores PSK mode */ + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + conn->handshake.handshake_type = INITIAL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED; + conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Session resumption APIs and session_ticket_cb return the same values + * when receiving a new ticket in TLS1.2 + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_wall_clock(client_config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + /* Client will use callback when server nst is received */ + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_callback, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + 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), now / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Expect values from the session_ticket_cb are equivalent to values from the APIs */ + EXPECT_EQUAL(cb_session_data_len, s2n_connection_get_session_length(client_conn)); + uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, session_data, cb_session_data_len)); + EXPECT_BYTEARRAY_EQUAL(cb_session_data, session_data, cb_session_data_len); + + EXPECT_EQUAL(cb_session_lifetime, s2n_connection_get_session_ticket_lifetime_hint(client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Session resumption APIs and session_ticket_cb return the same values + * when receiving a new ticket in TLS1.3 + */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + /* Freeze time */ + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); + EXPECT_OK(s2n_config_mock_wall_clock(config, &now)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + /* Send one NewSessionTicket */ + cb_session_data_len = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_callback, NULL)); + EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 1)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that TLS1.3 was negotiated */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Old TLS1.2 customer code will likely attempt to read the ticket here -- ensure we indicate no ticket yet */ + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); + + /* Receive and save the issued session ticket for the next test */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t out = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, &out, 1, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_NOT_EQUAL(cb_session_data_len, 0); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&tls13_serialized_session_state, cb_session_data, cb_session_data_len)); + + /* Verify correct session ticket lifetime "hint" */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), cb_session_lifetime); + + /* Verify the session ticket APIs produce the same results as the callback */ + DEFER_CLEANUP(struct s2n_blob legacy_api_ticket = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_realloc(&legacy_api_ticket, cb_session_data_len)); + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), cb_session_data_len); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, legacy_api_ticket.data, legacy_api_ticket.size)); + EXPECT_BYTEARRAY_EQUAL(cb_session_data, legacy_api_ticket.data, legacy_api_ticket.size); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Client has TLS1.3 ticket but negotiates TLS1.2 */ + if (s2n_is_tls13_fully_supported()) { + s2n_extension_type_id client_session_ticket_ext_id = 0, psk_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &client_session_ticket_ext_id)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_serialized_session_state.blob.data, + s2n_stuffer_data_available(&tls13_serialized_session_state))); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "20240501")); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that TLS1.2 was negotiated */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* Verify that the client did NOT try to use TLS1.2 tickets */ + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); + + /* Verify that the client tried to use TLS1.3 tickets, but the server ignored them */ + EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, psk_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, psk_ext_id)); + + /* Verify that a full handshake occurred instead */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Test: TLS1.3 resumption is successful when key used to encrypt ticket is in decrypt-only state */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(client_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); + + DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(server_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_configuration)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, + chain_and_key)); + + /* Add the key that encrypted the session ticket so that the server will be able to decrypt + * the ticket successfully. + */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, + &mock_delay)); + + /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_configuration->wall_clock(server_configuration->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name2, + s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), key_intro_time)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_session(client, tls13_serialized_session_state.blob.data, + s2n_stuffer_data_available(&tls13_serialized_session_state))); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Verify that TLS1.3 was negotiated */ + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + /* Expect a resumption handshake because the session ticket is valid. + * If a full handshake is performed instead, then the session ticket is incorrectly + * being evaluated as invalid. This was previously known to happen with a decrypt-only + * key because we'd incorrectly try to set a TLS1.2-only handshake type flag, + * triggering an error while decrypting the session ticket. + */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server)); + } + + /* Test TLS 1.2 Server sends a zero-length ticket in the NewSessionTicket handshake + * if the ticket key was expired after SERVER_HELLO + */ + { + DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(client_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "20240501")); + + DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(server_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "20240501")); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + /* Stop the handshake after the peers have established that a ticket + * will be sent in this handshake. + */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, + CLIENT_FINISHED)); + + /* Expire current session ticket key so that server no longer holds a valid key */ + uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, + &mock_delay)); + + /* Attempt to send a NewSessionTicket. This should send a zero-length NST message */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Verify that TLS1.2 was negotiated */ + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + /* Verify that the server issued zero-length session ticket */ + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server)); + + /* Client does not have a session ticket since it received zero-length NST message */ + EXPECT_EQUAL(client->client_ticket.size, 0); + EXPECT_EQUAL(client->ticket_lifetime_hint, 0); + } + + /* Test: Server disables tls12 tickets */ + { + DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(forward_secret_config); + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(forward_secret_config, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(forward_secret_config, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_client_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_client_config)); + /* Security policy that does not support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); + + DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_client_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls13_client_config)); + /* Security policy that does support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "default_tls13")); + + /* Server does not send ticket when forward secrecy is enforced and TLS1.2 is negotiated */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 0); + } + + /* Server does send tickets when forward secrecy is enforced and TLS1.3 is negotiated */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 1); + } + + /* Server does not accept valid TLS1.2 ticket when forward secrecy is enforced */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + /* Disable forward secrecy for the first handshake */ + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, false)); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 1); + + uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client, session_data, sizeof(session_data))); + + /* Enable forward secrecy for the second handshake */ + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); + + EXPECT_SUCCESS(s2n_connection_wipe(client)); + EXPECT_SUCCESS(s2n_connection_wipe(server)); + + EXPECT_SUCCESS(s2n_connection_set_session(client, session_data, sizeof(session_data))); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + /* Session ticket not accepted */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(client)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server)); + + /* No ticket issued */ + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 0); + } + } + + /* Test: Client disables tls12 tickets */ + { + DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(forward_secret_config); + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(forward_secret_config)); + + DEFER_CLEANUP(struct s2n_config *tls12_server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_server_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, 1)); + /* Security policy that does not support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls12_server_config, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + DEFER_CLEANUP(struct s2n_config *tls13_server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls13_server_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_server_config, 1)); + /* Security policy that does support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_server_config, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls13_server_config, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* No ticket is received when forward secrecy is enforced and TLS1.2 is negotiated */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_server_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + EXPECT_EQUAL(client->client_ticket.size, 0); + } + + /* A ticket is received when forward secrecy is enforced and TLS1.3 is negotiated */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, tls13_server_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + /* Do a recv call to pick up TLS1.3 ticket */ + uint8_t data = 1; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client, &data, 1, &blocked), S2N_ERR_IO_BLOCKED); + + EXPECT_NOT_EQUAL(client->client_ticket.size, 0); + } + } + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_sslv3_test.c b/tests/unit/s2n_sslv3_test.c index d0a5635f2dd..ce95730306e 100644 --- a/tests/unit/s2n_sslv3_test.c +++ b/tests/unit/s2n_sslv3_test.c @@ -1,131 +1,132 @@ -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_random.h" - -#define S2N_TEST_DATA_SIZE 100 - -S2N_RESULT s2n_test_send_receive_data(struct s2n_connection *sender, struct s2n_connection *receiver) -{ - uint8_t test_data[S2N_TEST_DATA_SIZE] = { 0 }; - struct s2n_blob test_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); - EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); - - /* Send data */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - ssize_t bytes_written = 0; - while (bytes_written < S2N_TEST_DATA_SIZE) { - ssize_t w = s2n_send(sender, test_data + bytes_written, S2N_TEST_DATA_SIZE - bytes_written, &blocked); - EXPECT_TRUE(w >= 0); - bytes_written += w; - } - - /* Receive data */ - uint8_t buffer[S2N_TEST_DATA_SIZE] = { 0 }; - ssize_t bytes_received = 0; - while (bytes_received < S2N_TEST_DATA_SIZE) { - ssize_t r = s2n_recv(receiver, buffer + bytes_received, S2N_TEST_DATA_SIZE - bytes_received, &blocked); - EXPECT_TRUE(r > 0); - bytes_received += r; - } - - EXPECT_BYTEARRAY_EQUAL(test_data, buffer, S2N_TEST_DATA_SIZE); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Self-talk test */ - { - size_t supported_record_alg_count = 0; - - for (size_t i = 0; i < security_policy_test_all.cipher_preferences->count; i++) { - struct s2n_cipher_suite *cipher_suite = security_policy_test_all.cipher_preferences->suites[i]; - - /* Skip non-sslv3 cipher suites. */ - if (!cipher_suite->sslv3_record_alg) { - continue; - } - - /* Skip unsupported record algorithms. */ - if (!cipher_suite->sslv3_record_alg->cipher->is_available()) { - continue; - } - supported_record_alg_count += 1; - - struct s2n_cipher_preferences test_cipher_preferences = { - .count = 1, - .suites = &cipher_suite, - }; - struct s2n_security_policy test_policy = security_policy_test_all; - test_policy.cipher_preferences = &test_cipher_preferences; - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - client_config->security_policy = &test_policy; - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - server_config->security_policy = &test_policy; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - client->client_protocol_version = S2N_SSLv3; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), S2N_SSLv3); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), S2N_SSLv3); - - EXPECT_OK(s2n_test_send_receive_data(client, server)); - EXPECT_OK(s2n_test_send_receive_data(server, client)); - } - - /* Ensure that a supported record algorithm was found, and SSLv3 was tested at least once. */ - EXPECT_TRUE(supported_record_alg_count > 0); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_DATA_SIZE 100 + +S2N_RESULT s2n_test_send_receive_data(struct s2n_connection *sender, struct s2n_connection *receiver) +{ + uint8_t test_data[S2N_TEST_DATA_SIZE] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* Send data */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t bytes_written = 0; + while (bytes_written < S2N_TEST_DATA_SIZE) { + ssize_t w = s2n_send(sender, test_data + bytes_written, S2N_TEST_DATA_SIZE - bytes_written, &blocked); + EXPECT_TRUE(w >= 0); + bytes_written += w; + } + + /* Receive data */ + uint8_t buffer[S2N_TEST_DATA_SIZE] = { 0 }; + ssize_t bytes_received = 0; + while (bytes_received < S2N_TEST_DATA_SIZE) { + ssize_t r = s2n_recv(receiver, buffer + bytes_received, S2N_TEST_DATA_SIZE - bytes_received, &blocked); + EXPECT_TRUE(r > 0); + bytes_received += r; + } + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, S2N_TEST_DATA_SIZE); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Self-talk test */ + { + size_t supported_record_alg_count = 0; + + for (size_t i = 0; i < security_policy_test_all.cipher_preferences->count; i++) { + struct s2n_cipher_suite *cipher_suite = security_policy_test_all.cipher_preferences->suites[i]; + + /* Skip non-sslv3 cipher suites. */ + if (!cipher_suite->sslv3_record_alg) { + continue; + } + + /* Skip unsupported record algorithms. */ + if (!cipher_suite->sslv3_record_alg->cipher->is_available()) { + continue; + } + supported_record_alg_count += 1; + + struct s2n_cipher_preferences test_cipher_preferences = { + .count = 1, + .suites = &cipher_suite, + }; + struct s2n_security_policy test_policy = security_policy_test_all; + test_policy.cipher_preferences = &test_cipher_preferences; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + client_config->security_policy = &test_policy; + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + server_config->security_policy = &test_policy; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + client->client_protocol_version = S2N_SSLv3; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), S2N_SSLv3); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), S2N_SSLv3); + + EXPECT_OK(s2n_test_send_receive_data(client, server)); + EXPECT_OK(s2n_test_send_receive_data(server, client)); + } + + /* Ensure that a supported record algorithm was found, and SSLv3 was tested at least once. */ + EXPECT_TRUE(supported_record_alg_count > 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls12_handshake_test.c b/tests/unit/s2n_tls12_handshake_test.c index 7af0ef52824..f4381bd2374 100644 --- a/tests/unit/s2n_tls12_handshake_test.c +++ b/tests/unit/s2n_tls12_handshake_test.c @@ -1,558 +1,559 @@ -/* - * 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 "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_safety.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" - -static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; - -static int expected_handler_called; -static int unexpected_handler_called; - -static int s2n_test_handler(struct s2n_connection *conn) -{ - unexpected_handler_called = 1; - return 0; -} - -static int s2n_test_expected_handler(struct s2n_connection *conn) -{ - expected_handler_called = 1; - return 0; -} - -static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) -{ - for (int i = 0; i < s2n_array_len(state_machine); i++) { - state_machine[i].handler[0] = s2n_test_handler; - state_machine[i].handler[1] = s2n_test_handler; - } - - state_machine[expected].handler[direction] = s2n_test_expected_handler; - - expected_handler_called = 0; - unexpected_handler_called = 0; - - return 0; -} - -static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) -{ - POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); - - /* TLS1.2 protocol version */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - - if (record_type == TLS_HANDSHAKE) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); - - POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); - - /* Handshake message data size */ - POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); - return 0; - } - - if (record_type == TLS_CHANGE_CIPHER_SPEC) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); - - /* change spec is always just 0x01 */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); - return 0; - } - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Construct an array of all valid tls1.2 handshake_types */ - uint16_t valid_tls12_handshakes[S2N_HANDSHAKES_COUNT]; - int valid_tls12_handshakes_size = 0; - for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { - if (memcmp(handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { - valid_tls12_handshakes[valid_tls12_handshakes_size] = i; - valid_tls12_handshakes_size++; - } - } - EXPECT_TRUE(valid_tls12_handshakes_size > 0); - EXPECT_TRUE(valid_tls12_handshakes_size < S2N_HANDSHAKES_COUNT); - - /* Test: When using TLS 1.2, use the existing state machine and handshakes */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &state_machine[0]); - EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &handshakes[0]); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 server waits for expected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: Client CCS messages always come before Client Finished messages */ - { - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - /* Initial handshake doesn't contain a CCS message */ - if (handshake == INITIAL) { - continue; - } - - bool ccs_encountered = false; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - ccs_encountered = true; - } - - if (handshakes[handshake][j] == CLIENT_FINISHED) { - EXPECT_TRUE(ccs_encountered); - } - } - /* Every valid handshake includes a CCS message */ - EXPECT_TRUE(ccs_encountered); - } - }; - - /* Test: TLS1.2 client waits for expected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 client handles expected server CCS messages - * but errors on unexpected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - conn->handshake.message_number = j; - conn->in_status = ENCRYPTED; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - EXPECT_TRUE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - EXPECT_FALSE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 server handles expected client CCS messages - * but errors on unexpected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); - conn->handshake.message_number = j; - conn->in_status = ENCRYPTED; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - EXPECT_TRUE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - EXPECT_FALSE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 client can receive a hello request message at any time. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { - uint16_t handshake = valid_tls12_handshakes[i]; - - for (size_t j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == APPLICATION_DATA) { - break; - } - - conn->handshake.message_number = j; - conn->in_status = ENCRYPTED; - conn->handshake.handshake_type = handshake; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_HELLO_REQUEST)); - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - EXPECT_EQUAL(conn->handshake.message_number, j); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_FALSE(unexpected_handler_called); - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 s2n_handshake_read_io should accept only the expected message */ - { - /* TLS1.2 should accept the expected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, 1); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.2 should error for an unexpected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.2 should error for an expected message from the wrong writer */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.2 should error for an expected message from the wrong record type */ - { - /* Unfortunately, all our non-handshake record types have a message type of 0, - * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) - * which can appear at any point in a TLS1.2 handshake. - * - * To test, temporarily modify the actions table. - * We MUST restore this after this test. - */ - uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - uint8_t server_css_message_number = 2; - conn->handshake.handshake_type = NEGOTIATED; - conn->handshake.message_number = server_css_message_number; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; - }; - }; - - /* Test: TLS1.2 handshake type name maximum size is set correctly. - * The maximum size is the size of a name with all flags set. */ - { - size_t correct_size = 0; - for (size_t i = 0; i < s2n_array_len(tls12_handshake_type_names); i++) { - correct_size += strlen(tls12_handshake_type_names[i]); - } - if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { - fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); - FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.2 handshakes"); - } - }; - - /* Test: TLS 1.2 handshake types are all properly printed */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - conn->handshake.handshake_type = INITIAL; - EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); - - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); - - const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT|" - "TLS12_PERFECT_FORWARD_SECRECY|OCSP_STATUS|WITH_SESSION_TICKET|WITH_NPN"; - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN; - EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - - const char *handshake_type_name = NULL; - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - conn->handshake.handshake_type = valid_tls12_handshakes[i]; - - handshake_type_name = s2n_connection_get_handshake_type_name(conn); - - /* The handshake type names must be unique */ - for (int j = 0; j < valid_tls12_handshakes_size; j++) { - conn->handshake.handshake_type = valid_tls12_handshakes[j]; - if (i == j) { - EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } else { - EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS 1.2 message types are all properly printed */ - { - uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN; - const char *expected[] = { "CLIENT_HELLO", - "SERVER_HELLO", "SERVER_CERT", "SERVER_CERT_STATUS", "SERVER_KEY", "SERVER_CERT_REQ", "SERVER_HELLO_DONE", - "CLIENT_CERT", "CLIENT_KEY", "CLIENT_CERT_VERIFY", "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_NPN", - "CLIENT_FINISHED", "SERVER_NEW_SESSION_TICKET", "SERVER_CHANGE_CIPHER_SPEC", "SERVER_FINISHED", - "APPLICATION_DATA" }; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - - conn->handshake.handshake_type = test_handshake_type; - - for (size_t i = 0; i < s2n_array_len(expected); i++) { - conn->handshake.message_number = i; - EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: A WITH_NPN form of every valid, negotiated handshake exists */ - { - for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { - uint32_t handshake_type_original = valid_tls12_handshakes[i]; - message_type_t *messages_original = handshakes[handshake_type_original]; - - /* Ignore INITIAL and WITH_NPN handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_NPN)) { - continue; - } - - /* Get the WITH_NPN form of the handshake */ - uint32_t handshake_type_npn = handshake_type_original | WITH_NPN; - message_type_t *messages_npn = handshakes[handshake_type_npn]; - - for (size_t j = 0, j_npn = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_npn < S2N_MAX_HANDSHAKE_LENGTH; j++, j_npn++) { - /* The original handshake cannot contain the Next Protocol message */ - EXPECT_NOT_EQUAL(messages_original[j], CLIENT_NPN); - - /* Skip the Next Protocol message in WITH_NPN handshake */ - if (messages_npn[j_npn] == CLIENT_NPN) { - j_npn++; - } - - /* Otherwise the handshakes must be equivalent */ - EXPECT_EQUAL(messages_original[j], messages_npn[j_npn]); - } - } - }; - - /* Test: ensure that there isn't a collision between TLS 1.2 and TLS 1.3 flags - * that share the same bit. - */ - { - /* sanity check: these flags are the same bit */ - EXPECT_EQUAL((int) TLS12_PERFECT_FORWARD_SECRECY, (int) HELLO_RETRY_REQUEST); - - uint32_t tls12_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; - uint32_t tls13_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - - /* Clear the cache entries so we start fresh */ - memset(handshake_type_str_tls12ish[tls12_type], 0, MAX_HANDSHAKE_TYPE_LEN); - memset(handshake_type_str_tls13[tls13_type], 0, MAX_HANDSHAKE_TYPE_LEN); - - /* First: populate cache via TLS 1.2 */ - conn->actual_protocol_version = S2N_TLS12; - conn->handshake.handshake_type = tls12_type; - const char *tls12_name = s2n_connection_get_handshake_type_name(conn); - EXPECT_NOT_NULL(tls12_name); - EXPECT_STRING_EQUAL(tls12_name, "NEGOTIATED|FULL_HANDSHAKE|TLS12_PERFECT_FORWARD_SECRECY"); - - /* Second: query cache via TLS 1.3 with same bitmap — should not collide */ - conn->actual_protocol_version = S2N_TLS13; - conn->handshake.handshake_type = tls13_type; - const char *tls13_name = s2n_connection_get_handshake_type_name(conn); - EXPECT_NOT_NULL(tls13_name); - EXPECT_STRING_EQUAL(tls13_name, "NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST"); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_safety.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" + +static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +static int expected_handler_called; +static int unexpected_handler_called; + +static int s2n_test_handler(struct s2n_connection *conn) +{ + unexpected_handler_called = 1; + return 0; +} + +static int s2n_test_expected_handler(struct s2n_connection *conn) +{ + expected_handler_called = 1; + return 0; +} + +static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) +{ + for (int i = 0; i < s2n_array_len(state_machine); i++) { + state_machine[i].handler[0] = s2n_test_handler; + state_machine[i].handler[1] = s2n_test_handler; + } + + state_machine[expected].handler[direction] = s2n_test_expected_handler; + + expected_handler_called = 0; + unexpected_handler_called = 0; + + return 0; +} + +static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); + + /* TLS1.2 protocol version */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + + if (record_type == TLS_HANDSHAKE) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); + + POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); + + /* Handshake message data size */ + POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); + return 0; + } + + if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); + + /* change spec is always just 0x01 */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); + return 0; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Construct an array of all valid tls1.2 handshake_types */ + uint16_t valid_tls12_handshakes[S2N_HANDSHAKES_COUNT]; + int valid_tls12_handshakes_size = 0; + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + if (memcmp(handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { + valid_tls12_handshakes[valid_tls12_handshakes_size] = i; + valid_tls12_handshakes_size++; + } + } + EXPECT_TRUE(valid_tls12_handshakes_size > 0); + EXPECT_TRUE(valid_tls12_handshakes_size < S2N_HANDSHAKES_COUNT); + + /* Test: When using TLS 1.2, use the existing state machine and handshakes */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &state_machine[0]); + EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &handshakes[0]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 server waits for expected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: Client CCS messages always come before Client Finished messages */ + { + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + /* Initial handshake doesn't contain a CCS message */ + if (handshake == INITIAL) { + continue; + } + + bool ccs_encountered = false; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + ccs_encountered = true; + } + + if (handshakes[handshake][j] == CLIENT_FINISHED) { + EXPECT_TRUE(ccs_encountered); + } + } + /* Every valid handshake includes a CCS message */ + EXPECT_TRUE(ccs_encountered); + } + }; + + /* Test: TLS1.2 client waits for expected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 client handles expected server CCS messages + * but errors on unexpected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_TRUE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 server handles expected client CCS messages + * but errors on unexpected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_TRUE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 client can receive a hello request message at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { + uint16_t handshake = valid_tls12_handshakes[i]; + + for (size_t j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == APPLICATION_DATA) { + break; + } + + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + conn->handshake.handshake_type = handshake; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_EQUAL(conn->handshake.message_number, j); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 s2n_handshake_read_io should accept only the expected message */ + { + /* TLS1.2 should accept the expected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, 1); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an unexpected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an expected message from the wrong writer */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an expected message from the wrong record type */ + { + /* Unfortunately, all our non-handshake record types have a message type of 0, + * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) + * which can appear at any point in a TLS1.2 handshake. + * + * To test, temporarily modify the actions table. + * We MUST restore this after this test. + */ + uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + uint8_t server_css_message_number = 2; + conn->handshake.handshake_type = NEGOTIATED; + conn->handshake.message_number = server_css_message_number; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; + }; + }; + + /* Test: TLS1.2 handshake type name maximum size is set correctly. + * The maximum size is the size of a name with all flags set. */ + { + size_t correct_size = 0; + for (size_t i = 0; i < s2n_array_len(tls12_handshake_type_names); i++) { + correct_size += strlen(tls12_handshake_type_names[i]); + } + if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { + fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); + FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.2 handshakes"); + } + }; + + /* Test: TLS 1.2 handshake types are all properly printed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + conn->handshake.handshake_type = INITIAL; + EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + + const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT|" + "TLS12_PERFECT_FORWARD_SECRECY|OCSP_STATUS|WITH_SESSION_TICKET|WITH_NPN"; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN; + EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + + const char *handshake_type_name = NULL; + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + conn->handshake.handshake_type = valid_tls12_handshakes[i]; + + handshake_type_name = s2n_connection_get_handshake_type_name(conn); + + /* The handshake type names must be unique */ + for (int j = 0; j < valid_tls12_handshakes_size; j++) { + conn->handshake.handshake_type = valid_tls12_handshakes[j]; + if (i == j) { + EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } else { + EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.2 message types are all properly printed */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN; + const char *expected[] = { "CLIENT_HELLO", + "SERVER_HELLO", "SERVER_CERT", "SERVER_CERT_STATUS", "SERVER_KEY", "SERVER_CERT_REQ", "SERVER_HELLO_DONE", + "CLIENT_CERT", "CLIENT_KEY", "CLIENT_CERT_VERIFY", "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_NPN", + "CLIENT_FINISHED", "SERVER_NEW_SESSION_TICKET", "SERVER_CHANGE_CIPHER_SPEC", "SERVER_FINISHED", + "APPLICATION_DATA" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + + conn->handshake.handshake_type = test_handshake_type; + + for (size_t i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: A WITH_NPN form of every valid, negotiated handshake exists */ + { + for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { + uint32_t handshake_type_original = valid_tls12_handshakes[i]; + message_type_t *messages_original = handshakes[handshake_type_original]; + + /* Ignore INITIAL and WITH_NPN handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_NPN)) { + continue; + } + + /* Get the WITH_NPN form of the handshake */ + uint32_t handshake_type_npn = handshake_type_original | WITH_NPN; + message_type_t *messages_npn = handshakes[handshake_type_npn]; + + for (size_t j = 0, j_npn = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_npn < S2N_MAX_HANDSHAKE_LENGTH; j++, j_npn++) { + /* The original handshake cannot contain the Next Protocol message */ + EXPECT_NOT_EQUAL(messages_original[j], CLIENT_NPN); + + /* Skip the Next Protocol message in WITH_NPN handshake */ + if (messages_npn[j_npn] == CLIENT_NPN) { + j_npn++; + } + + /* Otherwise the handshakes must be equivalent */ + EXPECT_EQUAL(messages_original[j], messages_npn[j_npn]); + } + } + }; + + /* Test: ensure that there isn't a collision between TLS 1.2 and TLS 1.3 flags + * that share the same bit. + */ + { + /* sanity check: these flags are the same bit */ + EXPECT_EQUAL((int) TLS12_PERFECT_FORWARD_SECRECY, (int) HELLO_RETRY_REQUEST); + + uint32_t tls12_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; + uint32_t tls13_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + /* Clear the cache entries so we start fresh */ + memset(handshake_type_str_tls12ish[tls12_type], 0, MAX_HANDSHAKE_TYPE_LEN); + memset(handshake_type_str_tls13[tls13_type], 0, MAX_HANDSHAKE_TYPE_LEN); + + /* First: populate cache via TLS 1.2 */ + conn->actual_protocol_version = S2N_TLS12; + conn->handshake.handshake_type = tls12_type; + const char *tls12_name = s2n_connection_get_handshake_type_name(conn); + EXPECT_NOT_NULL(tls12_name); + EXPECT_STRING_EQUAL(tls12_name, "NEGOTIATED|FULL_HANDSHAKE|TLS12_PERFECT_FORWARD_SECRECY"); + + /* Second: query cache via TLS 1.3 with same bitmap — should not collide */ + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.handshake_type = tls13_type; + const char *tls13_name = s2n_connection_get_handshake_type_name(conn); + EXPECT_NOT_NULL(tls13_name); + EXPECT_STRING_EQUAL(tls13_name, "NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST"); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_cert_verify_test.c b/tests/unit/s2n_tls13_cert_verify_test.c index 2657e4f8212..2a71a8266ec 100644 --- a/tests/unit/s2n_tls13_cert_verify_test.c +++ b/tests/unit/s2n_tls13_cert_verify_test.c @@ -1,360 +1,361 @@ -/* - * 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 "crypto/s2n_fips.h" -#include "crypto/s2n_rsa_pss.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_certificate_verify.c" - -uint8_t hello[] = "Hello, World!\n"; -uint8_t goodbye[] = "Goodbye, World!\n"; - -struct s2n_tls13_cert_verify_test { - const char *const cert_file; - const char *const key_file; - const struct s2n_signature_scheme *sig_scheme; - const struct s2n_signature_scheme *with_wrong_hash; -}; - -const struct s2n_tls13_cert_verify_test test_cases[] = { - { - .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, - .key_file = S2N_ECDSA_P256_PKCS1_KEY, - .sig_scheme = &s2n_ecdsa_sha256, - .with_wrong_hash = &s2n_ecdsa_sha384, - }, -#if RSA_PSS_CERTS_SUPPORTED - { - .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, - .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, - .sig_scheme = &s2n_rsa_pss_pss_sha256, - .with_wrong_hash = &s2n_rsa_pss_pss_sha384, - }, -#endif -}; - -S2N_RESULT s2n_cert_verify_connection_setup_and_send( - struct s2n_connection *sending_conn, struct s2n_connection *verifying_conn, - struct s2n_config *config, struct s2n_cert_chain_and_key *cert_chain, - struct s2n_signature_scheme *sig_scheme, struct s2n_blob *cert) -{ - sending_conn->handshake_params.our_chain_and_key = cert_chain; - sending_conn->handshake_params.server_cert_sig_scheme = sig_scheme; - sending_conn->handshake_params.client_cert_sig_scheme = sig_scheme; - sending_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - sending_conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_connection_set_config(sending_conn, config)); - - verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - verifying_conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); - - /* Extract public key from certificate and set it for verifying connection */ - s2n_pkey_type pkey_type = { 0 }; - if (verifying_conn->mode == S2N_CLIENT) { - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, cert)); - EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.server_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); - } else { - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, cert)); - EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.client_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); - } - - /* Hash initialization */ - EXPECT_SUCCESS(s2n_hash_init(&sending_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sending_conn->handshake.hashes->sha256, hello, sizeof(hello))); - EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, sizeof(hello))); - - /* Send cert verify */ - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, - s2n_stuffer_data_available(&sending_conn->handshake.io))); - - return S2N_RESULT_OK; -} - -int run_tests(const struct s2n_tls13_cert_verify_test *test_case, s2n_mode verifier_mode) -{ - const char *cert_file = test_case->cert_file; - const char *key_file = test_case->key_file; - struct s2n_signature_scheme sig_scheme = *test_case->sig_scheme; - - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(cert_file, &cert_chain_pem[0], S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(key_file, &private_key_pem[0], S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(cert_chain); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); - - /* Initialize a certificate */ - DEFER_CLEANUP(struct s2n_stuffer certificate_in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer certificate_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&certificate_in, (uint8_t *) cert_chain_pem, sizeof(cert_chain_pem))); - EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); - - uint32_t available_size = s2n_stuffer_data_available(&certificate_out); - struct s2n_blob cert = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&cert, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); - - /* Successfully send and receive certificate verify */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Receive and verify cert */ - EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); - - /* Repeat the above test successfully */ - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, - s2n_stuffer_data_available(&sending_conn->handshake.io))); - EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); - - /* Test fails if cipher suites hash is configured incorrectly */ - verifying_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, - s2n_stuffer_data_available(&sending_conn->handshake.io))); - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - }; - - /* Verifying connection errors with incorrect signed content */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Update receive hash with goodbye */ - EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, goodbye, sizeof(goodbye))); - - uint64_t verifying_bytes = 0; - uint64_t sending_bytes = 0; - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&verifying_conn->handshake.hashes->sha256, &verifying_bytes)); - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&sending_conn->handshake.hashes->sha256, &sending_bytes)); - EXPECT_NOT_EQUAL(sending_bytes, verifying_bytes); - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - }; - - /* Verifying connection errors with even 1 bit incorrect */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Flip one bit in verifying_conn io buffer */ - EXPECT_TRUE(10 < s2n_stuffer_data_available(&verifying_conn->handshake.io)); - verifying_conn->handshake.io.blob.data[10] ^= 1; - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - }; - - /* Verifying connection errors with wrong hash algorithms */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Use a hash algorithm different from sender by prepending corresponding iana value */ - struct s2n_stuffer rereader = verifying_conn->handshake.io; - EXPECT_SUCCESS(s2n_stuffer_rewrite(&rereader)); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&rereader, test_case->with_wrong_hash->iana_value)); - - /* We have special checks for ECDSA curves and therefore it errors earlier than signature verification */ - if (test_case->sig_scheme->sig_alg == S2N_SIGNATURE_ECDSA) { - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - } - }; - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - /* Run all tests for server sending and client receiving/verifying cert_verify message */ - run_tests(&test_cases[i], S2N_CLIENT); - - /* Run all tests for client sending and server receiving/verifying cert_verify message */ - run_tests(&test_cases[i], S2N_SERVER); - } - - /* Self-talk: Ensure that the signature algorithm used to sign the CertificateVerify message - * is validated against the certificate type - */ - if (s2n_is_tls13_fully_supported()) { - struct s2n_tls13_cert_verify_test test_server_parameters[] = { - { - .cert_file = S2N_RSA_2048_PKCS1_CERT_CHAIN, - .key_file = S2N_RSA_2048_PKCS1_KEY, - .sig_scheme = &s2n_rsa_pss_rsae_sha256, - }, - { - .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, - .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, - .sig_scheme = &s2n_rsa_pss_pss_sha256, - }, - { - .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, - .key_file = S2N_ECDSA_P256_PKCS1_KEY, - .sig_scheme = &s2n_ecdsa_sha256, - } - }; - - const struct s2n_signature_scheme *test_client_sig_schemes[] = { - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_pss_sha256, - &s2n_ecdsa_sha256, - }; - - for (size_t param_idx = 0; param_idx < s2n_array_len(test_server_parameters); param_idx++) { - struct s2n_tls13_cert_verify_test server_parameters = test_server_parameters[param_idx]; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, server_parameters.cert_file, - server_parameters.key_file)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* The server only supports the single test certificate. Due to the fallback logic in - * the s2n-tls server, the signature algorithm corresponding with the test certificate - * will always be used to sign the CertificateVerify message, regardless of the - * client's advertised signature schemes. - */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); - - for (size_t sig_idx = 0; sig_idx < s2n_array_len(test_client_sig_schemes); sig_idx++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* The client only supports the single test signature scheme, which allows for the - * server to sign the CertificateVerify message with a signature algorithm that - * isn't supported by the client. - */ - const struct s2n_signature_scheme *client_advertised_sig_scheme = test_client_sig_schemes[sig_idx]; - struct s2n_signature_preferences test_sig_preferences = { - .count = 1, - .signature_schemes = &client_advertised_sig_scheme, - }; - struct s2n_security_policy client_policy = security_policy_test_all_tls13; - client_policy.signature_preferences = &test_sig_preferences; - client_conn->security_policy_override = &client_policy; - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Send the CertificateVerify message. */ - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, - SERVER_CERT_VERIFY)); - EXPECT_SUCCESS(s2n_stuffer_rewrite(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(server_conn)); - - /* Check that the expected signature algorithm was used by the server. */ - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme, server_parameters.sig_scheme); - - /* Overwrite the SignatureScheme field of the CertificateVerify message to lie to the - * client about which signature algorithm was used to sign the signature content. This - * will trick the client into always thinking its advertised signature algorithm was - * used. - */ - struct s2n_stuffer cert_verify_stuffer = server_conn->handshake.io; - EXPECT_SUCCESS(s2n_stuffer_rewrite(&cert_verify_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&cert_verify_stuffer, - client_advertised_sig_scheme->iana_value)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - - int ret = s2n_tls13_cert_verify_recv(client_conn); - - if (client_advertised_sig_scheme == server_parameters.sig_scheme) { - /* If the client's advertised signature scheme matches what the server actually - * used to sign the CertificateVerify message, validation should succeed. - */ - EXPECT_SUCCESS(ret); - } else { - /* Otherwise, the client should observe that the indicated signature algorithm - * from the server doesn't match the certificate type, and the connection - * should fail. - */ - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - } - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_certificate_verify.c" + +uint8_t hello[] = "Hello, World!\n"; +uint8_t goodbye[] = "Goodbye, World!\n"; + +struct s2n_tls13_cert_verify_test { + const char *const cert_file; + const char *const key_file; + const struct s2n_signature_scheme *sig_scheme; + const struct s2n_signature_scheme *with_wrong_hash; +}; + +const struct s2n_tls13_cert_verify_test test_cases[] = { + { + .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, + .key_file = S2N_ECDSA_P256_PKCS1_KEY, + .sig_scheme = &s2n_ecdsa_sha256, + .with_wrong_hash = &s2n_ecdsa_sha384, + }, +#if RSA_PSS_CERTS_SUPPORTED + { + .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .sig_scheme = &s2n_rsa_pss_pss_sha256, + .with_wrong_hash = &s2n_rsa_pss_pss_sha384, + }, +#endif +}; + +S2N_RESULT s2n_cert_verify_connection_setup_and_send( + struct s2n_connection *sending_conn, struct s2n_connection *verifying_conn, + struct s2n_config *config, struct s2n_cert_chain_and_key *cert_chain, + struct s2n_signature_scheme *sig_scheme, struct s2n_blob *cert) +{ + sending_conn->handshake_params.our_chain_and_key = cert_chain; + sending_conn->handshake_params.server_cert_sig_scheme = sig_scheme; + sending_conn->handshake_params.client_cert_sig_scheme = sig_scheme; + sending_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + sending_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(sending_conn, config)); + + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + verifying_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); + + /* Extract public key from certificate and set it for verifying connection */ + s2n_pkey_type pkey_type = { 0 }; + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, cert)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.server_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); + } else { + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, cert)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.client_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); + } + + /* Hash initialization */ + EXPECT_SUCCESS(s2n_hash_init(&sending_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sending_conn->handshake.hashes->sha256, hello, sizeof(hello))); + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, sizeof(hello))); + + /* Send cert verify */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, + s2n_stuffer_data_available(&sending_conn->handshake.io))); + + return S2N_RESULT_OK; +} + +int run_tests(const struct s2n_tls13_cert_verify_test *test_case, s2n_mode verifier_mode) +{ + const char *cert_file = test_case->cert_file; + const char *key_file = test_case->key_file; + struct s2n_signature_scheme sig_scheme = *test_case->sig_scheme; + + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(cert_file, &cert_chain_pem[0], S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(key_file, &private_key_pem[0], S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(cert_chain); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + + /* Initialize a certificate */ + DEFER_CLEANUP(struct s2n_stuffer certificate_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer certificate_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&certificate_in, (uint8_t *) cert_chain_pem, sizeof(cert_chain_pem))); + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + + uint32_t available_size = s2n_stuffer_data_available(&certificate_out); + struct s2n_blob cert = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&cert, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + + /* Successfully send and receive certificate verify */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Receive and verify cert */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Repeat the above test successfully */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, + s2n_stuffer_data_available(&sending_conn->handshake.io))); + EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Test fails if cipher suites hash is configured incorrectly */ + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, + s2n_stuffer_data_available(&sending_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Verifying connection errors with incorrect signed content */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Update receive hash with goodbye */ + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, goodbye, sizeof(goodbye))); + + uint64_t verifying_bytes = 0; + uint64_t sending_bytes = 0; + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&verifying_conn->handshake.hashes->sha256, &verifying_bytes)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&sending_conn->handshake.hashes->sha256, &sending_bytes)); + EXPECT_NOT_EQUAL(sending_bytes, verifying_bytes); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Verifying connection errors with even 1 bit incorrect */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Flip one bit in verifying_conn io buffer */ + EXPECT_TRUE(10 < s2n_stuffer_data_available(&verifying_conn->handshake.io)); + verifying_conn->handshake.io.blob.data[10] ^= 1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Verifying connection errors with wrong hash algorithms */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Use a hash algorithm different from sender by prepending corresponding iana value */ + struct s2n_stuffer rereader = verifying_conn->handshake.io; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&rereader)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&rereader, test_case->with_wrong_hash->iana_value)); + + /* We have special checks for ECDSA curves and therefore it errors earlier than signature verification */ + if (test_case->sig_scheme->sig_alg == S2N_SIGNATURE_ECDSA) { + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + } + }; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + /* Run all tests for server sending and client receiving/verifying cert_verify message */ + run_tests(&test_cases[i], S2N_CLIENT); + + /* Run all tests for client sending and server receiving/verifying cert_verify message */ + run_tests(&test_cases[i], S2N_SERVER); + } + + /* Self-talk: Ensure that the signature algorithm used to sign the CertificateVerify message + * is validated against the certificate type + */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_tls13_cert_verify_test test_server_parameters[] = { + { + .cert_file = S2N_RSA_2048_PKCS1_CERT_CHAIN, + .key_file = S2N_RSA_2048_PKCS1_KEY, + .sig_scheme = &s2n_rsa_pss_rsae_sha256, + }, + { + .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .sig_scheme = &s2n_rsa_pss_pss_sha256, + }, + { + .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, + .key_file = S2N_ECDSA_P256_PKCS1_KEY, + .sig_scheme = &s2n_ecdsa_sha256, + } + }; + + const struct s2n_signature_scheme *test_client_sig_schemes[] = { + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_pss_sha256, + &s2n_ecdsa_sha256, + }; + + for (size_t param_idx = 0; param_idx < s2n_array_len(test_server_parameters); param_idx++) { + struct s2n_tls13_cert_verify_test server_parameters = test_server_parameters[param_idx]; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, server_parameters.cert_file, + server_parameters.key_file)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* The server only supports the single test certificate. Due to the fallback logic in + * the s2n-tls server, the signature algorithm corresponding with the test certificate + * will always be used to sign the CertificateVerify message, regardless of the + * client's advertised signature schemes. + */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + + for (size_t sig_idx = 0; sig_idx < s2n_array_len(test_client_sig_schemes); sig_idx++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* The client only supports the single test signature scheme, which allows for the + * server to sign the CertificateVerify message with a signature algorithm that + * isn't supported by the client. + */ + const struct s2n_signature_scheme *client_advertised_sig_scheme = test_client_sig_schemes[sig_idx]; + struct s2n_signature_preferences test_sig_preferences = { + .count = 1, + .signature_schemes = &client_advertised_sig_scheme, + }; + struct s2n_security_policy client_policy = security_policy_test_all_tls13; + client_policy.signature_preferences = &test_sig_preferences; + client_conn->security_policy_override = &client_policy; + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Send the CertificateVerify message. */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_CERT_VERIFY)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(server_conn)); + + /* Check that the expected signature algorithm was used by the server. */ + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme, server_parameters.sig_scheme); + + /* Overwrite the SignatureScheme field of the CertificateVerify message to lie to the + * client about which signature algorithm was used to sign the signature content. This + * will trick the client into always thinking its advertised signature algorithm was + * used. + */ + struct s2n_stuffer cert_verify_stuffer = server_conn->handshake.io; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cert_verify_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&cert_verify_stuffer, + client_advertised_sig_scheme->iana_value)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + int ret = s2n_tls13_cert_verify_recv(client_conn); + + if (client_advertised_sig_scheme == server_parameters.sig_scheme) { + /* If the client's advertised signature scheme matches what the server actually + * used to sign the CertificateVerify message, validation should succeed. + */ + EXPECT_SUCCESS(ret); + } else { + /* Otherwise, the client should observe that the indicated signature algorithm + * from the server doesn't match the certificate type, and the connection + * should fail. + */ + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + } + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_early_data_test.c b/tests/unit/s2n_tls13_handshake_early_data_test.c index d0f351a3447..8eaab1c5875 100644 --- a/tests/unit/s2n_tls13_handshake_early_data_test.c +++ b/tests/unit/s2n_tls13_handshake_early_data_test.c @@ -1,356 +1,357 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_array.h" -#include "utils/s2n_mem.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_handshake_transcript.c" -#include "tls/s2n_tls13_handshake.c" - -#define S2N_SECRET_TYPE_COUNT 5 - -const uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; -message_type_t empty_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; - -int main() -{ - BEGIN_TEST(); - - /* Test early data encryption */ - { - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-3 - *= type=test - *# {server} generate resumption secret "tls13 resumption": - *# - *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf - *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c - *# - *# hash (2 octets): 00 00 - *# - *# info (22 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 75 6d 70 74 - *# 69 6f 6e 02 00 00 - *# - *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c - *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 - */ - S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ - a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-3 - *= type=test - *# {server} construct a NewSessionTicket handshake message: - *# - *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa - *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 - *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c - *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 - *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 - *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 - *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c - *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 - *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 - *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 - *# 04 00 00 04 00 - */ - /* Skip past the message type, message size, ticket lifetime, - * ticket age add, nonce, and ticket size: - * 04 00 00 c9 00 00 00 1e fa d6 aa - * c5 02 00 00 00 b2 - */ - S2N_BLOB_FROM_HEX(psk_identity, - "2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ - 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ - 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ - 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ - 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ - a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ - 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ - 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ - 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57"); - /* Skip past the total extensions size, early data extension type, - * and early data extension size: 00 08 00 2a 00 - * 04 - */ - const uint32_t max_early_data = 0x00000400; - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} send handshake record: - *# - *# payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff - *# 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 - *# 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 - *# 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 - *# 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 - *# 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 - *# 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 - *# 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 - *# 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 - *# 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 - *# 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 - *# ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 - *# 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 - *# 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 - *# 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 - *# 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 - *# ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d - *# e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa - *# cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d - *# ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d - */ - S2N_BLOB_FROM_HEX(client_hello_msg, - "01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff \ - 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 \ - 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 \ - 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 \ - 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 \ - 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 \ - 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 \ - 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 \ - 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 \ - 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 \ - 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 \ - ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 \ - 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 \ - 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 \ - 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 \ - 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 \ - ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d \ - e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa \ - cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d \ - ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d") - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# - *# complete record (517 octets): 16 03 01 02 00 01 00 01 fc 03 03 1b - *# c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 - *# d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 - *# 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 - *# 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 - *# 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d - *# 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 - *# 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e - *# 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 - *# 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 - *# 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 - *# ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb - *# 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc - *# 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 - *# 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 - *# 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 - *# 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 - *# 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 - *# 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 - *# 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca - *# 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f - *# 9d - */ - S2N_BLOB_FROM_HEX(ch_record, "16 03 01 02 00 01 00 01 fc 03 03 1b \ - c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 \ - d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 \ - 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 \ - 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 \ - 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d \ - 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 \ - 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e \ - 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 \ - 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 \ - 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 \ - ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb \ - 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc \ - 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 \ - 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 \ - 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 \ - 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 \ - 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 \ - 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 \ - 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca \ - 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f \ - 9d"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} extract secret "early": - *# - *# salt: 0 (all zero octets) - *# - *# IKM (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 - *# 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 - *# - *# secret (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 - *# bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c - */ - S2N_BLOB_FROM_HEX(early_secret, - "9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 \ - bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} derive write traffic keys for early application data: - *# - *# PRK (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e - *# aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 - *# - *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 - *# - *# key expanded (16 octets): 92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 - *# 83 4f 54 - *# - *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 - *# - *# iv expanded (12 octets): 6d 47 5f 09 93 c8 e5 64 61 0d b2 b9 - */ - S2N_BLOB_FROM_HEX(iv, "6d 47 5f 09 93 c8 e5 64 61 0d b2 b9"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} send application_data record: - *# - *# payload (6 octets): 41 42 43 44 45 46 - */ - S2N_BLOB_FROM_HEX(payload, "41 42 43 44 45 46"); - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# - *# complete record (28 octets): 17 03 03 00 17 ab 1d f4 20 e7 5c 45 - *# 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0 - */ - S2N_BLOB_FROM_HEX(complete_record, "17 03 03 00 17 ab 1d f4 20 e7 5c 45 \ - 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0"); - - /* Test client early data encryption against known client outputs */ - { - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->early_data_state = S2N_EARLY_DATA_REQUESTED; - - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); - psk->hmac_alg = S2N_HMAC_SHA256; - EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); - client_conn->secure->cipher_suite = psk->early_data_config.cipher_suite; - - /* Rewrite early secret with known early secret. */ - EXPECT_SUCCESS(s2n_dup(&early_secret, &psk->early_secret)); - - /* Rewrite hashes with known ClientHello */ - EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(client_conn, &client_hello_msg)); - - EXPECT_OK(s2n_tls13_secrets_update(client_conn)); - EXPECT_OK(s2n_tls13_key_schedule_update(client_conn)); - - /* Check early secret secret set correctly */ - EXPECT_EQUAL(client_conn->secrets.extract_secret_type, S2N_EARLY_SECRET); - EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.extract_secret, early_secret.data, early_secret.size); - - /* Check IV calculated correctly */ - EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_implicit_iv, iv.data, iv.size); - - /* Check payload encrypted correctly */ - EXPECT_OK(s2n_record_write(client_conn, TLS_APPLICATION_DATA, &payload)); - EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->out), complete_record.size); - EXPECT_BYTEARRAY_EQUAL(client_conn->out.blob.data, complete_record.data, complete_record.size); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - -/* The known ClientHello uses the x25519 curve, - * which the S2N server won't accept if the EVP APIs are not supported */ -#if EVP_APIS_SUPPORTED - /* Test server early data encryption with known client inputs */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); - EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); - - DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); - psk->type = S2N_PSK_TYPE_RESUMPTION; - EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity.data, psk_identity.size)); - EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); - EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); - EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, psk)); - /* We need to explicitly set the psk_params type to skip our stateless session resumption recv - * code because the handshake traces we're using are meant for stateful session resumption. - * TODO: https://github.com/aws/s2n-tls/issues/2742 */ - server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - - DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); - - s2n_blocked_status blocked = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); - EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); - - EXPECT_SUCCESS(s2n_stuffer_write(&input, &complete_record)); - - DEFER_CLEANUP(struct s2n_blob actual_payload = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&actual_payload, payload.size)); - int r = s2n_recv(server_conn, actual_payload.data, actual_payload.size, &blocked); - EXPECT_EQUAL(r, payload.size); - EXPECT_BYTEARRAY_EQUAL(actual_payload.data, payload.data, payload.size); - EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; -#endif - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_handshake_transcript.c" +#include "tls/s2n_tls13_handshake.c" + +#define S2N_SECRET_TYPE_COUNT 5 + +const uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; +message_type_t empty_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +int main() +{ + BEGIN_TEST(); + + /* Test early data encryption */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-3 + *= type=test + *# {server} generate resumption secret "tls13 resumption": + *# + *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf + *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + *# + *# hash (2 octets): 00 00 + *# + *# info (22 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 75 6d 70 74 + *# 69 6f 6e 02 00 00 + *# + *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c + *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + */ + S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ + a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-3 + *= type=test + *# {server} construct a NewSessionTicket handshake message: + *# + *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa + *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 + *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c + *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 + *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 + *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 + *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c + *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 + *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 + *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 + *# 04 00 00 04 00 + */ + /* Skip past the message type, message size, ticket lifetime, + * ticket age add, nonce, and ticket size: + * 04 00 00 c9 00 00 00 1e fa d6 aa + * c5 02 00 00 00 b2 + */ + S2N_BLOB_FROM_HEX(psk_identity, + "2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ + 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ + 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ + 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ + 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ + a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ + 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ + 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ + 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57"); + /* Skip past the total extensions size, early data extension type, + * and early data extension size: 00 08 00 2a 00 + * 04 + */ + const uint32_t max_early_data = 0x00000400; + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send handshake record: + *# + *# payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff + *# 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 + *# 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 + *# 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 + *# 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 + *# 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 + *# 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 + *# 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 + *# 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 + *# 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 + *# 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 + *# ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 + *# 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 + *# 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 + *# 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 + *# 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 + *# ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d + *# e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa + *# cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d + *# ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d + */ + S2N_BLOB_FROM_HEX(client_hello_msg, + "01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff \ + 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 \ + 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 \ + 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 \ + 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 \ + 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 \ + 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 \ + 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 \ + 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 \ + 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 \ + 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 \ + ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 \ + 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 \ + 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 \ + 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 \ + 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 \ + ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d \ + e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa \ + cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d \ + ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d") + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (517 octets): 16 03 01 02 00 01 00 01 fc 03 03 1b + *# c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 + *# d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 + *# 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + *# 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + *# 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d + *# 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 + *# 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + *# 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + *# 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + *# 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 + *# ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb + *# 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc + *# 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 + *# 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 + *# 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 + *# 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 + *# 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 + *# 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 + *# 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca + *# 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f + *# 9d + */ + S2N_BLOB_FROM_HEX(ch_record, "16 03 01 02 00 01 00 01 fc 03 03 1b \ + c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 \ + d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 \ + 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 \ + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 \ + 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d \ + 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 \ + 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e \ + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 \ + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 \ + 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 \ + ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb \ + 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc \ + 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 \ + 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 \ + 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 \ + 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 \ + 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 \ + 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 \ + 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca \ + 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f \ + 9d"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} extract secret "early": + *# + *# salt: 0 (all zero octets) + *# + *# IKM (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 + *# 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + *# + *# secret (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 + *# bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c + */ + S2N_BLOB_FROM_HEX(early_secret, + "9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 \ + bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} derive write traffic keys for early application data: + *# + *# PRK (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e + *# aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 + *# 83 4f 54 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 6d 47 5f 09 93 c8 e5 64 61 0d b2 b9 + */ + S2N_BLOB_FROM_HEX(iv, "6d 47 5f 09 93 c8 e5 64 61 0d b2 b9"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send application_data record: + *# + *# payload (6 octets): 41 42 43 44 45 46 + */ + S2N_BLOB_FROM_HEX(payload, "41 42 43 44 45 46"); + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (28 octets): 17 03 03 00 17 ab 1d f4 20 e7 5c 45 + *# 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0 + */ + S2N_BLOB_FROM_HEX(complete_record, "17 03 03 00 17 ab 1d f4 20 e7 5c 45 \ + 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0"); + + /* Test client early data encryption against known client outputs */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); + client_conn->secure->cipher_suite = psk->early_data_config.cipher_suite; + + /* Rewrite early secret with known early secret. */ + EXPECT_SUCCESS(s2n_dup(&early_secret, &psk->early_secret)); + + /* Rewrite hashes with known ClientHello */ + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(client_conn, &client_hello_msg)); + + EXPECT_OK(s2n_tls13_secrets_update(client_conn)); + EXPECT_OK(s2n_tls13_key_schedule_update(client_conn)); + + /* Check early secret secret set correctly */ + EXPECT_EQUAL(client_conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.extract_secret, early_secret.data, early_secret.size); + + /* Check IV calculated correctly */ + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_implicit_iv, iv.data, iv.size); + + /* Check payload encrypted correctly */ + EXPECT_OK(s2n_record_write(client_conn, TLS_APPLICATION_DATA, &payload)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->out), complete_record.size); + EXPECT_BYTEARRAY_EQUAL(client_conn->out.blob.data, complete_record.data, complete_record.size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + +/* The known ClientHello uses the x25519 curve, + * which the S2N server won't accept if the EVP APIs are not supported */ +#if EVP_APIS_SUPPORTED + /* Test server early data encryption with known client inputs */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + psk->type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity.data, psk_identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, psk)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &complete_record)); + + DEFER_CLEANUP(struct s2n_blob actual_payload = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&actual_payload, payload.size)); + int r = s2n_recv(server_conn, actual_payload.data, actual_payload.size, &blocked); + EXPECT_EQUAL(r, payload.size); + EXPECT_BYTEARRAY_EQUAL(actual_payload.data, payload.data, payload.size); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; +#endif + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_state_machine_test.c b/tests/unit/s2n_tls13_handshake_state_machine_test.c index 35dd539fe3b..6bfeb936ed3 100644 --- a/tests/unit/s2n_tls13_handshake_state_machine_test.c +++ b/tests/unit/s2n_tls13_handshake_state_machine_test.c @@ -1,1007 +1,1008 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_tls13.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" - -static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; - -static int expected_handler_called; -static int unexpected_handler_called; - -static int s2n_test_handler(struct s2n_connection *conn) -{ - unexpected_handler_called = 1; - return 0; -} - -static int s2n_test_expected_handler(struct s2n_connection *conn) -{ - expected_handler_called = 1; - return 0; -} - -static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) -{ - for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { - tls13_state_machine[i].handler[0] = s2n_test_handler; - tls13_state_machine[i].handler[1] = s2n_test_handler; - } - - tls13_state_machine[expected].handler[direction] = s2n_test_expected_handler; - - expected_handler_called = 0; - unexpected_handler_called = 0; - - return 0; -} - -static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) -{ - POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); - - /* TLS1.2 protocol version */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - - if (record_type == TLS_HANDSHAKE) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); - - POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); - - /* Handshake message data size */ - POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); - return 0; - } - - if (record_type == TLS_CHANGE_CIPHER_SPEC) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); - - /* change spec is always just 0x01 */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); - return 0; - } - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Construct an array of all valid tls1.3 handshake_types */ - uint16_t valid_tls13_handshakes[S2N_HANDSHAKES_COUNT]; - int valid_tls13_handshakes_size = 0; - for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { - if (memcmp(tls13_handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { - valid_tls13_handshakes[valid_tls13_handshakes_size] = i; - valid_tls13_handshakes_size++; - } - } - EXPECT_TRUE(valid_tls13_handshakes_size > 0); - EXPECT_TRUE(valid_tls13_handshakes_size < S2N_HANDSHAKES_COUNT); - - /* Use handler stubs to avoid errors in handler implementation */ - for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { - tls13_state_machine[i].handler[0] = s2n_test_handler; - tls13_state_machine[i].handler[1] = s2n_test_handler; - } - - /* Test: A WITH_EARLY_DATA form of every non-full, non-retry handshake exists - * and matches the non-WITH_EARLY_DATA form EXCEPT for the END_OF_EARLY_DATA - * message and client CCS messages. - */ - { - uint32_t original_handshake_type = 0, early_data_handshake_type = 0; - message_type_t *original_messages = NULL, *early_data_messages = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - original_handshake_type = valid_tls13_handshakes[i]; - original_messages = tls13_handshakes[original_handshake_type]; - - /* WITH_EARLY_DATA form of the handshake */ - early_data_handshake_type = original_handshake_type | WITH_EARLY_DATA; - early_data_messages = tls13_handshakes[early_data_handshake_type]; - - /* No handshake exists for INITIAL, FULL_HANDSHAKE, or HELLO_RETRY_REQUEST handshakes */ - if (!(original_handshake_type & NEGOTIATED) || (original_handshake_type & FULL_HANDSHAKE) - || (original_handshake_type & HELLO_RETRY_REQUEST)) { - EXPECT_BYTEARRAY_EQUAL(early_data_messages, invalid_handshake, sizeof(invalid_handshake)); - continue; - } - - /* Ignore identical handshakes */ - if (original_handshake_type == early_data_handshake_type) { - continue; - } - - size_t end_of_early_data_messages = 0; - size_t j = 0, j_ed = 0; - while (j < S2N_MAX_HANDSHAKE_LENGTH && j_ed < S2N_MAX_HANDSHAKE_LENGTH) { - /* The original handshake must NOT contain END_OF_EARLY_DATA messages */ - EXPECT_NOT_EQUAL(original_messages[j], END_OF_EARLY_DATA); - - /* Count END_OF_EARLY_DATA messages in the WITH_EARLY_DATA handshake */ - if (early_data_messages[j_ed] == END_OF_EARLY_DATA) { - end_of_early_data_messages++; - j_ed++; - continue; - } - - /* Skip client CCS message in both handshakes - they won't appear at the same point */ - if (early_data_messages[j_ed] == CLIENT_CHANGE_CIPHER_SPEC) { - j_ed++; - continue; - } - if (original_messages[j] == CLIENT_CHANGE_CIPHER_SPEC) { - j++; - continue; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(original_messages[j], early_data_messages[j_ed]); - j++; - j_ed++; - } - if (original_handshake_type & NEGOTIATED) { - EXPECT_EQUAL(end_of_early_data_messages, 1); - } else { - EXPECT_EQUAL(end_of_early_data_messages, 0); - } - } - }; - - /* Test: A MIDDLEBOX_COMPAT form of every valid, negotiated handshake exists - * and matches the non-MIDDLEBOX_COMPAT form EXCEPT for CCS messages */ - { - uint32_t handshake_type_original = 0, handshake_type_mc = 0; - message_type_t *messages_original = NULL, *messages_mc = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - handshake_type_original = valid_tls13_handshakes[i]; - messages_original = tls13_handshakes[handshake_type_original]; - - /* Ignore INITIAL and MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & MIDDLEBOX_COMPAT)) { - continue; - } - - /* MIDDLEBOX_COMPAT form of the handshake */ - handshake_type_mc = handshake_type_original | MIDDLEBOX_COMPAT; - messages_mc = tls13_handshakes[handshake_type_mc]; - - for (size_t j = 0, j_mc = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_mc < S2N_MAX_HANDSHAKE_LENGTH; j++, j_mc++) { - /* The original handshake cannot contain CCS messages */ - EXPECT_NOT_EQUAL(messages_original[j], SERVER_CHANGE_CIPHER_SPEC); - EXPECT_NOT_EQUAL(messages_original[j], CLIENT_CHANGE_CIPHER_SPEC); - - /* Skip CCS messages in the MIDDLEBOX_COMPAT handshake */ - while (messages_mc[j_mc] == SERVER_CHANGE_CIPHER_SPEC - || messages_mc[j_mc] == CLIENT_CHANGE_CIPHER_SPEC) { - j_mc++; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(messages_original[j], messages_mc[j_mc]); - } - } - }; - - /* Test: A non-FULL_HANDSHAKE form of every valid, negotiated handshake exists */ - { - uint32_t handshake_type_original = 0, handshake_type_fh = 0; - message_type_t *messages_original = NULL, *messages_fh = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - handshake_type_original = valid_tls13_handshakes[i]; - messages_original = tls13_handshakes[handshake_type_original]; - - /* Ignore INITIAL and FULL_HANDSHAKE handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & FULL_HANDSHAKE)) { - continue; - } - - /* FULL_HANDSHAKE form of the handshake */ - handshake_type_fh = handshake_type_original | FULL_HANDSHAKE; - messages_fh = tls13_handshakes[handshake_type_fh]; - - /* No FULL handshake exists for INITIAL or WITH_EARLY_DATA handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_EARLY_DATA)) { - EXPECT_BYTEARRAY_EQUAL(messages_fh, invalid_handshake, sizeof(invalid_handshake)); - continue; - } - - /* Ignore identical handshakes */ - if (handshake_type_original == handshake_type_fh) { - continue; - } - - for (size_t j = 0, j_fh = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_fh < S2N_MAX_HANDSHAKE_LENGTH; j++, j_fh++) { - /* The original handshake cannot contain authentication messages */ - EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT); - EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT_VERIFY); - - /* Skip authentication messages in the FULL_HANDSHAKE handshake */ - while (messages_fh[j_fh] == SERVER_CERT || messages_fh[j_fh] == SERVER_CERT_VERIFY) { - j_fh++; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(messages_original[j], messages_fh[j_fh]); - } - } - }; - - /* Test: A EARLY_CLIENT_CCS form of every middlebox compatible handshake exists. - * Any handshake could start with early data, even if that early data is later rejected. */ - { - uint32_t handshake_type_original = 0, handshake_type_test = 0; - message_type_t *messages_original = NULL, *messages_test = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - handshake_type_original = valid_tls13_handshakes[i]; - messages_original = tls13_handshakes[handshake_type_original]; - - /* Ignore non-MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type_original & MIDDLEBOX_COMPAT)) { - continue; - } - - /* EARLY_CLIENT_CCS form of the handshake */ - handshake_type_test = handshake_type_original | EARLY_CLIENT_CCS; - messages_test = tls13_handshakes[handshake_type_test]; - - /* Ignore identical handshakes */ - if (handshake_type_original == handshake_type_test) { - continue; - } - - for (size_t j = 0, j_test = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_test < S2N_MAX_HANDSHAKE_LENGTH; j++, j_test++) { - /* Skip Client CCS messages */ - while (messages_original[j] == CLIENT_CHANGE_CIPHER_SPEC) { - j++; - } - while (messages_test[j_test] == CLIENT_CHANGE_CIPHER_SPEC) { - j_test++; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(messages_original[j], messages_test[j_test]); - } - } - }; - - /* Test: When using TLS 1.3, use the new state machine and handshakes */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &tls13_state_machine[0]); - EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &tls13_handshakes[0]); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 server does not wait for client cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 server does not skip server cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 client does not wait for server cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 client does not skip client cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 client can receive a server cipher change spec at any time. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - conn->in_status = ENCRYPTED; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - conn->handshake.message_number = j; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - } else { - EXPECT_EQUAL(conn->handshake.message_number, j); - } - - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 server can receive a client cipher change request at any time. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - conn->in_status = ENCRYPTED; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - conn->handshake.message_number = j; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - } else { - EXPECT_EQUAL(conn->handshake.message_number, j); - } - - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 s2n_handshake_read_io should accept only the expected message */ - { - /* TLS1.3 should accept the expected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, 1); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 should error for an unexpected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 should error for an expected message from the wrong writer */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 should error for an expected message from the wrong record type */ - { - /* Unfortunately, all our non-handshake record types have a message type of 0, - * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) - * which can appear at any point in a TLS1.2 handshake. - * - * To test, temporarily modify the actions table. - * We MUST restore this after this test. - */ - uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - uint8_t server_css_message_number = 2; - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; - conn->handshake.message_number = server_css_message_number; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; - }; - - /* Error if a client receives a client cert request in non-FULL_HANDSHAKE mode */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - POSIX_GUARD(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERT_REQ)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_HANDSHAKE_STATE); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test: TLS 1.3 MIDDLEBOX_COMPAT handshakes all follow CCS middlebox compatibility rules. - * - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# Field measurements [Ben17a] [Ben17b] [Res17a] [Res17b] have found - *# that a significant number of middleboxes misbehave when a TLS - *# client/server pair negotiates TLS 1.3. Implementations can increase - *# the chance of making connections through those middleboxes by making - *# the TLS 1.3 handshake look more like a TLS 1.2 handshake: - */ - { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# If not offering early data, the client sends a dummy - *# change_cipher_spec record (see the third paragraph of Section 5) - *# immediately before its second flight. This may either be before - *# its second ClientHello or before its encrypted handshake flight. - **/ - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - bool change_cipher_spec_found = false; - uint32_t handshake_type = valid_tls13_handshakes[i]; - message_type_t *messages = tls13_handshakes[handshake_type]; - - /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type & NEGOTIATED) - || !(handshake_type & MIDDLEBOX_COMPAT) - || (handshake_type & EARLY_CLIENT_CCS)) { - continue; - } - - for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - /* Is it the second client flight? - * Have we switched from the server sending to the client sending? */ - if (tls13_state_machine[messages[j]].writer != 'C' - || tls13_state_machine[messages[j - 1]].writer != 'S') { - continue; - } - - EXPECT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); - EXPECT_EQUAL(tls13_state_machine[messages[j + 1]].writer, 'C'); - - /* CCS message found. We are done with this handshake. */ - change_cipher_spec_found = true; - break; - } - - EXPECT_TRUE(change_cipher_spec_found); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# If offering early data, the record is placed immediately after the - *# first ClientHello. - */ - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - uint32_t handshake_type = valid_tls13_handshakes[i]; - message_type_t *messages = tls13_handshakes[handshake_type]; - - /* Ignore handshakes where early data did not trigger the change in CCS behavior */ - if (!(handshake_type & EARLY_CLIENT_CCS)) { - continue; - } - - EXPECT_EQUAL(messages[0], CLIENT_HELLO); - EXPECT_EQUAL(messages[1], CLIENT_CHANGE_CIPHER_SPEC); - for (size_t j = 2; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - EXPECT_NOT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); - } - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# The server sends a dummy change_cipher_spec record immediately - *# after its first handshake message. This may either be after a - *# ServerHello or a HelloRetryRequest. - **/ - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - bool change_cipher_spec_found = false; - uint32_t handshake_type = valid_tls13_handshakes[i]; - message_type_t *messages = tls13_handshakes[handshake_type]; - - /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type & NEGOTIATED) || !(handshake_type & MIDDLEBOX_COMPAT)) { - continue; - } - - for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - /* Is it the first server flight? - * Have we switched from the client sending to the server sending? */ - if (tls13_state_machine[messages[j]].writer != 'S' - || tls13_state_machine[messages[j - 1]].writer != 'C') { - continue; - } - - EXPECT_EQUAL(messages[j + 1], SERVER_CHANGE_CIPHER_SPEC); - EXPECT_TRUE(messages[j] == SERVER_HELLO || messages[j] == HELLO_RETRY_MSG); - - /* CCS message found. We are done with this handshake. */ - change_cipher_spec_found = true; - break; - } - - EXPECT_TRUE(change_cipher_spec_found); - } - }; - - /* Test: TLS1.3 s2n_conn_set_handshake_type sets only handshake flags allowed by TLS1.3 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - - /* Ensure WITH_SESSION_TICKETS is set */ - conn->config->use_tickets = 1; - conn->session_ticket_status = S2N_NEW_TICKET; - - /* Ensure CLIENT_AUTH is set */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_REQUIRED)); - - /* Ensure TLS12_PERFECT_FORWARD_SECRECY is set by choosing a cipher suite with is_ephemeral=1 on the kex */ - conn->secure->cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256; - - /* Ensure OCSP_STATUS is set by setting the connection status_type */ - conn->status_type = S2N_STATUS_REQUEST_OCSP; - - /* Verify that tls1.2 DOES set the flags allowed by tls1.2 */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & TLS12_PERFECT_FORWARD_SECRECY); - EXPECT_TRUE(conn->handshake.handshake_type & OCSP_STATUS); - EXPECT_TRUE(conn->handshake.handshake_type & WITH_SESSION_TICKET); - EXPECT_TRUE(conn->handshake.handshake_type & CLIENT_AUTH); - - /* Reset which state machine we're on */ - EXPECT_OK(s2n_handshake_type_reset(conn)); - conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; - - /* Verify that tls1.3 ONLY sets the flags allowed by tls1.3 */ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: s2n_conn_set_handshake_type only allows HELLO_RETRY_REQUEST with TLS1.3 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - - /* HELLO_RETRY_REQUEST allowed with tls1.3 */ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - /* Reset state machine */ - conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; - - /* HELLO_RETRY_REQUEST not allowed with tls1.2 */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS12)); - conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type does not set FULL_HANDSHAKE if - * a pre-shared key has been chosen. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - struct s2n_psk *psk = NULL; - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); - - conn->psk_params.chosen_psk = psk; - EXPECT_NOT_NULL(conn->psk_params.chosen_psk); - EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); - EXPECT_FALSE(conn->handshake.handshake_type & FULL_HANDSHAKE); - - conn->psk_params.chosen_psk = NULL; - EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & FULL_HANDSHAKE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is - * chosen and s2n is a client. */ - { - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); - client_conn->psk_params.chosen_psk = psk; - EXPECT_NOT_NULL(client_conn->psk_params.chosen_psk); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - - EXPECT_OK(s2n_conn_set_tls13_handshake_type(client_conn)); - EXPECT_FALSE(client_conn->handshake.handshake_type & CLIENT_AUTH); - EXPECT_FALSE(client_conn->handshake.handshake_type & FULL_HANDSHAKE); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is - * chosen and s2n is a server. */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - struct s2n_psk *psk = NULL; - - EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &psk)); - server_conn->psk_params.chosen_psk = psk; - EXPECT_NOT_NULL(server_conn->psk_params.chosen_psk); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - - EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); - EXPECT_FALSE(server_conn->handshake.handshake_type & CLIENT_AUTH); - EXPECT_FALSE(server_conn->handshake.handshake_type & FULL_HANDSHAKE); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type sets WITH_EARLY_DATA */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - server_conn->actual_protocol_version = S2N_TLS13; - - server_conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; - EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); - EXPECT_TRUE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); - EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type does not set WITH_EARLY_DATA if wrong state */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - server_conn->actual_protocol_version = S2N_TLS13; - - server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; - EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); - EXPECT_FALSE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); - EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test: TLS1.3 handshake type name maximum size is set correctly. - * The maximum size is the size of a name with all flags set. */ - { - size_t correct_size = 0; - for (size_t i = 0; i < s2n_array_len(tls13_handshake_type_names); i++) { - correct_size += strlen(tls13_handshake_type_names[i]); - } - if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { - fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); - FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.3 handshakes"); - } - }; - - /* Test: TLS 1.3 handshake types are all properly printed */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - - conn->handshake.handshake_type = INITIAL; - EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); - - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); - - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; - EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST", s2n_connection_get_handshake_type_name(conn)); - - const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT" - "|MIDDLEBOX_COMPAT|WITH_EARLY_DATA|EARLY_CLIENT_CCS"; - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT - | MIDDLEBOX_COMPAT | WITH_EARLY_DATA | EARLY_CLIENT_CCS; - EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - - const char *handshake_type_name = NULL; - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - conn->handshake.handshake_type = valid_tls13_handshakes[i]; - - handshake_type_name = s2n_connection_get_handshake_type_name(conn); - - /* The handshake type names must be unique */ - for (int j = 0; j < valid_tls13_handshakes_size; j++) { - conn->handshake.handshake_type = valid_tls13_handshakes[j]; - if (i == j) { - EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } else { - EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS 1.3 message types are all properly printed */ - { - uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; - const char *expected[] = { "CLIENT_HELLO", "SERVER_HELLO", "SERVER_CHANGE_CIPHER_SPEC", - "ENCRYPTED_EXTENSIONS", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", - "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_FINISHED", "APPLICATION_DATA" }; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - conn->handshake.handshake_type = test_handshake_type; - - for (int i = 0; i < s2n_array_len(expected); i++) { - conn->handshake.message_number = i; - EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS 1.3 message types are all properly printed for client auth */ - { - uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH; - const char *expected[] = { "CLIENT_HELLO", - "SERVER_HELLO", "ENCRYPTED_EXTENSIONS", "SERVER_CERT_REQ", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", - "CLIENT_CERT", "CLIENT_CERT_VERIFY", "CLIENT_FINISHED", - "APPLICATION_DATA" }; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - conn->handshake.handshake_type = test_handshake_type; - - for (int i = 0; i < s2n_array_len(expected); i++) { - conn->handshake.message_number = i; - EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: Make sure not to miss out populating any message names */ - { - for (int i = CLIENT_HELLO; i <= APPLICATION_DATA; i++) { - EXPECT_NOT_NULL(message_names[i]); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls13.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" + +static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +static int expected_handler_called; +static int unexpected_handler_called; + +static int s2n_test_handler(struct s2n_connection *conn) +{ + unexpected_handler_called = 1; + return 0; +} + +static int s2n_test_expected_handler(struct s2n_connection *conn) +{ + expected_handler_called = 1; + return 0; +} + +static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) +{ + for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[0] = s2n_test_handler; + tls13_state_machine[i].handler[1] = s2n_test_handler; + } + + tls13_state_machine[expected].handler[direction] = s2n_test_expected_handler; + + expected_handler_called = 0; + unexpected_handler_called = 0; + + return 0; +} + +static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); + + /* TLS1.2 protocol version */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + + if (record_type == TLS_HANDSHAKE) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); + + POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); + + /* Handshake message data size */ + POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); + return 0; + } + + if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); + + /* change spec is always just 0x01 */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); + return 0; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Construct an array of all valid tls1.3 handshake_types */ + uint16_t valid_tls13_handshakes[S2N_HANDSHAKES_COUNT]; + int valid_tls13_handshakes_size = 0; + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + if (memcmp(tls13_handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { + valid_tls13_handshakes[valid_tls13_handshakes_size] = i; + valid_tls13_handshakes_size++; + } + } + EXPECT_TRUE(valid_tls13_handshakes_size > 0); + EXPECT_TRUE(valid_tls13_handshakes_size < S2N_HANDSHAKES_COUNT); + + /* Use handler stubs to avoid errors in handler implementation */ + for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[0] = s2n_test_handler; + tls13_state_machine[i].handler[1] = s2n_test_handler; + } + + /* Test: A WITH_EARLY_DATA form of every non-full, non-retry handshake exists + * and matches the non-WITH_EARLY_DATA form EXCEPT for the END_OF_EARLY_DATA + * message and client CCS messages. + */ + { + uint32_t original_handshake_type = 0, early_data_handshake_type = 0; + message_type_t *original_messages = NULL, *early_data_messages = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + original_handshake_type = valid_tls13_handshakes[i]; + original_messages = tls13_handshakes[original_handshake_type]; + + /* WITH_EARLY_DATA form of the handshake */ + early_data_handshake_type = original_handshake_type | WITH_EARLY_DATA; + early_data_messages = tls13_handshakes[early_data_handshake_type]; + + /* No handshake exists for INITIAL, FULL_HANDSHAKE, or HELLO_RETRY_REQUEST handshakes */ + if (!(original_handshake_type & NEGOTIATED) || (original_handshake_type & FULL_HANDSHAKE) + || (original_handshake_type & HELLO_RETRY_REQUEST)) { + EXPECT_BYTEARRAY_EQUAL(early_data_messages, invalid_handshake, sizeof(invalid_handshake)); + continue; + } + + /* Ignore identical handshakes */ + if (original_handshake_type == early_data_handshake_type) { + continue; + } + + size_t end_of_early_data_messages = 0; + size_t j = 0, j_ed = 0; + while (j < S2N_MAX_HANDSHAKE_LENGTH && j_ed < S2N_MAX_HANDSHAKE_LENGTH) { + /* The original handshake must NOT contain END_OF_EARLY_DATA messages */ + EXPECT_NOT_EQUAL(original_messages[j], END_OF_EARLY_DATA); + + /* Count END_OF_EARLY_DATA messages in the WITH_EARLY_DATA handshake */ + if (early_data_messages[j_ed] == END_OF_EARLY_DATA) { + end_of_early_data_messages++; + j_ed++; + continue; + } + + /* Skip client CCS message in both handshakes - they won't appear at the same point */ + if (early_data_messages[j_ed] == CLIENT_CHANGE_CIPHER_SPEC) { + j_ed++; + continue; + } + if (original_messages[j] == CLIENT_CHANGE_CIPHER_SPEC) { + j++; + continue; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(original_messages[j], early_data_messages[j_ed]); + j++; + j_ed++; + } + if (original_handshake_type & NEGOTIATED) { + EXPECT_EQUAL(end_of_early_data_messages, 1); + } else { + EXPECT_EQUAL(end_of_early_data_messages, 0); + } + } + }; + + /* Test: A MIDDLEBOX_COMPAT form of every valid, negotiated handshake exists + * and matches the non-MIDDLEBOX_COMPAT form EXCEPT for CCS messages */ + { + uint32_t handshake_type_original = 0, handshake_type_mc = 0; + message_type_t *messages_original = NULL, *messages_mc = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore INITIAL and MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & MIDDLEBOX_COMPAT)) { + continue; + } + + /* MIDDLEBOX_COMPAT form of the handshake */ + handshake_type_mc = handshake_type_original | MIDDLEBOX_COMPAT; + messages_mc = tls13_handshakes[handshake_type_mc]; + + for (size_t j = 0, j_mc = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_mc < S2N_MAX_HANDSHAKE_LENGTH; j++, j_mc++) { + /* The original handshake cannot contain CCS messages */ + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CHANGE_CIPHER_SPEC); + EXPECT_NOT_EQUAL(messages_original[j], CLIENT_CHANGE_CIPHER_SPEC); + + /* Skip CCS messages in the MIDDLEBOX_COMPAT handshake */ + while (messages_mc[j_mc] == SERVER_CHANGE_CIPHER_SPEC + || messages_mc[j_mc] == CLIENT_CHANGE_CIPHER_SPEC) { + j_mc++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_mc[j_mc]); + } + } + }; + + /* Test: A non-FULL_HANDSHAKE form of every valid, negotiated handshake exists */ + { + uint32_t handshake_type_original = 0, handshake_type_fh = 0; + message_type_t *messages_original = NULL, *messages_fh = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore INITIAL and FULL_HANDSHAKE handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & FULL_HANDSHAKE)) { + continue; + } + + /* FULL_HANDSHAKE form of the handshake */ + handshake_type_fh = handshake_type_original | FULL_HANDSHAKE; + messages_fh = tls13_handshakes[handshake_type_fh]; + + /* No FULL handshake exists for INITIAL or WITH_EARLY_DATA handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_EARLY_DATA)) { + EXPECT_BYTEARRAY_EQUAL(messages_fh, invalid_handshake, sizeof(invalid_handshake)); + continue; + } + + /* Ignore identical handshakes */ + if (handshake_type_original == handshake_type_fh) { + continue; + } + + for (size_t j = 0, j_fh = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_fh < S2N_MAX_HANDSHAKE_LENGTH; j++, j_fh++) { + /* The original handshake cannot contain authentication messages */ + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT); + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT_VERIFY); + + /* Skip authentication messages in the FULL_HANDSHAKE handshake */ + while (messages_fh[j_fh] == SERVER_CERT || messages_fh[j_fh] == SERVER_CERT_VERIFY) { + j_fh++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_fh[j_fh]); + } + } + }; + + /* Test: A EARLY_CLIENT_CCS form of every middlebox compatible handshake exists. + * Any handshake could start with early data, even if that early data is later rejected. */ + { + uint32_t handshake_type_original = 0, handshake_type_test = 0; + message_type_t *messages_original = NULL, *messages_test = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type_original & MIDDLEBOX_COMPAT)) { + continue; + } + + /* EARLY_CLIENT_CCS form of the handshake */ + handshake_type_test = handshake_type_original | EARLY_CLIENT_CCS; + messages_test = tls13_handshakes[handshake_type_test]; + + /* Ignore identical handshakes */ + if (handshake_type_original == handshake_type_test) { + continue; + } + + for (size_t j = 0, j_test = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_test < S2N_MAX_HANDSHAKE_LENGTH; j++, j_test++) { + /* Skip Client CCS messages */ + while (messages_original[j] == CLIENT_CHANGE_CIPHER_SPEC) { + j++; + } + while (messages_test[j_test] == CLIENT_CHANGE_CIPHER_SPEC) { + j_test++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_test[j_test]); + } + } + }; + + /* Test: When using TLS 1.3, use the new state machine and handshakes */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &tls13_state_machine[0]); + EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &tls13_handshakes[0]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server does not wait for client cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server does not skip server cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client does not wait for server cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client does not skip client cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client can receive a server cipher change spec at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + conn->in_status = ENCRYPTED; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + conn->handshake.message_number = j; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + } else { + EXPECT_EQUAL(conn->handshake.message_number, j); + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server can receive a client cipher change request at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + conn->in_status = ENCRYPTED; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + conn->handshake.message_number = j; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + } else { + EXPECT_EQUAL(conn->handshake.message_number, j); + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 s2n_handshake_read_io should accept only the expected message */ + { + /* TLS1.3 should accept the expected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, 1); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an unexpected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an expected message from the wrong writer */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an expected message from the wrong record type */ + { + /* Unfortunately, all our non-handshake record types have a message type of 0, + * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) + * which can appear at any point in a TLS1.2 handshake. + * + * To test, temporarily modify the actions table. + * We MUST restore this after this test. + */ + uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + uint8_t server_css_message_number = 2; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; + conn->handshake.message_number = server_css_message_number; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; + }; + + /* Error if a client receives a client cert request in non-FULL_HANDSHAKE mode */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + POSIX_GUARD(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERT_REQ)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: TLS 1.3 MIDDLEBOX_COMPAT handshakes all follow CCS middlebox compatibility rules. + * + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# Field measurements [Ben17a] [Ben17b] [Res17a] [Res17b] have found + *# that a significant number of middleboxes misbehave when a TLS + *# client/server pair negotiates TLS 1.3. Implementations can increase + *# the chance of making connections through those middleboxes by making + *# the TLS 1.3 handshake look more like a TLS 1.2 handshake: + */ + { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# If not offering early data, the client sends a dummy + *# change_cipher_spec record (see the third paragraph of Section 5) + *# immediately before its second flight. This may either be before + *# its second ClientHello or before its encrypted handshake flight. + **/ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + bool change_cipher_spec_found = false; + uint32_t handshake_type = valid_tls13_handshakes[i]; + message_type_t *messages = tls13_handshakes[handshake_type]; + + /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type & NEGOTIATED) + || !(handshake_type & MIDDLEBOX_COMPAT) + || (handshake_type & EARLY_CLIENT_CCS)) { + continue; + } + + for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + /* Is it the second client flight? + * Have we switched from the server sending to the client sending? */ + if (tls13_state_machine[messages[j]].writer != 'C' + || tls13_state_machine[messages[j - 1]].writer != 'S') { + continue; + } + + EXPECT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_EQUAL(tls13_state_machine[messages[j + 1]].writer, 'C'); + + /* CCS message found. We are done with this handshake. */ + change_cipher_spec_found = true; + break; + } + + EXPECT_TRUE(change_cipher_spec_found); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# If offering early data, the record is placed immediately after the + *# first ClientHello. + */ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + uint32_t handshake_type = valid_tls13_handshakes[i]; + message_type_t *messages = tls13_handshakes[handshake_type]; + + /* Ignore handshakes where early data did not trigger the change in CCS behavior */ + if (!(handshake_type & EARLY_CLIENT_CCS)) { + continue; + } + + EXPECT_EQUAL(messages[0], CLIENT_HELLO); + EXPECT_EQUAL(messages[1], CLIENT_CHANGE_CIPHER_SPEC); + for (size_t j = 2; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_NOT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); + } + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# The server sends a dummy change_cipher_spec record immediately + *# after its first handshake message. This may either be after a + *# ServerHello or a HelloRetryRequest. + **/ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + bool change_cipher_spec_found = false; + uint32_t handshake_type = valid_tls13_handshakes[i]; + message_type_t *messages = tls13_handshakes[handshake_type]; + + /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type & NEGOTIATED) || !(handshake_type & MIDDLEBOX_COMPAT)) { + continue; + } + + for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + /* Is it the first server flight? + * Have we switched from the client sending to the server sending? */ + if (tls13_state_machine[messages[j]].writer != 'S' + || tls13_state_machine[messages[j - 1]].writer != 'C') { + continue; + } + + EXPECT_EQUAL(messages[j + 1], SERVER_CHANGE_CIPHER_SPEC); + EXPECT_TRUE(messages[j] == SERVER_HELLO || messages[j] == HELLO_RETRY_MSG); + + /* CCS message found. We are done with this handshake. */ + change_cipher_spec_found = true; + break; + } + + EXPECT_TRUE(change_cipher_spec_found); + } + }; + + /* Test: TLS1.3 s2n_conn_set_handshake_type sets only handshake flags allowed by TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* Ensure WITH_SESSION_TICKETS is set */ + conn->config->use_tickets = 1; + conn->session_ticket_status = S2N_NEW_TICKET; + + /* Ensure CLIENT_AUTH is set */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_REQUIRED)); + + /* Ensure TLS12_PERFECT_FORWARD_SECRECY is set by choosing a cipher suite with is_ephemeral=1 on the kex */ + conn->secure->cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256; + + /* Ensure OCSP_STATUS is set by setting the connection status_type */ + conn->status_type = S2N_STATUS_REQUEST_OCSP; + + /* Verify that tls1.2 DOES set the flags allowed by tls1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & TLS12_PERFECT_FORWARD_SECRECY); + EXPECT_TRUE(conn->handshake.handshake_type & OCSP_STATUS); + EXPECT_TRUE(conn->handshake.handshake_type & WITH_SESSION_TICKET); + EXPECT_TRUE(conn->handshake.handshake_type & CLIENT_AUTH); + + /* Reset which state machine we're on */ + EXPECT_OK(s2n_handshake_type_reset(conn)); + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + /* Verify that tls1.3 ONLY sets the flags allowed by tls1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_handshake_type only allows HELLO_RETRY_REQUEST with TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* HELLO_RETRY_REQUEST allowed with tls1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + /* Reset state machine */ + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + /* HELLO_RETRY_REQUEST not allowed with tls1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS12)); + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type does not set FULL_HANDSHAKE if + * a pre-shared key has been chosen. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + struct s2n_psk *psk = NULL; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + + conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); + EXPECT_FALSE(conn->handshake.handshake_type & FULL_HANDSHAKE); + + conn->psk_params.chosen_psk = NULL; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is + * chosen and s2n is a client. */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + client_conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(client_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_OK(s2n_conn_set_tls13_handshake_type(client_conn)); + EXPECT_FALSE(client_conn->handshake.handshake_type & CLIENT_AUTH); + EXPECT_FALSE(client_conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is + * chosen and s2n is a server. */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + struct s2n_psk *psk = NULL; + + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &psk)); + server_conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_FALSE(server_conn->handshake.handshake_type & CLIENT_AUTH); + EXPECT_FALSE(server_conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type sets WITH_EARLY_DATA */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + server_conn->actual_protocol_version = S2N_TLS13; + + server_conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); + EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type does not set WITH_EARLY_DATA if wrong state */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + server_conn->actual_protocol_version = S2N_TLS13; + + server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_FALSE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); + EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: TLS1.3 handshake type name maximum size is set correctly. + * The maximum size is the size of a name with all flags set. */ + { + size_t correct_size = 0; + for (size_t i = 0; i < s2n_array_len(tls13_handshake_type_names); i++) { + correct_size += strlen(tls13_handshake_type_names[i]); + } + if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { + fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); + FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.3 handshakes"); + } + }; + + /* Test: TLS 1.3 handshake types are all properly printed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + + conn->handshake.handshake_type = INITIAL; + EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST", s2n_connection_get_handshake_type_name(conn)); + + const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT" + "|MIDDLEBOX_COMPAT|WITH_EARLY_DATA|EARLY_CLIENT_CCS"; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT + | MIDDLEBOX_COMPAT | WITH_EARLY_DATA | EARLY_CLIENT_CCS; + EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + + const char *handshake_type_name = NULL; + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + conn->handshake.handshake_type = valid_tls13_handshakes[i]; + + handshake_type_name = s2n_connection_get_handshake_type_name(conn); + + /* The handshake type names must be unique */ + for (int j = 0; j < valid_tls13_handshakes_size; j++) { + conn->handshake.handshake_type = valid_tls13_handshakes[j]; + if (i == j) { + EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } else { + EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.3 message types are all properly printed */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; + const char *expected[] = { "CLIENT_HELLO", "SERVER_HELLO", "SERVER_CHANGE_CIPHER_SPEC", + "ENCRYPTED_EXTENSIONS", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", + "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_FINISHED", "APPLICATION_DATA" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_handshake_type; + + for (int i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.3 message types are all properly printed for client auth */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH; + const char *expected[] = { "CLIENT_HELLO", + "SERVER_HELLO", "ENCRYPTED_EXTENSIONS", "SERVER_CERT_REQ", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", + "CLIENT_CERT", "CLIENT_CERT_VERIFY", "CLIENT_FINISHED", + "APPLICATION_DATA" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_handshake_type; + + for (int i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: Make sure not to miss out populating any message names */ + { + for (int i = CLIENT_HELLO; i <= APPLICATION_DATA; i++) { + EXPECT_NOT_NULL(message_names[i]); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_test.c b/tests/unit/s2n_tls13_handshake_test.c index bef2388e832..17d550e3ff4 100644 --- a/tests/unit/s2n_tls13_handshake_test.c +++ b/tests/unit/s2n_tls13_handshake_test.c @@ -1,331 +1,332 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -#include "tls/s2n_tls13_handshake.h" - -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_client_key_share.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_handshake_transcript.c" -#include "tls/s2n_tls13_handshake.c" - -#define S2N_SECRET_TYPE_COUNT 5 -#define S2N_TEST_PSK_COUNT 10 - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* Test wiping PSKs after use */ - { - /* PSKs are wiped when chosen PSK is NULL */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - EXPECT_NOT_NULL(ecc_preferences); - - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - const uint8_t psk_data[] = "test identity data"; - const uint8_t secret_data[] = "test secret data"; - for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); - EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); - EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NOT_EQUAL(psk->identity.data, NULL); - EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); - EXPECT_NOT_EQUAL(psk->secret.size, 0); - EXPECT_NOT_EQUAL(psk->secret.data, NULL); - } - - EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); - EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); - EXPECT_NULL(conn->psk_params.chosen_psk); - - DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); - - /* Verify secrets are wiped */ - for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NULL(psk->secret.data); - EXPECT_EQUAL(psk->secret.size, 0); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* PSKs are wiped when chosen PSK is NOT NULL */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - EXPECT_NOT_NULL(ecc_preferences); - - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - const uint8_t psk_data[] = "test identity data"; - const uint8_t secret_data[] = "test secret data"; - const uint8_t early_secret_data[SHA256_DIGEST_LENGTH] = "test early secret data"; - for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); - EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); - EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NOT_EQUAL(psk->identity.data, NULL); - EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); - EXPECT_NOT_EQUAL(psk->secret.size, 0); - EXPECT_NOT_EQUAL(psk->secret.data, NULL); - EXPECT_SUCCESS(s2n_realloc(&psk->early_secret, sizeof(early_secret_data))); - POSIX_CHECKED_MEMCPY(psk->early_secret.data, early_secret_data, sizeof(early_secret_data)); - EXPECT_NOT_EQUAL(psk->early_secret.size, 0); - EXPECT_NOT_EQUAL(psk->early_secret.data, NULL); - } - - /* Set chosen PSK */ - struct s2n_psk *chosen_psk = NULL; - EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &chosen_psk)); - EXPECT_NOT_NULL(chosen_psk); - conn->psk_params.chosen_psk = chosen_psk; - conn->psk_params.chosen_psk_wire_index = 0; - - EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); - EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); - - DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); - - /* Verify secrets are wiped */ - for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NULL(psk->secret.data); - EXPECT_EQUAL(psk->secret.size, 0); - EXPECT_NULL(psk->early_secret.data); - EXPECT_EQUAL(psk->early_secret.size, 0); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test: Handshake self-talks using s2n_handshake_write_io and s2n_handshake_read_io */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - uint8_t *cert_chain = NULL; - uint8_t *private_key = NULL; - uint32_t cert_chain_len = 0; - uint32_t private_key_len = 0; - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_cert_chain_and_key *default_cert = NULL; - EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - struct s2n_blob server_seq = { .data = server_conn->secure->server_sequence_number, .size = sizeof(server_conn->secure->server_sequence_number) }; - S2N_BLOB_FROM_HEX(seq_0, "0000000000000000"); - S2N_BLOB_FROM_HEX(seq_1, "0000000000000001"); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - /* Client sends ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); - - s2n_tls13_connection_keys(server_secrets_0, server_conn); - EXPECT_EQUAL(server_secrets_0.size, 0); - - EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); - - /* Server reads ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT); - - s2n_tls13_connection_keys(server_secrets, server_conn); - EXPECT_EQUAL(server_secrets.size, SHA256_DIGEST_LENGTH); - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); - - /* Server sends ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - S2N_BLOB_EXPECT_EQUAL(server_seq, seq_0); - - /* Server sends EncryptedExtensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - S2N_BLOB_EXPECT_EQUAL(server_seq, seq_1); - - /* Server sends ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Client reads ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - s2n_tls13_connection_keys(client_secrets, client_conn); - EXPECT_EQUAL(client_secrets.size, SHA256_DIGEST_LENGTH); - - /* Verify that derive and extract secrets match */ - S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); - S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); - - /* Client reads Encrypted extensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Server sends ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Client reads ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Client sends ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Server reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - /* Server reads ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); - - /* Verify that derive and extract secrets match */ - S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); - S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(private_key); - free(cert_chain); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include "tls/s2n_tls13_handshake.h" + +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_handshake_transcript.c" +#include "tls/s2n_tls13_handshake.c" + +#define S2N_SECRET_TYPE_COUNT 5 +#define S2N_TEST_PSK_COUNT 10 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test wiping PSKs after use */ + { + /* PSKs are wiped when chosen PSK is NULL */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + const uint8_t psk_data[] = "test identity data"; + const uint8_t secret_data[] = "test secret data"; + for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NOT_EQUAL(psk->identity.data, NULL); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); + EXPECT_NOT_EQUAL(psk->secret.size, 0); + EXPECT_NOT_EQUAL(psk->secret.data, NULL); + } + + EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); + EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); + EXPECT_NULL(conn->psk_params.chosen_psk); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* PSKs are wiped when chosen PSK is NOT NULL */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + const uint8_t psk_data[] = "test identity data"; + const uint8_t secret_data[] = "test secret data"; + const uint8_t early_secret_data[SHA256_DIGEST_LENGTH] = "test early secret data"; + for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NOT_EQUAL(psk->identity.data, NULL); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); + EXPECT_NOT_EQUAL(psk->secret.size, 0); + EXPECT_NOT_EQUAL(psk->secret.data, NULL); + EXPECT_SUCCESS(s2n_realloc(&psk->early_secret, sizeof(early_secret_data))); + POSIX_CHECKED_MEMCPY(psk->early_secret.data, early_secret_data, sizeof(early_secret_data)); + EXPECT_NOT_EQUAL(psk->early_secret.size, 0); + EXPECT_NOT_EQUAL(psk->early_secret.data, NULL); + } + + /* Set chosen PSK */ + struct s2n_psk *chosen_psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &chosen_psk)); + EXPECT_NOT_NULL(chosen_psk); + conn->psk_params.chosen_psk = chosen_psk; + conn->psk_params.chosen_psk_wire_index = 0; + + EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); + EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + EXPECT_NULL(psk->early_secret.data); + EXPECT_EQUAL(psk->early_secret.size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: Handshake self-talks using s2n_handshake_write_io and s2n_handshake_read_io */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + uint8_t *cert_chain = NULL; + uint8_t *private_key = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_cert_chain_and_key *default_cert = NULL; + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + struct s2n_blob server_seq = { .data = server_conn->secure->server_sequence_number, .size = sizeof(server_conn->secure->server_sequence_number) }; + S2N_BLOB_FROM_HEX(seq_0, "0000000000000000"); + S2N_BLOB_FROM_HEX(seq_1, "0000000000000001"); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Client sends ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + s2n_tls13_connection_keys(server_secrets_0, server_conn); + EXPECT_EQUAL(server_secrets_0.size, 0); + + EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT); + + s2n_tls13_connection_keys(server_secrets, server_conn); + EXPECT_EQUAL(server_secrets.size, SHA256_DIGEST_LENGTH); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); + + /* Server sends ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + S2N_BLOB_EXPECT_EQUAL(server_seq, seq_0); + + /* Server sends EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + S2N_BLOB_EXPECT_EQUAL(server_seq, seq_1); + + /* Server sends ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + s2n_tls13_connection_keys(client_secrets, client_conn); + EXPECT_EQUAL(client_secrets.size, SHA256_DIGEST_LENGTH); + + /* Verify that derive and extract secrets match */ + S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); + S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); + + /* Client reads Encrypted extensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Server sends ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Client sends ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Server reads ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + /* Verify that derive and extract secrets match */ + S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); + S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(private_key); + free(cert_chain); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_parse_record_type_test.c b/tests/unit/s2n_tls13_parse_record_type_test.c index e962d05bbfa..42f06ae14b2 100644 --- a/tests/unit/s2n_tls13_parse_record_type_test.c +++ b/tests/unit/s2n_tls13_parse_record_type_test.c @@ -1,256 +1,257 @@ -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_record.h" - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - uint8_t record_type = 0; - - /* In tls13 the true record type is inserted in the last byte of the encrypted payload. This - * test creates a fake unencrypted payload and checks that the helper function - * s2n_tls13_parse_record_type() correctly parses the type. - */ - { - uint16_t plaintext = 0xdaf3; - struct s2n_stuffer plaintext_stuffer = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0xf3); - EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); - } - - /* Test for failure when stuffer is completely empty */ - { - struct s2n_stuffer empty_stuffer = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_alloc(&empty_stuffer, 0)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&empty_stuffer, &record_type)); - }; - - /* Test for case where there is a record type in the stuffer but no content */ - { - uint16_t plaintext = 0xf3; - struct s2n_stuffer plaintext_stuffer = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0xf3); - EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); - }; - - /* Test for record padding handling */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* no padding */ - S2N_BLOB_FROM_HEX(padding_0, "16"); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_0)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0x16); - - /* 1 byte padding */ - S2N_BLOB_FROM_HEX(padding_1, "1600"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_1)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0x16); - - /* 2 byte padding */ - S2N_BLOB_FROM_HEX(padding_2, "160000"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_2)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0x16); - - /** test: padding without record type should fail - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.4 - *= type=test - *# If a receiving implementation does not - *# find a non-zero octet in the cleartext, it MUST terminate the - *# connection with an "unexpected_message" alert. - **/ - S2N_BLOB_FROM_HEX(no_type, "00"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); - - /* multiple padding without record type should fail */ - S2N_BLOB_FROM_HEX(no_type2, "0000"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type2)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); - - /* empty stuffer should fail */ - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - }; - - /* Defining these here as variables as they aren't used in prior tests. */ - const uint8_t padding_value = 0x00; - const uint8_t not_padding_value = 0x16; - - /* Test maximum record length size (empty data) */ - { - EXPECT_EQUAL(S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH, 16385); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - - /* fill up stuffer to before the limit */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); - - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* There was no data before the record type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test maximum record length size (maximum data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* fill up stuffer to before the limit */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); - - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* The last byte is stripped as the content type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Certain versions of Java can generate inner plaintexts with lengths up to - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) - * However, after the padding is stripped, the result will always be no more than - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 - */ - { - const size_t extra_length_tolerated = 16; - /* Test slightly overlarge record for compatibility (empty data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - /* fill up stuffer the limit + 16 */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* There was no data before the record type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test slightly overlarge record for compatibility (maximum data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* fill up stuffer to before the limit */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - } - /* pad up stuffer the limit + 16 */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); - - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* The last byte is stripped as the content type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test slightly overlarge record for compatibility (with too much data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* Finally, do this with an overall length which should pass, but too much data before the padding */ - /* fill up stuffer to the maximum amount of data */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); /* Record type */ - /* 16 bytes of padding*/ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test slightly overlarge + 1 record for compatibility (empty data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1); - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - } - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t record_type = 0; + + /* In tls13 the true record type is inserted in the last byte of the encrypted payload. This + * test creates a fake unencrypted payload and checks that the helper function + * s2n_tls13_parse_record_type() correctly parses the type. + */ + { + uint16_t plaintext = 0xdaf3; + struct s2n_stuffer plaintext_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0xf3); + EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); + } + + /* Test for failure when stuffer is completely empty */ + { + struct s2n_stuffer empty_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&empty_stuffer, 0)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&empty_stuffer, &record_type)); + }; + + /* Test for case where there is a record type in the stuffer but no content */ + { + uint16_t plaintext = 0xf3; + struct s2n_stuffer plaintext_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0xf3); + EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); + }; + + /* Test for record padding handling */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* no padding */ + S2N_BLOB_FROM_HEX(padding_0, "16"); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_0)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /* 1 byte padding */ + S2N_BLOB_FROM_HEX(padding_1, "1600"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_1)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /* 2 byte padding */ + S2N_BLOB_FROM_HEX(padding_2, "160000"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_2)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /** test: padding without record type should fail + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.4 + *= type=test + *# If a receiving implementation does not + *# find a non-zero octet in the cleartext, it MUST terminate the + *# connection with an "unexpected_message" alert. + **/ + S2N_BLOB_FROM_HEX(no_type, "00"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + /* multiple padding without record type should fail */ + S2N_BLOB_FROM_HEX(no_type2, "0000"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type2)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + /* empty stuffer should fail */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Defining these here as variables as they aren't used in prior tests. */ + const uint8_t padding_value = 0x00; + const uint8_t not_padding_value = 0x16; + + /* Test maximum record length size (empty data) */ + { + EXPECT_EQUAL(S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH, 16385); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* There was no data before the record type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test maximum record length size (maximum data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* The last byte is stripped as the content type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Certain versions of Java can generate inner plaintexts with lengths up to + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) + * However, after the padding is stripped, the result will always be no more than + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 + */ + { + const size_t extra_length_tolerated = 16; + /* Test slightly overlarge record for compatibility (empty data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + /* fill up stuffer the limit + 16 */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* There was no data before the record type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge record for compatibility (maximum data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + } + /* pad up stuffer the limit + 16 */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* The last byte is stripped as the content type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge record for compatibility (with too much data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* Finally, do this with an overall length which should pass, but too much data before the padding */ + /* fill up stuffer to the maximum amount of data */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); /* Record type */ + /* 16 bytes of padding*/ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge + 1 record for compatibility (empty data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + } + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_pq_handshake_test.c b/tests/unit/s2n_tls13_pq_handshake_test.c index 972a0a1a2da..354970d1b3b 100644 --- a/tests/unit/s2n_tls13_pq_handshake_test.c +++ b/tests/unit/s2n_tls13_pq_handshake_test.c @@ -1,714 +1,715 @@ -/* - * 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 "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/policy/s2n_policy_feature.h" -#include "tls/s2n_ecc_preferences.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_kem_preferences.h" -#include "tls/s2n_security_policies.h" - -/* Include C file directly to access static functions */ -#include "tls/s2n_handshake_io.c" - -const struct s2n_kem_group *s2n_get_predicted_negotiated_kem_group(const struct s2n_security_policy *client_policy, const struct s2n_security_policy *server_policy) -{ - PTR_ENSURE_REF(client_policy); - PTR_ENSURE_REF(server_policy); - - const struct s2n_kem_preferences *client_prefs = client_policy->kem_preferences; - const struct s2n_kem_preferences *server_prefs = server_policy->kem_preferences; - - PTR_ENSURE_REF(client_prefs); - PTR_ENSURE_REF(server_prefs); - - /* Client will offer their highest priority PQ KeyShare in their ClientHello. This PQ KeyShare - * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually - * supported PQ KeyShares that the server would prefer over this one but would require 2-RTT's). - */ - const struct s2n_kem_group *client_default = client_prefs->tls13_kem_groups[0]; - PTR_ENSURE_REF(client_default); - - for (int i = 0; server_policy->strongly_preferred_groups != NULL && i < server_policy->strongly_preferred_groups->count; i++) { - for (int j = 0; j < client_policy->kem_preferences->tls13_kem_group_count; j++) { - if (server_policy->strongly_preferred_groups->iana_ids[i] == client_policy->kem_preferences->tls13_kem_groups[j]->iana_id - && s2n_kem_group_is_available(client_policy->kem_preferences->tls13_kem_groups[j])) { - return client_policy->kem_preferences->tls13_kem_groups[j]; - } - } - } - - for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { - const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; - PTR_ENSURE_REF(server_group); - if (s2n_kem_group_is_available(client_default) && s2n_kem_group_is_available(server_group) - && client_default->iana_id == server_group->iana_id - && s2n_kem_group_is_available(client_default)) { - return client_default; - } - } - - /* Otherwise, if the client's default isn't supported, and a 2-RTT PQ handshake is required, the server will choose - * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ - for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { - const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; - - /* j starts at 1 since we already checked client_prefs->tls13_kem_groups[0] above */ - for (int j = 1; j < client_prefs->tls13_kem_group_count; j++) { - const struct s2n_kem_group *client_group = client_prefs->tls13_kem_groups[j]; - PTR_ENSURE_REF(client_group); - PTR_ENSURE_REF(server_group); - if (s2n_kem_group_is_available(client_group) && s2n_kem_group_is_available(server_group) - && client_group->iana_id == server_group->iana_id - && s2n_kem_group_is_available(client_group)) { - return client_group; - } - } - } - - return NULL; -} - -const struct s2n_ecc_named_curve *s2n_get_predicted_negotiated_ecdhe_curve(const struct s2n_security_policy *client_sec_policy, - const struct s2n_security_policy *server_sec_policy) -{ - PTR_ENSURE_REF(client_sec_policy); - PTR_ENSURE_REF(server_sec_policy); - - /* Client will offer their highest priority ECDHE KeyShare in their ClientHello. This KeyShare - * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually - * supported ECDHE KeyShares that the server would prefer over this one but would require 2-RTT's). - */ - const struct s2n_ecc_named_curve *client_default = client_sec_policy->ecc_preferences->ecc_curves[0]; - PTR_ENSURE_REF(client_default); - - for (int i = 0; server_sec_policy->strongly_preferred_groups != NULL && i < server_sec_policy->strongly_preferred_groups->count; i++) { - for (int j = 0; j < client_sec_policy->ecc_preferences->count; j++) { - if (server_sec_policy->strongly_preferred_groups->iana_ids[i] == client_sec_policy->ecc_preferences->ecc_curves[j]->iana_id) { - return client_sec_policy->ecc_preferences->ecc_curves[j]; - } - } - } - - for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { - const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; - PTR_ENSURE_REF(server_curve); - if (server_curve->iana_id == client_default->iana_id) { - return client_default; - } - } - - /* Otherwise, if the client's default isn't supported, and a 2-RTT handshake is required, the server will choose - * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ - for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { - const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; - - /* j starts at 1 since we already checked client_sec_policy->ecc_preferences->ecc_curves[0] above */ - for (int j = 1; j < client_sec_policy->ecc_preferences->count; j++) { - const struct s2n_ecc_named_curve *client_curve = client_sec_policy->ecc_preferences->ecc_curves[j]; - PTR_ENSURE_REF(client_curve); - PTR_ENSURE_REF(server_curve); - if (client_curve->iana_id == server_curve->iana_id) { - return client_curve; - } - } - } - - return NULL; -} - -int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_policy, - const struct s2n_security_policy *server_sec_policy, const struct s2n_kem_group *expected_kem_group, - const struct s2n_ecc_named_curve *expected_curve, bool hrr_expected, bool len_prefix_expected) -{ - /* XOR check: can expect to negotiate either a KEM group, or a classic EC curve, but not both/neither */ - POSIX_ENSURE((expected_kem_group == NULL) != (expected_curve == NULL), S2N_ERR_SAFETY); - - /* Set up connections */ - struct s2n_connection *client_conn = NULL, *server_conn = NULL; - POSIX_ENSURE_REF(client_conn = s2n_connection_new(S2N_CLIENT)); - POSIX_ENSURE_REF(server_conn = s2n_connection_new(S2N_SERVER)); - - struct s2n_config *client_config = NULL, *server_config = NULL; - POSIX_ENSURE_REF(client_config = s2n_config_new()); - POSIX_ENSURE_REF(server_config = s2n_config_new()); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }, private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - POSIX_ENSURE_REF(chain_and_key = s2n_cert_chain_and_key_new()); - POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); - POSIX_GUARD(s2n_connection_set_config(server_conn, server_config)); - - struct s2n_stuffer client_to_server = { 0 }, server_to_client = { 0 }; - POSIX_GUARD(s2n_stuffer_growable_alloc(&client_to_server, 2048)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&server_to_client, 2048)); - - POSIX_GUARD(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - POSIX_GUARD(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - client_conn->security_policy_override = client_sec_policy; - server_conn->security_policy_override = server_sec_policy; - - /* Client sends ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_write_io(client_conn)); - - POSIX_ENSURE_EQ(client_conn->actual_protocol_version, S2N_TLS13); - POSIX_ENSURE_EQ(server_conn->actual_protocol_version, 0); /* Won't get set until after server reads ClientHello */ - POSIX_ENSURE_EQ(client_conn->handshake.handshake_type, INITIAL); - - /* Server reads ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_read_io(server_conn)); - - POSIX_ENSURE_EQ(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ - - /* Assert that the server chose the correct group */ - if (expected_kem_group) { - /* Client should always determine whether the KEM group used len_prefixed format, and server should match client's behavior. */ - POSIX_ENSURE_EQ(len_prefix_expected, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); - POSIX_ENSURE_EQ(len_prefix_expected, s2n_tls13_client_must_use_hybrid_kem_length_prefix(client_sec_policy->kem_preferences)); - POSIX_ENSURE_EQ(server_conn->kex_params.client_kem_group_params.kem_params.len_prefixed, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); - - POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); - if (expected_kem_group->curve != &s2n_ecc_curve_none) { - POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - } - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - } else { - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - if (expected_curve != server_conn->kex_params.server_ecc_evp_params.negotiated_curve) { - const char *expected_name = "NULL"; - const char *actual_name = "NULL"; - if (expected_curve != NULL && expected_curve->name != NULL) { - expected_name = expected_curve->name; - } - if (server_conn->kex_params.server_ecc_evp_params.negotiated_curve != NULL && server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name != NULL) { - actual_name = server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name; - } - - fprintf(stderr, "\n\nError: Unexpected curve was negotiated. Expected: %s, Actual: %s\n", expected_name, actual_name); - fflush(stderr); - } - - POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - } - - /* Server sends ServerHello or HRR */ - POSIX_GUARD(s2n_conn_set_handshake_type(server_conn)); - POSIX_ENSURE_EQ(hrr_expected, s2n_handshake_type_check_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); - POSIX_GUARD(s2n_handshake_write_io(server_conn)); - - /* Server sends CCS */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); - POSIX_GUARD(s2n_handshake_write_io(server_conn)); - - if (hrr_expected) { - /* Client reads HRR */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - POSIX_GUARD(s2n_handshake_read_io(client_conn)); - POSIX_GUARD(s2n_conn_set_handshake_type(client_conn)); - POSIX_ENSURE_NE(0, client_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - /* Client reads CCS */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - POSIX_GUARD(s2n_handshake_read_io(client_conn)); - - /* Client sends CCS and new ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - POSIX_GUARD(s2n_handshake_write_io(client_conn)); - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_write_io(client_conn)); - - /* Server reads CCS (doesn't change state machine) */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_read_io(server_conn)); - - /* Server reads new ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_read_io(server_conn)); - - /* Server sends ServerHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - POSIX_GUARD(s2n_handshake_write_io(server_conn)); - } - - /* Client reads ServerHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - POSIX_GUARD(s2n_handshake_read_io(client_conn)); - - /* We've gotten far enough in the handshake that both client and server should have - * derived the shared secrets, so we don't send/receive any more messages. */ - - /* Assert that the correct group was negotiated (we re-check the server group to assert that - * nothing unexpected changed between then and now while e.g. processing HRR) */ - if (expected_kem_group) { - POSIX_ENSURE_EQ(expected_kem_group, client_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(expected_kem_group->kem, client_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - if (expected_kem_group->curve != &s2n_ecc_curve_none) { - POSIX_ENSURE_EQ(expected_kem_group->curve, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - } - - /* Ensure s2n_connection_get_kem_group_name() gives the correct answer for both client and server */ - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(server_conn))); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(server_conn), strlen(expected_kem_group->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(client_conn))); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(client_conn), strlen(expected_kem_group->name)), 0); - - /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ - const char *server_group_name = NULL; - const char *client_group_name = NULL; - POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); - POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(server_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, server_group_name, strlen(expected_kem_group->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(client_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, client_group_name, strlen(expected_kem_group->name)), 0); - } else { - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - POSIX_ENSURE_EQ(expected_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - /* Ensure s2n_connection_get_curve() gives the correct answer for both client and server */ - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(server_conn))); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(server_conn), strlen(expected_curve->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(client_conn))); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(client_conn), strlen(expected_curve->name)), 0); - - /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ - const char *server_group_name = NULL; - const char *client_group_name = NULL; - POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); - POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(server_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, server_group_name, strlen(expected_curve->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(client_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, client_group_name, strlen(expected_curve->name)), 0); - } - - /* Verify basic properties of secrets */ - s2n_tls13_connection_keys(server_secret_info, server_conn); - s2n_tls13_connection_keys(client_secret_info, client_conn); - POSIX_ENSURE_EQ(server_conn->secure->cipher_suite, client_conn->secure->cipher_suite); - if (server_conn->secure->cipher_suite == &s2n_tls13_aes_256_gcm_sha384) { - POSIX_ENSURE_EQ(server_secret_info.size, 48); - POSIX_ENSURE_EQ(client_secret_info.size, 48); - } else { - POSIX_ENSURE_EQ(server_secret_info.size, 32); - POSIX_ENSURE_EQ(client_secret_info.size, 32); - } - - /* Verify secrets aren't just zero'ed memory */ - uint8_t all_zeros[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - POSIX_CHECKED_MEMSET((void *) all_zeros, 0, S2N_TLS13_SECRET_MAX_LEN); - struct s2n_tls13_secrets *client_secrets = &client_conn->secrets.version.tls13; - struct s2n_tls13_secrets *server_secrets = &server_conn->secrets.version.tls13; - POSIX_ENSURE_EQ(server_secret_info.size, client_secret_info.size); - uint8_t size = server_secret_info.size; - POSIX_ENSURE_EQ(client_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); - POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->extract_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->client_handshake_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->server_handshake_secret, size)); - POSIX_ENSURE_EQ(server_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); - POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->extract_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->client_handshake_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->server_handshake_secret, size)); - - /* Verify client and server secrets are equal to each other */ - POSIX_ENSURE_EQ(0, memcmp(server_secrets->extract_secret, client_secrets->extract_secret, size)); - POSIX_ENSURE_EQ(0, memcmp(server_secrets->client_handshake_secret, client_secrets->client_handshake_secret, size)); - POSIX_ENSURE_EQ(0, memcmp(server_secrets->server_handshake_secret, client_secrets->server_handshake_secret, size)); - - /* Clean up */ - POSIX_GUARD(s2n_stuffer_free(&client_to_server)); - POSIX_GUARD(s2n_stuffer_free(&server_to_client)); - - POSIX_GUARD(s2n_connection_free(client_conn)); - POSIX_GUARD(s2n_connection_free(server_conn)); - - POSIX_GUARD(s2n_cert_chain_and_key_free(chain_and_key)); - POSIX_GUARD(s2n_config_free(server_config)); - POSIX_GUARD(s2n_config_free(client_config)); - - return S2N_SUCCESS; -} - -int main() -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* Additional KEM preferences/security policies to test against. These policies can only be used - * as the server's policy in this test: when generating the ClientHello, the client relies on - * the security_policy_selection[] array (in s2n_security_policies.c) to determine if it should - * write the supported_groups extension. Because these unofficial policies don't exist in that - * array, the supported_groups extension won't get sent and the handshake won't complete as expected. */ - - const struct s2n_kem_group *mlkem768_test_groups[] = { - &s2n_x25519_mlkem_768, - &s2n_secp256r1_mlkem_768, - }; - - const struct s2n_kem_preferences mlkem768_test_prefs = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = s2n_array_len(mlkem768_test_groups), - .tls13_kem_groups = mlkem768_test_groups, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy mlkem768_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &mlkem768_test_prefs, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct s2n_kem_group *mlkem1024_test_groups[] = { - &s2n_secp384r1_mlkem_1024, - }; - - const struct s2n_kem_preferences mlkem1024_test_prefs = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = s2n_array_len(mlkem1024_test_groups), - .tls13_kem_groups = mlkem1024_test_groups, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy mlkem1024_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &mlkem1024_test_prefs, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct s2n_kem_group *pure_mlkem1024_test_groups[] = { - &s2n_pure_mlkem_1024, - }; - - const struct s2n_kem_preferences pure_mlkem1024_test_prefs = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = s2n_array_len(pure_mlkem1024_test_groups), - .tls13_kem_groups = pure_mlkem1024_test_groups, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy pure_mlkem1024_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &pure_mlkem1024_test_prefs, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct s2n_ecc_named_curve *default_curve = &s2n_ecc_curve_x25519; - - if (!s2n_is_evp_apis_supported()) { - default_curve = &s2n_ecc_curve_secp256r1; - } - - struct pq_handshake_test_vector { - const struct s2n_security_policy *client_policy; - const struct s2n_security_policy *server_policy; - const struct s2n_kem_group *expected_kem_group; - const struct s2n_ecc_named_curve *expected_curve; - bool hrr_expected; - bool len_prefix_expected; - }; - - /* Self talk test with each TLS 1.3 KemGroup we support */ - for (size_t i = 0; i < S2N_KEM_GROUPS_COUNT; i++) { - const struct s2n_kem_group *kem_group = ALL_SUPPORTED_KEM_GROUPS[i]; - - if (kem_group == NULL || !s2n_kem_group_is_available(kem_group)) { - continue; - } - - const struct s2n_kem_preferences singleton_test_pref = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = 1, - .tls13_kem_groups = &kem_group, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy singleton_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &singleton_test_pref, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct pq_handshake_test_vector test_vec = { - .client_policy = &singleton_test_policy, - .server_policy = &singleton_test_policy, - .expected_kem_group = kem_group, - .expected_curve = NULL, - .hrr_expected = false, - .len_prefix_expected = false, - }; - - EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(test_vec.client_policy, test_vec.server_policy, - test_vec.expected_kem_group, test_vec.expected_curve, test_vec.hrr_expected, test_vec.len_prefix_expected)); - } - - /* ML-KEM is only available on newer versions of AWS-LC. If it's - * unavailable, we must downgrade the assertions to EC. */ - const struct s2n_kem_group *null_if_no_mlkem_768 = &s2n_x25519_mlkem_768; - const struct s2n_kem_group *null_if_no_mlkem_1024 = &s2n_secp384r1_mlkem_1024; - const struct s2n_kem_group *null_if_no_pure_mlkem_1024 = &s2n_pure_mlkem_1024; - const struct s2n_ecc_named_curve *ec_if_no_mlkem = NULL; - bool hrr_expected_if_mlkem = true; - if (!s2n_libcrypto_supports_mlkem()) { - null_if_no_mlkem_768 = NULL; - null_if_no_mlkem_1024 = NULL; - null_if_no_pure_mlkem_1024 = NULL; - ec_if_no_mlkem = default_curve; - hrr_expected_if_mlkem = false; - } - - /* Test vectors that expect to negotiate PQ assume that PQ is enabled in s2n. - * If PQ is disabled, the expected negotiation outcome is overridden below - * before performing the handshake test. */ - const struct pq_handshake_test_vector test_vectors[] = { - /* Server does not support PQ; client sends a PQ key share and an EC key share; - * server should negotiate EC without HRR. */ - { - .client_policy = &security_policy_pq_tls_1_2_2024_10_09, - .server_policy = &security_policy_test_all_tls13, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = false, - .len_prefix_expected = true, - }, - - /* Server does not support PQ; client sends a PQ key share, but no EC shares; - * server should negotiate EC and send HRR. */ - { - .client_policy = &security_policy_test_tls13_retry_with_pq, - .server_policy = &security_policy_test_all_tls13, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = true, - .len_prefix_expected = true, - }, - - /* Server supports PQ, but client does not. Client sent an EC share, - * EC should be negotiated without HRR */ - { - .client_policy = &security_policy_test_all_tls13, - .server_policy = &security_policy_pq_tls_1_2_2024_10_09, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = false, - .len_prefix_expected = true, - }, - - /* Server supports PQ, but client does not. Client did not send any EC shares, - * EC should be negotiated after exchanging HRR */ - { - .client_policy = &security_policy_test_tls13_retry, - .server_policy = &security_policy_pq_tls_1_2_2024_10_09, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = true, - .len_prefix_expected = true, - }, - - /* Confirm that MLKEM768 is negotiable */ - { - .client_policy = &mlkem768_test_policy, - .server_policy = &mlkem768_test_policy, - .expected_kem_group = null_if_no_mlkem_768, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = false, - .len_prefix_expected = false, - }, - - /* Confirm that MLKEM1024 is negotiable */ - { - .client_policy = &mlkem1024_test_policy, - .server_policy = &mlkem1024_test_policy, - .expected_kem_group = null_if_no_mlkem_1024, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = false, - .len_prefix_expected = false, - }, - - /* Confirm that pure MLKEM1024 is negotiable; fall back to EC when MLKEM is not supported. */ - { - .client_policy = &pure_mlkem1024_test_policy, - .server_policy = &pure_mlkem1024_test_policy, - .expected_kem_group = null_if_no_pure_mlkem_1024, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = false, - .len_prefix_expected = false, - }, - - /* Client supports pure MLKEM but did not send that key share. Pure MLKEM should be negotiated after exchanging HRR. - * If ML-KEM is not supported, EC should be negotiated without HRR. */ - { - .client_policy = &security_policy_test_all, - .server_policy = &pure_mlkem1024_test_policy, - .expected_kem_group = null_if_no_pure_mlkem_1024, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = hrr_expected_if_mlkem, - .len_prefix_expected = false, - }, - - /* Client supports p384 but did not send that key share when connecting to server that strongly prefers p384. */ - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251113, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251114, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251115, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251116, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - }; - - for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { - const struct pq_handshake_test_vector *vector = &test_vectors[i]; - const struct s2n_security_policy *client_policy = vector->client_policy; - const struct s2n_security_policy *server_policy = vector->server_policy; - const struct s2n_kem_group *kem_group = vector->expected_kem_group; - const struct s2n_ecc_named_curve *curve = vector->expected_curve; - bool hrr_expected = vector->hrr_expected; - bool len_prefix_expected = vector->len_prefix_expected; - - /* Print Test Vector Info for easier debugging on failure. */ - { - const char *kem_group_str = (kem_group == NULL) ? "NULL" : kem_group->name; - const char *curve_str = (curve == NULL) ? "NULL" : curve->name; - fprintf(stderr, "\n\nRunning test (%zu/%zu)...\n", i + 1, s2n_array_len(test_vectors)); - fflush(stderr); - fprintf(stderr, "Test Vector: kem_group: %s, curve: %s, hrr_expected: %d, len_prefix_expected: %d\n", kem_group_str, curve_str, hrr_expected, len_prefix_expected); - fflush(stderr); - uint32_t output_size = 0; - fprintf(stderr, "\nClient Security Policy: \n"); - EXPECT_SUCCESS(s2n_security_policy_write_fd(client_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); - - fprintf(stderr, "\nServer Security Policy: \n"); - EXPECT_SUCCESS(s2n_security_policy_write_fd(server_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); - fflush(stderr); - } - - /* Check if we need to override the test vector. This may need to be done due to some LibCryptos not supporting PQ. */ - if (!s2n_kem_group_is_available(kem_group)) { - kem_group = NULL; - if (curve == NULL) { - curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); - } - } - - if (!s2n_pq_is_enabled()) { - EXPECT_TRUE(client_policy->ecc_preferences->count > 0); - const struct s2n_ecc_named_curve *client_default = client_policy->ecc_preferences->ecc_curves[0]; - const struct s2n_ecc_named_curve *predicted_curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); - - /* If the default curve is x25519, and the test vector is predicting the default, but neither policy supports x25519, - * fall back to predict p256 as it should be in common with every ECC preference list. */ - if (default_curve->iana_id == s2n_ecc_curve_x25519.iana_id - && curve->iana_id == default_curve->iana_id - && (!s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id) - || !s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id))) { - EXPECT_TRUE(s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); - EXPECT_TRUE(s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); - curve = &s2n_ecc_curve_secp256r1; - } - - /* The client's preferred curve will be a higher priority than the default if both sides - * support TLS 1.3, and if the client's default can be chosen by the server in 1-RTT. */ - if (s2n_security_policy_supports_tls13(client_policy) && s2n_security_policy_supports_tls13(server_policy) - && (server_policy->strongly_preferred_groups == NULL || server_policy->strongly_preferred_groups->count == 0) - && s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, client_default->iana_id)) { - curve = client_default; - } - - /* Finally, confirm that the expected curve listed in the test vector matches the output of s2n_get_predicted_negotiated_ecdhe_curve() */ - EXPECT_EQUAL(curve->iana_id, predicted_curve->iana_id); - } - - if (kem_group != NULL) { - const struct s2n_kem_group *predicted_kem_group = s2n_get_predicted_negotiated_kem_group(client_policy, server_policy); - POSIX_ENSURE_REF(predicted_kem_group); - - /* Confirm that the expected KEM Group listed in the test vector matches the output of - * s2n_get_predicted_negotiated_kem_group() */ - POSIX_ENSURE_EQ(kem_group->iana_id, predicted_kem_group->iana_id); - } - - EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, kem_group, curve, hrr_expected, len_prefix_expected)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/policy/s2n_policy_feature.h" +#include "tls/s2n_ecc_preferences.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem_preferences.h" +#include "tls/s2n_security_policies.h" + +/* Include C file directly to access static functions */ +#include "tls/s2n_handshake_io.c" + +const struct s2n_kem_group *s2n_get_predicted_negotiated_kem_group(const struct s2n_security_policy *client_policy, const struct s2n_security_policy *server_policy) +{ + PTR_ENSURE_REF(client_policy); + PTR_ENSURE_REF(server_policy); + + const struct s2n_kem_preferences *client_prefs = client_policy->kem_preferences; + const struct s2n_kem_preferences *server_prefs = server_policy->kem_preferences; + + PTR_ENSURE_REF(client_prefs); + PTR_ENSURE_REF(server_prefs); + + /* Client will offer their highest priority PQ KeyShare in their ClientHello. This PQ KeyShare + * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually + * supported PQ KeyShares that the server would prefer over this one but would require 2-RTT's). + */ + const struct s2n_kem_group *client_default = client_prefs->tls13_kem_groups[0]; + PTR_ENSURE_REF(client_default); + + for (int i = 0; server_policy->strongly_preferred_groups != NULL && i < server_policy->strongly_preferred_groups->count; i++) { + for (int j = 0; j < client_policy->kem_preferences->tls13_kem_group_count; j++) { + if (server_policy->strongly_preferred_groups->iana_ids[i] == client_policy->kem_preferences->tls13_kem_groups[j]->iana_id + && s2n_kem_group_is_available(client_policy->kem_preferences->tls13_kem_groups[j])) { + return client_policy->kem_preferences->tls13_kem_groups[j]; + } + } + } + + for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { + const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; + PTR_ENSURE_REF(server_group); + if (s2n_kem_group_is_available(client_default) && s2n_kem_group_is_available(server_group) + && client_default->iana_id == server_group->iana_id + && s2n_kem_group_is_available(client_default)) { + return client_default; + } + } + + /* Otherwise, if the client's default isn't supported, and a 2-RTT PQ handshake is required, the server will choose + * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ + for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { + const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; + + /* j starts at 1 since we already checked client_prefs->tls13_kem_groups[0] above */ + for (int j = 1; j < client_prefs->tls13_kem_group_count; j++) { + const struct s2n_kem_group *client_group = client_prefs->tls13_kem_groups[j]; + PTR_ENSURE_REF(client_group); + PTR_ENSURE_REF(server_group); + if (s2n_kem_group_is_available(client_group) && s2n_kem_group_is_available(server_group) + && client_group->iana_id == server_group->iana_id + && s2n_kem_group_is_available(client_group)) { + return client_group; + } + } + } + + return NULL; +} + +const struct s2n_ecc_named_curve *s2n_get_predicted_negotiated_ecdhe_curve(const struct s2n_security_policy *client_sec_policy, + const struct s2n_security_policy *server_sec_policy) +{ + PTR_ENSURE_REF(client_sec_policy); + PTR_ENSURE_REF(server_sec_policy); + + /* Client will offer their highest priority ECDHE KeyShare in their ClientHello. This KeyShare + * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually + * supported ECDHE KeyShares that the server would prefer over this one but would require 2-RTT's). + */ + const struct s2n_ecc_named_curve *client_default = client_sec_policy->ecc_preferences->ecc_curves[0]; + PTR_ENSURE_REF(client_default); + + for (int i = 0; server_sec_policy->strongly_preferred_groups != NULL && i < server_sec_policy->strongly_preferred_groups->count; i++) { + for (int j = 0; j < client_sec_policy->ecc_preferences->count; j++) { + if (server_sec_policy->strongly_preferred_groups->iana_ids[i] == client_sec_policy->ecc_preferences->ecc_curves[j]->iana_id) { + return client_sec_policy->ecc_preferences->ecc_curves[j]; + } + } + } + + for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; + PTR_ENSURE_REF(server_curve); + if (server_curve->iana_id == client_default->iana_id) { + return client_default; + } + } + + /* Otherwise, if the client's default isn't supported, and a 2-RTT handshake is required, the server will choose + * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ + for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; + + /* j starts at 1 since we already checked client_sec_policy->ecc_preferences->ecc_curves[0] above */ + for (int j = 1; j < client_sec_policy->ecc_preferences->count; j++) { + const struct s2n_ecc_named_curve *client_curve = client_sec_policy->ecc_preferences->ecc_curves[j]; + PTR_ENSURE_REF(client_curve); + PTR_ENSURE_REF(server_curve); + if (client_curve->iana_id == server_curve->iana_id) { + return client_curve; + } + } + } + + return NULL; +} + +int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_policy, + const struct s2n_security_policy *server_sec_policy, const struct s2n_kem_group *expected_kem_group, + const struct s2n_ecc_named_curve *expected_curve, bool hrr_expected, bool len_prefix_expected) +{ + /* XOR check: can expect to negotiate either a KEM group, or a classic EC curve, but not both/neither */ + POSIX_ENSURE((expected_kem_group == NULL) != (expected_curve == NULL), S2N_ERR_SAFETY); + + /* Set up connections */ + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + POSIX_ENSURE_REF(client_conn = s2n_connection_new(S2N_CLIENT)); + POSIX_ENSURE_REF(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_config *client_config = NULL, *server_config = NULL; + POSIX_ENSURE_REF(client_config = s2n_config_new()); + POSIX_ENSURE_REF(server_config = s2n_config_new()); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }, private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + POSIX_ENSURE_REF(chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_connection_set_config(server_conn, server_config)); + + struct s2n_stuffer client_to_server = { 0 }, server_to_client = { 0 }; + POSIX_GUARD(s2n_stuffer_growable_alloc(&client_to_server, 2048)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&server_to_client, 2048)); + + POSIX_GUARD(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + POSIX_GUARD(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + client_conn->security_policy_override = client_sec_policy; + server_conn->security_policy_override = server_sec_policy; + + /* Client sends ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + + POSIX_ENSURE_EQ(client_conn->actual_protocol_version, S2N_TLS13); + POSIX_ENSURE_EQ(server_conn->actual_protocol_version, 0); /* Won't get set until after server reads ClientHello */ + POSIX_ENSURE_EQ(client_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + POSIX_ENSURE_EQ(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + + /* Assert that the server chose the correct group */ + if (expected_kem_group) { + /* Client should always determine whether the KEM group used len_prefixed format, and server should match client's behavior. */ + POSIX_ENSURE_EQ(len_prefix_expected, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); + POSIX_ENSURE_EQ(len_prefix_expected, s2n_tls13_client_must_use_hybrid_kem_length_prefix(client_sec_policy->kem_preferences)); + POSIX_ENSURE_EQ(server_conn->kex_params.client_kem_group_params.kem_params.len_prefixed, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); + + POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); + if (expected_kem_group->curve != &s2n_ecc_curve_none) { + POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + } + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } else { + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + if (expected_curve != server_conn->kex_params.server_ecc_evp_params.negotiated_curve) { + const char *expected_name = "NULL"; + const char *actual_name = "NULL"; + if (expected_curve != NULL && expected_curve->name != NULL) { + expected_name = expected_curve->name; + } + if (server_conn->kex_params.server_ecc_evp_params.negotiated_curve != NULL && server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name != NULL) { + actual_name = server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name; + } + + fprintf(stderr, "\n\nError: Unexpected curve was negotiated. Expected: %s, Actual: %s\n", expected_name, actual_name); + fflush(stderr); + } + + POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } + + /* Server sends ServerHello or HRR */ + POSIX_GUARD(s2n_conn_set_handshake_type(server_conn)); + POSIX_ENSURE_EQ(hrr_expected, s2n_handshake_type_check_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + + if (hrr_expected) { + /* Client reads HRR */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + POSIX_GUARD(s2n_conn_set_handshake_type(client_conn)); + POSIX_ENSURE_NE(0, client_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + /* Client reads CCS */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS and new ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS (doesn't change state machine) */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + /* Server reads new ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + /* Server sends ServerHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + } + + /* Client reads ServerHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + + /* We've gotten far enough in the handshake that both client and server should have + * derived the shared secrets, so we don't send/receive any more messages. */ + + /* Assert that the correct group was negotiated (we re-check the server group to assert that + * nothing unexpected changed between then and now while e.g. processing HRR) */ + if (expected_kem_group) { + POSIX_ENSURE_EQ(expected_kem_group, client_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, client_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + if (expected_kem_group->curve != &s2n_ecc_curve_none) { + POSIX_ENSURE_EQ(expected_kem_group->curve, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + } + + /* Ensure s2n_connection_get_kem_group_name() gives the correct answer for both client and server */ + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(server_conn))); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(server_conn), strlen(expected_kem_group->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(client_conn))); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(client_conn), strlen(expected_kem_group->name)), 0); + + /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ + const char *server_group_name = NULL; + const char *client_group_name = NULL; + POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); + POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(server_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, server_group_name, strlen(expected_kem_group->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(client_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, client_group_name, strlen(expected_kem_group->name)), 0); + } else { + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + /* Ensure s2n_connection_get_curve() gives the correct answer for both client and server */ + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(server_conn))); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(server_conn), strlen(expected_curve->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(client_conn))); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(client_conn), strlen(expected_curve->name)), 0); + + /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ + const char *server_group_name = NULL; + const char *client_group_name = NULL; + POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); + POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(server_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, server_group_name, strlen(expected_curve->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(client_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, client_group_name, strlen(expected_curve->name)), 0); + } + + /* Verify basic properties of secrets */ + s2n_tls13_connection_keys(server_secret_info, server_conn); + s2n_tls13_connection_keys(client_secret_info, client_conn); + POSIX_ENSURE_EQ(server_conn->secure->cipher_suite, client_conn->secure->cipher_suite); + if (server_conn->secure->cipher_suite == &s2n_tls13_aes_256_gcm_sha384) { + POSIX_ENSURE_EQ(server_secret_info.size, 48); + POSIX_ENSURE_EQ(client_secret_info.size, 48); + } else { + POSIX_ENSURE_EQ(server_secret_info.size, 32); + POSIX_ENSURE_EQ(client_secret_info.size, 32); + } + + /* Verify secrets aren't just zero'ed memory */ + uint8_t all_zeros[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + POSIX_CHECKED_MEMSET((void *) all_zeros, 0, S2N_TLS13_SECRET_MAX_LEN); + struct s2n_tls13_secrets *client_secrets = &client_conn->secrets.version.tls13; + struct s2n_tls13_secrets *server_secrets = &server_conn->secrets.version.tls13; + POSIX_ENSURE_EQ(server_secret_info.size, client_secret_info.size); + uint8_t size = server_secret_info.size; + POSIX_ENSURE_EQ(client_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->extract_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->client_handshake_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->server_handshake_secret, size)); + POSIX_ENSURE_EQ(server_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->extract_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->client_handshake_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->server_handshake_secret, size)); + + /* Verify client and server secrets are equal to each other */ + POSIX_ENSURE_EQ(0, memcmp(server_secrets->extract_secret, client_secrets->extract_secret, size)); + POSIX_ENSURE_EQ(0, memcmp(server_secrets->client_handshake_secret, client_secrets->client_handshake_secret, size)); + POSIX_ENSURE_EQ(0, memcmp(server_secrets->server_handshake_secret, client_secrets->server_handshake_secret, size)); + + /* Clean up */ + POSIX_GUARD(s2n_stuffer_free(&client_to_server)); + POSIX_GUARD(s2n_stuffer_free(&server_to_client)); + + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_connection_free(server_conn)); + + POSIX_GUARD(s2n_cert_chain_and_key_free(chain_and_key)); + POSIX_GUARD(s2n_config_free(server_config)); + POSIX_GUARD(s2n_config_free(client_config)); + + return S2N_SUCCESS; +} + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Additional KEM preferences/security policies to test against. These policies can only be used + * as the server's policy in this test: when generating the ClientHello, the client relies on + * the security_policy_selection[] array (in s2n_security_policies.c) to determine if it should + * write the supported_groups extension. Because these unofficial policies don't exist in that + * array, the supported_groups extension won't get sent and the handshake won't complete as expected. */ + + const struct s2n_kem_group *mlkem768_test_groups[] = { + &s2n_x25519_mlkem_768, + &s2n_secp256r1_mlkem_768, + }; + + const struct s2n_kem_preferences mlkem768_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(mlkem768_test_groups), + .tls13_kem_groups = mlkem768_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy mlkem768_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &mlkem768_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct s2n_kem_group *mlkem1024_test_groups[] = { + &s2n_secp384r1_mlkem_1024, + }; + + const struct s2n_kem_preferences mlkem1024_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(mlkem1024_test_groups), + .tls13_kem_groups = mlkem1024_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy mlkem1024_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &mlkem1024_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct s2n_kem_group *pure_mlkem1024_test_groups[] = { + &s2n_pure_mlkem_1024, + }; + + const struct s2n_kem_preferences pure_mlkem1024_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(pure_mlkem1024_test_groups), + .tls13_kem_groups = pure_mlkem1024_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy pure_mlkem1024_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &pure_mlkem1024_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct s2n_ecc_named_curve *default_curve = &s2n_ecc_curve_x25519; + + if (!s2n_is_evp_apis_supported()) { + default_curve = &s2n_ecc_curve_secp256r1; + } + + struct pq_handshake_test_vector { + const struct s2n_security_policy *client_policy; + const struct s2n_security_policy *server_policy; + const struct s2n_kem_group *expected_kem_group; + const struct s2n_ecc_named_curve *expected_curve; + bool hrr_expected; + bool len_prefix_expected; + }; + + /* Self talk test with each TLS 1.3 KemGroup we support */ + for (size_t i = 0; i < S2N_KEM_GROUPS_COUNT; i++) { + const struct s2n_kem_group *kem_group = ALL_SUPPORTED_KEM_GROUPS[i]; + + if (kem_group == NULL || !s2n_kem_group_is_available(kem_group)) { + continue; + } + + const struct s2n_kem_preferences singleton_test_pref = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = 1, + .tls13_kem_groups = &kem_group, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy singleton_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &singleton_test_pref, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct pq_handshake_test_vector test_vec = { + .client_policy = &singleton_test_policy, + .server_policy = &singleton_test_policy, + .expected_kem_group = kem_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }; + + EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(test_vec.client_policy, test_vec.server_policy, + test_vec.expected_kem_group, test_vec.expected_curve, test_vec.hrr_expected, test_vec.len_prefix_expected)); + } + + /* ML-KEM is only available on newer versions of AWS-LC. If it's + * unavailable, we must downgrade the assertions to EC. */ + const struct s2n_kem_group *null_if_no_mlkem_768 = &s2n_x25519_mlkem_768; + const struct s2n_kem_group *null_if_no_mlkem_1024 = &s2n_secp384r1_mlkem_1024; + const struct s2n_kem_group *null_if_no_pure_mlkem_1024 = &s2n_pure_mlkem_1024; + const struct s2n_ecc_named_curve *ec_if_no_mlkem = NULL; + bool hrr_expected_if_mlkem = true; + if (!s2n_libcrypto_supports_mlkem()) { + null_if_no_mlkem_768 = NULL; + null_if_no_mlkem_1024 = NULL; + null_if_no_pure_mlkem_1024 = NULL; + ec_if_no_mlkem = default_curve; + hrr_expected_if_mlkem = false; + } + + /* Test vectors that expect to negotiate PQ assume that PQ is enabled in s2n. + * If PQ is disabled, the expected negotiation outcome is overridden below + * before performing the handshake test. */ + const struct pq_handshake_test_vector test_vectors[] = { + /* Server does not support PQ; client sends a PQ key share and an EC key share; + * server should negotiate EC without HRR. */ + { + .client_policy = &security_policy_pq_tls_1_2_2024_10_09, + .server_policy = &security_policy_test_all_tls13, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server does not support PQ; client sends a PQ key share, but no EC shares; + * server should negotiate EC and send HRR. */ + { + .client_policy = &security_policy_test_tls13_retry_with_pq, + .server_policy = &security_policy_test_all_tls13, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = true, + .len_prefix_expected = true, + }, + + /* Server supports PQ, but client does not. Client sent an EC share, + * EC should be negotiated without HRR */ + { + .client_policy = &security_policy_test_all_tls13, + .server_policy = &security_policy_pq_tls_1_2_2024_10_09, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server supports PQ, but client does not. Client did not send any EC shares, + * EC should be negotiated after exchanging HRR */ + { + .client_policy = &security_policy_test_tls13_retry, + .server_policy = &security_policy_pq_tls_1_2_2024_10_09, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = true, + .len_prefix_expected = true, + }, + + /* Confirm that MLKEM768 is negotiable */ + { + .client_policy = &mlkem768_test_policy, + .server_policy = &mlkem768_test_policy, + .expected_kem_group = null_if_no_mlkem_768, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Confirm that MLKEM1024 is negotiable */ + { + .client_policy = &mlkem1024_test_policy, + .server_policy = &mlkem1024_test_policy, + .expected_kem_group = null_if_no_mlkem_1024, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Confirm that pure MLKEM1024 is negotiable; fall back to EC when MLKEM is not supported. */ + { + .client_policy = &pure_mlkem1024_test_policy, + .server_policy = &pure_mlkem1024_test_policy, + .expected_kem_group = null_if_no_pure_mlkem_1024, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Client supports pure MLKEM but did not send that key share. Pure MLKEM should be negotiated after exchanging HRR. + * If ML-KEM is not supported, EC should be negotiated without HRR. */ + { + .client_policy = &security_policy_test_all, + .server_policy = &pure_mlkem1024_test_policy, + .expected_kem_group = null_if_no_pure_mlkem_1024, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = hrr_expected_if_mlkem, + .len_prefix_expected = false, + }, + + /* Client supports p384 but did not send that key share when connecting to server that strongly prefers p384. */ + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251113, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251114, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251115, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251116, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + }; + + for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { + const struct pq_handshake_test_vector *vector = &test_vectors[i]; + const struct s2n_security_policy *client_policy = vector->client_policy; + const struct s2n_security_policy *server_policy = vector->server_policy; + const struct s2n_kem_group *kem_group = vector->expected_kem_group; + const struct s2n_ecc_named_curve *curve = vector->expected_curve; + bool hrr_expected = vector->hrr_expected; + bool len_prefix_expected = vector->len_prefix_expected; + + /* Print Test Vector Info for easier debugging on failure. */ + { + const char *kem_group_str = (kem_group == NULL) ? "NULL" : kem_group->name; + const char *curve_str = (curve == NULL) ? "NULL" : curve->name; + fprintf(stderr, "\n\nRunning test (%zu/%zu)...\n", i + 1, s2n_array_len(test_vectors)); + fflush(stderr); + fprintf(stderr, "Test Vector: kem_group: %s, curve: %s, hrr_expected: %d, len_prefix_expected: %d\n", kem_group_str, curve_str, hrr_expected, len_prefix_expected); + fflush(stderr); + uint32_t output_size = 0; + fprintf(stderr, "\nClient Security Policy: \n"); + EXPECT_SUCCESS(s2n_security_policy_write_fd(client_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); + + fprintf(stderr, "\nServer Security Policy: \n"); + EXPECT_SUCCESS(s2n_security_policy_write_fd(server_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); + fflush(stderr); + } + + /* Check if we need to override the test vector. This may need to be done due to some LibCryptos not supporting PQ. */ + if (!s2n_kem_group_is_available(kem_group)) { + kem_group = NULL; + if (curve == NULL) { + curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); + } + } + + if (!s2n_pq_is_enabled()) { + EXPECT_TRUE(client_policy->ecc_preferences->count > 0); + const struct s2n_ecc_named_curve *client_default = client_policy->ecc_preferences->ecc_curves[0]; + const struct s2n_ecc_named_curve *predicted_curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); + + /* If the default curve is x25519, and the test vector is predicting the default, but neither policy supports x25519, + * fall back to predict p256 as it should be in common with every ECC preference list. */ + if (default_curve->iana_id == s2n_ecc_curve_x25519.iana_id + && curve->iana_id == default_curve->iana_id + && (!s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id) + || !s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id))) { + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); + curve = &s2n_ecc_curve_secp256r1; + } + + /* The client's preferred curve will be a higher priority than the default if both sides + * support TLS 1.3, and if the client's default can be chosen by the server in 1-RTT. */ + if (s2n_security_policy_supports_tls13(client_policy) && s2n_security_policy_supports_tls13(server_policy) + && (server_policy->strongly_preferred_groups == NULL || server_policy->strongly_preferred_groups->count == 0) + && s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, client_default->iana_id)) { + curve = client_default; + } + + /* Finally, confirm that the expected curve listed in the test vector matches the output of s2n_get_predicted_negotiated_ecdhe_curve() */ + EXPECT_EQUAL(curve->iana_id, predicted_curve->iana_id); + } + + if (kem_group != NULL) { + const struct s2n_kem_group *predicted_kem_group = s2n_get_predicted_negotiated_kem_group(client_policy, server_policy); + POSIX_ENSURE_REF(predicted_kem_group); + + /* Confirm that the expected KEM Group listed in the test vector matches the output of + * s2n_get_predicted_negotiated_kem_group() */ + POSIX_ENSURE_EQ(kem_group->iana_id, predicted_kem_group->iana_id); + } + + EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, kem_group, curve, hrr_expected, len_prefix_expected)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_server_cert_test.c b/tests/unit/s2n_tls13_server_cert_test.c index c18ab419dfb..046852b2f81 100644 --- a/tests/unit/s2n_tls13_server_cert_test.c +++ b/tests/unit/s2n_tls13_server_cert_test.c @@ -1,199 +1,200 @@ -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* Test vectors from https://tools.ietf.org/html/rfc8448#section-3 */ - -/* whole cert message without 0b0001b9 header */ -const char tls13_cert_message_hex[] = - "000001b50001b03082" - "01ac30820115a003020102020102300d06092a8648" - "86f70d01010b0500300e310c300a06035504031303" - "727361301e170d3136303733303031323335395a17" - "0d3236303733303031323335395a300e310c300a06" - "03550403130372736130819f300d06092a864886f7" - "0d010101050003818d0030818902818100b4bb498f" - "8279303d980836399b36c6988c0c68de55e1bdb826" - "d3901a2461eafd2de49a91d015abbc9a95137ace6c" - "1af19eaa6af98c7ced43120998e187a80ee0ccb052" - "4b1b018c3e0b63264d449a6d38e22a5fda43084674" - "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" - "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" - "010001a31a301830090603551d1304023000300b06" - "03551d0f0404030205a0300d06092a864886f70d01" - "010b05000381810085aad2a0e5b9276b908c65f73a" - "7267170618a54c5f8a7b337d2df7a594365417f2ea" - "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" - "5156726096fd335e5e67f2dbf102702e608ccae6be" - "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" - "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" - "961229ac9187b42b4de10000"; - -/* cert only */ -const char tls13_cert_hex[] = - "3082" /* without certificate chain header */ - "01ac30820115a003020102020102300d06092a8648" - "86f70d01010b0500300e310c300a06035504031303" - "727361301e170d3136303733303031323335395a17" - "0d3236303733303031323335395a300e310c300a06" - "03550403130372736130819f300d06092a864886f7" - "0d010101050003818d0030818902818100b4bb498f" - "8279303d980836399b36c6988c0c68de55e1bdb826" - "d3901a2461eafd2de49a91d015abbc9a95137ace6c" - "1af19eaa6af98c7ced43120998e187a80ee0ccb052" - "4b1b018c3e0b63264d449a6d38e22a5fda43084674" - "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" - "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" - "010001a31a301830090603551d1304023000300b06" - "03551d0f0404030205a0300d06092a864886f70d01" - "010b05000381810085aad2a0e5b9276b908c65f73a" - "7267170618a54c5f8a7b337d2df7a594365417f2ea" - "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" - "5156726096fd335e5e67f2dbf102702e608ccae6be" - "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" - "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" - "961229ac9187b42b4de1"; - -/* certificate chain header. It contains - 1. Request Context length (00) - 2. Cert chain length (0001b5) - 3. Cert length (0001b0) - */ -const char tls13_cert_chain_header_hex[] = - "000001b50001b0"; - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Test s2n_server_cert_recv() parses tls13 certificate */ - { - S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_message_hex); - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - conn->x509_validator.skip_cert_validation = 1; - - /* success case in tls13 parsing mode */ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); - EXPECT_SUCCESS(s2n_server_cert_recv(conn)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), 0); - - /* failure case in tls12 parsing mode */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); - EXPECT_FAILURE(s2n_server_cert_recv(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Test s2n_server_cert_send() verify server's certificate */ - { - char *tls13_cert_chain_hex = NULL; - /* creating a certificate chain by concatenating - 1. chain header - 2. certificate - */ - EXPECT_NOT_NULL(tls13_cert_chain_hex = malloc(S2N_MAX_TEST_PEM_SIZE)); - strcpy(tls13_cert_chain_hex, tls13_cert_chain_header_hex); - strcat(tls13_cert_chain_hex, tls13_cert_hex); - - S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_chain_hex); - S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); - - struct s2n_connection *conn = NULL; - uint8_t certificate_request_context_len = 0; - - struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; - /* .chain_size is size of cert + 3 for the 3 bytes to express the length */ - struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; - struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; - - /* tls13 mode */ - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - conn->actual_protocol_version = S2N_TLS13; - conn->handshake_params.our_chain_and_key = &cert_chain_and_key; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); - EXPECT_SUCCESS(s2n_server_cert_send(conn)); - - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size + 2); - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->handshake.io, &certificate_request_context_len)); - - /* server's certificate request context should always be of zero length */ - EXPECT_EQUAL(certificate_request_context_len, 0); - EXPECT_SUCCESS(s2n_connection_free(conn)); - - /* tls12 mode */ - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - conn->actual_protocol_version = S2N_TLS12; - conn->handshake_params.our_chain_and_key = &cert_chain_and_key; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - EXPECT_SUCCESS(s2n_server_cert_send(conn)); - /* In tls1.2 there is no certificate request context. - TLS1.2 Cert length = TLS1.3 Cert length -1 (server's request context)*/ - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size - 1); - EXPECT_SUCCESS(s2n_connection_free(conn)); - - free(tls13_cert_chain_hex); - } - - /* Test server sends cert and client receives cert for tls 1.3 */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - server_conn->actual_protocol_version = S2N_TLS13; - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->x509_validator.skip_cert_validation = 1; - - S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); - S2N_BLOB_FROM_HEX(tls13_cert_message, tls13_cert_message_hex); - - struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; - struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; - struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; - server_conn->handshake_params.our_chain_and_key = &cert_chain_and_key; - - EXPECT_SUCCESS(s2n_server_cert_send(server_conn)); - EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), tls13_cert_message.size); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); - EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), tls13_cert_message.size); - EXPECT_SUCCESS(s2n_server_cert_recv(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* Test vectors from https://tools.ietf.org/html/rfc8448#section-3 */ + +/* whole cert message without 0b0001b9 header */ +const char tls13_cert_message_hex[] = + "000001b50001b03082" + "01ac30820115a003020102020102300d06092a8648" + "86f70d01010b0500300e310c300a06035504031303" + "727361301e170d3136303733303031323335395a17" + "0d3236303733303031323335395a300e310c300a06" + "03550403130372736130819f300d06092a864886f7" + "0d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826" + "d3901a2461eafd2de49a91d015abbc9a95137ace6c" + "1af19eaa6af98c7ced43120998e187a80ee0ccb052" + "4b1b018c3e0b63264d449a6d38e22a5fda43084674" + "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" + "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" + "010001a31a301830090603551d1304023000300b06" + "03551d0f0404030205a0300d06092a864886f70d01" + "010b05000381810085aad2a0e5b9276b908c65f73a" + "7267170618a54c5f8a7b337d2df7a594365417f2ea" + "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" + "5156726096fd335e5e67f2dbf102702e608ccae6be" + "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" + "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" + "961229ac9187b42b4de10000"; + +/* cert only */ +const char tls13_cert_hex[] = + "3082" /* without certificate chain header */ + "01ac30820115a003020102020102300d06092a8648" + "86f70d01010b0500300e310c300a06035504031303" + "727361301e170d3136303733303031323335395a17" + "0d3236303733303031323335395a300e310c300a06" + "03550403130372736130819f300d06092a864886f7" + "0d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826" + "d3901a2461eafd2de49a91d015abbc9a95137ace6c" + "1af19eaa6af98c7ced43120998e187a80ee0ccb052" + "4b1b018c3e0b63264d449a6d38e22a5fda43084674" + "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" + "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" + "010001a31a301830090603551d1304023000300b06" + "03551d0f0404030205a0300d06092a864886f70d01" + "010b05000381810085aad2a0e5b9276b908c65f73a" + "7267170618a54c5f8a7b337d2df7a594365417f2ea" + "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" + "5156726096fd335e5e67f2dbf102702e608ccae6be" + "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" + "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" + "961229ac9187b42b4de1"; + +/* certificate chain header. It contains + 1. Request Context length (00) + 2. Cert chain length (0001b5) + 3. Cert length (0001b0) + */ +const char tls13_cert_chain_header_hex[] = + "000001b50001b0"; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_server_cert_recv() parses tls13 certificate */ + { + S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_message_hex); + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + conn->x509_validator.skip_cert_validation = 1; + + /* success case in tls13 parsing mode */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); + EXPECT_SUCCESS(s2n_server_cert_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), 0); + + /* failure case in tls12 parsing mode */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); + EXPECT_FAILURE(s2n_server_cert_recv(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test s2n_server_cert_send() verify server's certificate */ + { + char *tls13_cert_chain_hex = NULL; + /* creating a certificate chain by concatenating + 1. chain header + 2. certificate + */ + EXPECT_NOT_NULL(tls13_cert_chain_hex = malloc(S2N_MAX_TEST_PEM_SIZE)); + strcpy(tls13_cert_chain_hex, tls13_cert_chain_header_hex); + strcat(tls13_cert_chain_hex, tls13_cert_hex); + + S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_chain_hex); + S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); + + struct s2n_connection *conn = NULL; + uint8_t certificate_request_context_len = 0; + + struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; + /* .chain_size is size of cert + 3 for the 3 bytes to express the length */ + struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; + struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; + + /* tls13 mode */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_server_cert_send(conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size + 2); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->handshake.io, &certificate_request_context_len)); + + /* server's certificate request context should always be of zero length */ + EXPECT_EQUAL(certificate_request_context_len, 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* tls12 mode */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_SUCCESS(s2n_server_cert_send(conn)); + /* In tls1.2 there is no certificate request context. + TLS1.2 Cert length = TLS1.3 Cert length -1 (server's request context)*/ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size - 1); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + free(tls13_cert_chain_hex); + } + + /* Test server sends cert and client receives cert for tls 1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->x509_validator.skip_cert_validation = 1; + + S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); + S2N_BLOB_FROM_HEX(tls13_cert_message, tls13_cert_message_hex); + + struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; + struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; + struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; + server_conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + + EXPECT_SUCCESS(s2n_server_cert_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), tls13_cert_message.size); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), tls13_cert_message.size); + EXPECT_SUCCESS(s2n_server_cert_recv(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls_prf_test.c b/tests/unit/s2n_tls_prf_test.c index 3587bacc49b..02eef7d02d0 100644 --- a/tests/unit/s2n_tls_prf_test.c +++ b/tests/unit/s2n_tls_prf_test.c @@ -1,528 +1,529 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_libcrypto.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -/* To gain access to handshake_read and handshake_write */ -#include "tls/s2n_handshake_io.c" - -#define TEST_BLOB_SIZE 64 - -int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, - struct s2n_blob *label, struct s2n_blob *seed_a, - struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out); -S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, - struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output); -S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, - struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); -int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret); - -/* - * Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0 - * - * |<9>| INT: PREMASTER SECRET[48]: 0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748 - * |<9>| INT: CLIENT RANDOM[32]: 537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286 - * |<9>| INT: SERVER RANDOM[32]: 537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821 - * |<9>| INT: MASTER SECRET: c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120 - */ -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - S2N_BLOB_FROM_HEX(premaster_secret_in, - "0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748"); - S2N_BLOB_FROM_HEX(client_random_in, - "537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286"); - S2N_BLOB_FROM_HEX(server_random_in, - "537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821"); - S2N_BLOB_FROM_HEX(master_secret_in, - "c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120"); - - /* s2n_prf tests */ - { - uint8_t secret_bytes[TEST_BLOB_SIZE] = "secret"; - struct s2n_blob secret = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&secret, secret_bytes, sizeof(secret_bytes))); - - uint8_t label_bytes[TEST_BLOB_SIZE] = "label"; - struct s2n_blob label = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&label, label_bytes, sizeof(label_bytes))); - - uint8_t seed_a_bytes[TEST_BLOB_SIZE] = "seed a"; - struct s2n_blob seed_a = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seed_a, seed_a_bytes, sizeof(seed_a_bytes))); - - uint8_t seed_b_bytes[TEST_BLOB_SIZE] = "seed b"; - struct s2n_blob seed_b = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seed_b, seed_b_bytes, sizeof(seed_b_bytes))); - - uint8_t seed_c_bytes[TEST_BLOB_SIZE] = "seed c"; - struct s2n_blob seed_c = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seed_c, seed_c_bytes, sizeof(seed_c_bytes))); - - /* Safety */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(NULL, &secret, &label, &seed_a, &seed_b, &seed_c, &out), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, NULL, &label, &seed_a, &seed_b, &seed_c, &out), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, NULL, &seed_a, &seed_b, &seed_c, &out), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, NULL), - S2N_ERR_NULL); - - /* seed_a is required */ - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, NULL, &seed_b, &seed_c, &out), - S2N_ERR_PRF_INVALID_SEED); - - /* seed_b and seed_c are optional */ - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, NULL, NULL, &out)); - - /* seed_b is required if seed_c is provided */ - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, NULL, &seed_c, &out), - S2N_ERR_PRF_INVALID_SEED); - - /* seed_c is optional */ - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, NULL, &out)); - } - - /* Test: secret and label */ - { - struct s2n_blob empty = { 0 }; - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "d6 4d 7e e1 f3 ca ff a0 d4 7e 84 18 ff 64 97 d8" - "56 4d d1 99 5e ea 53 0d 29 b0 42 68 89 45 f3 58 86" - "f5 4b 12 ff b3 83 87 80 d5 ba 7d f6 26 10 a5 cc 39" - "d9 e7 37 6e eb 28 8e 21 29 53 13 54 f5 f6"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &empty, NULL, NULL, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: secret, label, and seed_a */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "4e f2 13 65 22 07 f1 e5 86 dc 03 aa 60 a2 c9 5c" - "ab 05 5e 43 f0 c8 3a 70 6c f4 be 21 44 9e b8 45 70" - "d9 e8 e1 83 3b f7 65 dd e9 d1 ea ae 14 f7 73 c5 47" - "57 da 0a a8 5f cc 75 ff b2 9e 3d 02 01 e9"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, NULL, NULL, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: secret, label, seed_a, and seed_b */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "45 0b b0 da 24 d5 23 05 b6 00 f9 1c 71 6f ca 40" - "54 ba 1b 6c 91 43 77 9c 0f 6f 2c e6 e4 24 30 ee b3" - "fc 83 85 3a 02 60 1e 37 2c 2c ee d3 b1 8a 6a cc 75" - "d9 84 87 42 d8 b6 a9 9c 1b f8 72 8c 6b 71"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, NULL, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: all inputs */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "4f b6 e2 ac d0 68 dc 55 70 43 ab 98 f6 23 a8 93" - "6b f2 0f 48 1c 74 50 7e a0 f2 ef 49 53 a6 b0 56 84" - "fe 2e a9 76 31 50 44 f7 8d e7 3d 52 97 ce 36 82 a8" - "d7 27 59 f7 7a 73 19 06 6c 0a 1a df 68 c6"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: cipher specific digest */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS12; - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - const char *expected_hex = - "5b 54 71 ec b9 8a 49 ce f1 6a d9 31 cf c9 76 be" - "5a e6 25 bd a3 45 69 45 8c 6c 1a a1 98 06 d9 0d cc" - "c8 cd 7c aa d7 e2 59 25 4b 36 ff f7 01 a6 7e 89 22" - "0f bd 06 15 bf 9d 7e d1 53 45 a3 1b 36 da"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: large seeds fail for openssl-3.0-fips */ - if (s2n_libcrypto_is_openssl_fips()) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - DEFER_CLEANUP(struct s2n_blob small_seed = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&small_seed, 30)); - - DEFER_CLEANUP(struct s2n_blob medium_seed = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&medium_seed, 300)); - - DEFER_CLEANUP(struct s2n_blob large_seed = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&large_seed, 3000)); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_FAILURE_WITH_ERRNO( - s2n_prf(conn, &secret, &small_seed, &small_seed, &small_seed, &large_seed, &out), - S2N_ERR_PRF_INVALID_SEED); - EXPECT_FAILURE_WITH_ERRNO( - s2n_prf(conn, &secret, &medium_seed, &medium_seed, &medium_seed, &medium_seed, &out), - S2N_ERR_PRF_INVALID_SEED); - EXPECT_SUCCESS(s2n_prf(conn, &secret, - &small_seed, &small_seed, &small_seed, &small_seed, &out)); - } - - /* The custom PRF implementation is used when s2n-tls is not operating in FIPS mode */ - if (!s2n_is_in_fips_mode()) { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - - uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - - /* The custom PRF implementation should modify the digest fields in the prf_space */ - EXPECT_NOT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - } - - /* The libcrypto PRF implementation is used when s2n-tls is in FIPS mode */ - if (s2n_is_in_fips_mode()) { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - - uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - - /* The libcrypto PRF implementation will not modify the digest fields in the prf_space */ - EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - } - } - - /* s2n_tls_prf_master_secret */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - /* Check the most common PRF */ - conn->actual_protocol_version = S2N_TLS11; - - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - - struct s2n_blob pms = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); - EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - }; - - /* s2n_tls_prf_extended_master_secret */ - { - /* The test premaster secret, hash digest, and resulting - * extended master secret were pulled from an OpenSSL TLS1.2 EMS session - * using the s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384 ciphersuite. - */ - S2N_BLOB_FROM_HEX(premaster_secret, - "05e12675c9264d82b53fa15d589c829af9be1ae3d881ab0b023b7b8cad8bc058"); - - S2N_BLOB_FROM_HEX(hash_digest, - "e6cbbaa03909ea387714fe70c07546086dedfcee086fd2985dfdd50924393619" - "009115758e490e2e3b0c13bebdad5fbb"); - - S2N_BLOB_FROM_HEX(extended_master_secret, - "aef116e65e2cd77d4e96b1ceeadb7912ddd9aaf3a907aa3344ec3a2de6cc3b69" - "9ca768fe389eab3b53c98d8ccd830b06"); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-4 - *= type=test - *# When the extended master secret extension is negotiated in a full - *# handshake, the "master_secret" is computed as - *# - *# master_secret = PRF(pre_master_secret, "extended master secret", - *# session_hash) - *# [0..47]; - */ - EXPECT_OK(s2n_prf_tls_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL)); - EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); - }; - - /* s2n_prf_calculate_master_secret */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - - struct s2n_blob pms = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); - - /* Errors when handshake is not at the Client Key Exchange message */ - EXPECT_FAILURE_WITH_ERRNO(s2n_prf_calculate_master_secret(conn, &pms), S2N_ERR_SAFETY); - - /* Advance handshake to Client Key Exchange message */ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - while (ACTIVE_MESSAGE(conn) != CLIENT_KEY) { - conn->handshake.message_number++; - } - - /* Master secret is calculated when handshake is at Client Key Exchange message*/ - EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - /* s2n_prf_calculate_master_secret will produce the same master secret if given the same inputs */ - EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - conn->ems_negotiated = true; - EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); - - /* Extended master secret calculated is different than the master secret calculated */ - EXPECT_NOT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - }; - - /* s2n_prf_get_digest_for_ems calculates the correct digest to generate an extended master secret. - * Here we test that the retrieved digest is the same as the digest after the Client Key Exchange - * message is added to the transcript hash. - * - *= https://www.rfc-editor.org/rfc/rfc7627#section-3 - *= type=test - *# When a full TLS handshake takes place, we define - *# - *# session_hash = Hash(handshake_messages) - *# - *# where "handshake_messages" refers to all handshake messages sent or - *# received, starting at the ClientHello up to and including the - *# ClientKeyExchange message, including the type and length fields of - *# the handshake messages. - */ - { - struct s2n_cert_chain_and_key *tls12_chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls12_chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, - S2N_DEFAULT_TEST_PRIVATE_KEY)); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls12_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); - - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_KEY)); - - /* Client writes Client Key Exchange message */ - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob digest_for_ems = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&digest_for_ems, data, sizeof(data))); - - /* Get the Client Key transcript */ - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH)); - uint8_t client_key_message_length = s2n_stuffer_data_available(&client_to_server); - uint8_t *client_key_message = s2n_stuffer_raw_read(&client_to_server, client_key_message_length); - struct s2n_blob message = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&message, client_key_message, client_key_message_length)); - - s2n_hmac_algorithm prf_alg = server_conn->secure->cipher_suite->prf_alg; - s2n_hash_algorithm hash_alg = 0; - POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); - EXPECT_OK(s2n_prf_get_digest_for_ems(server_conn, &message, hash_alg, &digest_for_ems)); - - /* Server reads Client Key Exchange message */ - EXPECT_SUCCESS(s2n_stuffer_rewind_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH + client_key_message_length)); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - /* Calculate the digest message after the Server read the Client Key message */ - DEFER_CLEANUP(struct s2n_hash_state current_hash_state = { 0 }, s2n_hash_free); - uint8_t server_digest[S2N_MAX_DIGEST_LEN] = { 0 }; - uint8_t digest_size = 0; - EXPECT_SUCCESS(s2n_hash_digest_size(hash_alg, &digest_size)); - EXPECT_SUCCESS(s2n_hash_new(¤t_hash_state)); - EXPECT_OK(s2n_handshake_copy_hash_state(server_conn, hash_alg, ¤t_hash_state)); - EXPECT_SUCCESS(s2n_hash_digest(¤t_hash_state, server_digest, digest_size)); - - /* Digest for generating the EMS and digest after reading the Client Key message - * should be the same. */ - EXPECT_BYTEARRAY_EQUAL(server_digest, digest_for_ems.data, digest_size); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls12_chain_and_key)); - } - - /* PRF lifecyle */ - { - /* Safety */ - { - EXPECT_ERROR_WITH_ERRNO(s2n_prf_new(NULL), S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(NULL), S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO(s2n_prf_free(NULL), S2N_ERR_NULL); - - struct s2n_connection conn_with_null_prf_space = { 0 }; - EXPECT_NULL(conn_with_null_prf_space.prf_space); - - EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(&conn_with_null_prf_space), S2N_ERR_NULL); - EXPECT_OK(s2n_prf_free(&conn_with_null_prf_space)); - }; - - /* Basic lifecyle */ - { - struct s2n_connection connection = { 0 }; - EXPECT_NULL(connection.prf_space); - - EXPECT_OK(s2n_prf_new(&connection)); - EXPECT_NOT_NULL(connection.prf_space); - - EXPECT_OK(s2n_prf_wipe(&connection)); - EXPECT_NOT_NULL(connection.prf_space); - - EXPECT_OK(s2n_prf_free(&connection)); - EXPECT_NULL(connection.prf_space); - }; - - /* PRF freed by s2n_connection_free_handshake */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(conn->prf_space); - - EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); - EXPECT_NULL(conn->prf_space); - }; - - /* Freed PRF restored by s2n_connection_wipe */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_prf_free(conn)); - EXPECT_NULL(conn->prf_space); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_NOT_NULL(conn->prf_space); - }; - - /* PRF usable throughout connection lifecycle */ - { - struct s2n_blob pms = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&pms, premaster_secret_in.data, premaster_secret_in.size)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf_tls_master_secret(conn, &pms), S2N_ERR_NULL); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - }; - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_libcrypto.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +/* To gain access to handshake_read and handshake_write */ +#include "tls/s2n_handshake_io.c" + +#define TEST_BLOB_SIZE 64 + +int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, + struct s2n_blob *label, struct s2n_blob *seed_a, + struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out); +S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, + struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output); +S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, + struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); +int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret); + +/* + * Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0 + * + * |<9>| INT: PREMASTER SECRET[48]: 0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748 + * |<9>| INT: CLIENT RANDOM[32]: 537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286 + * |<9>| INT: SERVER RANDOM[32]: 537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821 + * |<9>| INT: MASTER SECRET: c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120 + */ +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + S2N_BLOB_FROM_HEX(premaster_secret_in, + "0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748"); + S2N_BLOB_FROM_HEX(client_random_in, + "537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286"); + S2N_BLOB_FROM_HEX(server_random_in, + "537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821"); + S2N_BLOB_FROM_HEX(master_secret_in, + "c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120"); + + /* s2n_prf tests */ + { + uint8_t secret_bytes[TEST_BLOB_SIZE] = "secret"; + struct s2n_blob secret = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&secret, secret_bytes, sizeof(secret_bytes))); + + uint8_t label_bytes[TEST_BLOB_SIZE] = "label"; + struct s2n_blob label = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&label, label_bytes, sizeof(label_bytes))); + + uint8_t seed_a_bytes[TEST_BLOB_SIZE] = "seed a"; + struct s2n_blob seed_a = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_a, seed_a_bytes, sizeof(seed_a_bytes))); + + uint8_t seed_b_bytes[TEST_BLOB_SIZE] = "seed b"; + struct s2n_blob seed_b = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_b, seed_b_bytes, sizeof(seed_b_bytes))); + + uint8_t seed_c_bytes[TEST_BLOB_SIZE] = "seed c"; + struct s2n_blob seed_c = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_c, seed_c_bytes, sizeof(seed_c_bytes))); + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(NULL, &secret, &label, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, NULL, &label, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, NULL, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, NULL), + S2N_ERR_NULL); + + /* seed_a is required */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, NULL, &seed_b, &seed_c, &out), + S2N_ERR_PRF_INVALID_SEED); + + /* seed_b and seed_c are optional */ + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, NULL, NULL, &out)); + + /* seed_b is required if seed_c is provided */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, NULL, &seed_c, &out), + S2N_ERR_PRF_INVALID_SEED); + + /* seed_c is optional */ + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, NULL, &out)); + } + + /* Test: secret and label */ + { + struct s2n_blob empty = { 0 }; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "d6 4d 7e e1 f3 ca ff a0 d4 7e 84 18 ff 64 97 d8" + "56 4d d1 99 5e ea 53 0d 29 b0 42 68 89 45 f3 58 86" + "f5 4b 12 ff b3 83 87 80 d5 ba 7d f6 26 10 a5 cc 39" + "d9 e7 37 6e eb 28 8e 21 29 53 13 54 f5 f6"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &empty, NULL, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: secret, label, and seed_a */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "4e f2 13 65 22 07 f1 e5 86 dc 03 aa 60 a2 c9 5c" + "ab 05 5e 43 f0 c8 3a 70 6c f4 be 21 44 9e b8 45 70" + "d9 e8 e1 83 3b f7 65 dd e9 d1 ea ae 14 f7 73 c5 47" + "57 da 0a a8 5f cc 75 ff b2 9e 3d 02 01 e9"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, NULL, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: secret, label, seed_a, and seed_b */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "45 0b b0 da 24 d5 23 05 b6 00 f9 1c 71 6f ca 40" + "54 ba 1b 6c 91 43 77 9c 0f 6f 2c e6 e4 24 30 ee b3" + "fc 83 85 3a 02 60 1e 37 2c 2c ee d3 b1 8a 6a cc 75" + "d9 84 87 42 d8 b6 a9 9c 1b f8 72 8c 6b 71"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: all inputs */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "4f b6 e2 ac d0 68 dc 55 70 43 ab 98 f6 23 a8 93" + "6b f2 0f 48 1c 74 50 7e a0 f2 ef 49 53 a6 b0 56 84" + "fe 2e a9 76 31 50 44 f7 8d e7 3d 52 97 ce 36 82 a8" + "d7 27 59 f7 7a 73 19 06 6c 0a 1a df 68 c6"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: cipher specific digest */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + const char *expected_hex = + "5b 54 71 ec b9 8a 49 ce f1 6a d9 31 cf c9 76 be" + "5a e6 25 bd a3 45 69 45 8c 6c 1a a1 98 06 d9 0d cc" + "c8 cd 7c aa d7 e2 59 25 4b 36 ff f7 01 a6 7e 89 22" + "0f bd 06 15 bf 9d 7e d1 53 45 a3 1b 36 da"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: large seeds fail for openssl-3.0-fips */ + if (s2n_libcrypto_is_openssl_fips()) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + DEFER_CLEANUP(struct s2n_blob small_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&small_seed, 30)); + + DEFER_CLEANUP(struct s2n_blob medium_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&medium_seed, 300)); + + DEFER_CLEANUP(struct s2n_blob large_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&large_seed, 3000)); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_FAILURE_WITH_ERRNO( + s2n_prf(conn, &secret, &small_seed, &small_seed, &small_seed, &large_seed, &out), + S2N_ERR_PRF_INVALID_SEED); + EXPECT_FAILURE_WITH_ERRNO( + s2n_prf(conn, &secret, &medium_seed, &medium_seed, &medium_seed, &medium_seed, &out), + S2N_ERR_PRF_INVALID_SEED); + EXPECT_SUCCESS(s2n_prf(conn, &secret, + &small_seed, &small_seed, &small_seed, &small_seed, &out)); + } + + /* The custom PRF implementation is used when s2n-tls is not operating in FIPS mode */ + if (!s2n_is_in_fips_mode()) { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + + /* The custom PRF implementation should modify the digest fields in the prf_space */ + EXPECT_NOT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + } + + /* The libcrypto PRF implementation is used when s2n-tls is in FIPS mode */ + if (s2n_is_in_fips_mode()) { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + + /* The libcrypto PRF implementation will not modify the digest fields in the prf_space */ + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + } + } + + /* s2n_tls_prf_master_secret */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Check the most common PRF */ + conn->actual_protocol_version = S2N_TLS11; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); + EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + }; + + /* s2n_tls_prf_extended_master_secret */ + { + /* The test premaster secret, hash digest, and resulting + * extended master secret were pulled from an OpenSSL TLS1.2 EMS session + * using the s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384 ciphersuite. + */ + S2N_BLOB_FROM_HEX(premaster_secret, + "05e12675c9264d82b53fa15d589c829af9be1ae3d881ab0b023b7b8cad8bc058"); + + S2N_BLOB_FROM_HEX(hash_digest, + "e6cbbaa03909ea387714fe70c07546086dedfcee086fd2985dfdd50924393619" + "009115758e490e2e3b0c13bebdad5fbb"); + + S2N_BLOB_FROM_HEX(extended_master_secret, + "aef116e65e2cd77d4e96b1ceeadb7912ddd9aaf3a907aa3344ec3a2de6cc3b69" + "9ca768fe389eab3b53c98d8ccd830b06"); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-4 + *= type=test + *# When the extended master secret extension is negotiated in a full + *# handshake, the "master_secret" is computed as + *# + *# master_secret = PRF(pre_master_secret, "extended master secret", + *# session_hash) + *# [0..47]; + */ + EXPECT_OK(s2n_prf_tls_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL)); + EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); + }; + + /* s2n_prf_calculate_master_secret */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); + + /* Errors when handshake is not at the Client Key Exchange message */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf_calculate_master_secret(conn, &pms), S2N_ERR_SAFETY); + + /* Advance handshake to Client Key Exchange message */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + while (ACTIVE_MESSAGE(conn) != CLIENT_KEY) { + conn->handshake.message_number++; + } + + /* Master secret is calculated when handshake is at Client Key Exchange message*/ + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + /* s2n_prf_calculate_master_secret will produce the same master secret if given the same inputs */ + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + conn->ems_negotiated = true; + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + + /* Extended master secret calculated is different than the master secret calculated */ + EXPECT_NOT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + }; + + /* s2n_prf_get_digest_for_ems calculates the correct digest to generate an extended master secret. + * Here we test that the retrieved digest is the same as the digest after the Client Key Exchange + * message is added to the transcript hash. + * + *= https://www.rfc-editor.org/rfc/rfc7627#section-3 + *= type=test + *# When a full TLS handshake takes place, we define + *# + *# session_hash = Hash(handshake_messages) + *# + *# where "handshake_messages" refers to all handshake messages sent or + *# received, starting at the ClientHello up to and including the + *# ClientKeyExchange message, including the type and length fields of + *# the handshake messages. + */ + { + struct s2n_cert_chain_and_key *tls12_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls12_chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, + S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls12_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_KEY)); + + /* Client writes Client Key Exchange message */ + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest_for_ems = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&digest_for_ems, data, sizeof(data))); + + /* Get the Client Key transcript */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH)); + uint8_t client_key_message_length = s2n_stuffer_data_available(&client_to_server); + uint8_t *client_key_message = s2n_stuffer_raw_read(&client_to_server, client_key_message_length); + struct s2n_blob message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&message, client_key_message, client_key_message_length)); + + s2n_hmac_algorithm prf_alg = server_conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg = 0; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + EXPECT_OK(s2n_prf_get_digest_for_ems(server_conn, &message, hash_alg, &digest_for_ems)); + + /* Server reads Client Key Exchange message */ + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH + client_key_message_length)); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Calculate the digest message after the Server read the Client Key message */ + DEFER_CLEANUP(struct s2n_hash_state current_hash_state = { 0 }, s2n_hash_free); + uint8_t server_digest[S2N_MAX_DIGEST_LEN] = { 0 }; + uint8_t digest_size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(hash_alg, &digest_size)); + EXPECT_SUCCESS(s2n_hash_new(¤t_hash_state)); + EXPECT_OK(s2n_handshake_copy_hash_state(server_conn, hash_alg, ¤t_hash_state)); + EXPECT_SUCCESS(s2n_hash_digest(¤t_hash_state, server_digest, digest_size)); + + /* Digest for generating the EMS and digest after reading the Client Key message + * should be the same. */ + EXPECT_BYTEARRAY_EQUAL(server_digest, digest_for_ems.data, digest_size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls12_chain_and_key)); + } + + /* PRF lifecyle */ + { + /* Safety */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_prf_new(NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_prf_free(NULL), S2N_ERR_NULL); + + struct s2n_connection conn_with_null_prf_space = { 0 }; + EXPECT_NULL(conn_with_null_prf_space.prf_space); + + EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(&conn_with_null_prf_space), S2N_ERR_NULL); + EXPECT_OK(s2n_prf_free(&conn_with_null_prf_space)); + }; + + /* Basic lifecyle */ + { + struct s2n_connection connection = { 0 }; + EXPECT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_new(&connection)); + EXPECT_NOT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_wipe(&connection)); + EXPECT_NOT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_free(&connection)); + EXPECT_NULL(connection.prf_space); + }; + + /* PRF freed by s2n_connection_free_handshake */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_NULL(conn->prf_space); + }; + + /* Freed PRF restored by s2n_connection_wipe */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_prf_free(conn)); + EXPECT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_NOT_NULL(conn->prf_space); + }; + + /* PRF usable throughout connection lifecycle */ + { + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, premaster_secret_in.data, premaster_secret_in.size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf_tls_master_secret(conn, &pms), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_wildcard_hostname_test.c b/tests/unit/s2n_wildcard_hostname_test.c index 011319ae15e..936e45ff3a7 100644 --- a/tests/unit/s2n_wildcard_hostname_test.c +++ b/tests/unit/s2n_wildcard_hostname_test.c @@ -1,212 +1,213 @@ -/* - * 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 "api/s2n.h" -#include "crypto/s2n_certificate.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -struct wildcardify_test_case { - const char *hostname; - const char *output; -}; - -struct wildcardify_test_case wildcardify_test_cases[] = { - { .hostname = "foo.bar.com", .output = "*.bar.com" }, - { .hostname = "localhost", .output = NULL }, - { .hostname = "one.com", .output = "*.com" }, - { .hostname = "foo*.bar*.com*", .output = "*.bar*.com*" }, - { .hostname = "foo.bar.com.", .output = "*.bar.com." }, - { .hostname = "*.a.c", .output = "*.a.c" }, - { .hostname = "*", .output = NULL }, - { .hostname = "foo.", .output = "*." }, -}; - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - const int num_wildcardify_tests = s2n_array_len(wildcardify_test_cases); - for (size_t i = 0; i < num_wildcardify_tests; i++) { - const char *hostname = wildcardify_test_cases[i].hostname; - struct s2n_blob hostname_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) hostname, strlen(hostname))); - uint8_t output[S2N_MAX_SERVER_NAME] = { 0 }; - struct s2n_blob output_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&output_blob, (uint8_t *) (uintptr_t) output, sizeof(output))); - struct s2n_stuffer hostname_stuffer = { 0 }; - struct s2n_stuffer output_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_init(&hostname_stuffer, &hostname_blob)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&hostname_stuffer, hostname_blob.size)); - EXPECT_SUCCESS(s2n_stuffer_init(&output_stuffer, &output_blob)); - EXPECT_SUCCESS(s2n_create_wildcard_hostname(&hostname_stuffer, &output_stuffer)); - - /* Make sure the wildcard generated matches the output we expect. */ - const uint32_t wildcard_len = s2n_stuffer_data_available(&output_stuffer); - const char *expected_output = wildcardify_test_cases[i].output; - if (wildcard_len > 0) { - EXPECT_EQUAL(wildcard_len, strlen(expected_output)); - EXPECT_SUCCESS(memcmp(output, expected_output, wildcard_len)); - } else { - EXPECT_EQUAL(expected_output, NULL); - } - } - - /* s2n_connection_get_certificate_match */ - { - /* Safety checks */ - { - s2n_cert_sni_match match_status = 0; - struct s2n_connection *conn = NULL; - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(NULL, &match_status), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(conn, NULL), - S2N_ERR_INVALID_ARGUMENT); - - /* This API does not work on a client connection */ - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(client_conn, &match_status), - S2N_ERR_CLIENT_MODE); - - /* This API will not work if a certificate isn't selected yet. */ - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(server_conn, &match_status), - S2N_ERR_NO_CERT_FOUND); - } - - const struct { - const char *cert_path; - const char *key_path; - const char *server_name; - s2n_cert_sni_match expected_match_status; - } test_cases[] = { - /* Client does not send an SNI extension */ - { - .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, - .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, - .server_name = NULL, - .expected_match_status = S2N_SNI_NONE, - }, - /* Server has a certificate that matches the client's SNI extension */ - { - .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, - .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, - .server_name = "localhost", - .expected_match_status = S2N_SNI_EXACT_MATCH, - }, - /* Server has a certificate with a domain name containing a wildcard character - * which can be matched to the client's SNI extension */ - { - .cert_path = S2N_RSA_2048_SHA256_WILDCARD_CERT, - .key_path = S2N_RSA_2048_SHA256_WILDCARD_KEY, - .server_name = "alligator.localhost", - .expected_match_status = S2N_SNI_WILDCARD_MATCH, - }, - /* Server does not have a certificate that can be matched to the client's - * SNI extension. */ - { - .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, - .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, - .server_name = "This cert name is unlikely to exist.", - .expected_match_status = S2N_SNI_NO_MATCH, - }, - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[i].cert_path, test_cases[i].key_path)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); - - const char *server_name = test_cases[i].server_name; - if (server_name) { - EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); - } - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - s2n_cert_sni_match match_status = 0; - EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); - EXPECT_EQUAL(match_status, test_cases[i].expected_match_status); - } - - /* Test that cert info can still be retrieved in the case of a failed handshake */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "This cert name is unlikely to exist.")); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_INVALID_HOSTNAME); - - s2n_cert_sni_match match_status = 0; - EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); - EXPECT_EQUAL(match_status, S2N_SNI_NO_MATCH); - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_certificate.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +struct wildcardify_test_case { + const char *hostname; + const char *output; +}; + +struct wildcardify_test_case wildcardify_test_cases[] = { + { .hostname = "foo.bar.com", .output = "*.bar.com" }, + { .hostname = "localhost", .output = NULL }, + { .hostname = "one.com", .output = "*.com" }, + { .hostname = "foo*.bar*.com*", .output = "*.bar*.com*" }, + { .hostname = "foo.bar.com.", .output = "*.bar.com." }, + { .hostname = "*.a.c", .output = "*.a.c" }, + { .hostname = "*", .output = NULL }, + { .hostname = "foo.", .output = "*." }, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const int num_wildcardify_tests = s2n_array_len(wildcardify_test_cases); + for (size_t i = 0; i < num_wildcardify_tests; i++) { + const char *hostname = wildcardify_test_cases[i].hostname; + struct s2n_blob hostname_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) hostname, strlen(hostname))); + uint8_t output[S2N_MAX_SERVER_NAME] = { 0 }; + struct s2n_blob output_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output_blob, (uint8_t *) (uintptr_t) output, sizeof(output))); + struct s2n_stuffer hostname_stuffer = { 0 }; + struct s2n_stuffer output_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&hostname_stuffer, &hostname_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&hostname_stuffer, hostname_blob.size)); + EXPECT_SUCCESS(s2n_stuffer_init(&output_stuffer, &output_blob)); + EXPECT_SUCCESS(s2n_create_wildcard_hostname(&hostname_stuffer, &output_stuffer)); + + /* Make sure the wildcard generated matches the output we expect. */ + const uint32_t wildcard_len = s2n_stuffer_data_available(&output_stuffer); + const char *expected_output = wildcardify_test_cases[i].output; + if (wildcard_len > 0) { + EXPECT_EQUAL(wildcard_len, strlen(expected_output)); + EXPECT_SUCCESS(memcmp(output, expected_output, wildcard_len)); + } else { + EXPECT_EQUAL(expected_output, NULL); + } + } + + /* s2n_connection_get_certificate_match */ + { + /* Safety checks */ + { + s2n_cert_sni_match match_status = 0; + struct s2n_connection *conn = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(NULL, &match_status), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(conn, NULL), + S2N_ERR_INVALID_ARGUMENT); + + /* This API does not work on a client connection */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(client_conn, &match_status), + S2N_ERR_CLIENT_MODE); + + /* This API will not work if a certificate isn't selected yet. */ + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(server_conn, &match_status), + S2N_ERR_NO_CERT_FOUND); + } + + const struct { + const char *cert_path; + const char *key_path; + const char *server_name; + s2n_cert_sni_match expected_match_status; + } test_cases[] = { + /* Client does not send an SNI extension */ + { + .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, + .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, + .server_name = NULL, + .expected_match_status = S2N_SNI_NONE, + }, + /* Server has a certificate that matches the client's SNI extension */ + { + .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, + .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, + .server_name = "localhost", + .expected_match_status = S2N_SNI_EXACT_MATCH, + }, + /* Server has a certificate with a domain name containing a wildcard character + * which can be matched to the client's SNI extension */ + { + .cert_path = S2N_RSA_2048_SHA256_WILDCARD_CERT, + .key_path = S2N_RSA_2048_SHA256_WILDCARD_KEY, + .server_name = "alligator.localhost", + .expected_match_status = S2N_SNI_WILDCARD_MATCH, + }, + /* Server does not have a certificate that can be matched to the client's + * SNI extension. */ + { + .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, + .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, + .server_name = "This cert name is unlikely to exist.", + .expected_match_status = S2N_SNI_NO_MATCH, + }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_path, test_cases[i].key_path)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); + + const char *server_name = test_cases[i].server_name; + if (server_name) { + EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); + } + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + s2n_cert_sni_match match_status = 0; + EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); + EXPECT_EQUAL(match_status, test_cases[i].expected_match_status); + } + + /* Test that cert info can still be retrieved in the case of a failed handshake */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "This cert name is unlikely to exist.")); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_INVALID_HOSTNAME); + + s2n_cert_sni_match match_status = 0; + EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); + EXPECT_EQUAL(match_status, S2N_SNI_NO_MATCH); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_intent_verification_test.c b/tests/unit/s2n_x509_intent_verification_test.c index a37d3c4ca22..fc74f0c39fe 100644 --- a/tests/unit/s2n_x509_intent_verification_test.c +++ b/tests/unit/s2n_x509_intent_verification_test.c @@ -1,636 +1,637 @@ -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static S2N_RESULT s2n_test_pem_paths_from_intent_dir(const char *intent_cert_dir, char *cert_chain_path, - char *leaf_key_path, char *root_cert_path) -{ - sprintf(cert_chain_path, "%s/cert-chain.pem", intent_cert_dir); - sprintf(leaf_key_path, "%s/leaf-key.pem", intent_cert_dir); - sprintf(root_cert_path, "%s/root-cert.pem", intent_cert_dir); - return S2N_RESULT_OK; -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* Test s2n_config_disable_x509_intent_verification(). */ - { - /* Safety */ - { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_intent_verification(NULL), S2N_ERR_INVALID_ARGUMENT); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); - } - - /* The verification is enabled by default. */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - EXPECT_FALSE(config->disable_x509_intent_verification); - } - - /* Disabling the verification on the config updates the proper flags. */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - EXPECT_FALSE(config->disable_x509_intent_verification); - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); - EXPECT_TRUE(config->disable_x509_intent_verification); - } - } - - /* Test certificate intent verification. */ - { - struct { - const char *cert_chain_dir; - s2n_error expected_client_error; - s2n_error expected_server_error; - } test_cases[] = { - { - /* A certificate chain with no optional intent extensions specified. This - * certificate chain includes the mandatory BasicConstraints extension for CA - * certificates, but doesn't include the KeyUsage or ExtendedKeyUsage - * extensions. - */ - .cert_chain_dir = "../pems/intent/cert_chains/no_intent", - /* The KeyUsage and ExtendedKeyUsage extensions are optional, so validation - * should succeed. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with digitalSignature set in the leaf KeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_leaf", - /* Setting digitalSignature is valid for both client and server leaf - * certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, -#if !defined(LIBRESSL_VERSION_NUMBER) - /* This test is skipped for LibreSSL since LibreSSL doesn't consider keyAgreement to be - * valid for client or server leaf certificates: - * https://github.com/libressl/openbsd/blob/8bb14039f52469491bf0058b1efdf0c75db6befc/src/lib/libcrypto/x509/x509_purp.c#L662-L691 - */ - { - /* A certificate chain with keyAgreement set in the leaf KeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_agreement_leaf", - /* Setting keyAgreement is valid for both client and server leaf certificates. */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, -#endif - { - /* A certificate chain with digitalSignature and keyAgreement set in the leaf - * KeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_key_agreement_leaf", - /* Setting digitalSignature OR keyAgreement is valid for both client and server - * leaf certificates. Setting both fields is also valid. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with digitalSignature and contentCommitment set in the leaf - * KeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_content_commitment_leaf", - /* Setting digitalSignature is valid for both client and server leaf - * certificates. contentCommitment is not relevant for client or server - * certificates, but since digitalSignature is set, validation should succeed. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with keyEncipherment set in the leaf KeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_encipherment_leaf", - /* Setting keyEncipherment is valid for server leaf certificates, but is NOT - * valid for client leaf certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with keyCertSign set in the leaf KeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_leaf", - /* Setting keyCertSign is only valid for CA certificates, NOT for leaf - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with keyCertSign set in an intermediate KeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_intermediate", - /* Setting keyCertSign is valid for CA certificates. */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with keyCertSign and contentCommitment set in an - * intermediate KeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_and_content_commitment_intermediate", - /* Setting keyCertSign is valid for CA certificates. The irrelevant - * contentCommitment field should not impact validation. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth and serverAuth set in the leaf - * ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_leaf", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth and serverAuth set in an intermediate - * ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_intermediate", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth, serverAuth, and emailProtection set in - * the leaf ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_leaf", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. The irrelevant emailProtection field should - * not impact validation. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth, serverAuth, and emailProtection set in - * an intermediate ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_intermediate", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. The irrelevant emailProtection field should - * not impact validation. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth set in the leaf ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_leaf", - /* A certificate that sets clientAuth is valid for client certificates, but NOT - * for server certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth set in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_intermediate", - /* A certificate that sets clientAuth is valid for client certificates, but NOT - * for server certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with serverAuth set in the leaf ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_leaf", - /* A certificate that sets serverAuth is valid for server certificates but NOT - * for client certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with serverAuth set in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_intermediate", - /* A certificate that sets serverAuth is valid for server certificates but NOT - * for client certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_leaf", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A long certificate chain with codeSigning set in the fourth intermediate - * ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate_long", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with crlSign set in the leaf KeyUsage extension, but the - * extension is marked as non-critical. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_crl_sign_leaf_non_critical", - /* A certificate that sets crlSign is NOT valid for client or server - * certificates. This should be validated regardless of the extension - * criticality. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning set in the leaf ExtendedKeyUsage - * extension, but the extension is marked as non-critical. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_code_signing_intermediate_non_critical", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. This should be validated regardless of the extension - * criticality. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - }; - - for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { - char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, - leaf_key_path, root_cert_path)); - - /* Intent is verified for server certificates received by the client. */ - for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - cert_chain_path, leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - if (disable_intent_verification) { - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); - } - - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[test_idx].expected_client_error; - if (disable_intent_verification) { - EXPECT_SUCCESS(ret); - } else { - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - } - - /* Intent is verified for client certificates received by the server. */ - for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, - cert_chain_path, leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, root_cert_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - if (disable_intent_verification) { - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); - } - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - 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, client_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[test_idx].expected_server_error; - if (disable_intent_verification) { - EXPECT_SUCCESS(ret); - } else { - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - - /* Ensure that a client certificate was received. In the case of an expected error, - * this ensures that the error occurred on the server side, after the client - * successfully validated the server's certificate. - */ - 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); - } - } - } - - /* Test default s2n-tls intent verification. - * - * s2n-tls already verifies intent fields for intermediate certificates in the call to - * X509_verify_cert. So this verification should continue to be performed whether certificate - * intent verification is enabled or not. - */ - { - struct { - const char *cert_chain_dir; - } test_cases[] = { - { - /* A certificate chain with the CA field set to false in an intermediate - * BasicConstraints extension. - * - * CA certificates MUST set the CA field to true. - */ - .cert_chain_dir = "../pems/intent/cert_chains/bc_non_ca_intermediate", - }, - { - /* A certificate chain with digitalSignature set in an intermediate KeyUsage - * extension. - * - * The digitalSignature field is valid only for leaf certificates, NOT CA - * certificates. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate", - }, - { - /* A long certificate chain with digitalSignature set in the fourth - * intermediate KeyUsage extension. - * - * The digitalSignature field is valid only for leaf certificates, NOT CA - * certificates. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate_long", - }, - }; - - for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { - char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, - leaf_key_path, root_cert_path)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, cert_chain_path, leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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 intent verification doesn't apply to trust anchors. - * - * Despite certificate intent fields that may indicate otherwise, it is assumed that trust - * anchors were intended to be used for a TLS purpose, given that they were included in the - * s2n-tls trust store. - */ - { - struct { - const char *cert_chain_path; - const char *leaf_key_path; - const char *trust_anchor_path; - s2n_error expected_error; - } test_cases[] = { - { - /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. When the invalid - * leaf certificate is not a trust anchor, validation should fail. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/root-cert.pem", - .expected_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. However, this is - * acceptable if the invalid leaf certificate is a trust anchor. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-cert.pem", - .expected_error = S2N_ERR_OK, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. When the invalid - * intermediate certificate is not a trust anchor, validation should fail. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/root-cert.pem", - .expected_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. However, this is - * acceptable if the invalid intermediate certificate is a trust anchor. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/intermediate_1-cert.pem", - .expected_error = S2N_ERR_OK, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. However, this is - * acceptable if the leaf certificate is a trust anchor, since the invalid - * intermediate certificate won't be in the chain of trust. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-cert.pem", - .expected_error = S2N_ERR_OK, - }, - { - /* A certificate that sets emailProtection in the root ExtendedKeyUsage extension. */ - .cert_chain_path = "../pems/intent/cert_chains/eku_email_protection_root/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_email_protection_root/leaf-key.pem", - /* A certificate that sets emailProtection is not valid for TLS. However, this is - * acceptable if the certificate is a trust anchor. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_email_protection_root/root-cert.pem", - .expected_error = S2N_ERR_OK, - }, - }; - - for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[test_idx].cert_chain_path, test_cases[test_idx].leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, - test_cases[test_idx].trust_anchor_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[test_idx].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static S2N_RESULT s2n_test_pem_paths_from_intent_dir(const char *intent_cert_dir, char *cert_chain_path, + char *leaf_key_path, char *root_cert_path) +{ + sprintf(cert_chain_path, "%s/cert-chain.pem", intent_cert_dir); + sprintf(leaf_key_path, "%s/leaf-key.pem", intent_cert_dir); + sprintf(root_cert_path, "%s/root-cert.pem", intent_cert_dir); + return S2N_RESULT_OK; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Test s2n_config_disable_x509_intent_verification(). */ + { + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_intent_verification(NULL), S2N_ERR_INVALID_ARGUMENT); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); + } + + /* The verification is enabled by default. */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(config->disable_x509_intent_verification); + } + + /* Disabling the verification on the config updates the proper flags. */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(config->disable_x509_intent_verification); + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); + EXPECT_TRUE(config->disable_x509_intent_verification); + } + } + + /* Test certificate intent verification. */ + { + struct { + const char *cert_chain_dir; + s2n_error expected_client_error; + s2n_error expected_server_error; + } test_cases[] = { + { + /* A certificate chain with no optional intent extensions specified. This + * certificate chain includes the mandatory BasicConstraints extension for CA + * certificates, but doesn't include the KeyUsage or ExtendedKeyUsage + * extensions. + */ + .cert_chain_dir = "../pems/intent/cert_chains/no_intent", + /* The KeyUsage and ExtendedKeyUsage extensions are optional, so validation + * should succeed. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with digitalSignature set in the leaf KeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_leaf", + /* Setting digitalSignature is valid for both client and server leaf + * certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, +#if !defined(LIBRESSL_VERSION_NUMBER) + /* This test is skipped for LibreSSL since LibreSSL doesn't consider keyAgreement to be + * valid for client or server leaf certificates: + * https://github.com/libressl/openbsd/blob/8bb14039f52469491bf0058b1efdf0c75db6befc/src/lib/libcrypto/x509/x509_purp.c#L662-L691 + */ + { + /* A certificate chain with keyAgreement set in the leaf KeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_agreement_leaf", + /* Setting keyAgreement is valid for both client and server leaf certificates. */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, +#endif + { + /* A certificate chain with digitalSignature and keyAgreement set in the leaf + * KeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_key_agreement_leaf", + /* Setting digitalSignature OR keyAgreement is valid for both client and server + * leaf certificates. Setting both fields is also valid. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with digitalSignature and contentCommitment set in the leaf + * KeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_content_commitment_leaf", + /* Setting digitalSignature is valid for both client and server leaf + * certificates. contentCommitment is not relevant for client or server + * certificates, but since digitalSignature is set, validation should succeed. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with keyEncipherment set in the leaf KeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_encipherment_leaf", + /* Setting keyEncipherment is valid for server leaf certificates, but is NOT + * valid for client leaf certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with keyCertSign set in the leaf KeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_leaf", + /* Setting keyCertSign is only valid for CA certificates, NOT for leaf + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with keyCertSign set in an intermediate KeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_intermediate", + /* Setting keyCertSign is valid for CA certificates. */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with keyCertSign and contentCommitment set in an + * intermediate KeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_and_content_commitment_intermediate", + /* Setting keyCertSign is valid for CA certificates. The irrelevant + * contentCommitment field should not impact validation. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth and serverAuth set in the leaf + * ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_leaf", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth and serverAuth set in an intermediate + * ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_intermediate", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth, serverAuth, and emailProtection set in + * the leaf ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_leaf", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. The irrelevant emailProtection field should + * not impact validation. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth, serverAuth, and emailProtection set in + * an intermediate ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_intermediate", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. The irrelevant emailProtection field should + * not impact validation. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth set in the leaf ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_leaf", + /* A certificate that sets clientAuth is valid for client certificates, but NOT + * for server certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth set in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_intermediate", + /* A certificate that sets clientAuth is valid for client certificates, but NOT + * for server certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with serverAuth set in the leaf ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_leaf", + /* A certificate that sets serverAuth is valid for server certificates but NOT + * for client certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with serverAuth set in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_intermediate", + /* A certificate that sets serverAuth is valid for server certificates but NOT + * for client certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_leaf", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A long certificate chain with codeSigning set in the fourth intermediate + * ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate_long", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with crlSign set in the leaf KeyUsage extension, but the + * extension is marked as non-critical. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_crl_sign_leaf_non_critical", + /* A certificate that sets crlSign is NOT valid for client or server + * certificates. This should be validated regardless of the extension + * criticality. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning set in the leaf ExtendedKeyUsage + * extension, but the extension is marked as non-critical. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_code_signing_intermediate_non_critical", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. This should be validated regardless of the extension + * criticality. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + }; + + for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { + char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, + leaf_key_path, root_cert_path)); + + /* Intent is verified for server certificates received by the client. */ + for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + cert_chain_path, leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + if (disable_intent_verification) { + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); + } + + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[test_idx].expected_client_error; + if (disable_intent_verification) { + EXPECT_SUCCESS(ret); + } else { + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + /* Intent is verified for client certificates received by the server. */ + for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + cert_chain_path, leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, root_cert_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + if (disable_intent_verification) { + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); + } + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + 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, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[test_idx].expected_server_error; + if (disable_intent_verification) { + EXPECT_SUCCESS(ret); + } else { + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + + /* Ensure that a client certificate was received. In the case of an expected error, + * this ensures that the error occurred on the server side, after the client + * successfully validated the server's certificate. + */ + 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); + } + } + } + + /* Test default s2n-tls intent verification. + * + * s2n-tls already verifies intent fields for intermediate certificates in the call to + * X509_verify_cert. So this verification should continue to be performed whether certificate + * intent verification is enabled or not. + */ + { + struct { + const char *cert_chain_dir; + } test_cases[] = { + { + /* A certificate chain with the CA field set to false in an intermediate + * BasicConstraints extension. + * + * CA certificates MUST set the CA field to true. + */ + .cert_chain_dir = "../pems/intent/cert_chains/bc_non_ca_intermediate", + }, + { + /* A certificate chain with digitalSignature set in an intermediate KeyUsage + * extension. + * + * The digitalSignature field is valid only for leaf certificates, NOT CA + * certificates. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate", + }, + { + /* A long certificate chain with digitalSignature set in the fourth + * intermediate KeyUsage extension. + * + * The digitalSignature field is valid only for leaf certificates, NOT CA + * certificates. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate_long", + }, + }; + + for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { + char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, + leaf_key_path, root_cert_path)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, cert_chain_path, leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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 intent verification doesn't apply to trust anchors. + * + * Despite certificate intent fields that may indicate otherwise, it is assumed that trust + * anchors were intended to be used for a TLS purpose, given that they were included in the + * s2n-tls trust store. + */ + { + struct { + const char *cert_chain_path; + const char *leaf_key_path; + const char *trust_anchor_path; + s2n_error expected_error; + } test_cases[] = { + { + /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. When the invalid + * leaf certificate is not a trust anchor, validation should fail. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/root-cert.pem", + .expected_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. However, this is + * acceptable if the invalid leaf certificate is a trust anchor. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-cert.pem", + .expected_error = S2N_ERR_OK, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. When the invalid + * intermediate certificate is not a trust anchor, validation should fail. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/root-cert.pem", + .expected_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. However, this is + * acceptable if the invalid intermediate certificate is a trust anchor. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/intermediate_1-cert.pem", + .expected_error = S2N_ERR_OK, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. However, this is + * acceptable if the leaf certificate is a trust anchor, since the invalid + * intermediate certificate won't be in the chain of trust. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-cert.pem", + .expected_error = S2N_ERR_OK, + }, + { + /* A certificate that sets emailProtection in the root ExtendedKeyUsage extension. */ + .cert_chain_path = "../pems/intent/cert_chains/eku_email_protection_root/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_email_protection_root/leaf-key.pem", + /* A certificate that sets emailProtection is not valid for TLS. However, this is + * acceptable if the certificate is a trust anchor. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_email_protection_root/root-cert.pem", + .expected_error = S2N_ERR_OK, + }, + }; + + for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[test_idx].cert_chain_path, test_cases[test_idx].leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, + test_cases[test_idx].trust_anchor_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[test_idx].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_validator_certificate_signatures_test.c b/tests/unit/s2n_x509_validator_certificate_signatures_test.c index d0b8b8095ee..a83f70f5619 100644 --- a/tests/unit/s2n_x509_validator_certificate_signatures_test.c +++ b/tests/unit/s2n_x509_validator_certificate_signatures_test.c @@ -1,219 +1,220 @@ -/* - * 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" -#include "crypto/s2n_openssl_x509.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_signature_scheme.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_x509_validator.h" -#include "utils/s2n_safety.h" - -/* forward declaration */ -S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert); - -DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - X509 *cert = NULL; - BIO *certBio = NULL; - size_t certLen = 0; - - const struct s2n_signature_scheme *const test_sig_scheme_list[] = { - &s2n_ecdsa_sha256, - &s2n_rsa_pkcs1_sha1, - }; - - const struct s2n_signature_preferences test_certificate_signature_preferences = { - .count = s2n_array_len(test_sig_scheme_list), - .signature_schemes = test_sig_scheme_list, - }; - - const struct s2n_security_policy test_sp = { - .minimum_protocol_version = S2N_TLS12, - .certificate_signature_preferences = &test_certificate_signature_preferences, - }; - - /* s2n_x509_validator_check_cert_preferences */ - { - /* Connection using a security policy with no certificate_signature_preferences allows SHA-1 signatures in certificates */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *config = s2n_config_new(); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - /* 20140601 is a security policy with no certificate_signature_preferences list */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - certLen = strlen((const char *) cert_file); - - /* Read the test certificates into an Openssl X509 struct */ - EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); - EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); - - EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(BIO_free(certBio)); - X509_free(cert); - }; - - /* Connection using the default_tls13 security policy does not validate SHA-1 signatures in certificates */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *config = s2n_config_new(); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - certLen = strlen((const char *) cert_file); - - /* Read the test certificates into an Openssl X509 struct */ - EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); - EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, cert), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(BIO_free(certBio)); - X509_free(cert); - }; - - /* Connection using the default_tls13 security policy ignores a SHA-1 signature on a root certificate */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *config = s2n_config_new(); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_SHA1_ROOT_SIGNATURE_CA_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - certLen = strlen((const char *) cert_file); - - /* Read the test certificates into an Openssl X509 struct */ - EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); - EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); - - EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(BIO_free(certBio)); - X509_free(cert); - }; - - /* When the certificate signature algorithm is not in the preferences list, then an S2N_ERR_CERT_UNTRUSTED err - * is returned. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = &test_sp; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_p384_sha256 = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&ecdsa_p384_sha256, "ec", - "ecdsa", "p384", "sha384")); - DEFER_CLEANUP(X509 *test_cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse(&ecdsa_p384_sha256->cert_chain->head->raw, &test_cert)); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - }; - - /* Certificate signature algorithm is in the test certificate signature preferences list but signature is SHA-1 - * and TLS 1.3 has been negotiated. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = &test_sp; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint32_t cert_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, - &cert_len, S2N_MAX_TEST_PEM_SIZE)); - - /* Read the test certificates into an Openssl X509 struct */ - DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); - EXPECT_NOT_NULL(cert_bio); - EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); - DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); - EXPECT_NOT_NULL(test_cert); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_CERT_UNTRUSTED); - }; - - /* Certificate signature algorithm is in the test certificate signature preferences list and signature is SHA-1 - * and TLS 1.2 has been negotiated. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = &test_sp; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint32_t cert_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, - &cert_len, S2N_MAX_TEST_PEM_SIZE)); - - /* Read the test certificates into an Openssl X509 struct */ - DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); - EXPECT_NOT_NULL(cert_bio); - EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); - DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); - EXPECT_NOT_NULL(test_cert); - - EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, test_cert)); - }; - }; - END_TEST(); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "crypto/s2n_openssl_x509.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_signature_scheme.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_x509_validator.h" +#include "utils/s2n_safety.h" + +/* forward declaration */ +S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert); + +DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + X509 *cert = NULL; + BIO *certBio = NULL; + size_t certLen = 0; + + const struct s2n_signature_scheme *const test_sig_scheme_list[] = { + &s2n_ecdsa_sha256, + &s2n_rsa_pkcs1_sha1, + }; + + const struct s2n_signature_preferences test_certificate_signature_preferences = { + .count = s2n_array_len(test_sig_scheme_list), + .signature_schemes = test_sig_scheme_list, + }; + + const struct s2n_security_policy test_sp = { + .minimum_protocol_version = S2N_TLS12, + .certificate_signature_preferences = &test_certificate_signature_preferences, + }; + + /* s2n_x509_validator_check_cert_preferences */ + { + /* Connection using a security policy with no certificate_signature_preferences allows SHA-1 signatures in certificates */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + /* 20140601 is a security policy with no certificate_signature_preferences list */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Connection using the default_tls13 security policy does not validate SHA-1 signatures in certificates */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, cert), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Connection using the default_tls13 security policy ignores a SHA-1 signature on a root certificate */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_SHA1_ROOT_SIGNATURE_CA_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* When the certificate signature algorithm is not in the preferences list, then an S2N_ERR_CERT_UNTRUSTED err + * is returned. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = &test_sp; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_p384_sha256 = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&ecdsa_p384_sha256, "ec", + "ecdsa", "p384", "sha384")); + DEFER_CLEANUP(X509 *test_cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse(&ecdsa_p384_sha256->cert_chain->head->raw, &test_cert)); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + }; + + /* Certificate signature algorithm is in the test certificate signature preferences list but signature is SHA-1 + * and TLS 1.3 has been negotiated. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = &test_sp; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint32_t cert_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, + &cert_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Read the test certificates into an Openssl X509 struct */ + DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); + EXPECT_NOT_NULL(cert_bio); + EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); + DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); + EXPECT_NOT_NULL(test_cert); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_CERT_UNTRUSTED); + }; + + /* Certificate signature algorithm is in the test certificate signature preferences list and signature is SHA-1 + * and TLS 1.2 has been negotiated. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = &test_sp; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint32_t cert_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, + &cert_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Read the test certificates into an Openssl X509 struct */ + DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); + EXPECT_NOT_NULL(cert_bio); + EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); + DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); + EXPECT_NOT_NULL(test_cert); + + EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, test_cert)); + }; + }; + END_TEST(); + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_x509_validator_test.c b/tests/unit/s2n_x509_validator_test.c index 4f698d869b8..2a3aa8dff7e 100644 --- a/tests/unit/s2n_x509_validator_test.c +++ b/tests/unit/s2n_x509_validator_test.c @@ -1,2308 +1,2309 @@ -/* - * 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 "crypto/s2n_libcrypto.h" -#include "crypto/s2n_openssl.h" -#include "crypto/s2n_openssl_x509.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_certificate_keys.h" - -static int fetch_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2250-01-01 */ - *timestamp = 8835984000000000000; - return 0; -} - -static int fetch_early_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2038-01-01 */ - *timestamp = 2145920461000000000; - return 0; -} - -#if S2N_OCSP_STAPLING_SUPPORTED -static int mock_time(void *data, uint64_t *timestamp) -{ - *timestamp = *(uint64_t *) data; - return 0; -} -static int fetch_invalid_before_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2015-02-27 */ - *timestamp = 1425019604000000000; - return 0; -} - -static int fetch_not_expired_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2019-03-17 */ - *timestamp = 1552824239000000000; - return 0; -} -#endif /* S2N_OCSP_STAPLING_SUPPORTED */ - -static int read_file(struct s2n_stuffer *file_output, const char *path, uint32_t max_len) -{ - FILE *fd = fopen(path, "rb"); - POSIX_GUARD(s2n_stuffer_alloc(file_output, max_len)); - - if (fd) { - char data[1024]; - size_t r = 0; - while ((r = fread(data, 1, sizeof(data), fd)) > 0) { - POSIX_GUARD(s2n_stuffer_write_bytes(file_output, (const uint8_t *) data, (const uint32_t) r)); - } - fclose(fd); - return s2n_stuffer_data_available(file_output) > 0; - } - - return -1; -} - -struct host_verify_data { - const char *name; - uint8_t found_name; - uint8_t callback_invoked; -}; - -static uint8_t verify_host_reject_everything(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return 0; -} - -static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return 1; -} - -static uint8_t verify_host_verify_alt(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - - verify_data->callback_invoked = 1; - if (!strcmp(host_name, verify_data->name)) { - verify_data->found_name = 1; - return 1; - } - - return 0; -} - -/* some tests try to mock the system time to a date post 2038. If this test is - * run on a platform where time_t is 32 bits, the time_t will overflow, so we - * only run these tests on platforms with a 64 bit time_t. - */ -static bool s2n_supports_large_time_t() -{ - return sizeof(time_t) == 8; -} - -/* Early versions of Openssl (Openssl-1.0.2k confirmed) included a bug where UTCTime - * formatted dates in certificates could not be compared to dates after the year 2050, - * because Openssl would assume that the validation date was also UTCTime formatted - * and therefore reject any date with a year after 2050. - * This is an issue because RFC5280 requires that dates in certificates be in - * UTCTime format for years before 2050. - * Affected tests are modified to account for this bug. - * See https://github.com/openssl/openssl/blob/OpenSSL_1_0_2k/crypto/x509/x509_vfy.c#L2027C1-L2027C26 - */ -static bool s2n_libcrypto_supports_2050() -{ - ASN1_TIME *utc_time = ASN1_UTCTIME_set(NULL, 0); - if (!utc_time) { - return false; - } - - /* The `32BitBuildAndUnit` job in s2nGeneralBatch runs on a 32-bit system - * where time_t cannot represent the year 2050 (2524608000) and triggers - * -Wconstant-conversion (treated as an error). - * - * The libcrypto used by the job (i386-linux-gnu) does support year 2050. - * Return true to skip the `X509_cmp_time` call. - */ - if (sizeof(time_t) < 8) { - ASN1_STRING_free(utc_time); - return true; - } - - time_t time_2050 = (time_t) 2524608000LL; - int result = X509_cmp_time(utc_time, &time_2050); - ASN1_STRING_free(utc_time); - return (result != 0); -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* The issues with 2050 only affected openssl-1.0.2 */ - if (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { - EXPECT_TRUE(s2n_libcrypto_supports_2050()); - } - - /* test empty trust store */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); - }; - - /* test trust store from PEM file */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL); - EXPECT_EQUAL(0, err_code); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test trust store from PEM */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - EXPECT_EQUAL(0, err_code); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); - - /* s2n_x509_trust_store_add_pem returns success when trying to add a - * certificate that already exists in the trust store */ - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - free(cert_chain); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test trust store from non-existent PEM file */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, "dskfjasdklfjsdkl", NULL); - EXPECT_EQUAL(-1, err_code); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test trust store from invalid PEM file */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_INVALID_HEADER_KEY, NULL); - EXPECT_EQUAL(-1, err_code); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in unsafe mode */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* test validator in unsafe mode, make sure max depth is honored on the read, but not an error condition */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* test validator in safe mode, but no configured trust store */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - EXPECT_FAILURE_WITH_ERRNO(s2n_x509_validator_set_max_chain_depth(&validator, 0), S2N_ERR_INVALID_ARGUMENT); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, but no configured trust store */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - - EXPECT_NOT_NULL(connection); - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - EXPECT_EQUAL(0, verify_data.callback_invoked); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store and test that SAN URI callback is invoked. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_URI_SANS_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "foo://bar" }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_URI_SANS_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(1, verify_data.found_name); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, using s2n PEM Parser. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, but max chain depth is exceeded*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); - - EXPECT_EQUAL(0, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test post-2038 certificate expiration. - * - * The expired certificate should fail as untrusted. This test fails on - * platforms where time_t is 4 bytes because representing dates past 2038 as - * unix seconds overflows the time_t. - */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - int expected_errno = S2N_ERR_CERT_EXPIRED; - /* In some cases validation may fail with a less specific error due to - * issues with large dates, but validation does always fail. */ - if (!s2n_supports_large_time_t()) { - expected_errno = S2N_ERR_SAFETY; - } else if (!s2n_libcrypto_supports_2050()) { - expected_errno = S2N_ERR_CERT_UNTRUSTED; - } - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, connection, - chain_data, chain_len, &pkey_type, &public_key_out), - expected_errno); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test pre-2038 certificate expiration - * - * After the expiration date, the certificate should fail as untrusted. This - * test uses pre-2038 dates for 32 bit time_t concerns - */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, connection, - chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_EXPIRED); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); - }; - - /* test validator in safe mode, with properly configured trust store, but the server's end-entity cert is invalid. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - /* alter a random byte in the certificate to make it invalid */ - size_t corrupt_index = 200; - EXPECT_TRUE(chain_len > corrupt_index); - chain_data[corrupt_index] = (uint8_t) (chain_data[corrupt_index] << 2); - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, but host isn't trusted*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, but host isn't trusted, using s2n PEM Parser */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_connection_free(connection); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name validation succeeds */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name validation succeeds, using s2n PEM Parser */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_pkey_free(&public_key_out); - - s2n_connection_free(connection); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name via alternative name validation succeeds - * note: in this case, we don't have valid certs but it's enough to make sure we are properly pulling alternative names - * from the certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_EQUAL(1, verify_data.found_name); - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name via alternative name validation fails, and - * no Common Name validation happens as DNS alternative name is present. note: in this case, we don't have valid certs but - * it's enough to make sure we are properly validating alternative names and common name.*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Name matches CN on certificate (CN=localhost), but no match in alternative names */ - struct host_verify_data verify_data = { - .name = "localhost", - .found_name = 0, - .callback_invoked = 0, - }; - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(0, verify_data.found_name); - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name via common name validation succeeds, - * non-dns alternative names are ignored. note: in this case, we don't have valid certs but it's enough to make sure - * we are properly validating alternative names and common name.*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Name matches CN on certificate (CN=localhost) */ - struct host_verify_data verify_data = { - .name = "localhost", - .found_name = 0, - .callback_invoked = 0, - }; - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_NO_DNS_SANS_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_EQUAL(1, verify_data.found_name); - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; -#if S2N_OCSP_STAPLING_SUPPORTED - /* Test valid OCSP date range */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test valid OCSP date range without nextUpdate field */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_NO_NEXT_UPDATE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_not_expired_ocsp_timestamp, NULL); - - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test valid OCSP date range, but with s2n PEM Parser */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - s2n_pkey_free(&public_key_out); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /** - * Test invalid OCSP date range post-2038 - * - * After the "Next Update" time in the OCSP response, the certificate should - * fail as expired. - */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); - - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - if (s2n_supports_large_time_t()) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_EXPIRED); - } else { - /* fetch_expired_after_ocsp_timestamp is in 2200 which is not - * representable for 32 bit time_t's. - */ - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_SAFETY); - } - - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /** - * Test invalid OCSP date range pre-2038 - * - * This test sets the clock time to be after the expiration date of the cert - * and after the "Next Update" field of the OCSP response. - */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); - - DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_EXPIRED); - - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); - } - - /* Test invalid OCSP date range (thisupdate is off) */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_invalid_before_ocsp_timestamp, NULL); - - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_INVALID); - - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /* Test valid OCSP date range, but the data itself is untrusted */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - /* flip a byte right in the middle of the cert */ - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - raw_data[800] = (uint8_t) (raw_data[800] + 1); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test valid OCSP date range and data, but the stapled response was signed with an issuer not in the chain of trust */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test OCSP response signed by the correct responder certificate, but not for the requested certificate. - * (So this would be a completely valid response to a different OCSP request for the other certificate.) */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /* Test OCSP response signed by the wrong responder certificate, but the requested certificate was signed. - * (however this incorrect OCSP responder certificate is a valid OCSP responder for some other case and chains - * to a trusted root). Thus, this response is not valid for any request. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_WRONG_SIGNER_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /* Test OCSP response status is revoked */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_REVOKED_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_REVOKED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /** - * Test OCSP validation at various offsets from update times. - * - * libcrypto ASN1 comparison calculates differences in terms of days and seconds, - * so try mocking the system time to a collection of more than day & less - * than day differences. - * The T's in the below diagram represent test cases that should fail - * The F's represent test cases that should succeed - * S2N_ERR_CERT_INVALID S2N_ERR_CERT_EXPIRED - * | | | | - * v v v v - * F F T T T T F F - * v v v v v v v v - * <----------|---|---|----------------------------|---|---|---> - * ^ ^ - * this update next update - * |---| - * one day - * - * If this test is failing make sure that the this_update_timestamp_nanoseconds - * matches the actual timestamp of ocsp_response_early_expire.der - * - * openssl ocsp -respin ocsp_response_early_expire.der -text -noverify | grep "This Update" - */ - { - /* Apr 28 22:11:56 2023 GMT */ - uint64_t this_update_timestamp_nanoseconds = (uint64_t) 1682719916 * ONE_SEC_IN_NANOS; - - /* Apr 28 22:11:56 2023 GMT */ - uint64_t next_update_timestamp_nanoseconds = (uint64_t) 2082838316 * ONE_SEC_IN_NANOS; - - uint64_t one_hour_nanoseconds = (uint64_t) 60 * 60 * ONE_SEC_IN_NANOS; - uint64_t one_day_nanoseconds = 24 * one_hour_nanoseconds; - - struct { - uint64_t time; - int result; - } test_cases[] = { - { - .time = this_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_CERT_INVALID, - }, - { - .time = this_update_timestamp_nanoseconds - one_hour_nanoseconds, - .result = S2N_ERR_CERT_INVALID, - }, - { - .time = this_update_timestamp_nanoseconds + one_hour_nanoseconds, - .result = S2N_ERR_OK, - }, - { - .time = this_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_OK, - }, - { - .time = next_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_OK, - }, - { - .time = next_update_timestamp_nanoseconds - one_hour_nanoseconds, - .result = S2N_ERR_OK, - }, - { - .time = next_update_timestamp_nanoseconds + one_hour_nanoseconds, - .result = S2N_ERR_CERT_EXPIRED, - }, - { - .time = next_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_CERT_EXPIRED, - } - }; - - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - /** - * keep track of the old clock, because we want cert validation to happen - * with the default system clock, and not the "mock_time" clock. - */ - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - uint64_t timestamp_nanoseconds = test_cases[i].time; - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, mock_time, ×tamp_nanoseconds)); - - DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - if (test_cases[i].result != S2N_ERR_OK) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - test_cases[i].result); - } else { - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - } - - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); - }; - }; -#endif /* S2N_OCSP_STAPLING_SUPPORTED */ - /* test validator in safe mode, with default host name validator. Connection server name matches alternative name on a certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with default host name validator. Connection server name matches wildcard alternative name on a certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - EXPECT_SUCCESS(s2n_set_server_name(connection, "test.localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with default host name validator. Connection server does not match alternative names on a certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - EXPECT_SUCCESS(s2n_set_server_name(connection, "127.0.0.1")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with default host name validator. Connection server matches the IPv6 address on the certificate. */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_IP_V6_LO_RSA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - s2n_x509_validator_init(&validator, &trust_store, 1); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - /* the provided hostname should be an empty string */ - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "::1" }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem( - connection, - S2N_IP_V6_LO_RSA_CERT, - &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - }; - - /* Server matches the empty string when there are no usable identifiers in the cert. */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_WITHOUT_CN_RSA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - s2n_x509_validator_init(&validator, &trust_store, 1); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - /* the provided hostname should be an empty string */ - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "" }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem( - connection, - S2N_WITHOUT_CN_RSA_CERT, - &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - }; - - /* test validator in safe mode, with default host name validator. No connection server name supplied. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test trust store in a configuration can handle invalid PEM without crashing */ - { - struct s2n_config *cfg = s2n_config_new(); - s2n_config_add_pem_to_trust_store(cfg, ""); - s2n_config_free(cfg); - /* Expect no crash. */ - }; - - /* Test one trailing byte in cert validator */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct s2n_stuffer chain_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - s2n_stuffer_free(&chain_stuffer); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* Test more trailing bytes in cert validator for negative case */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct s2n_stuffer chain_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, - connection, chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_DECODE_CERTIFICATE); - - s2n_stuffer_free(&chain_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* Test unknown curve in cert validator for negative case */ - if (s2n_libcrypto_is_awslc()) { - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - - char pem_str[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_BRAINPOOL_CURVE_CERT, pem_str, sizeof(pem_str))); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, pem_str)); - - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_send_cert_chain(conn, &message, chain_and_key)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&message, 3)); - - uint32_t chain_len = s2n_stuffer_data_available(&message); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&message, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, - conn, chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_DECODE_CERTIFICATE); - }; - - /* Ensure that certs after the leaf cert can have an arbitrary number of trailing bytes */ - { - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - DEFER_CLEANUP(struct s2n_stuffer one_trailing_byte_chain = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&one_trailing_byte_chain, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t one_trailing_byte_chain_len = s2n_stuffer_data_available(&one_trailing_byte_chain); - uint8_t *one_trailing_byte_chain_data = s2n_stuffer_raw_read(&one_trailing_byte_chain, - one_trailing_byte_chain_len); - - DEFER_CLEANUP(struct s2n_stuffer four_trailing_bytes_chain = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&four_trailing_bytes_chain, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t four_trailing_bytes_chain_len = s2n_stuffer_data_available(&four_trailing_bytes_chain); - uint8_t *four_trailing_bytes_chain_data = s2n_stuffer_raw_read(&four_trailing_bytes_chain, - four_trailing_bytes_chain_len); - - DEFER_CLEANUP(struct s2n_stuffer chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_alloc(&chain_stuffer, S2N_MAX_TEST_PEM_SIZE * 2)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, one_trailing_byte_chain_data, - one_trailing_byte_chain_len)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, four_trailing_bytes_chain_data, - four_trailing_bytes_chain_len)); - - uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); - - DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key)); - - EXPECT_EQUAL(sk_X509_num(validator.cert_chain_from_wire), 2); - }; - - /* Test validator trusts a SHA-1 signature in a certificate chain if certificate validation is off */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - - EXPECT_NOT_NULL(connection); - connection->actual_protocol_version = S2N_TLS13; - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - /* This cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - validator.skip_cert_validation = 1; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_config_free(config); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test validator does not trust a SHA-1 signature in a certificate chain */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - - EXPECT_NOT_NULL(connection); - connection->actual_protocol_version = S2N_TLS13; - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, - chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_config_free(config); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Validator fails if cert chain is empty */ - { - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct s2n_pkey public_key = { 0 }; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, NULL, 0, &pkey_type, - &public_key), - S2N_ERR_NO_CERT_FOUND); - } - - /* Test trust store can be wiped */ - { - /* Wipe new s2n_config, which is initialized with certs from the system default locations. */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Wipe repeatedly without crash */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Wipe after setting verification location */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Set verification location after wipe */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Wipe after adding PEM */ - { - struct s2n_config *cfg = s2n_config_new(); - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - free(cert_chain); - s2n_config_free(cfg); - }; - - /* Add PEM after wipe */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - free(cert_chain); - s2n_config_free(cfg); - }; - }; - - /* Ensure that non-root certificates added to the trust store are trusted */ - { - const char *non_root_cert_path = S2N_RSA_2048_PKCS1_LEAF_CERT; - -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - /* Ensure that the test certificate isn't self-signed, and is therefore not a root. - * - * The X509_get_extension_flags API wasn't added to OpenSSL until 1.1.0. - */ - { - const char *non_root_key_path = S2N_RSA_2048_PKCS1_KEY; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, non_root_cert_path, non_root_key_path)); - struct s2n_cert *cert = NULL; - EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &cert, 0)); - EXPECT_NOT_NULL(cert); - - /* Use the s2n_cert to convert the PEM to ASN.1. */ - const uint8_t *asn1_data = NULL; - uint32_t asn1_len = 0; - EXPECT_SUCCESS(s2n_cert_get_der(cert, &asn1_data, &asn1_len)); - EXPECT_NOT_NULL(asn1_data); - - /* Parse the ASN.1 data with the libcrypto */ - DEFER_CLEANUP(X509 *x509 = d2i_X509(NULL, &asn1_data, asn1_len), X509_free_pointer); - EXPECT_NOT_NULL(x509); - - /* Ensure that the self-signed flag isn't set */ - uint32_t extension_flags = X509_get_extension_flags(x509); - EXPECT_EQUAL(extension_flags & EXFLAG_SS, 0); - } -#endif - - /* Test s2n_config_set_verification_ca_location */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, non_root_cert_path, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - } - - /* Test s2n_config_add_pem_to_trust_store */ - { - char non_root_cert_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(non_root_cert_path, non_root_cert_pem, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, non_root_cert_pem)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - } - - /* Test system trust store - * - * This test uses the SSL_CERT_FILE environment variable to override the system trust store - * location, which isn't supported by LibreSSL. - */ - if (!s2n_libcrypto_is_libressl()) { - /* Override the system cert file with the non-root test cert. */ - EXPECT_SUCCESS(setenv("SSL_CERT_FILE", non_root_cert_path, 1)); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - - EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); - } - } - - /* Test that CAs must comply with cert preferences */ - { - uint8_t invalid_root_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t root_pem_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CA, &invalid_root_pem[0], &root_pem_len, - S2N_MAX_TEST_PEM_SIZE)); - - uint8_t chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t chain_pem_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CERTS, &chain_pem[0], &chain_pem_len, - S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_security_policy security_policy_not_local = security_policy_20250429; - security_policy_not_local.certificate_preferences_apply_locally = false; - - /* when the peer sends the full chain with a non-compliant CA, verification fails when reading in the certs */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->security_policy_override = &security_policy_not_local; - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - - EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - /* Failed while processing/reading in the cert chain */ - EXPECT_TRUE(validator.state == INIT); - }; - - /* when the peer sends only compliant certs from a non-compliant CA, - * validation fails on the local trust store - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->security_policy_override = &security_policy_not_local; - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - - /* `chain_pem_len - root_pem_len`: only use the first two certs in the chain (leaf and intermediate) to - * ensure we are correctly failing on the local trust store and not the chain that the peer sent. - */ - EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len - root_pem_len, - &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - /* X509_verify_cert finished successfully */ - EXPECT_TRUE(validator.state == VALIDATED); - }; - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_libcrypto.h" +#include "crypto/s2n_openssl.h" +#include "crypto/s2n_openssl_x509.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_certificate_keys.h" + +static int fetch_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2250-01-01 */ + *timestamp = 8835984000000000000; + return 0; +} + +static int fetch_early_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2038-01-01 */ + *timestamp = 2145920461000000000; + return 0; +} + +#if S2N_OCSP_STAPLING_SUPPORTED +static int mock_time(void *data, uint64_t *timestamp) +{ + *timestamp = *(uint64_t *) data; + return 0; +} +static int fetch_invalid_before_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2015-02-27 */ + *timestamp = 1425019604000000000; + return 0; +} + +static int fetch_not_expired_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2019-03-17 */ + *timestamp = 1552824239000000000; + return 0; +} +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ + +static int read_file(struct s2n_stuffer *file_output, const char *path, uint32_t max_len) +{ + FILE *fd = fopen(path, "rb"); + POSIX_GUARD(s2n_stuffer_alloc(file_output, max_len)); + + if (fd) { + char data[1024]; + size_t r = 0; + while ((r = fread(data, 1, sizeof(data), fd)) > 0) { + POSIX_GUARD(s2n_stuffer_write_bytes(file_output, (const uint8_t *) data, (const uint32_t) r)); + } + fclose(fd); + return s2n_stuffer_data_available(file_output) > 0; + } + + return -1; +} + +struct host_verify_data { + const char *name; + uint8_t found_name; + uint8_t callback_invoked; +}; + +static uint8_t verify_host_reject_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 0; +} + +static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 1; +} + +static uint8_t verify_host_verify_alt(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + + verify_data->callback_invoked = 1; + if (!strcmp(host_name, verify_data->name)) { + verify_data->found_name = 1; + return 1; + } + + return 0; +} + +/* some tests try to mock the system time to a date post 2038. If this test is + * run on a platform where time_t is 32 bits, the time_t will overflow, so we + * only run these tests on platforms with a 64 bit time_t. + */ +static bool s2n_supports_large_time_t() +{ + return sizeof(time_t) == 8; +} + +/* Early versions of Openssl (Openssl-1.0.2k confirmed) included a bug where UTCTime + * formatted dates in certificates could not be compared to dates after the year 2050, + * because Openssl would assume that the validation date was also UTCTime formatted + * and therefore reject any date with a year after 2050. + * This is an issue because RFC5280 requires that dates in certificates be in + * UTCTime format for years before 2050. + * Affected tests are modified to account for this bug. + * See https://github.com/openssl/openssl/blob/OpenSSL_1_0_2k/crypto/x509/x509_vfy.c#L2027C1-L2027C26 + */ +static bool s2n_libcrypto_supports_2050() +{ + ASN1_TIME *utc_time = ASN1_UTCTIME_set(NULL, 0); + if (!utc_time) { + return false; + } + + /* The `32BitBuildAndUnit` job in s2nGeneralBatch runs on a 32-bit system + * where time_t cannot represent the year 2050 (2524608000) and triggers + * -Wconstant-conversion (treated as an error). + * + * The libcrypto used by the job (i386-linux-gnu) does support year 2050. + * Return true to skip the `X509_cmp_time` call. + */ + if (sizeof(time_t) < 8) { + ASN1_STRING_free(utc_time); + return true; + } + + time_t time_2050 = (time_t) 2524608000LL; + int result = X509_cmp_time(utc_time, &time_2050); + ASN1_STRING_free(utc_time); + return (result != 0); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* The issues with 2050 only affected openssl-1.0.2 */ + if (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { + EXPECT_TRUE(s2n_libcrypto_supports_2050()); + } + + /* test empty trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + }; + + /* test trust store from PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL); + EXPECT_EQUAL(0, err_code); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from PEM */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + EXPECT_EQUAL(0, err_code); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); + + /* s2n_x509_trust_store_add_pem returns success when trying to add a + * certificate that already exists in the trust store */ + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + free(cert_chain); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from non-existent PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, "dskfjasdklfjsdkl", NULL); + EXPECT_EQUAL(-1, err_code); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from invalid PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_INVALID_HEADER_KEY, NULL); + EXPECT_EQUAL(-1, err_code); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in unsafe mode */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* test validator in unsafe mode, make sure max depth is honored on the read, but not an error condition */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* test validator in safe mode, but no configured trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + EXPECT_FAILURE_WITH_ERRNO(s2n_x509_validator_set_max_chain_depth(&validator, 0), S2N_ERR_INVALID_ARGUMENT); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, but no configured trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + + EXPECT_NOT_NULL(connection); + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + EXPECT_EQUAL(0, verify_data.callback_invoked); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store and test that SAN URI callback is invoked. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_URI_SANS_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "foo://bar" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_URI_SANS_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, using s2n PEM Parser. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but max chain depth is exceeded*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); + + EXPECT_EQUAL(0, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test post-2038 certificate expiration. + * + * The expired certificate should fail as untrusted. This test fails on + * platforms where time_t is 4 bytes because representing dates past 2038 as + * unix seconds overflows the time_t. + */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + int expected_errno = S2N_ERR_CERT_EXPIRED; + /* In some cases validation may fail with a less specific error due to + * issues with large dates, but validation does always fail. */ + if (!s2n_supports_large_time_t()) { + expected_errno = S2N_ERR_SAFETY; + } else if (!s2n_libcrypto_supports_2050()) { + expected_errno = S2N_ERR_CERT_UNTRUSTED; + } + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + expected_errno); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test pre-2038 certificate expiration + * + * After the expiration date, the certificate should fail as untrusted. This + * test uses pre-2038 dates for 32 bit time_t concerns + */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_EXPIRED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + }; + + /* test validator in safe mode, with properly configured trust store, but the server's end-entity cert is invalid. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + /* alter a random byte in the certificate to make it invalid */ + size_t corrupt_index = 200; + EXPECT_TRUE(chain_len > corrupt_index); + chain_data[corrupt_index] = (uint8_t) (chain_data[corrupt_index] << 2); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but host isn't trusted*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but host isn't trusted, using s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name validation succeeds */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name validation succeeds, using s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_pkey_free(&public_key_out); + + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via alternative name validation succeeds + * note: in this case, we don't have valid certs but it's enough to make sure we are properly pulling alternative names + * from the certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via alternative name validation fails, and + * no Common Name validation happens as DNS alternative name is present. note: in this case, we don't have valid certs but + * it's enough to make sure we are properly validating alternative names and common name.*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Name matches CN on certificate (CN=localhost), but no match in alternative names */ + struct host_verify_data verify_data = { + .name = "localhost", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(0, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via common name validation succeeds, + * non-dns alternative names are ignored. note: in this case, we don't have valid certs but it's enough to make sure + * we are properly validating alternative names and common name.*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Name matches CN on certificate (CN=localhost) */ + struct host_verify_data verify_data = { + .name = "localhost", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_NO_DNS_SANS_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; +#if S2N_OCSP_STAPLING_SUPPORTED + /* Test valid OCSP date range */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range without nextUpdate field */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_NO_NEXT_UPDATE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_not_expired_ocsp_timestamp, NULL); + + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range, but with s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + s2n_pkey_free(&public_key_out); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /** + * Test invalid OCSP date range post-2038 + * + * After the "Next Update" time in the OCSP response, the certificate should + * fail as expired. + */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + if (s2n_supports_large_time_t()) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_EXPIRED); + } else { + /* fetch_expired_after_ocsp_timestamp is in 2200 which is not + * representable for 32 bit time_t's. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_SAFETY); + } + + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /** + * Test invalid OCSP date range pre-2038 + * + * This test sets the clock time to be after the expiration date of the cert + * and after the "Next Update" field of the OCSP response. + */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); + + DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_EXPIRED); + + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + } + + /* Test invalid OCSP date range (thisupdate is off) */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_invalid_before_ocsp_timestamp, NULL); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_INVALID); + + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test valid OCSP date range, but the data itself is untrusted */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + /* flip a byte right in the middle of the cert */ + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + raw_data[800] = (uint8_t) (raw_data[800] + 1); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range and data, but the stapled response was signed with an issuer not in the chain of trust */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test OCSP response signed by the correct responder certificate, but not for the requested certificate. + * (So this would be a completely valid response to a different OCSP request for the other certificate.) */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test OCSP response signed by the wrong responder certificate, but the requested certificate was signed. + * (however this incorrect OCSP responder certificate is a valid OCSP responder for some other case and chains + * to a trusted root). Thus, this response is not valid for any request. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_WRONG_SIGNER_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test OCSP response status is revoked */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_REVOKED_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_REVOKED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /** + * Test OCSP validation at various offsets from update times. + * + * libcrypto ASN1 comparison calculates differences in terms of days and seconds, + * so try mocking the system time to a collection of more than day & less + * than day differences. + * The T's in the below diagram represent test cases that should fail + * The F's represent test cases that should succeed + * S2N_ERR_CERT_INVALID S2N_ERR_CERT_EXPIRED + * | | | | + * v v v v + * F F T T T T F F + * v v v v v v v v + * <----------|---|---|----------------------------|---|---|---> + * ^ ^ + * this update next update + * |---| + * one day + * + * If this test is failing make sure that the this_update_timestamp_nanoseconds + * matches the actual timestamp of ocsp_response_early_expire.der + * + * openssl ocsp -respin ocsp_response_early_expire.der -text -noverify | grep "This Update" + */ + { + /* Apr 28 22:11:56 2023 GMT */ + uint64_t this_update_timestamp_nanoseconds = (uint64_t) 1682719916 * ONE_SEC_IN_NANOS; + + /* Apr 28 22:11:56 2023 GMT */ + uint64_t next_update_timestamp_nanoseconds = (uint64_t) 2082838316 * ONE_SEC_IN_NANOS; + + uint64_t one_hour_nanoseconds = (uint64_t) 60 * 60 * ONE_SEC_IN_NANOS; + uint64_t one_day_nanoseconds = 24 * one_hour_nanoseconds; + + struct { + uint64_t time; + int result; + } test_cases[] = { + { + .time = this_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_CERT_INVALID, + }, + { + .time = this_update_timestamp_nanoseconds - one_hour_nanoseconds, + .result = S2N_ERR_CERT_INVALID, + }, + { + .time = this_update_timestamp_nanoseconds + one_hour_nanoseconds, + .result = S2N_ERR_OK, + }, + { + .time = this_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds - one_hour_nanoseconds, + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds + one_hour_nanoseconds, + .result = S2N_ERR_CERT_EXPIRED, + }, + { + .time = next_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_CERT_EXPIRED, + } + }; + + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + /** + * keep track of the old clock, because we want cert validation to happen + * with the default system clock, and not the "mock_time" clock. + */ + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + uint64_t timestamp_nanoseconds = test_cases[i].time; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, mock_time, ×tamp_nanoseconds)); + + DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + if (test_cases[i].result != S2N_ERR_OK) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + test_cases[i].result); + } else { + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + } + + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + }; + }; +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ + /* test validator in safe mode, with default host name validator. Connection server name matches alternative name on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server name matches wildcard alternative name on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "test.localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server does not match alternative names on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "127.0.0.1")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server matches the IPv6 address on the certificate. */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_IP_V6_LO_RSA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + /* the provided hostname should be an empty string */ + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "::1" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem( + connection, + S2N_IP_V6_LO_RSA_CERT, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + }; + + /* Server matches the empty string when there are no usable identifiers in the cert. */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_WITHOUT_CN_RSA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + /* the provided hostname should be an empty string */ + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem( + connection, + S2N_WITHOUT_CN_RSA_CERT, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + }; + + /* test validator in safe mode, with default host name validator. No connection server name supplied. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test trust store in a configuration can handle invalid PEM without crashing */ + { + struct s2n_config *cfg = s2n_config_new(); + s2n_config_add_pem_to_trust_store(cfg, ""); + s2n_config_free(cfg); + /* Expect no crash. */ + }; + + /* Test one trailing byte in cert validator */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct s2n_stuffer chain_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + s2n_stuffer_free(&chain_stuffer); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* Test more trailing bytes in cert validator for negative case */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct s2n_stuffer chain_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, + connection, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_DECODE_CERTIFICATE); + + s2n_stuffer_free(&chain_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* Test unknown curve in cert validator for negative case */ + if (s2n_libcrypto_is_awslc()) { + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + + char pem_str[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_BRAINPOOL_CURVE_CERT, pem_str, sizeof(pem_str))); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, pem_str)); + + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &message, chain_and_key)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&message, 3)); + + uint32_t chain_len = s2n_stuffer_data_available(&message); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&message, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, + conn, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_DECODE_CERTIFICATE); + }; + + /* Ensure that certs after the leaf cert can have an arbitrary number of trailing bytes */ + { + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + DEFER_CLEANUP(struct s2n_stuffer one_trailing_byte_chain = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&one_trailing_byte_chain, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t one_trailing_byte_chain_len = s2n_stuffer_data_available(&one_trailing_byte_chain); + uint8_t *one_trailing_byte_chain_data = s2n_stuffer_raw_read(&one_trailing_byte_chain, + one_trailing_byte_chain_len); + + DEFER_CLEANUP(struct s2n_stuffer four_trailing_bytes_chain = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&four_trailing_bytes_chain, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t four_trailing_bytes_chain_len = s2n_stuffer_data_available(&four_trailing_bytes_chain); + uint8_t *four_trailing_bytes_chain_data = s2n_stuffer_raw_read(&four_trailing_bytes_chain, + four_trailing_bytes_chain_len); + + DEFER_CLEANUP(struct s2n_stuffer chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&chain_stuffer, S2N_MAX_TEST_PEM_SIZE * 2)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, one_trailing_byte_chain_data, + one_trailing_byte_chain_len)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, four_trailing_bytes_chain_data, + four_trailing_bytes_chain_len)); + + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key)); + + EXPECT_EQUAL(sk_X509_num(validator.cert_chain_from_wire), 2); + }; + + /* Test validator trusts a SHA-1 signature in a certificate chain if certificate validation is off */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + + EXPECT_NOT_NULL(connection); + connection->actual_protocol_version = S2N_TLS13; + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* This cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + validator.skip_cert_validation = 1; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_config_free(config); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test validator does not trust a SHA-1 signature in a certificate chain */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + + EXPECT_NOT_NULL(connection); + connection->actual_protocol_version = S2N_TLS13; + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_config_free(config); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Validator fails if cert chain is empty */ + { + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct s2n_pkey public_key = { 0 }; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, NULL, 0, &pkey_type, + &public_key), + S2N_ERR_NO_CERT_FOUND); + } + + /* Test trust store can be wiped */ + { + /* Wipe new s2n_config, which is initialized with certs from the system default locations. */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe repeatedly without crash */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe after setting verification location */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Set verification location after wipe */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe after adding PEM */ + { + struct s2n_config *cfg = s2n_config_new(); + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + free(cert_chain); + s2n_config_free(cfg); + }; + + /* Add PEM after wipe */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + free(cert_chain); + s2n_config_free(cfg); + }; + }; + + /* Ensure that non-root certificates added to the trust store are trusted */ + { + const char *non_root_cert_path = S2N_RSA_2048_PKCS1_LEAF_CERT; + +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + /* Ensure that the test certificate isn't self-signed, and is therefore not a root. + * + * The X509_get_extension_flags API wasn't added to OpenSSL until 1.1.0. + */ + { + const char *non_root_key_path = S2N_RSA_2048_PKCS1_KEY; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, non_root_cert_path, non_root_key_path)); + struct s2n_cert *cert = NULL; + EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &cert, 0)); + EXPECT_NOT_NULL(cert); + + /* Use the s2n_cert to convert the PEM to ASN.1. */ + const uint8_t *asn1_data = NULL; + uint32_t asn1_len = 0; + EXPECT_SUCCESS(s2n_cert_get_der(cert, &asn1_data, &asn1_len)); + EXPECT_NOT_NULL(asn1_data); + + /* Parse the ASN.1 data with the libcrypto */ + DEFER_CLEANUP(X509 *x509 = d2i_X509(NULL, &asn1_data, asn1_len), X509_free_pointer); + EXPECT_NOT_NULL(x509); + + /* Ensure that the self-signed flag isn't set */ + uint32_t extension_flags = X509_get_extension_flags(x509); + EXPECT_EQUAL(extension_flags & EXFLAG_SS, 0); + } +#endif + + /* Test s2n_config_set_verification_ca_location */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, non_root_cert_path, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + } + + /* Test s2n_config_add_pem_to_trust_store */ + { + char non_root_cert_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(non_root_cert_path, non_root_cert_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, non_root_cert_pem)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + } + + /* Test system trust store + * + * This test uses the SSL_CERT_FILE environment variable to override the system trust store + * location, which isn't supported by LibreSSL. + */ + if (!s2n_libcrypto_is_libressl()) { + /* Override the system cert file with the non-root test cert. */ + EXPECT_SUCCESS(setenv("SSL_CERT_FILE", non_root_cert_path, 1)); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + + EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); + } + } + + /* Test that CAs must comply with cert preferences */ + { + uint8_t invalid_root_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t root_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CA, &invalid_root_pem[0], &root_pem_len, + S2N_MAX_TEST_PEM_SIZE)); + + uint8_t chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t chain_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CERTS, &chain_pem[0], &chain_pem_len, + S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_security_policy security_policy_not_local = security_policy_20250429; + security_policy_not_local.certificate_preferences_apply_locally = false; + + /* when the peer sends the full chain with a non-compliant CA, verification fails when reading in the certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &security_policy_not_local; + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + /* Failed while processing/reading in the cert chain */ + EXPECT_TRUE(validator.state == INIT); + }; + + /* when the peer sends only compliant certs from a non-compliant CA, + * validation fails on the local trust store + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &security_policy_not_local; + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + + /* `chain_pem_len - root_pem_len`: only use the first two certs in the chain (leaf and intermediate) to + * ensure we are correctly failing on the local trust store and not the chain that the peer sent. + */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len - root_pem_len, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + /* X509_verify_cert finished successfully */ + EXPECT_TRUE(validator.state == VALIDATED); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_validator_time_verification_test.c b/tests/unit/s2n_x509_validator_time_verification_test.c index 1eb7a35c199..dfc1e246839 100644 --- a/tests/unit/s2n_x509_validator_time_verification_test.c +++ b/tests/unit/s2n_x509_validator_time_verification_test.c @@ -1,286 +1,287 @@ -/* -* 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 "crypto/s2n_libcrypto.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -bool s2n_libcrypto_supports_flag_no_check_time(); -uint64_t s2n_libcrypto_awslc_api_version(void); - -static uint8_t s2n_verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) -{ - return 1; -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* Test the NO_CHECK_TIME flag feature probe. The flag was added to AWS-LC in API version 19. */ - if (s2n_libcrypto_is_awslc() && s2n_libcrypto_awslc_api_version() > 19) { - EXPECT_TRUE(s2n_libcrypto_supports_flag_no_check_time()); - } - - /* Test disabling x509 time validation. - * - * By default, validation should fail for certificates with invalid timestamps. However, if - * x509 time validation is disabled, validation should succeed. - * - * When time validation is disabled, s2n_config_set_wall_clock() will not set a custom time on - * the libcrypto, so this function cannot be used to set a fake time for testing. Instead, the - * test certificates themselves contain invalid timestamps. - */ - { - /* clang-format off */ - struct { - const char *cert_pem_path; - const char *key_pem_path; - bool disable_x509_time_validation; - s2n_error expected_error; - } test_cases[] = { - /* Validation should fail for a certificate that is not yet valid. */ - { - .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, - .key_pem_path = S2N_NOT_YET_VALID_KEY, - .disable_x509_time_validation = false, - .expected_error = S2N_ERR_CERT_NOT_YET_VALID, - }, - - /* Validation should succeed for a certificate that is not yet valid when time - * validation is disabled. - */ - { - .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, - .key_pem_path = S2N_NOT_YET_VALID_KEY, - .disable_x509_time_validation = true, - .expected_error = S2N_ERR_OK, - }, - - /* Validation should fail for an expired certificate. */ - { - .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, - .key_pem_path = S2N_EXPIRED_KEY, - .disable_x509_time_validation = false, - .expected_error = S2N_ERR_CERT_EXPIRED, - }, - - /* Validation should succeed for an expired certificate when time validation is - * disabled. - */ - { - .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, - .key_pem_path = S2N_EXPIRED_KEY, - .disable_x509_time_validation = true, - .expected_error = S2N_ERR_OK, - }, - }; - /* clang-format on */ - - /* s2n_x509_validator test */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(test_cases[i].cert_pem_path, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - if (test_cases[i].disable_x509_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - } - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, test_cases[i].cert_pem_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, - &public_key_out); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_OK(ret); - } else { - EXPECT_ERROR_WITH_ERRNO(ret, expected_error); - } - } - - /* Self-talk: Disable validity period validation on client for server auth */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, test_cases[i].cert_pem_path, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - if (test_cases[i].disable_x509_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - } - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - - /* Self-talk: Disable validity period validation on server for client auth */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *default_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, test_cases[i].cert_pem_path, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - if (test_cases[i].disable_x509_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(server_config)); - } - - /* Disable verify host validation for client auth */ - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, s2n_verify_host_accept_everything, NULL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - } - - /* Ensure that certificate validation can fail for reasons other than time validation when time - * validation is disabled. - */ - for (int trust_cert = 0; trust_cert <= 1; trust_cert += 1) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - if (trust_cert) { - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - } - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, - &public_key_out); - - if (trust_cert) { - EXPECT_OK(ret); - } else { - /* If the certificate was not added to the trust store, validation should fail even - * though time validation was disabled. - */ - EXPECT_ERROR_WITH_ERRNO(ret, S2N_ERR_CERT_UNTRUSTED); - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* 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 "crypto/s2n_libcrypto.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +bool s2n_libcrypto_supports_flag_no_check_time(); +uint64_t s2n_libcrypto_awslc_api_version(void); + +static uint8_t s2n_verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Test the NO_CHECK_TIME flag feature probe. The flag was added to AWS-LC in API version 19. */ + if (s2n_libcrypto_is_awslc() && s2n_libcrypto_awslc_api_version() > 19) { + EXPECT_TRUE(s2n_libcrypto_supports_flag_no_check_time()); + } + + /* Test disabling x509 time validation. + * + * By default, validation should fail for certificates with invalid timestamps. However, if + * x509 time validation is disabled, validation should succeed. + * + * When time validation is disabled, s2n_config_set_wall_clock() will not set a custom time on + * the libcrypto, so this function cannot be used to set a fake time for testing. Instead, the + * test certificates themselves contain invalid timestamps. + */ + { + /* clang-format off */ + struct { + const char *cert_pem_path; + const char *key_pem_path; + bool disable_x509_time_validation; + s2n_error expected_error; + } test_cases[] = { + /* Validation should fail for a certificate that is not yet valid. */ + { + .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, + .key_pem_path = S2N_NOT_YET_VALID_KEY, + .disable_x509_time_validation = false, + .expected_error = S2N_ERR_CERT_NOT_YET_VALID, + }, + + /* Validation should succeed for a certificate that is not yet valid when time + * validation is disabled. + */ + { + .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, + .key_pem_path = S2N_NOT_YET_VALID_KEY, + .disable_x509_time_validation = true, + .expected_error = S2N_ERR_OK, + }, + + /* Validation should fail for an expired certificate. */ + { + .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, + .key_pem_path = S2N_EXPIRED_KEY, + .disable_x509_time_validation = false, + .expected_error = S2N_ERR_CERT_EXPIRED, + }, + + /* Validation should succeed for an expired certificate when time validation is + * disabled. + */ + { + .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, + .key_pem_path = S2N_EXPIRED_KEY, + .disable_x509_time_validation = true, + .expected_error = S2N_ERR_OK, + }, + }; + /* clang-format on */ + + /* s2n_x509_validator test */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(test_cases[i].cert_pem_path, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, test_cases[i].cert_pem_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, + &public_key_out); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(ret); + } else { + EXPECT_ERROR_WITH_ERRNO(ret, expected_error); + } + } + + /* Self-talk: Disable validity period validation on client for server auth */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, test_cases[i].cert_pem_path, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + + /* Self-talk: Disable validity period validation on server for client auth */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *default_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, test_cases[i].cert_pem_path, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(server_config)); + } + + /* Disable verify host validation for client auth */ + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, s2n_verify_host_accept_everything, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + /* Ensure that certificate validation can fail for reasons other than time validation when time + * validation is disabled. + */ + for (int trust_cert = 0; trust_cert <= 1; trust_cert += 1) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + if (trust_cert) { + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + } + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, + &public_key_out); + + if (trust_cert) { + EXPECT_OK(ret); + } else { + /* If the certificate was not added to the trust store, validation should fail even + * though time validation was disabled. + */ + EXPECT_ERROR_WITH_ERRNO(ret, S2N_ERR_CERT_UNTRUSTED); + } + } + + END_TEST(); +} diff --git a/tests/viz/s2n_state_machine_viz.c b/tests/viz/s2n_state_machine_viz.c index b7426475421..826d69a4627 100644 --- a/tests/viz/s2n_state_machine_viz.c +++ b/tests/viz/s2n_state_machine_viz.c @@ -1,114 +1,115 @@ -/* - * 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 "tls/s2n_handshake_io.c" - -#define MAX_STATE_TYPE (APPLICATION_DATA + 1) - -struct state { - const char *name; - int children[MAX_STATE_TYPE]; -}; - -int traverse_handshakes(message_type_t hs_table[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH], const char *version, const char *destination) -{ - FILE *out; - char cmd[255]; - const char *dot = "dot -Tsvg > %s"; - snprintf(cmd, sizeof(cmd), dot, destination); - - out = popen(cmd, "w"); - if (!out) { - fprintf(stdout, "Failed to run graphviz. Check if you have graphviz installed?\n"); - return 1; - } - - struct state states[MAX_STATE_TYPE] = { 0 }; - - /* generate struct for all states */ - struct state initial = { .name = "INITIAL" }; - - for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { - struct state node = { .name = message_names[i] }; - states[i] = node; - } - - /* traverse handshakes */ - for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { - /* to detect client_hello from empty 0-init value, we check for the following value */ - if (!hs_table[i][1]) - continue; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - message_type_t msg = hs_table[i][j]; - if (j > 0 && !msg) - continue; - - /* register oneself as parent's child */ - if (j == 0) { - initial.children[msg] = 1; - } else { - states[hs_table[i][j - 1]].children[msg] = 1; - } - } - } - - /* find associated descendents of this node */ - #define print_children(state) \ - for (int c = 0; c < MAX_STATE_TYPE; c++) { \ - if (!state.children[c]) continue; \ - fprintf(out, " %s -> %s\n", state.name, states[c].name); \ - } - - /* produce dot format header */ - fprintf(out, "digraph G {\n"); - fprintf(out, " labelloc=\"t\";\n"); - fprintf(out, " label=<s2n TLS %s State Machine>\n", version); - - /* output initial root node */ - print_children(initial); - - /* iterate thru all possible nodes */ - for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { - print_children(states[i]); - } - - /* produce dot format footer */ - fprintf(out, " INITIAL [shape=diamond];\n"); - fprintf(out, " APPLICATION_DATA [shape=square];\n"); - fprintf(out, "}"); - - pclose(out); - - return 0; -} - -/* - * This program generates a visualization of the s2n TLS state machine. - * It does so by generating a directed acyclic graph, before piping - * a dot graph format output to graphviz to generate svg files in the - * document image directory. - */ - -int main(int argc, char **argv) -{ - fprintf(stdout, "Generating graphs for s2n TLS state machine...\n"); - traverse_handshakes(handshakes, "1.2", "../../docs/images/tls12_state_machine.svg"); - traverse_handshakes(tls13_handshakes, "1.3", "../../docs/images/tls13_state_machine.svg"); - fprintf(stdout, "Done.\n"); -} +#include "utils/s2n_prelude.h" +/* + * 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 "tls/s2n_handshake_io.c" + +#define MAX_STATE_TYPE (APPLICATION_DATA + 1) + +struct state { + const char *name; + int children[MAX_STATE_TYPE]; +}; + +int traverse_handshakes(message_type_t hs_table[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH], const char *version, const char *destination) +{ + FILE *out; + char cmd[255]; + const char *dot = "dot -Tsvg > %s"; + snprintf(cmd, sizeof(cmd), dot, destination); + + out = popen(cmd, "w"); + if (!out) { + fprintf(stdout, "Failed to run graphviz. Check if you have graphviz installed?\n"); + return 1; + } + + struct state states[MAX_STATE_TYPE] = { 0 }; + + /* generate struct for all states */ + struct state initial = { .name = "INITIAL" }; + + for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { + struct state node = { .name = message_names[i] }; + states[i] = node; + } + + /* traverse handshakes */ + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + /* to detect client_hello from empty 0-init value, we check for the following value */ + if (!hs_table[i][1]) + continue; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + message_type_t msg = hs_table[i][j]; + if (j > 0 && !msg) + continue; + + /* register oneself as parent's child */ + if (j == 0) { + initial.children[msg] = 1; + } else { + states[hs_table[i][j - 1]].children[msg] = 1; + } + } + } + + /* find associated descendents of this node */ + #define print_children(state) \ + for (int c = 0; c < MAX_STATE_TYPE; c++) { \ + if (!state.children[c]) continue; \ + fprintf(out, " %s -> %s\n", state.name, states[c].name); \ + } + + /* produce dot format header */ + fprintf(out, "digraph G {\n"); + fprintf(out, " labelloc=\"t\";\n"); + fprintf(out, " label=<s2n TLS %s State Machine>\n", version); + + /* output initial root node */ + print_children(initial); + + /* iterate thru all possible nodes */ + for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { + print_children(states[i]); + } + + /* produce dot format footer */ + fprintf(out, " INITIAL [shape=diamond];\n"); + fprintf(out, " APPLICATION_DATA [shape=square];\n"); + fprintf(out, "}"); + + pclose(out); + + return 0; +} + +/* + * This program generates a visualization of the s2n TLS state machine. + * It does so by generating a directed acyclic graph, before piping + * a dot graph format output to graphviz to generate svg files in the + * document image directory. + */ + +int main(int argc, char **argv) +{ + fprintf(stdout, "Generating graphs for s2n TLS state machine...\n"); + traverse_handshakes(handshakes, "1.2", "../../docs/images/tls12_state_machine.svg"); + traverse_handshakes(tls13_handshakes, "1.3", "../../docs/images/tls13_state_machine.svg"); + fprintf(stdout, "Done.\n"); +} diff --git a/tls/extensions/s2n_client_psk.c b/tls/extensions/s2n_client_psk.c index 4e69e6d8a8b..b61718cea89 100644 --- a/tls/extensions/s2n_client_psk.c +++ b/tls/extensions/s2n_client_psk.c @@ -1,421 +1,422 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_client_psk.h" - -#include - -#include "crypto/s2n_hash.h" -#include "tls/s2n_psk.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define SIZE_OF_BINDER_SIZE sizeof(uint8_t) -#define SIZE_OF_BINDER_LIST_SIZE sizeof(uint16_t) - -/* To avoid a DoS attack triggered by decrypting too many session tickets, - * set a limit on the number of tickets we will attempt to decrypt before giving up. - * We may want to make this configurable someday, but just set a reasonable maximum for now. */ -#define MAX_REJECTED_TICKETS 3 - -static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out); -static int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); -static int s2n_client_psk_is_missing(struct s2n_connection *conn); - -const s2n_extension_type s2n_client_psk_extension = { - .iana_value = TLS_EXTENSION_PRE_SHARED_KEY, - .minimum_version = S2N_TLS13, - .is_response = false, - .send = s2n_client_psk_send, - .recv = s2n_client_psk_recv, - .should_send = s2n_client_psk_should_send, - .if_missing = s2n_client_psk_is_missing, -}; - -int s2n_client_psk_is_missing(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* If the PSK extension is missing, we must not have received - * a request for early data. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# When a PSK is used and early data is allowed for that PSK, the client - *# can send Application Data in its first flight of messages. If the - *# client opts to do so, it MUST supply both the "pre_shared_key" and - *# "early_data" extensions. - */ - POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_UNSUPPORTED_EXTENSION); - return S2N_SUCCESS; -} - -bool s2n_client_psk_should_send(struct s2n_connection *conn) -{ - if (!conn || !conn->secure) { - return false; - } - - /* If this is NOT the second ClientHello after a retry, then all PSKs are viable. - * Send the extension if any PSKs are configured. - */ - if (!s2n_is_hello_retry_handshake(conn)) { - return conn->psk_params.psk_list.len > 0; - } - - /* If this is the second ClientHello after a retry, then only PSKs that match the cipher suite - * are viable. Only send the extension if at least one configured PSK matches the cipher suite. - */ - for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { - struct s2n_psk *psk = NULL; - if (s2n_result_is_ok(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)) - && psk != NULL - && conn->secure->cipher_suite->prf_alg == psk->hmac_alg) { - return true; - } - } - return false; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11.1 - *# The "obfuscated_ticket_age" - *# field of each PskIdentity contains an obfuscated version of the - *# ticket age formed by taking the age in milliseconds and adding the - *# "ticket_age_add" value that was included with the ticket (see - *# Section 4.6.1), modulo 2^32. -*/ -static S2N_RESULT s2n_generate_obfuscated_ticket_age(struct s2n_psk *psk, uint64_t current_time, uint32_t *output) -{ - RESULT_ENSURE_REF(psk); - RESULT_ENSURE_MUT(output); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# For identities - *# established externally, an obfuscated_ticket_age of 0 SHOULD be - *# used, - **/ - if (psk->type == S2N_PSK_TYPE_EXTERNAL) { - *output = 0; - return S2N_RESULT_OK; - } - - RESULT_ENSURE(current_time >= psk->ticket_issue_time, S2N_ERR_SAFETY); - - /* Calculate ticket age */ - uint64_t ticket_age_in_nanos = current_time - psk->ticket_issue_time; - - /* Convert ticket age to milliseconds */ - uint64_t ticket_age_in_millis = ticket_age_in_nanos / ONE_MILLISEC_IN_NANOS; - RESULT_ENSURE(ticket_age_in_millis <= UINT32_MAX, S2N_ERR_SAFETY); - - /* Add the ticket_age_add value to the ticket age in milliseconds. The resulting uint32_t value - * may wrap, resulting in the modulo 2^32 operation. */ - *output = ticket_age_in_millis + psk->ticket_age_add; - - return S2N_RESULT_OK; -} - -static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - struct s2n_psk_parameters *psk_params = &conn->psk_params; - struct s2n_array *psk_list = &psk_params->psk_list; - - struct s2n_stuffer_reservation identity_list_size; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &identity_list_size)); - - uint16_t binder_list_size = SIZE_OF_BINDER_LIST_SIZE; - - for (size_t i = 0; i < psk_list->len; i++) { - struct s2n_psk *psk = NULL; - POSIX_GUARD_RESULT(s2n_array_get(psk_list, i, (void **) &psk)); - POSIX_ENSURE_REF(psk); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 - *# In addition, in its updated ClientHello, the client SHOULD NOT offer - *# any pre-shared keys associated with a hash other than that of the - *# selected cipher suite. - */ - if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { - continue; - } - - /* Write the identity */ - POSIX_GUARD(s2n_stuffer_write_uint16(out, psk->identity.size)); - POSIX_GUARD(s2n_stuffer_write(out, &psk->identity)); - - /* Write obfuscated ticket age */ - uint32_t obfuscated_ticket_age = 0; - uint64_t current_time = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, ¤t_time)); - POSIX_GUARD_RESULT(s2n_generate_obfuscated_ticket_age(psk, current_time, &obfuscated_ticket_age)); - POSIX_GUARD(s2n_stuffer_write_uint32(out, obfuscated_ticket_age)); - - /* Calculate binder size */ - uint8_t hash_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); - binder_list_size += hash_size + SIZE_OF_BINDER_SIZE; - } - - POSIX_GUARD(s2n_stuffer_write_vector_size(&identity_list_size)); - - /* Calculating the binders requires a complete ClientHello, and at this point - * the extension size, extension list size, and message size are all blank. - * - * We'll write placeholder data to ensure the extension and extension list sizes - * are calculated correctly, then rewrite the binders with real data later. */ - psk_params->binder_list_size = binder_list_size; - POSIX_GUARD(s2n_stuffer_skip_write(out, binder_list_size)); - - return S2N_SUCCESS; -} - -/* Find the first of the server's PSK identities that matches the client's identities. - * This method compares all server identities to all client identities. - * - * While both the client's identities and whether a match was found are public, we should make an attempt - * to keep the server's identities a secret. We will make comparisons to the server's identities constant - * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). - * - * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, - * and an attacker could probably guess the server's known identities just by observing the public identities - * sent by clients. - */ -static S2N_RESULT s2n_select_external_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(client_identity_list); - - struct s2n_array *server_psks = &conn->psk_params.psk_list; - conn->psk_params.chosen_psk = NULL; - - for (size_t i = 0; i < server_psks->len; i++) { - struct s2n_psk *server_psk = NULL; - RESULT_GUARD(s2n_array_get(server_psks, i, (void **) &server_psk)); - RESULT_ENSURE_REF(server_psk); - - struct s2n_offered_psk client_psk = { 0 }; - uint16_t wire_index = 0; - - RESULT_GUARD_POSIX(s2n_offered_psk_list_reread(client_identity_list)); - while (s2n_offered_psk_list_has_next(client_identity_list)) { - RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); - uint16_t compare_size = S2N_MIN(client_psk.identity.size, server_psk->identity.size); - if (s2n_constant_time_equals(client_psk.identity.data, server_psk->identity.data, compare_size) - & (client_psk.identity.size == server_psk->identity.size) - & (conn->psk_params.chosen_psk == NULL)) { - conn->psk_params.chosen_psk = server_psk; - conn->psk_params.chosen_psk_wire_index = wire_index; - } - wire_index++; - }; - } - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_select_resumption_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(client_identity_list); - - struct s2n_offered_psk client_psk = { 0 }; - conn->psk_params.chosen_psk = NULL; - - uint8_t rejected_count = 0; - while (s2n_offered_psk_list_has_next(client_identity_list) && (rejected_count < MAX_REJECTED_TICKETS)) { - RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); - /* Select the first resumption PSK that can be decrypted */ - if (s2n_offered_psk_list_choose_psk(client_identity_list, &client_psk) == S2N_SUCCESS) { - return S2N_RESULT_OK; - } - rejected_count++; - } - - RESULT_BAIL(S2N_ERR_INVALID_SESSION_TICKET); -} - -static S2N_RESULT s2n_client_psk_recv_identity_list(struct s2n_connection *conn, struct s2n_stuffer *wire_identities_in) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_REF(wire_identities_in); - - struct s2n_offered_psk_list identity_list = { - .conn = conn, - .wire_data = *wire_identities_in, - }; - - if (conn->config->psk_selection_cb) { - RESULT_GUARD_POSIX(conn->config->psk_selection_cb(conn, conn->config->psk_selection_ctx, &identity_list)); - } else if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { - RESULT_GUARD(s2n_select_external_psk(conn, &identity_list)); - } else if (conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION) { - RESULT_GUARD(s2n_select_resumption_psk(conn, &identity_list)); - } - - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_client_psk_recv_binder_list(struct s2n_connection *conn, struct s2n_blob *partial_client_hello, - struct s2n_stuffer *wire_binders_in) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(wire_binders_in); - - uint16_t wire_index = 0; - while (s2n_stuffer_data_available(wire_binders_in) > 0) { - uint8_t wire_binder_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(wire_binders_in, &wire_binder_size)); - - uint8_t *wire_binder_data = NULL; - RESULT_ENSURE_REF(wire_binder_data = s2n_stuffer_raw_read(wire_binders_in, wire_binder_size)); - - struct s2n_blob wire_binder = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&wire_binder, wire_binder_data, wire_binder_size)); - - if (wire_index == conn->psk_params.chosen_psk_wire_index) { - RESULT_GUARD_POSIX(s2n_psk_verify_binder(conn, conn->psk_params.chosen_psk, - partial_client_hello, &wire_binder)); - return S2N_RESULT_OK; - } - wire_index++; - } - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); -} - -static S2N_RESULT s2n_client_psk_recv_identities(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - RESULT_ENSURE_REF(conn); - - uint16_t identity_list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &identity_list_size)); - - uint8_t *identity_list_data = NULL; - RESULT_ENSURE_REF(identity_list_data = s2n_stuffer_raw_read(extension, identity_list_size)); - - struct s2n_blob identity_list_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&identity_list_blob, identity_list_data, identity_list_size)); - - struct s2n_stuffer identity_list = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&identity_list, &identity_list_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&identity_list, identity_list_blob.size)); - - return s2n_client_psk_recv_identity_list(conn, &identity_list); -} - -static S2N_RESULT s2n_client_psk_recv_binders(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - RESULT_ENSURE_REF(conn); - - uint16_t binder_list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &binder_list_size)); - - uint8_t *binder_list_data = NULL; - RESULT_ENSURE_REF(binder_list_data = s2n_stuffer_raw_read(extension, binder_list_size)); - - struct s2n_blob binder_list_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&binder_list_blob, binder_list_data, binder_list_size)); - - struct s2n_stuffer binder_list = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&binder_list, &binder_list_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&binder_list, binder_list_blob.size)); - - /* Record the ClientHello message up to but not including the binder list. - * This is required to calculate the binder for the chosen PSK. */ - struct s2n_blob partial_client_hello = { 0 }; - const struct s2n_stuffer *client_hello = &conn->handshake.io; - uint32_t binders_size = binder_list_blob.size + SIZE_OF_BINDER_LIST_SIZE; - RESULT_ENSURE_GTE(client_hello->write_cursor, binders_size); - uint32_t partial_client_hello_size = client_hello->write_cursor - binders_size; - RESULT_GUARD_POSIX(s2n_blob_slice(&client_hello->blob, &partial_client_hello, 0, partial_client_hello_size)); - - return s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &binder_list); -} - -int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - POSIX_ENSURE_REF(conn); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# The "pre_shared_key" extension MUST be the last extension in the - *# ClientHello (this facilitates implementation as described below). - *# Servers MUST check that it is the last extension and otherwise fail - *# the handshake with an "illegal_parameter" alert. - */ - s2n_extension_type_id psk_ext_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); - POSIX_ENSURE_NE(conn->client_hello.extensions.count, 0); - uint16_t last_wire_index = conn->client_hello.extensions.count - 1; - uint16_t extension_wire_index = conn->client_hello.extensions.parsed_extensions[psk_ext_id].wire_index; - POSIX_ENSURE(extension_wire_index == last_wire_index, S2N_ERR_UNSUPPORTED_EXTENSION); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.9 - *# If clients offer "pre_shared_key" without a "psk_key_exchange_modes" extension, - *# servers MUST abort the handshake. - * - * We can safely do this check here because s2n_client_psk is - * required to be the last extension sent in the list. - */ - s2n_extension_type_id psk_ke_mode_ext_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &psk_ke_mode_ext_id)); - POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, psk_ke_mode_ext_id), S2N_ERR_MISSING_EXTENSION); - - if (conn->psk_params.psk_ke_mode == S2N_PSK_DHE_KE) { - s2n_extension_type_id key_share_ext_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_ext_id)); - /* A key_share extension must have been received in order to use a pre-shared key - * in (EC)DHE key exchange mode. - */ - POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, key_share_ext_id), S2N_ERR_MISSING_EXTENSION); - } else { - /* s2n currently only supports pre-shared keys in (EC)DHE key exchange mode. If we receive keys with any other - * exchange mode we fall back to a full handshake. - */ - return S2N_SUCCESS; - } - - if (s2n_result_is_error(s2n_client_psk_recv_identities(conn, extension))) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# If no acceptable PSKs are found, the server SHOULD perform a non-PSK - *# handshake if possible. - */ - conn->psk_params.chosen_psk = NULL; - } - - if (conn->psk_params.chosen_psk) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# Prior to accepting PSK key establishment, the server MUST validate - *# the corresponding binder value (see Section 4.2.11.2 below). If this - *# value is not present or does not validate, the server MUST abort the - *# handshake. - */ - POSIX_GUARD_RESULT(s2n_client_psk_recv_binders(conn, extension)); - } - - /* At this point, we have either chosen a PSK or fallen back to a full handshake. */ - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_client_psk.h" + +#include + +#include "crypto/s2n_hash.h" +#include "tls/s2n_psk.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define SIZE_OF_BINDER_SIZE sizeof(uint8_t) +#define SIZE_OF_BINDER_LIST_SIZE sizeof(uint16_t) + +/* To avoid a DoS attack triggered by decrypting too many session tickets, + * set a limit on the number of tickets we will attempt to decrypt before giving up. + * We may want to make this configurable someday, but just set a reasonable maximum for now. */ +#define MAX_REJECTED_TICKETS 3 + +static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out); +static int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); +static int s2n_client_psk_is_missing(struct s2n_connection *conn); + +const s2n_extension_type s2n_client_psk_extension = { + .iana_value = TLS_EXTENSION_PRE_SHARED_KEY, + .minimum_version = S2N_TLS13, + .is_response = false, + .send = s2n_client_psk_send, + .recv = s2n_client_psk_recv, + .should_send = s2n_client_psk_should_send, + .if_missing = s2n_client_psk_is_missing, +}; + +int s2n_client_psk_is_missing(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* If the PSK extension is missing, we must not have received + * a request for early data. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# When a PSK is used and early data is allowed for that PSK, the client + *# can send Application Data in its first flight of messages. If the + *# client opts to do so, it MUST supply both the "pre_shared_key" and + *# "early_data" extensions. + */ + POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_UNSUPPORTED_EXTENSION); + return S2N_SUCCESS; +} + +bool s2n_client_psk_should_send(struct s2n_connection *conn) +{ + if (!conn || !conn->secure) { + return false; + } + + /* If this is NOT the second ClientHello after a retry, then all PSKs are viable. + * Send the extension if any PSKs are configured. + */ + if (!s2n_is_hello_retry_handshake(conn)) { + return conn->psk_params.psk_list.len > 0; + } + + /* If this is the second ClientHello after a retry, then only PSKs that match the cipher suite + * are viable. Only send the extension if at least one configured PSK matches the cipher suite. + */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + if (s2n_result_is_ok(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)) + && psk != NULL + && conn->secure->cipher_suite->prf_alg == psk->hmac_alg) { + return true; + } + } + return false; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11.1 + *# The "obfuscated_ticket_age" + *# field of each PskIdentity contains an obfuscated version of the + *# ticket age formed by taking the age in milliseconds and adding the + *# "ticket_age_add" value that was included with the ticket (see + *# Section 4.6.1), modulo 2^32. +*/ +static S2N_RESULT s2n_generate_obfuscated_ticket_age(struct s2n_psk *psk, uint64_t current_time, uint32_t *output) +{ + RESULT_ENSURE_REF(psk); + RESULT_ENSURE_MUT(output); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# For identities + *# established externally, an obfuscated_ticket_age of 0 SHOULD be + *# used, + **/ + if (psk->type == S2N_PSK_TYPE_EXTERNAL) { + *output = 0; + return S2N_RESULT_OK; + } + + RESULT_ENSURE(current_time >= psk->ticket_issue_time, S2N_ERR_SAFETY); + + /* Calculate ticket age */ + uint64_t ticket_age_in_nanos = current_time - psk->ticket_issue_time; + + /* Convert ticket age to milliseconds */ + uint64_t ticket_age_in_millis = ticket_age_in_nanos / ONE_MILLISEC_IN_NANOS; + RESULT_ENSURE(ticket_age_in_millis <= UINT32_MAX, S2N_ERR_SAFETY); + + /* Add the ticket_age_add value to the ticket age in milliseconds. The resulting uint32_t value + * may wrap, resulting in the modulo 2^32 operation. */ + *output = ticket_age_in_millis + psk->ticket_age_add; + + return S2N_RESULT_OK; +} + +static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + struct s2n_psk_parameters *psk_params = &conn->psk_params; + struct s2n_array *psk_list = &psk_params->psk_list; + + struct s2n_stuffer_reservation identity_list_size; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &identity_list_size)); + + uint16_t binder_list_size = SIZE_OF_BINDER_LIST_SIZE; + + for (size_t i = 0; i < psk_list->len; i++) { + struct s2n_psk *psk = NULL; + POSIX_GUARD_RESULT(s2n_array_get(psk_list, i, (void **) &psk)); + POSIX_ENSURE_REF(psk); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. + */ + if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { + continue; + } + + /* Write the identity */ + POSIX_GUARD(s2n_stuffer_write_uint16(out, psk->identity.size)); + POSIX_GUARD(s2n_stuffer_write(out, &psk->identity)); + + /* Write obfuscated ticket age */ + uint32_t obfuscated_ticket_age = 0; + uint64_t current_time = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, ¤t_time)); + POSIX_GUARD_RESULT(s2n_generate_obfuscated_ticket_age(psk, current_time, &obfuscated_ticket_age)); + POSIX_GUARD(s2n_stuffer_write_uint32(out, obfuscated_ticket_age)); + + /* Calculate binder size */ + uint8_t hash_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); + binder_list_size += hash_size + SIZE_OF_BINDER_SIZE; + } + + POSIX_GUARD(s2n_stuffer_write_vector_size(&identity_list_size)); + + /* Calculating the binders requires a complete ClientHello, and at this point + * the extension size, extension list size, and message size are all blank. + * + * We'll write placeholder data to ensure the extension and extension list sizes + * are calculated correctly, then rewrite the binders with real data later. */ + psk_params->binder_list_size = binder_list_size; + POSIX_GUARD(s2n_stuffer_skip_write(out, binder_list_size)); + + return S2N_SUCCESS; +} + +/* Find the first of the server's PSK identities that matches the client's identities. + * This method compares all server identities to all client identities. + * + * While both the client's identities and whether a match was found are public, we should make an attempt + * to keep the server's identities a secret. We will make comparisons to the server's identities constant + * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). + * + * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, + * and an attacker could probably guess the server's known identities just by observing the public identities + * sent by clients. + */ +static S2N_RESULT s2n_select_external_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(client_identity_list); + + struct s2n_array *server_psks = &conn->psk_params.psk_list; + conn->psk_params.chosen_psk = NULL; + + for (size_t i = 0; i < server_psks->len; i++) { + struct s2n_psk *server_psk = NULL; + RESULT_GUARD(s2n_array_get(server_psks, i, (void **) &server_psk)); + RESULT_ENSURE_REF(server_psk); + + struct s2n_offered_psk client_psk = { 0 }; + uint16_t wire_index = 0; + + RESULT_GUARD_POSIX(s2n_offered_psk_list_reread(client_identity_list)); + while (s2n_offered_psk_list_has_next(client_identity_list)) { + RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); + uint16_t compare_size = S2N_MIN(client_psk.identity.size, server_psk->identity.size); + if (s2n_constant_time_equals(client_psk.identity.data, server_psk->identity.data, compare_size) + & (client_psk.identity.size == server_psk->identity.size) + & (conn->psk_params.chosen_psk == NULL)) { + conn->psk_params.chosen_psk = server_psk; + conn->psk_params.chosen_psk_wire_index = wire_index; + } + wire_index++; + }; + } + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_select_resumption_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(client_identity_list); + + struct s2n_offered_psk client_psk = { 0 }; + conn->psk_params.chosen_psk = NULL; + + uint8_t rejected_count = 0; + while (s2n_offered_psk_list_has_next(client_identity_list) && (rejected_count < MAX_REJECTED_TICKETS)) { + RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); + /* Select the first resumption PSK that can be decrypted */ + if (s2n_offered_psk_list_choose_psk(client_identity_list, &client_psk) == S2N_SUCCESS) { + return S2N_RESULT_OK; + } + rejected_count++; + } + + RESULT_BAIL(S2N_ERR_INVALID_SESSION_TICKET); +} + +static S2N_RESULT s2n_client_psk_recv_identity_list(struct s2n_connection *conn, struct s2n_stuffer *wire_identities_in) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_REF(wire_identities_in); + + struct s2n_offered_psk_list identity_list = { + .conn = conn, + .wire_data = *wire_identities_in, + }; + + if (conn->config->psk_selection_cb) { + RESULT_GUARD_POSIX(conn->config->psk_selection_cb(conn, conn->config->psk_selection_ctx, &identity_list)); + } else if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { + RESULT_GUARD(s2n_select_external_psk(conn, &identity_list)); + } else if (conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION) { + RESULT_GUARD(s2n_select_resumption_psk(conn, &identity_list)); + } + + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_client_psk_recv_binder_list(struct s2n_connection *conn, struct s2n_blob *partial_client_hello, + struct s2n_stuffer *wire_binders_in) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(wire_binders_in); + + uint16_t wire_index = 0; + while (s2n_stuffer_data_available(wire_binders_in) > 0) { + uint8_t wire_binder_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(wire_binders_in, &wire_binder_size)); + + uint8_t *wire_binder_data = NULL; + RESULT_ENSURE_REF(wire_binder_data = s2n_stuffer_raw_read(wire_binders_in, wire_binder_size)); + + struct s2n_blob wire_binder = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&wire_binder, wire_binder_data, wire_binder_size)); + + if (wire_index == conn->psk_params.chosen_psk_wire_index) { + RESULT_GUARD_POSIX(s2n_psk_verify_binder(conn, conn->psk_params.chosen_psk, + partial_client_hello, &wire_binder)); + return S2N_RESULT_OK; + } + wire_index++; + } + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); +} + +static S2N_RESULT s2n_client_psk_recv_identities(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + RESULT_ENSURE_REF(conn); + + uint16_t identity_list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &identity_list_size)); + + uint8_t *identity_list_data = NULL; + RESULT_ENSURE_REF(identity_list_data = s2n_stuffer_raw_read(extension, identity_list_size)); + + struct s2n_blob identity_list_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&identity_list_blob, identity_list_data, identity_list_size)); + + struct s2n_stuffer identity_list = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&identity_list, &identity_list_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&identity_list, identity_list_blob.size)); + + return s2n_client_psk_recv_identity_list(conn, &identity_list); +} + +static S2N_RESULT s2n_client_psk_recv_binders(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + RESULT_ENSURE_REF(conn); + + uint16_t binder_list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &binder_list_size)); + + uint8_t *binder_list_data = NULL; + RESULT_ENSURE_REF(binder_list_data = s2n_stuffer_raw_read(extension, binder_list_size)); + + struct s2n_blob binder_list_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&binder_list_blob, binder_list_data, binder_list_size)); + + struct s2n_stuffer binder_list = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&binder_list, &binder_list_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&binder_list, binder_list_blob.size)); + + /* Record the ClientHello message up to but not including the binder list. + * This is required to calculate the binder for the chosen PSK. */ + struct s2n_blob partial_client_hello = { 0 }; + const struct s2n_stuffer *client_hello = &conn->handshake.io; + uint32_t binders_size = binder_list_blob.size + SIZE_OF_BINDER_LIST_SIZE; + RESULT_ENSURE_GTE(client_hello->write_cursor, binders_size); + uint32_t partial_client_hello_size = client_hello->write_cursor - binders_size; + RESULT_GUARD_POSIX(s2n_blob_slice(&client_hello->blob, &partial_client_hello, 0, partial_client_hello_size)); + + return s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &binder_list); +} + +int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + POSIX_ENSURE_REF(conn); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# The "pre_shared_key" extension MUST be the last extension in the + *# ClientHello (this facilitates implementation as described below). + *# Servers MUST check that it is the last extension and otherwise fail + *# the handshake with an "illegal_parameter" alert. + */ + s2n_extension_type_id psk_ext_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); + POSIX_ENSURE_NE(conn->client_hello.extensions.count, 0); + uint16_t last_wire_index = conn->client_hello.extensions.count - 1; + uint16_t extension_wire_index = conn->client_hello.extensions.parsed_extensions[psk_ext_id].wire_index; + POSIX_ENSURE(extension_wire_index == last_wire_index, S2N_ERR_UNSUPPORTED_EXTENSION); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.9 + *# If clients offer "pre_shared_key" without a "psk_key_exchange_modes" extension, + *# servers MUST abort the handshake. + * + * We can safely do this check here because s2n_client_psk is + * required to be the last extension sent in the list. + */ + s2n_extension_type_id psk_ke_mode_ext_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &psk_ke_mode_ext_id)); + POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, psk_ke_mode_ext_id), S2N_ERR_MISSING_EXTENSION); + + if (conn->psk_params.psk_ke_mode == S2N_PSK_DHE_KE) { + s2n_extension_type_id key_share_ext_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_ext_id)); + /* A key_share extension must have been received in order to use a pre-shared key + * in (EC)DHE key exchange mode. + */ + POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, key_share_ext_id), S2N_ERR_MISSING_EXTENSION); + } else { + /* s2n currently only supports pre-shared keys in (EC)DHE key exchange mode. If we receive keys with any other + * exchange mode we fall back to a full handshake. + */ + return S2N_SUCCESS; + } + + if (s2n_result_is_error(s2n_client_psk_recv_identities(conn, extension))) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# If no acceptable PSKs are found, the server SHOULD perform a non-PSK + *# handshake if possible. + */ + conn->psk_params.chosen_psk = NULL; + } + + if (conn->psk_params.chosen_psk) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# Prior to accepting PSK key establishment, the server MUST validate + *# the corresponding binder value (see Section 4.2.11.2 below). If this + *# value is not present or does not validate, the server MUST abort the + *# handshake. + */ + POSIX_GUARD_RESULT(s2n_client_psk_recv_binders(conn, extension)); + } + + /* At this point, we have either chosen a PSK or fallen back to a full handshake. */ + return S2N_SUCCESS; +} diff --git a/tls/extensions/s2n_client_server_name.c b/tls/extensions/s2n_client_server_name.c index a1596e8e98c..c9c98d3faff 100644 --- a/tls/extensions/s2n_client_server_name.c +++ b/tls/extensions/s2n_client_server_name.c @@ -1,102 +1,103 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_client_server_name.h" - -#include - -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" - -#define S2N_NAME_TYPE_HOST_NAME 0 - -static bool s2n_client_server_name_should_send(struct s2n_connection *conn); -static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out); -static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); - -const s2n_extension_type s2n_client_server_name_extension = { - .iana_value = TLS_EXTENSION_SERVER_NAME, - .is_response = false, - .send = s2n_client_server_name_send, - .recv = s2n_client_server_name_recv, - .should_send = s2n_client_server_name_should_send, - .if_missing = s2n_extension_noop_if_missing, -}; - -static bool s2n_client_server_name_should_send(struct s2n_connection *conn) -{ - return conn && conn->server_name[0] != '\0'; -} - -static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - struct s2n_stuffer_reservation server_name_list_size = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &server_name_list_size)); - - /* NameType, as described by RFC6066. - * host_name is currently the only possible NameType defined. */ - POSIX_GUARD(s2n_stuffer_write_uint8(out, S2N_NAME_TYPE_HOST_NAME)); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, strlen(conn->server_name))); - POSIX_GUARD(s2n_stuffer_write_bytes(out, (const uint8_t *) conn->server_name, strlen(conn->server_name))); - - POSIX_GUARD(s2n_stuffer_write_vector_size(&server_name_list_size)); - return S2N_SUCCESS; -} - -/* Read the extension up to the first item in ServerNameList. Instantiates the server_name blob to - * point to the first entry. For now s2n ignores all subsequent items in ServerNameList. - */ -S2N_RESULT s2n_client_server_name_parse(struct s2n_stuffer *extension, struct s2n_blob *server_name) -{ - uint16_t list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &list_size)); - RESULT_ENSURE_LTE(list_size, s2n_stuffer_data_available(extension)); - - uint8_t server_name_type = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(extension, &server_name_type)); - RESULT_ENSURE_EQ(server_name_type, S2N_NAME_TYPE_HOST_NAME); - - uint16_t length = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &length)); - RESULT_ENSURE_LTE(length, s2n_stuffer_data_available(extension)); - - uint8_t *data = s2n_stuffer_raw_read(extension, length); - RESULT_ENSURE_REF(data); - RESULT_GUARD_POSIX(s2n_blob_init(server_name, data, length)); - - return S2N_RESULT_OK; -} - -static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - POSIX_ENSURE_REF(conn); - - /* Exit early if we've already parsed the server name */ - if (conn->server_name[0]) { - return S2N_SUCCESS; - } - - /* Ignore if malformed or we don't have enough space to store it. We just won't use the server name. */ - struct s2n_blob server_name = { 0 }; - if (!s2n_result_is_ok(s2n_client_server_name_parse(extension, &server_name)) || server_name.size > S2N_MAX_SERVER_NAME) { - return S2N_SUCCESS; - } - - POSIX_CHECKED_MEMCPY(conn->server_name, server_name.data, server_name.size); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_client_server_name.h" + +#include + +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +#define S2N_NAME_TYPE_HOST_NAME 0 + +static bool s2n_client_server_name_should_send(struct s2n_connection *conn); +static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out); +static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); + +const s2n_extension_type s2n_client_server_name_extension = { + .iana_value = TLS_EXTENSION_SERVER_NAME, + .is_response = false, + .send = s2n_client_server_name_send, + .recv = s2n_client_server_name_recv, + .should_send = s2n_client_server_name_should_send, + .if_missing = s2n_extension_noop_if_missing, +}; + +static bool s2n_client_server_name_should_send(struct s2n_connection *conn) +{ + return conn && conn->server_name[0] != '\0'; +} + +static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + struct s2n_stuffer_reservation server_name_list_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &server_name_list_size)); + + /* NameType, as described by RFC6066. + * host_name is currently the only possible NameType defined. */ + POSIX_GUARD(s2n_stuffer_write_uint8(out, S2N_NAME_TYPE_HOST_NAME)); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, strlen(conn->server_name))); + POSIX_GUARD(s2n_stuffer_write_bytes(out, (const uint8_t *) conn->server_name, strlen(conn->server_name))); + + POSIX_GUARD(s2n_stuffer_write_vector_size(&server_name_list_size)); + return S2N_SUCCESS; +} + +/* Read the extension up to the first item in ServerNameList. Instantiates the server_name blob to + * point to the first entry. For now s2n ignores all subsequent items in ServerNameList. + */ +S2N_RESULT s2n_client_server_name_parse(struct s2n_stuffer *extension, struct s2n_blob *server_name) +{ + uint16_t list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &list_size)); + RESULT_ENSURE_LTE(list_size, s2n_stuffer_data_available(extension)); + + uint8_t server_name_type = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(extension, &server_name_type)); + RESULT_ENSURE_EQ(server_name_type, S2N_NAME_TYPE_HOST_NAME); + + uint16_t length = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &length)); + RESULT_ENSURE_LTE(length, s2n_stuffer_data_available(extension)); + + uint8_t *data = s2n_stuffer_raw_read(extension, length); + RESULT_ENSURE_REF(data); + RESULT_GUARD_POSIX(s2n_blob_init(server_name, data, length)); + + return S2N_RESULT_OK; +} + +static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + POSIX_ENSURE_REF(conn); + + /* Exit early if we've already parsed the server name */ + if (conn->server_name[0]) { + return S2N_SUCCESS; + } + + /* Ignore if malformed or we don't have enough space to store it. We just won't use the server name. */ + struct s2n_blob server_name = { 0 }; + if (!s2n_result_is_ok(s2n_client_server_name_parse(extension, &server_name)) || server_name.size > S2N_MAX_SERVER_NAME) { + return S2N_SUCCESS; + } + + POSIX_CHECKED_MEMCPY(conn->server_name, server_name.data, server_name.size); + + return S2N_SUCCESS; +} diff --git a/tls/extensions/s2n_client_supported_versions.c b/tls/extensions/s2n_client_supported_versions.c index ab892cd509a..e9e943bcd96 100644 --- a/tls/extensions/s2n_client_supported_versions.c +++ b/tls/extensions/s2n_client_supported_versions.c @@ -1,203 +1,204 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_client_supported_versions.h" - -#include - -#include "tls/extensions/s2n_supported_versions.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" - -/** - * Specified in https://tools.ietf.org/html/rfc8446#section-4.2.1 - * - * "The "supported_versions" extension is used by the client to indicate - * which versions of TLS it supports and by the server to indicate which - * version it is using. The extension contains a list of supported - * versions in preference order, with the most preferred version first." - * - * Structure: - * Extension type (2 bytes) - * Extension size (2 bytes) - * Version list length (1 byte) - * Version list (number of versions * 2 bytes) - * - * Note: We assume in these functions that the supported version numbers - * are consecutive. This is true because S2N does not support SSLv2, and - * is already an assumption made in the old client hello version handling. - **/ - -static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out); -static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *in); - -const s2n_extension_type s2n_client_supported_versions_extension = { - .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, - .is_response = false, - .send = s2n_client_supported_versions_send, - .recv = s2n_client_supported_versions_recv, - .should_send = s2n_extension_send_if_tls13_connection, - .if_missing = s2n_extension_noop_if_missing, -}; - -static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - uint8_t highest_supported_version = conn->client_protocol_version; - uint8_t minimum_supported_version = s2n_unknown_protocol_version; - POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); - POSIX_ENSURE(highest_supported_version >= minimum_supported_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; - POSIX_GUARD(s2n_stuffer_write_uint8(out, version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); - - for (uint8_t i = highest_supported_version; i >= minimum_supported_version; i--) { - POSIX_GUARD(s2n_stuffer_write_uint8(out, i / 10)); - POSIX_GUARD(s2n_stuffer_write_uint8(out, i % 10)); - } - - return S2N_SUCCESS; -} - -int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension, - uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out) -{ - uint8_t highest_supported_version = conn->server_protocol_version; - uint8_t minimum_supported_version = s2n_unknown_protocol_version; - POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); - - uint8_t size_of_version_list = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(extension, &size_of_version_list)); - S2N_ERROR_IF(size_of_version_list != s2n_stuffer_data_available(extension), S2N_ERR_BAD_MESSAGE); - S2N_ERROR_IF(size_of_version_list % S2N_TLS_PROTOCOL_VERSION_LEN != 0, S2N_ERR_BAD_MESSAGE); - - uint8_t client_protocol_version = s2n_unknown_protocol_version; - uint8_t actual_protocol_version = s2n_unknown_protocol_version; - - for (int i = 0; i < size_of_version_list; i += S2N_TLS_PROTOCOL_VERSION_LEN) { - uint8_t client_version_parts[S2N_TLS_PROTOCOL_VERSION_LEN]; - POSIX_GUARD(s2n_stuffer_read_bytes(extension, client_version_parts, S2N_TLS_PROTOCOL_VERSION_LEN)); - - /* If the client version is outside of our supported versions, then ignore the value. - * S2N does not support SSLv2 except for upgrading connections. Since this extension is - * a TLS1.3 extension, we will skip any SSLv2 values. */ - if (client_version_parts[0] != 3 || client_version_parts[1] > 4) { - continue; - } - - uint16_t client_version = (client_version_parts[0] * 10) + client_version_parts[1]; - - client_protocol_version = S2N_MAX(client_version, client_protocol_version); - - if (client_version > highest_supported_version) { - continue; - } - - if (client_version < minimum_supported_version) { - continue; - } - - /* We ignore the client's preferred order and instead choose - * the highest version that both client and server support. */ - actual_protocol_version = S2N_MAX(client_version, actual_protocol_version); - } - - *client_protocol_version_out = client_protocol_version; - *actual_protocol_version_out = actual_protocol_version; - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_client_supported_versions_recv_impl(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(extension); - - RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, extension, &conn->client_protocol_version, - &conn->actual_protocol_version)); - - RESULT_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION); - RESULT_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - return S2N_RESULT_OK; -} - -static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - /* For backwards compatibility, the supported versions extension isn't used for protocol - * version selection if the server doesn't support TLS 1.3. This ensures that TLS 1.2 servers - * experience no behavior change due to processing the TLS 1.3 extension. See - * https://github.com/aws/s2n-tls/issues/4240. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.1 - *= type=exception - *= reason=The client hello legacy version is used for version selection on TLS 1.2 servers for backwards compatibility - *# If this extension is present in the ClientHello, servers MUST NOT use - *# the ClientHello.legacy_version value for version negotiation and MUST - *# use only the "supported_versions" extension to determine client - *# preferences. - */ - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - return S2N_SUCCESS; - } - - /* A TLS 1.3 state machine flag is used to determine if a HelloRetryRequest is negotiated. A - * protocol version of TLS 1.3 must be set in order to query the TLS 1.3 state machine, so - * it must be queried before the protocol version is potentially reset due to processing the - * extension. - */ - bool is_hrr_handshake = s2n_is_hello_retry_handshake(conn); - - s2n_result result = s2n_client_supported_versions_recv_impl(conn, extension); - if (s2n_result_is_error(result)) { - conn->client_protocol_version = s2n_unknown_protocol_version; - conn->actual_protocol_version = s2n_unknown_protocol_version; - - s2n_queue_reader_unsupported_protocol_version_alert(conn); - POSIX_ENSURE(s2n_errno != S2N_ERR_SAFETY, S2N_ERR_BAD_MESSAGE); - } - POSIX_GUARD_RESULT(result); - - /* When the supported versions extension is received in a ClientHello sent in response to a - * HelloRetryRequest, ensure that TLS 1.3 is selected as the protocol version. - */ - if (is_hrr_handshake && conn->handshake.message_number > 0) { - POSIX_ENSURE(conn->client_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - POSIX_ENSURE(conn->actual_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - return S2N_SUCCESS; -} - -/* Old-style extension functions -- remove after extensions refactor is complete */ - -int s2n_extensions_client_supported_versions_size(struct s2n_connection *conn) -{ - uint8_t minimum_supported_version = s2n_unknown_protocol_version; - POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); - uint8_t highest_supported_version = conn->client_protocol_version; - - uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; - - return version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 5; -} - -/* still used in fuzz test */ -int s2n_extensions_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - return s2n_extension_recv(&s2n_client_supported_versions_extension, conn, extension); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_client_supported_versions.h" + +#include + +#include "tls/extensions/s2n_supported_versions.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +/** + * Specified in https://tools.ietf.org/html/rfc8446#section-4.2.1 + * + * "The "supported_versions" extension is used by the client to indicate + * which versions of TLS it supports and by the server to indicate which + * version it is using. The extension contains a list of supported + * versions in preference order, with the most preferred version first." + * + * Structure: + * Extension type (2 bytes) + * Extension size (2 bytes) + * Version list length (1 byte) + * Version list (number of versions * 2 bytes) + * + * Note: We assume in these functions that the supported version numbers + * are consecutive. This is true because S2N does not support SSLv2, and + * is already an assumption made in the old client hello version handling. + **/ + +static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out); +static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *in); + +const s2n_extension_type s2n_client_supported_versions_extension = { + .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, + .is_response = false, + .send = s2n_client_supported_versions_send, + .recv = s2n_client_supported_versions_recv, + .should_send = s2n_extension_send_if_tls13_connection, + .if_missing = s2n_extension_noop_if_missing, +}; + +static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + uint8_t highest_supported_version = conn->client_protocol_version; + uint8_t minimum_supported_version = s2n_unknown_protocol_version; + POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); + POSIX_ENSURE(highest_supported_version >= minimum_supported_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; + POSIX_GUARD(s2n_stuffer_write_uint8(out, version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); + + for (uint8_t i = highest_supported_version; i >= minimum_supported_version; i--) { + POSIX_GUARD(s2n_stuffer_write_uint8(out, i / 10)); + POSIX_GUARD(s2n_stuffer_write_uint8(out, i % 10)); + } + + return S2N_SUCCESS; +} + +int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension, + uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out) +{ + uint8_t highest_supported_version = conn->server_protocol_version; + uint8_t minimum_supported_version = s2n_unknown_protocol_version; + POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); + + uint8_t size_of_version_list = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(extension, &size_of_version_list)); + S2N_ERROR_IF(size_of_version_list != s2n_stuffer_data_available(extension), S2N_ERR_BAD_MESSAGE); + S2N_ERROR_IF(size_of_version_list % S2N_TLS_PROTOCOL_VERSION_LEN != 0, S2N_ERR_BAD_MESSAGE); + + uint8_t client_protocol_version = s2n_unknown_protocol_version; + uint8_t actual_protocol_version = s2n_unknown_protocol_version; + + for (int i = 0; i < size_of_version_list; i += S2N_TLS_PROTOCOL_VERSION_LEN) { + uint8_t client_version_parts[S2N_TLS_PROTOCOL_VERSION_LEN]; + POSIX_GUARD(s2n_stuffer_read_bytes(extension, client_version_parts, S2N_TLS_PROTOCOL_VERSION_LEN)); + + /* If the client version is outside of our supported versions, then ignore the value. + * S2N does not support SSLv2 except for upgrading connections. Since this extension is + * a TLS1.3 extension, we will skip any SSLv2 values. */ + if (client_version_parts[0] != 3 || client_version_parts[1] > 4) { + continue; + } + + uint16_t client_version = (client_version_parts[0] * 10) + client_version_parts[1]; + + client_protocol_version = S2N_MAX(client_version, client_protocol_version); + + if (client_version > highest_supported_version) { + continue; + } + + if (client_version < minimum_supported_version) { + continue; + } + + /* We ignore the client's preferred order and instead choose + * the highest version that both client and server support. */ + actual_protocol_version = S2N_MAX(client_version, actual_protocol_version); + } + + *client_protocol_version_out = client_protocol_version; + *actual_protocol_version_out = actual_protocol_version; + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_client_supported_versions_recv_impl(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(extension); + + RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, extension, &conn->client_protocol_version, + &conn->actual_protocol_version)); + + RESULT_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION); + RESULT_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + return S2N_RESULT_OK; +} + +static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + /* For backwards compatibility, the supported versions extension isn't used for protocol + * version selection if the server doesn't support TLS 1.3. This ensures that TLS 1.2 servers + * experience no behavior change due to processing the TLS 1.3 extension. See + * https://github.com/aws/s2n-tls/issues/4240. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.1 + *= type=exception + *= reason=The client hello legacy version is used for version selection on TLS 1.2 servers for backwards compatibility + *# If this extension is present in the ClientHello, servers MUST NOT use + *# the ClientHello.legacy_version value for version negotiation and MUST + *# use only the "supported_versions" extension to determine client + *# preferences. + */ + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + return S2N_SUCCESS; + } + + /* A TLS 1.3 state machine flag is used to determine if a HelloRetryRequest is negotiated. A + * protocol version of TLS 1.3 must be set in order to query the TLS 1.3 state machine, so + * it must be queried before the protocol version is potentially reset due to processing the + * extension. + */ + bool is_hrr_handshake = s2n_is_hello_retry_handshake(conn); + + s2n_result result = s2n_client_supported_versions_recv_impl(conn, extension); + if (s2n_result_is_error(result)) { + conn->client_protocol_version = s2n_unknown_protocol_version; + conn->actual_protocol_version = s2n_unknown_protocol_version; + + s2n_queue_reader_unsupported_protocol_version_alert(conn); + POSIX_ENSURE(s2n_errno != S2N_ERR_SAFETY, S2N_ERR_BAD_MESSAGE); + } + POSIX_GUARD_RESULT(result); + + /* When the supported versions extension is received in a ClientHello sent in response to a + * HelloRetryRequest, ensure that TLS 1.3 is selected as the protocol version. + */ + if (is_hrr_handshake && conn->handshake.message_number > 0) { + POSIX_ENSURE(conn->client_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + POSIX_ENSURE(conn->actual_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + return S2N_SUCCESS; +} + +/* Old-style extension functions -- remove after extensions refactor is complete */ + +int s2n_extensions_client_supported_versions_size(struct s2n_connection *conn) +{ + uint8_t minimum_supported_version = s2n_unknown_protocol_version; + POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); + uint8_t highest_supported_version = conn->client_protocol_version; + + uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; + + return version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 5; +} + +/* still used in fuzz test */ +int s2n_extensions_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + return s2n_extension_recv(&s2n_client_supported_versions_extension, conn, extension); +} diff --git a/tls/extensions/s2n_extension_type.c b/tls/extensions/s2n_extension_type.c index 6612b1efc44..01ab6e02242 100644 --- a/tls/extensions/s2n_extension_type.c +++ b/tls/extensions/s2n_extension_type.c @@ -1,257 +1,258 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_extension_type.h" - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define TLS_EXTENSION_DATA_LENGTH_BYTES 2 - -/* Because there are 65536 possible extension IANAs, we will only - * put the lowest (and most common) in a lookup table to conserve space. */ -#define S2N_MAX_INDEXED_EXTENSION_IANA 60 - -const s2n_extension_type_id s2n_unsupported_extension = S2N_SUPPORTED_EXTENSIONS_COUNT; -s2n_extension_type_id s2n_extension_ianas_to_ids[S2N_MAX_INDEXED_EXTENSION_IANA]; - -int s2n_extension_type_init() -{ - /* Initialize to s2n_unsupported_extension */ - for (size_t i = 0; i < S2N_MAX_INDEXED_EXTENSION_IANA; i++) { - s2n_extension_ianas_to_ids[i] = s2n_unsupported_extension; - } - - /* Reverse the mapping */ - for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { - uint16_t iana_value = s2n_supported_extensions[i]; - if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { - s2n_extension_ianas_to_ids[iana_value] = i; - } - } - - return S2N_SUCCESS; -} - -/* Convert the IANA value (which ranges from 0->65535) to an id with a more - * constrained range. That id can be used for bitfields, array indexes, etc. - * to avoid allocating too much memory. */ -s2n_extension_type_id s2n_extension_iana_value_to_id(const uint16_t iana_value) -{ - /* Check the lookup table */ - if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { - return s2n_extension_ianas_to_ids[iana_value]; - } - - /* Fall back to the full list. We can handle this more - * efficiently later if our extension list gets long. */ - for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { - if (s2n_supported_extensions[i] == iana_value) { - return i; - } - } - - return s2n_unsupported_extension; -} - -int s2n_extension_supported_iana_value_to_id(const uint16_t iana_value, s2n_extension_type_id *internal_id) -{ - POSIX_ENSURE_REF(internal_id); - - *internal_id = s2n_extension_iana_value_to_id(iana_value); - S2N_ERROR_IF(*internal_id == s2n_unsupported_extension, S2N_ERR_UNRECOGNIZED_EXTENSION); - return S2N_SUCCESS; -} - -int s2n_extension_send(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *out) -{ - POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(extension_type->should_send); - POSIX_ENSURE_REF(extension_type->send); - POSIX_ENSURE_REF(conn); - - s2n_extension_type_id extension_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - - /* Do not send response if request not received. */ - if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_received, extension_id)) { - return S2N_SUCCESS; - } - - /* Do not send an extension that is not valid for the protocol version */ - if (extension_type->minimum_version > conn->actual_protocol_version) { - return S2N_SUCCESS; - } - - /* Check if we need to send. Some extensions are only sent if specific conditions are met. */ - if (!extension_type->should_send(conn)) { - return S2N_SUCCESS; - } - - /* Write extension type */ - POSIX_GUARD(s2n_stuffer_write_uint16(out, extension_type->iana_value)); - - /* Reserve space for extension size */ - struct s2n_stuffer_reservation extension_size_bytes = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &extension_size_bytes)); - - /* Write extension data */ - POSIX_GUARD(extension_type->send(conn, out)); - - /** - * Reset the tainted flag in the out stuffer (handshake.io stuffer). - * - * Some extension send functions call s2n_stuffer_raw_write(), which - * makes the stuffer tainted, preventing further resizing of the handshake.io stuffer. - * We need to reset this flag after each extension is sent, because handshake.io - * might need to be resized to send subsequent extensions. - * - * This is safe because the outstanding pointer from s2n_stuffer_raw_write() is scoped to - * the send function of each extension, and it is never stored in s2n_connection. - */ - out->tainted = false; - - /* Record extension size */ - POSIX_GUARD(s2n_stuffer_write_vector_size(&extension_size_bytes)); - - /* Set request bit flag */ - if (!extension_type->is_response) { - S2N_CBIT_SET(conn->extension_requests_sent, extension_id); - } - - return S2N_SUCCESS; -} - -int s2n_extension_recv(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *in) -{ - POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(extension_type->recv); - POSIX_ENSURE_REF(conn); - - s2n_extension_type_id extension_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2 - *# Implementations MUST NOT send extension responses if the remote - *# endpoint did not send the corresponding extension requests, with the - *# exception of the "cookie" extension in the HelloRetryRequest. Upon - *# receiving such an extension, an endpoint MUST abort the handshake - *# with an "unsupported_extension" alert. - * - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *# If the original session did not use the "extended_master_secret" - *# extension but the new ServerHello contains the extension, the - *# client MUST abort the handshake. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any - *# extensions that were not first offered by the client in its - *# ClientHello, with the exception of optionally the "cookie" (see - *# Section 4.2.2) extension. - **/ - if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { - POSIX_BAIL(S2N_ERR_UNSUPPORTED_EXTENSION); - } - - /* Do not process an extension not valid for the protocol version */ - if (extension_type->minimum_version > conn->actual_protocol_version) { - return S2N_SUCCESS; - } - - POSIX_GUARD(extension_type->recv(conn, in)); - - /* Set request bit flag */ - if (extension_type->is_response) { - S2N_CBIT_SET(conn->extension_responses_received, extension_id); - } else { - S2N_CBIT_SET(conn->extension_requests_received, extension_id); - } - - return S2N_SUCCESS; -} - -int s2n_extension_is_missing(const s2n_extension_type *extension_type, struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(extension_type->if_missing); - POSIX_ENSURE_REF(conn); - - s2n_extension_type_id extension_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - - /* Do not consider an extension missing if we did not send a request */ - if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { - return S2N_SUCCESS; - } - - /* Do not consider an extension missing if it is not valid for the protocol version */ - if (extension_type->minimum_version > conn->actual_protocol_version) { - return S2N_SUCCESS; - } - - POSIX_GUARD(extension_type->if_missing(conn)); - - return S2N_SUCCESS; -} - -int s2n_extension_send_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_extension_recv_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *in) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_extension_send_noop(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - return S2N_SUCCESS; -} - -int s2n_extension_recv_noop(struct s2n_connection *conn, struct s2n_stuffer *in) -{ - return S2N_SUCCESS; -} - -bool s2n_extension_always_send(struct s2n_connection *conn) -{ - return true; -} - -bool s2n_extension_never_send(struct s2n_connection *conn) -{ - return false; -} - -bool s2n_extension_send_if_tls13_connection(struct s2n_connection *conn) -{ - return s2n_connection_get_protocol_version(conn) >= S2N_TLS13; -} - -int s2n_extension_error_if_missing(struct s2n_connection *conn) -{ - POSIX_BAIL(S2N_ERR_MISSING_EXTENSION); -} - -int s2n_extension_noop_if_missing(struct s2n_connection *conn) -{ - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_extension_type.h" + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define TLS_EXTENSION_DATA_LENGTH_BYTES 2 + +/* Because there are 65536 possible extension IANAs, we will only + * put the lowest (and most common) in a lookup table to conserve space. */ +#define S2N_MAX_INDEXED_EXTENSION_IANA 60 + +const s2n_extension_type_id s2n_unsupported_extension = S2N_SUPPORTED_EXTENSIONS_COUNT; +s2n_extension_type_id s2n_extension_ianas_to_ids[S2N_MAX_INDEXED_EXTENSION_IANA]; + +int s2n_extension_type_init() +{ + /* Initialize to s2n_unsupported_extension */ + for (size_t i = 0; i < S2N_MAX_INDEXED_EXTENSION_IANA; i++) { + s2n_extension_ianas_to_ids[i] = s2n_unsupported_extension; + } + + /* Reverse the mapping */ + for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { + uint16_t iana_value = s2n_supported_extensions[i]; + if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { + s2n_extension_ianas_to_ids[iana_value] = i; + } + } + + return S2N_SUCCESS; +} + +/* Convert the IANA value (which ranges from 0->65535) to an id with a more + * constrained range. That id can be used for bitfields, array indexes, etc. + * to avoid allocating too much memory. */ +s2n_extension_type_id s2n_extension_iana_value_to_id(const uint16_t iana_value) +{ + /* Check the lookup table */ + if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { + return s2n_extension_ianas_to_ids[iana_value]; + } + + /* Fall back to the full list. We can handle this more + * efficiently later if our extension list gets long. */ + for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { + if (s2n_supported_extensions[i] == iana_value) { + return i; + } + } + + return s2n_unsupported_extension; +} + +int s2n_extension_supported_iana_value_to_id(const uint16_t iana_value, s2n_extension_type_id *internal_id) +{ + POSIX_ENSURE_REF(internal_id); + + *internal_id = s2n_extension_iana_value_to_id(iana_value); + S2N_ERROR_IF(*internal_id == s2n_unsupported_extension, S2N_ERR_UNRECOGNIZED_EXTENSION); + return S2N_SUCCESS; +} + +int s2n_extension_send(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(extension_type); + POSIX_ENSURE_REF(extension_type->should_send); + POSIX_ENSURE_REF(extension_type->send); + POSIX_ENSURE_REF(conn); + + s2n_extension_type_id extension_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); + + /* Do not send response if request not received. */ + if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_received, extension_id)) { + return S2N_SUCCESS; + } + + /* Do not send an extension that is not valid for the protocol version */ + if (extension_type->minimum_version > conn->actual_protocol_version) { + return S2N_SUCCESS; + } + + /* Check if we need to send. Some extensions are only sent if specific conditions are met. */ + if (!extension_type->should_send(conn)) { + return S2N_SUCCESS; + } + + /* Write extension type */ + POSIX_GUARD(s2n_stuffer_write_uint16(out, extension_type->iana_value)); + + /* Reserve space for extension size */ + struct s2n_stuffer_reservation extension_size_bytes = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &extension_size_bytes)); + + /* Write extension data */ + POSIX_GUARD(extension_type->send(conn, out)); + + /** + * Reset the tainted flag in the out stuffer (handshake.io stuffer). + * + * Some extension send functions call s2n_stuffer_raw_write(), which + * makes the stuffer tainted, preventing further resizing of the handshake.io stuffer. + * We need to reset this flag after each extension is sent, because handshake.io + * might need to be resized to send subsequent extensions. + * + * This is safe because the outstanding pointer from s2n_stuffer_raw_write() is scoped to + * the send function of each extension, and it is never stored in s2n_connection. + */ + out->tainted = false; + + /* Record extension size */ + POSIX_GUARD(s2n_stuffer_write_vector_size(&extension_size_bytes)); + + /* Set request bit flag */ + if (!extension_type->is_response) { + S2N_CBIT_SET(conn->extension_requests_sent, extension_id); + } + + return S2N_SUCCESS; +} + +int s2n_extension_recv(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *in) +{ + POSIX_ENSURE_REF(extension_type); + POSIX_ENSURE_REF(extension_type->recv); + POSIX_ENSURE_REF(conn); + + s2n_extension_type_id extension_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2 + *# Implementations MUST NOT send extension responses if the remote + *# endpoint did not send the corresponding extension requests, with the + *# exception of the "cookie" extension in the HelloRetryRequest. Upon + *# receiving such an extension, an endpoint MUST abort the handshake + *# with an "unsupported_extension" alert. + * + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *# If the original session did not use the "extended_master_secret" + *# extension but the new ServerHello contains the extension, the + *# client MUST abort the handshake. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { + POSIX_BAIL(S2N_ERR_UNSUPPORTED_EXTENSION); + } + + /* Do not process an extension not valid for the protocol version */ + if (extension_type->minimum_version > conn->actual_protocol_version) { + return S2N_SUCCESS; + } + + POSIX_GUARD(extension_type->recv(conn, in)); + + /* Set request bit flag */ + if (extension_type->is_response) { + S2N_CBIT_SET(conn->extension_responses_received, extension_id); + } else { + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + } + + return S2N_SUCCESS; +} + +int s2n_extension_is_missing(const s2n_extension_type *extension_type, struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(extension_type); + POSIX_ENSURE_REF(extension_type->if_missing); + POSIX_ENSURE_REF(conn); + + s2n_extension_type_id extension_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); + + /* Do not consider an extension missing if we did not send a request */ + if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { + return S2N_SUCCESS; + } + + /* Do not consider an extension missing if it is not valid for the protocol version */ + if (extension_type->minimum_version > conn->actual_protocol_version) { + return S2N_SUCCESS; + } + + POSIX_GUARD(extension_type->if_missing(conn)); + + return S2N_SUCCESS; +} + +int s2n_extension_send_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_extension_recv_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *in) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_extension_send_noop(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + return S2N_SUCCESS; +} + +int s2n_extension_recv_noop(struct s2n_connection *conn, struct s2n_stuffer *in) +{ + return S2N_SUCCESS; +} + +bool s2n_extension_always_send(struct s2n_connection *conn) +{ + return true; +} + +bool s2n_extension_never_send(struct s2n_connection *conn) +{ + return false; +} + +bool s2n_extension_send_if_tls13_connection(struct s2n_connection *conn) +{ + return s2n_connection_get_protocol_version(conn) >= S2N_TLS13; +} + +int s2n_extension_error_if_missing(struct s2n_connection *conn) +{ + POSIX_BAIL(S2N_ERR_MISSING_EXTENSION); +} + +int s2n_extension_noop_if_missing(struct s2n_connection *conn) +{ + return S2N_SUCCESS; +} diff --git a/tls/extensions/s2n_supported_versions.c b/tls/extensions/s2n_supported_versions.c index acf130d6c55..0f2d41b3c1d 100644 --- a/tls/extensions/s2n_supported_versions.c +++ b/tls/extensions/s2n_supported_versions.c @@ -1,38 +1,39 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_supported_versions.h" - -#include - -#include "tls/s2n_security_policies.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_connection_get_minimum_supported_version(struct s2n_connection *conn, uint8_t *min_version) -{ - RESULT_ENSURE_REF(min_version); - - const struct s2n_security_policy *security_policy = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); - RESULT_ENSURE_REF(security_policy); - *min_version = security_policy->minimum_protocol_version; - - /* QUIC requires >= TLS1.3 */ - if (s2n_connection_is_quic_enabled(conn)) { - *min_version = S2N_MAX(*min_version, S2N_TLS13); - } - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_supported_versions.h" + +#include + +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_connection_get_minimum_supported_version(struct s2n_connection *conn, uint8_t *min_version) +{ + RESULT_ENSURE_REF(min_version); + + const struct s2n_security_policy *security_policy = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); + RESULT_ENSURE_REF(security_policy); + *min_version = security_policy->minimum_protocol_version; + + /* QUIC requires >= TLS1.3 */ + if (s2n_connection_is_quic_enabled(conn)) { + *min_version = S2N_MAX(*min_version, S2N_TLS13); + } + + return S2N_RESULT_OK; +} diff --git a/tls/policy/s2n_policy_defaults.c b/tls/policy/s2n_policy_defaults.c index 20d6c7d617b..c4dc7d07a76 100644 --- a/tls/policy/s2n_policy_defaults.c +++ b/tls/policy/s2n_policy_defaults.c @@ -1,128 +1,129 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/policy/s2n_policy_defaults.h" - -#include "tls/s2n_security_policies.h" - -/* clang-format off */ -S2N_INLINE_SECURITY_POLICY_V1( - default_policy_strict, - S2N_TLS13, - S2N_CIPHER_PREF_LIST( - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - ), - S2N_SIG_PREF_LIST( - &s2n_mldsa44, - &s2n_mldsa65, - &s2n_mldsa87, - &s2n_ecdsa_sha256, - &s2n_ecdsa_sha384, - &s2n_ecdsa_sha512, - &s2n_rsa_pss_pss_sha256, - &s2n_rsa_pss_pss_sha384, - &s2n_rsa_pss_pss_sha512, - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_rsae_sha384, - &s2n_rsa_pss_rsae_sha512, - ), - S2N_CURVE_PREF_LIST( - &s2n_ecc_curve_secp256r1, - &s2n_ecc_curve_secp384r1, - &s2n_ecc_curve_secp521r1, - ), - S2N_KEM_PREF_LIST( - &s2n_secp256r1_mlkem_768, - &s2n_x25519_mlkem_768, - &s2n_secp384r1_mlkem_1024, - ) -); -/* clang-format on */ - -/* clang-format off */ -S2N_INLINE_SECURITY_POLICY_V1( - default_policy_compat, - S2N_TLS12, - S2N_CIPHER_PREF_LIST( - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - ), - S2N_SIG_PREF_LIST( - &s2n_mldsa44, - &s2n_mldsa65, - &s2n_mldsa87, - &s2n_ecdsa_sha256, - &s2n_ecdsa_sha384, - &s2n_ecdsa_sha512, - &s2n_rsa_pss_pss_sha256, - &s2n_rsa_pss_pss_sha384, - &s2n_rsa_pss_pss_sha512, - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_rsae_sha384, - &s2n_rsa_pss_rsae_sha512, - &s2n_rsa_pkcs1_sha256, - &s2n_rsa_pkcs1_sha384, - &s2n_rsa_pkcs1_sha512, - ), - S2N_CURVE_PREF_LIST( - &s2n_ecc_curve_secp256r1, - &s2n_ecc_curve_x25519, - &s2n_ecc_curve_secp384r1, - &s2n_ecc_curve_secp521r1, - ), - S2N_KEM_PREF_LIST( - &s2n_secp256r1_mlkem_768, - &s2n_x25519_mlkem_768, - &s2n_secp384r1_mlkem_1024, - ) -); -/* clang-format on */ - -const struct s2n_security_policy *default_policies[S2N_MAX_DEFAULT_POLICIES][S2N_MAX_POLICY_VERSIONS] = { - [S2N_POLICY_STRICT] = { - [S2N_STRICT_2025_08_20] = &default_policy_strict, - }, - [S2N_POLICY_COMPATIBLE] = { - [S2N_COMPAT_2025_08_20] = &default_policy_compat, - }, -}; - -const struct s2n_security_policy *s2n_security_policy_get(s2n_policy_name policy, uint64_t version) -{ - /* The uint64_t cast here is required for some older compilers to avoid a - * "tautological-constant-out-of-range-compare" error. That error assumes - * "policy" will be a valid s2n_default_policy, but that is not guaranteed by - * the standard. - */ - PTR_ENSURE((uint64_t) policy < S2N_MAX_DEFAULT_POLICIES, S2N_ERR_INVALID_SECURITY_POLICY); - PTR_ENSURE(version < S2N_MAX_POLICY_VERSIONS, S2N_ERR_INVALID_SECURITY_POLICY); - - const struct s2n_security_policy *match = default_policies[policy][version]; - PTR_ENSURE(match, S2N_ERR_INVALID_SECURITY_POLICY); - - return match; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/policy/s2n_policy_defaults.h" + +#include "tls/s2n_security_policies.h" + +/* clang-format off */ +S2N_INLINE_SECURITY_POLICY_V1( + default_policy_strict, + S2N_TLS13, + S2N_CIPHER_PREF_LIST( + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + ), + S2N_SIG_PREF_LIST( + &s2n_mldsa44, + &s2n_mldsa65, + &s2n_mldsa87, + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, + &s2n_ecdsa_sha512, + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + ), + S2N_CURVE_PREF_LIST( + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_secp384r1, + &s2n_ecc_curve_secp521r1, + ), + S2N_KEM_PREF_LIST( + &s2n_secp256r1_mlkem_768, + &s2n_x25519_mlkem_768, + &s2n_secp384r1_mlkem_1024, + ) +); +/* clang-format on */ + +/* clang-format off */ +S2N_INLINE_SECURITY_POLICY_V1( + default_policy_compat, + S2N_TLS12, + S2N_CIPHER_PREF_LIST( + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + ), + S2N_SIG_PREF_LIST( + &s2n_mldsa44, + &s2n_mldsa65, + &s2n_mldsa87, + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, + &s2n_ecdsa_sha512, + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + &s2n_rsa_pkcs1_sha256, + &s2n_rsa_pkcs1_sha384, + &s2n_rsa_pkcs1_sha512, + ), + S2N_CURVE_PREF_LIST( + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_x25519, + &s2n_ecc_curve_secp384r1, + &s2n_ecc_curve_secp521r1, + ), + S2N_KEM_PREF_LIST( + &s2n_secp256r1_mlkem_768, + &s2n_x25519_mlkem_768, + &s2n_secp384r1_mlkem_1024, + ) +); +/* clang-format on */ + +const struct s2n_security_policy *default_policies[S2N_MAX_DEFAULT_POLICIES][S2N_MAX_POLICY_VERSIONS] = { + [S2N_POLICY_STRICT] = { + [S2N_STRICT_2025_08_20] = &default_policy_strict, + }, + [S2N_POLICY_COMPATIBLE] = { + [S2N_COMPAT_2025_08_20] = &default_policy_compat, + }, +}; + +const struct s2n_security_policy *s2n_security_policy_get(s2n_policy_name policy, uint64_t version) +{ + /* The uint64_t cast here is required for some older compilers to avoid a + * "tautological-constant-out-of-range-compare" error. That error assumes + * "policy" will be a valid s2n_default_policy, but that is not guaranteed by + * the standard. + */ + PTR_ENSURE((uint64_t) policy < S2N_MAX_DEFAULT_POLICIES, S2N_ERR_INVALID_SECURITY_POLICY); + PTR_ENSURE(version < S2N_MAX_POLICY_VERSIONS, S2N_ERR_INVALID_SECURITY_POLICY); + + const struct s2n_security_policy *match = default_policies[policy][version]; + PTR_ENSURE(match, S2N_ERR_INVALID_SECURITY_POLICY); + + return match; +} diff --git a/tls/policy/s2n_policy_writer.c b/tls/policy/s2n_policy_writer.c index 5f7c3800f76..6e9d48901cd 100644 --- a/tls/policy/s2n_policy_writer.c +++ b/tls/policy/s2n_policy_writer.c @@ -1,209 +1,211 @@ -/* - * 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 "stuffer/s2n_stuffer.h" -#include "tls/policy/s2n_policy_feature.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_security_rules.h" -#include "utils/s2n_safety.h" - -#define BOOL_STR(b) ((b) ? "yes" : "no") - -extern const struct s2n_security_rule security_rule_definitions[S2N_SECURITY_RULES_COUNT]; - -static const char *version_strs[] = { - [S2N_SSLv2] = "SSLv2", - [S2N_SSLv3] = "SSLv3", - [S2N_TLS10] = "TLS1.0", - [S2N_TLS11] = "TLS1.1", - [S2N_TLS12] = "TLS1.2", - [S2N_TLS13] = "TLS1.3", -}; - -static S2N_RESULT s2n_security_policy_write_format_v1_to_stuffer(const struct s2n_security_policy *policy, struct s2n_stuffer *stuffer) -{ - RESULT_ENSURE_REF(policy); - RESULT_ENSURE_REF(stuffer); - - const char *version_str = NULL; - if (policy->minimum_protocol_version <= S2N_TLS13) { - version_str = version_strs[policy->minimum_protocol_version]; - } - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "min version: %s\n", version_str ? version_str : "None")); - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "rules:\n")); - for (size_t i = 0; i < S2N_SECURITY_RULES_COUNT; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s: %s\n", - security_rule_definitions[i].name, BOOL_STR(policy->rules[i]))); - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "cipher suites:\n")); - if (policy->cipher_preferences->allow_chacha20_boosting) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- chacha20 boosting enabled\n")); - } - for (size_t i = 0; i < policy->cipher_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->cipher_preferences->suites[i]->iana_name)); - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "signature schemes:\n")); - for (size_t i = 0; i < policy->signature_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->signature_preferences->signature_schemes[i]->name)); - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "curves:\n")); - for (size_t i = 0; i < policy->ecc_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->ecc_preferences->ecc_curves[i]->name)); - } - - for (size_t i = 0; policy->strongly_preferred_groups != NULL && i < policy->strongly_preferred_groups->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "strongly preferred groups:\n")); - const struct s2n_ecc_named_curve *strongly_preferred_curve = NULL; - const struct s2n_kem_group *strongly_preferred_kem_group = NULL; - bool found = false; - RESULT_GUARD_POSIX(s2n_find_ecc_curve_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_curve, &found)); - RESULT_GUARD_POSIX(s2n_find_kem_group_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_kem_group, &found)); - RESULT_ENSURE((strongly_preferred_curve == NULL) != (strongly_preferred_kem_group == NULL), S2N_ERR_INVALID_SUPPORTED_GROUP_STATE); - - if (strongly_preferred_curve != NULL) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_curve->name)); - } - - if (strongly_preferred_kem_group != NULL) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_kem_group->name)); - } - } - - if (policy->certificate_signature_preferences) { - if (policy->certificate_preferences_apply_locally) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate preferences apply locally\n")); - } - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate signature schemes:\n")); - for (size_t i = 0; i < policy->certificate_signature_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", - policy->certificate_signature_preferences->signature_schemes[i]->name)); - } - } - - if (policy->certificate_key_preferences) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate keys:\n")); - for (size_t i = 0; i < policy->certificate_key_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", - policy->certificate_key_preferences->certificate_keys[i]->name)); - } - } - - if (policy->kem_preferences && policy->kem_preferences != &kem_preferences_null) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "pq:\n")); - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- revision: %i\n", - policy->kem_preferences->tls13_pq_hybrid_draft_revision)); - - if (policy->kem_preferences->kem_count > 0) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kems:\n")); - for (size_t i = 0; i < policy->kem_preferences->kem_count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", - policy->kem_preferences->kems[i]->name)); - } - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kem groups:\n")); - for (size_t i = 0; i < policy->kem_preferences->tls13_kem_group_count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", - policy->kem_preferences->tls13_kem_groups[i]->name)); - } - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_security_policy_write_to_stuffer(const struct s2n_security_policy *policy, - s2n_policy_format format, struct s2n_stuffer *stuffer) -{ - RESULT_ENSURE_REF(policy); - RESULT_ENSURE_REF(stuffer); - - switch (format) { - case S2N_POLICY_FORMAT_DEBUG_V1: - RESULT_GUARD(s2n_security_policy_write_format_v1_to_stuffer(policy, stuffer)); - break; - default: - RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); - } - - return S2N_RESULT_OK; -} - -int s2n_security_policy_write_length(const struct s2n_security_policy *policy, - s2n_policy_format format, uint32_t *length) -{ - POSIX_ENSURE_REF(policy); - POSIX_ENSURE_REF(length); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); - - *length = s2n_stuffer_data_available(&stuffer); - - return S2N_SUCCESS; -} - -int s2n_security_policy_write_bytes(const struct s2n_security_policy *policy, - s2n_policy_format format, uint8_t *buffer, uint32_t buffer_length, uint32_t *output_size) -{ - POSIX_ENSURE_REF(policy); - POSIX_ENSURE_REF(buffer); - POSIX_ENSURE_REF(output_size); - *output_size = 0; - - /* Intermediate stuffer is needed because s2n_stuffer_printf requires temporary space for null - * terminators. We cannot write directly to application memory which may not have the extra byte - * available - */ - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); - POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); - uint32_t required_size = s2n_stuffer_data_available(&stuffer); - POSIX_ENSURE(buffer_length >= required_size, S2N_ERR_INSUFFICIENT_MEM_SIZE); - - POSIX_CHECKED_MEMCPY(buffer, stuffer.blob.data, required_size); - *output_size = s2n_stuffer_data_available(&stuffer); - return S2N_SUCCESS; -} - -int s2n_security_policy_write_fd(const struct s2n_security_policy *policy, - s2n_policy_format format, int fd, uint32_t *output_size) -{ - POSIX_ENSURE_REF(policy); - POSIX_ENSURE_REF(output_size); - POSIX_ENSURE(fd >= 0, S2N_ERR_INVALID_ARGUMENT); - *output_size = 0; - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); - - uint32_t data_size = s2n_stuffer_data_available(&stuffer); - ssize_t written = write(fd, stuffer.blob.data, data_size); - POSIX_ENSURE(written == (ssize_t) data_size, S2N_ERR_IO); - - *output_size = (uint32_t) written; - return S2N_SUCCESS; -} +/* + * 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 +#if !defined(_MSC_VER) +#include +#endif + +#include "stuffer/s2n_stuffer.h" +#include "tls/policy/s2n_policy_feature.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_security_rules.h" +#include "utils/s2n_safety.h" + +#define BOOL_STR(b) ((b) ? "yes" : "no") + +extern const struct s2n_security_rule security_rule_definitions[S2N_SECURITY_RULES_COUNT]; + +static const char *version_strs[] = { + [S2N_SSLv2] = "SSLv2", + [S2N_SSLv3] = "SSLv3", + [S2N_TLS10] = "TLS1.0", + [S2N_TLS11] = "TLS1.1", + [S2N_TLS12] = "TLS1.2", + [S2N_TLS13] = "TLS1.3", +}; + +static S2N_RESULT s2n_security_policy_write_format_v1_to_stuffer(const struct s2n_security_policy *policy, struct s2n_stuffer *stuffer) +{ + RESULT_ENSURE_REF(policy); + RESULT_ENSURE_REF(stuffer); + + const char *version_str = NULL; + if (policy->minimum_protocol_version <= S2N_TLS13) { + version_str = version_strs[policy->minimum_protocol_version]; + } + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "min version: %s\n", version_str ? version_str : "None")); + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "rules:\n")); + for (size_t i = 0; i < S2N_SECURITY_RULES_COUNT; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s: %s\n", + security_rule_definitions[i].name, BOOL_STR(policy->rules[i]))); + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "cipher suites:\n")); + if (policy->cipher_preferences->allow_chacha20_boosting) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- chacha20 boosting enabled\n")); + } + for (size_t i = 0; i < policy->cipher_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->cipher_preferences->suites[i]->iana_name)); + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "signature schemes:\n")); + for (size_t i = 0; i < policy->signature_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->signature_preferences->signature_schemes[i]->name)); + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "curves:\n")); + for (size_t i = 0; i < policy->ecc_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->ecc_preferences->ecc_curves[i]->name)); + } + + for (size_t i = 0; policy->strongly_preferred_groups != NULL && i < policy->strongly_preferred_groups->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "strongly preferred groups:\n")); + const struct s2n_ecc_named_curve *strongly_preferred_curve = NULL; + const struct s2n_kem_group *strongly_preferred_kem_group = NULL; + bool found = false; + RESULT_GUARD_POSIX(s2n_find_ecc_curve_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_curve, &found)); + RESULT_GUARD_POSIX(s2n_find_kem_group_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_kem_group, &found)); + RESULT_ENSURE((strongly_preferred_curve == NULL) != (strongly_preferred_kem_group == NULL), S2N_ERR_INVALID_SUPPORTED_GROUP_STATE); + + if (strongly_preferred_curve != NULL) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_curve->name)); + } + + if (strongly_preferred_kem_group != NULL) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_kem_group->name)); + } + } + + if (policy->certificate_signature_preferences) { + if (policy->certificate_preferences_apply_locally) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate preferences apply locally\n")); + } + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate signature schemes:\n")); + for (size_t i = 0; i < policy->certificate_signature_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", + policy->certificate_signature_preferences->signature_schemes[i]->name)); + } + } + + if (policy->certificate_key_preferences) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate keys:\n")); + for (size_t i = 0; i < policy->certificate_key_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", + policy->certificate_key_preferences->certificate_keys[i]->name)); + } + } + + if (policy->kem_preferences && policy->kem_preferences != &kem_preferences_null) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "pq:\n")); + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- revision: %i\n", + policy->kem_preferences->tls13_pq_hybrid_draft_revision)); + + if (policy->kem_preferences->kem_count > 0) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kems:\n")); + for (size_t i = 0; i < policy->kem_preferences->kem_count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", + policy->kem_preferences->kems[i]->name)); + } + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kem groups:\n")); + for (size_t i = 0; i < policy->kem_preferences->tls13_kem_group_count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", + policy->kem_preferences->tls13_kem_groups[i]->name)); + } + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_security_policy_write_to_stuffer(const struct s2n_security_policy *policy, + s2n_policy_format format, struct s2n_stuffer *stuffer) +{ + RESULT_ENSURE_REF(policy); + RESULT_ENSURE_REF(stuffer); + + switch (format) { + case S2N_POLICY_FORMAT_DEBUG_V1: + RESULT_GUARD(s2n_security_policy_write_format_v1_to_stuffer(policy, stuffer)); + break; + default: + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + + return S2N_RESULT_OK; +} + +int s2n_security_policy_write_length(const struct s2n_security_policy *policy, + s2n_policy_format format, uint32_t *length) +{ + POSIX_ENSURE_REF(policy); + POSIX_ENSURE_REF(length); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); + + *length = s2n_stuffer_data_available(&stuffer); + + return S2N_SUCCESS; +} + +int s2n_security_policy_write_bytes(const struct s2n_security_policy *policy, + s2n_policy_format format, uint8_t *buffer, uint32_t buffer_length, uint32_t *output_size) +{ + POSIX_ENSURE_REF(policy); + POSIX_ENSURE_REF(buffer); + POSIX_ENSURE_REF(output_size); + *output_size = 0; + + /* Intermediate stuffer is needed because s2n_stuffer_printf requires temporary space for null + * terminators. We cannot write directly to application memory which may not have the extra byte + * available + */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); + POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); + uint32_t required_size = s2n_stuffer_data_available(&stuffer); + POSIX_ENSURE(buffer_length >= required_size, S2N_ERR_INSUFFICIENT_MEM_SIZE); + + POSIX_CHECKED_MEMCPY(buffer, stuffer.blob.data, required_size); + *output_size = s2n_stuffer_data_available(&stuffer); + return S2N_SUCCESS; +} + +int s2n_security_policy_write_fd(const struct s2n_security_policy *policy, + s2n_policy_format format, int fd, uint32_t *output_size) +{ + POSIX_ENSURE_REF(policy); + POSIX_ENSURE_REF(output_size); + POSIX_ENSURE(fd >= 0, S2N_ERR_INVALID_ARGUMENT); + *output_size = 0; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); + + uint32_t data_size = s2n_stuffer_data_available(&stuffer); + ssize_t written = write(fd, stuffer.blob.data, data_size); + POSIX_ENSURE(written == (ssize_t) data_size, S2N_ERR_IO); + + *output_size = (uint32_t) written; + return S2N_SUCCESS; +} diff --git a/tls/s2n_alerts.c b/tls/s2n_alerts.c index 2b7264603b5..4eb28888540 100644 --- a/tls/s2n_alerts.c +++ b/tls/s2n_alerts.c @@ -1,392 +1,393 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_alerts.h" - -#include - -#include "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_atomic.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -#define S2N_ALERT_CASE(error, alert_code) \ - case (error): \ - *alert = (alert_code); \ - return S2N_RESULT_OK - -#define S2N_NO_ALERT(error) \ - case (error): \ - RESULT_BAIL(S2N_ERR_NO_ALERT) - -static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t *alert) -{ - RESULT_ENSURE_REF(alert); - - switch (error_code) { - S2N_ALERT_CASE(S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION); - S2N_ALERT_CASE(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, S2N_TLS_ALERT_HANDSHAKE_FAILURE); - S2N_ALERT_CASE(S2N_ERR_MISSING_CLIENT_CERT, S2N_TLS_ALERT_CERTIFICATE_REQUIRED); - - /* TODO: The ERR_BAD_MESSAGE -> ALERT_UNEXPECTED_MESSAGE mapping - * isn't always correct. Sometimes s2n-tls uses ERR_BAD_MESSAGE - * to indicate S2N_TLS_ALERT_ILLEGAL_PARAMETER instead. - * We'll want to add a new error to distinguish between the two usages: - * our errors should be equally or more specific than alerts, not less. - */ - S2N_ALERT_CASE(S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - S2N_ALERT_CASE(S2N_ERR_UNEXPECTED_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - S2N_ALERT_CASE(S2N_ERR_MISSING_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - - /* For errors involving secure renegotiation: - *= https://www.rfc-editor.org/rfc/rfc5746#3.4 - *# Note: later in Section 3, "abort the handshake" is used as - *# shorthand for "send a fatal handshake_failure alert and - *# terminate the connection". - */ - S2N_ALERT_CASE(S2N_ERR_NO_RENEGOTIATION, S2N_TLS_ALERT_HANDSHAKE_FAILURE); - - S2N_ALERT_CASE(S2N_ERR_KTLS_KEYUPDATE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - - /* For errors involving certificates */ - - /* This error is used in several ways so make it a general certificate issue - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# certificate_unknown: Some other (unspecified) issue arose in - *# processing the certificate, rendering it unacceptable. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - S2N_ALERT_CASE(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - S2N_ALERT_CASE(S2N_ERR_CERT_INVALID_HOSTNAME, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# certificate_revoked: A certificate was revoked by its signer. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_REVOKED, S2N_TLS_ALERT_CERTIFICATE_REVOKED); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# certificate_expired: A certificate has expired or is not currently - *# valid. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_NOT_YET_VALID, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); - S2N_ALERT_CASE(S2N_ERR_CERT_EXPIRED, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# unsupported_certificate: A certificate was of an unsupported type. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_TYPE_UNSUPPORTED, S2N_TLS_ALERT_UNSUPPORTED_CERTIFICATE); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# access_denied: A valid certificate or PSK was received, but when - *# access control was applied, the sender decided not to proceed with - *# negotiation. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_REJECTED, S2N_TLS_ALERT_ACCESS_DENIED); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# bad_certificate: A certificate was corrupt, contained signatures - *# that did not verify correctly, etc. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, S2N_TLS_ALERT_BAD_CERTIFICATE); - S2N_ALERT_CASE(S2N_ERR_CERT_INVALID, S2N_TLS_ALERT_BAD_CERTIFICATE); - S2N_ALERT_CASE(S2N_ERR_DECODE_CERTIFICATE, S2N_TLS_ALERT_BAD_CERTIFICATE); - - /* TODO: Add mappings for other protocol errors. - */ - S2N_NO_ALERT(S2N_ERR_ENCRYPT); - S2N_NO_ALERT(S2N_ERR_DECRYPT); - S2N_NO_ALERT(S2N_ERR_KEY_INIT); - S2N_NO_ALERT(S2N_ERR_KEY_DESTROY); - S2N_NO_ALERT(S2N_ERR_DH_SERIALIZING); - S2N_NO_ALERT(S2N_ERR_DH_SHARED_SECRET); - S2N_NO_ALERT(S2N_ERR_DH_WRITING_PUBLIC_KEY); - S2N_NO_ALERT(S2N_ERR_DH_FAILED_SIGNING); - S2N_NO_ALERT(S2N_ERR_DH_COPYING_PARAMETERS); - S2N_NO_ALERT(S2N_ERR_DH_GENERATING_PARAMETERS); - S2N_NO_ALERT(S2N_ERR_CIPHER_NOT_SUPPORTED); - S2N_NO_ALERT(S2N_ERR_NO_APPLICATION_PROTOCOL); - S2N_NO_ALERT(S2N_ERR_FALLBACK_DETECTED); - S2N_NO_ALERT(S2N_ERR_HASH_DIGEST_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_INIT_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_UPDATE_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_COPY_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_WIPE_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_NOT_READY); - S2N_NO_ALERT(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED); - S2N_NO_ALERT(S2N_ERR_DECODE_PRIVATE_KEY); - S2N_NO_ALERT(S2N_ERR_INVALID_HELLO_RETRY); - S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_SCHEME); - S2N_NO_ALERT(S2N_ERR_CBC_VERIFY); - S2N_NO_ALERT(S2N_ERR_DH_COPYING_PUBLIC_KEY); - S2N_NO_ALERT(S2N_ERR_SIGN); - S2N_NO_ALERT(S2N_ERR_VERIFY_SIGNATURE); - S2N_NO_ALERT(S2N_ERR_ECDHE_GEN_KEY); - S2N_NO_ALERT(S2N_ERR_ECDHE_SHARED_SECRET); - S2N_NO_ALERT(S2N_ERR_ECDHE_UNSUPPORTED_CURVE); - S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY); - S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS); - S2N_NO_ALERT(S2N_ERR_ECDSA_UNSUPPORTED_CURVE); - S2N_NO_ALERT(S2N_ERR_ECDHE_SERIALIZING); - S2N_NO_ALERT(S2N_ERR_KEM_UNSUPPORTED_PARAMS); - S2N_NO_ALERT(S2N_ERR_SHUTDOWN_RECORD_TYPE); - S2N_NO_ALERT(S2N_ERR_SHUTDOWN_CLOSED); - S2N_NO_ALERT(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); - S2N_NO_ALERT(S2N_ERR_RECORD_LIMIT); - S2N_NO_ALERT(S2N_ERR_CERT_INTENT_INVALID); - S2N_NO_ALERT(S2N_ERR_CRL_LOOKUP_FAILED); - S2N_NO_ALERT(S2N_ERR_CRL_SIGNATURE); - S2N_NO_ALERT(S2N_ERR_CRL_ISSUER); - S2N_NO_ALERT(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); - S2N_NO_ALERT(S2N_ERR_CRL_INVALID_THIS_UPDATE); - S2N_NO_ALERT(S2N_ERR_CRL_INVALID_NEXT_UPDATE); - S2N_NO_ALERT(S2N_ERR_CRL_NOT_YET_VALID); - S2N_NO_ALERT(S2N_ERR_CRL_EXPIRED); - S2N_NO_ALERT(S2N_ERR_INVALID_MAX_FRAG_LEN); - S2N_NO_ALERT(S2N_ERR_MAX_FRAG_LEN_MISMATCH); - S2N_NO_ALERT(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - S2N_NO_ALERT(S2N_ERR_BAD_KEY_SHARE); - S2N_NO_ALERT(S2N_ERR_CANCELLED); - S2N_NO_ALERT(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - S2N_NO_ALERT(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_SIZE); - S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - S2N_NO_ALERT(S2N_ERR_UNSUPPORTED_EXTENSION); - S2N_NO_ALERT(S2N_ERR_DUPLICATE_EXTENSION); - S2N_NO_ALERT(S2N_ERR_MAX_EARLY_DATA_SIZE); - S2N_NO_ALERT(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); - } - - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -static bool s2n_alerts_supported(struct s2n_connection *conn) -{ - /* If running in QUIC mode, QUIC handles alerting. - * S2N should not send or receive alerts. */ - return !s2n_connection_is_quic_enabled(conn); -} - -/* In TLS1.3 all Alerts - *= https://www.rfc-editor.org/rfc/rfc8446#section-6 - *# MUST be treated as error alerts when received - *# regardless of the AlertLevel in the message. - */ -static bool s2n_process_as_warning(struct s2n_connection *conn, uint8_t level, uint8_t type) -{ - /* Only TLS1.2 considers the alert level. The alert level field is - * considered deprecated in TLS1.3. If the protocol version has not - * been negotiated yet, we allow for warnings to avoid premature - * handshake failures before we know the protocol version. */ - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13 || !conn->actual_protocol_version_established) { - return level == S2N_TLS_ALERT_LEVEL_WARNING - && conn->config->alert_behavior == S2N_ALERT_IGNORE_WARNINGS; - } - - /* user_canceled is the only alert currently treated as a warning in TLS1.3. - * We need to treat it as a warning regardless of alert_behavior to avoid marking - * correctly-closed connections as failed. */ - return type == S2N_TLS_ALERT_USER_CANCELED; -} - -int s2n_error_get_alert(int error, uint8_t *alert) -{ - int error_type = s2n_error_get_type(error); - - POSIX_ENSURE_REF(alert); - - switch (error_type) { - case S2N_ERR_T_OK: - case S2N_ERR_T_CLOSED: - case S2N_ERR_T_BLOCKED: - case S2N_ERR_T_USAGE: - case S2N_ERR_T_ALERT: - POSIX_BAIL(S2N_ERR_NO_ALERT); - break; - case S2N_ERR_T_PROTO: - POSIX_GUARD_RESULT(s2n_translate_protocol_error_to_alert(error, alert)); - break; - case S2N_ERR_T_IO: - case S2N_ERR_T_INTERNAL: - *alert = S2N_TLS_ALERT_INTERNAL_ERROR; - break; - } - - return S2N_SUCCESS; -} - -int s2n_process_alert_fragment(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) == 2, S2N_ERR_ALERT_PRESENT); - POSIX_ENSURE(s2n_alerts_supported(conn), S2N_ERR_BAD_MESSAGE); - - while (s2n_stuffer_data_available(&conn->in)) { - uint8_t bytes_required = 2; - - /* Alerts are two bytes long, but can still be fragmented or coalesced */ - if (s2n_stuffer_data_available(&conn->alert_in) == 1) { - bytes_required = 1; - } - - int bytes_to_read = S2N_MIN(bytes_required, s2n_stuffer_data_available(&conn->in)); - - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->alert_in, bytes_to_read)); - - if (s2n_stuffer_data_available(&conn->alert_in) == 2) { - /* Close notifications are handled as shutdowns */ - if (conn->alert_in_data[1] == S2N_TLS_ALERT_CLOSE_NOTIFY) { - s2n_atomic_flag_set(&conn->read_closed); - s2n_atomic_flag_set(&conn->close_notify_received); - return 0; - } - - /* Ignore warning-level alerts if we're in warning-tolerant mode */ - if (s2n_process_as_warning(conn, conn->alert_in_data[0], conn->alert_in_data[1])) { - POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); - return 0; - } - - /* RFC 5077 5.1 - Expire any cached session on an error alert */ - if (s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { - conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); - } - - /* All other alerts are treated as fatal errors. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-6 - *# Unknown Alert types MUST be treated as error alerts. - */ - POSIX_GUARD_RESULT(s2n_connection_set_closed(conn)); - s2n_atomic_flag_set(&conn->error_alert_received); - POSIX_BAIL(S2N_ERR_ALERT); - } - } - - return 0; -} - -static S2N_RESULT s2n_queue_reader_alert(struct s2n_connection *conn, s2n_tls_alert_code code) -{ - RESULT_ENSURE_REF(conn); - if (!conn->reader_alert_out) { - conn->reader_alert_out = code; - } - return S2N_RESULT_OK; -} - -int s2n_queue_reader_unsupported_protocol_version_alert(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_PROTOCOL_VERSION)); - return S2N_SUCCESS; -} - -int s2n_queue_reader_handshake_failure_alert(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_HANDSHAKE_FAILURE)); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_queue_reader_no_renegotiation_alert(struct s2n_connection *conn) -{ - /** - *= https://www.rfc-editor.org/rfc/rfc5746#4.5 - *# SSLv3 does not define the "no_renegotiation" alert (and does - *# not offer a way to indicate a refusal to renegotiate at a "warning" - *# level). SSLv3 clients that refuse renegotiation SHOULD use a fatal - *# handshake_failure alert. - **/ - if (s2n_connection_get_protocol_version(conn) == S2N_SSLv3) { - RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); - } - - if (!conn->reader_warning_out) { - conn->reader_warning_out = S2N_TLS_ALERT_NO_RENEGOTIATION; - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_alerts_write_error_or_close_notify(struct s2n_connection *conn) -{ - if (!s2n_alerts_supported(conn)) { - return S2N_RESULT_OK; - } - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *= type=exception - *= reason=Specific alerts could expose a side-channel attack vector. - *# The phrases "terminate the connection with an X - *# alert" and "abort the handshake with an X alert" mean that the - *# implementation MUST send alert X if it sends any alert. - * - * By default, s2n-tls sends a generic close_notify alert, even in - * response to fatal errors. This is done to avoid potential - * side-channel attacks since specific alerts could reveal information - * about why the error occurred. - */ - uint8_t code = S2N_TLS_ALERT_CLOSE_NOTIFY; - uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; - - /* s2n-tls sends a very small subset of more specific error alerts. - * Since either the reader or the writer can produce one of these alerts, - * but only a single alert can be reported, we prioritize writer alerts. - */ - if (conn->writer_alert_out) { - code = conn->writer_alert_out; - level = S2N_TLS_ALERT_LEVEL_FATAL; - } else if (conn->reader_alert_out) { - code = conn->reader_alert_out; - level = S2N_TLS_ALERT_LEVEL_FATAL; - } - - struct s2n_blob alert = { 0 }; - uint8_t alert_bytes[] = { level, code }; - RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); - - RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); - conn->alert_sent = true; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_alerts_write_warning(struct s2n_connection *conn) -{ - if (!s2n_alerts_supported(conn)) { - return S2N_RESULT_OK; - } - - uint8_t code = conn->reader_warning_out; - uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; - - struct s2n_blob alert = { 0 }; - uint8_t alert_bytes[] = { level, code }; - RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); - - RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_alerts.h" + +#include + +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_atomic.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +#define S2N_ALERT_CASE(error, alert_code) \ + case (error): \ + *alert = (alert_code); \ + return S2N_RESULT_OK + +#define S2N_NO_ALERT(error) \ + case (error): \ + RESULT_BAIL(S2N_ERR_NO_ALERT) + +static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t *alert) +{ + RESULT_ENSURE_REF(alert); + + switch (error_code) { + S2N_ALERT_CASE(S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION); + S2N_ALERT_CASE(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + S2N_ALERT_CASE(S2N_ERR_MISSING_CLIENT_CERT, S2N_TLS_ALERT_CERTIFICATE_REQUIRED); + + /* TODO: The ERR_BAD_MESSAGE -> ALERT_UNEXPECTED_MESSAGE mapping + * isn't always correct. Sometimes s2n-tls uses ERR_BAD_MESSAGE + * to indicate S2N_TLS_ALERT_ILLEGAL_PARAMETER instead. + * We'll want to add a new error to distinguish between the two usages: + * our errors should be equally or more specific than alerts, not less. + */ + S2N_ALERT_CASE(S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + S2N_ALERT_CASE(S2N_ERR_UNEXPECTED_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + S2N_ALERT_CASE(S2N_ERR_MISSING_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + + /* For errors involving secure renegotiation: + *= https://www.rfc-editor.org/rfc/rfc5746#3.4 + *# Note: later in Section 3, "abort the handshake" is used as + *# shorthand for "send a fatal handshake_failure alert and + *# terminate the connection". + */ + S2N_ALERT_CASE(S2N_ERR_NO_RENEGOTIATION, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + + S2N_ALERT_CASE(S2N_ERR_KTLS_KEYUPDATE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + + /* For errors involving certificates */ + + /* This error is used in several ways so make it a general certificate issue + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# certificate_unknown: Some other (unspecified) issue arose in + *# processing the certificate, rendering it unacceptable. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + S2N_ALERT_CASE(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + S2N_ALERT_CASE(S2N_ERR_CERT_INVALID_HOSTNAME, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# certificate_revoked: A certificate was revoked by its signer. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_REVOKED, S2N_TLS_ALERT_CERTIFICATE_REVOKED); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# certificate_expired: A certificate has expired or is not currently + *# valid. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_NOT_YET_VALID, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); + S2N_ALERT_CASE(S2N_ERR_CERT_EXPIRED, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# unsupported_certificate: A certificate was of an unsupported type. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_TYPE_UNSUPPORTED, S2N_TLS_ALERT_UNSUPPORTED_CERTIFICATE); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# access_denied: A valid certificate or PSK was received, but when + *# access control was applied, the sender decided not to proceed with + *# negotiation. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_REJECTED, S2N_TLS_ALERT_ACCESS_DENIED); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# bad_certificate: A certificate was corrupt, contained signatures + *# that did not verify correctly, etc. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, S2N_TLS_ALERT_BAD_CERTIFICATE); + S2N_ALERT_CASE(S2N_ERR_CERT_INVALID, S2N_TLS_ALERT_BAD_CERTIFICATE); + S2N_ALERT_CASE(S2N_ERR_DECODE_CERTIFICATE, S2N_TLS_ALERT_BAD_CERTIFICATE); + + /* TODO: Add mappings for other protocol errors. + */ + S2N_NO_ALERT(S2N_ERR_ENCRYPT); + S2N_NO_ALERT(S2N_ERR_DECRYPT); + S2N_NO_ALERT(S2N_ERR_KEY_INIT); + S2N_NO_ALERT(S2N_ERR_KEY_DESTROY); + S2N_NO_ALERT(S2N_ERR_DH_SERIALIZING); + S2N_NO_ALERT(S2N_ERR_DH_SHARED_SECRET); + S2N_NO_ALERT(S2N_ERR_DH_WRITING_PUBLIC_KEY); + S2N_NO_ALERT(S2N_ERR_DH_FAILED_SIGNING); + S2N_NO_ALERT(S2N_ERR_DH_COPYING_PARAMETERS); + S2N_NO_ALERT(S2N_ERR_DH_GENERATING_PARAMETERS); + S2N_NO_ALERT(S2N_ERR_CIPHER_NOT_SUPPORTED); + S2N_NO_ALERT(S2N_ERR_NO_APPLICATION_PROTOCOL); + S2N_NO_ALERT(S2N_ERR_FALLBACK_DETECTED); + S2N_NO_ALERT(S2N_ERR_HASH_DIGEST_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_INIT_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_UPDATE_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_COPY_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_WIPE_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_NOT_READY); + S2N_NO_ALERT(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED); + S2N_NO_ALERT(S2N_ERR_DECODE_PRIVATE_KEY); + S2N_NO_ALERT(S2N_ERR_INVALID_HELLO_RETRY); + S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_SCHEME); + S2N_NO_ALERT(S2N_ERR_CBC_VERIFY); + S2N_NO_ALERT(S2N_ERR_DH_COPYING_PUBLIC_KEY); + S2N_NO_ALERT(S2N_ERR_SIGN); + S2N_NO_ALERT(S2N_ERR_VERIFY_SIGNATURE); + S2N_NO_ALERT(S2N_ERR_ECDHE_GEN_KEY); + S2N_NO_ALERT(S2N_ERR_ECDHE_SHARED_SECRET); + S2N_NO_ALERT(S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY); + S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS); + S2N_NO_ALERT(S2N_ERR_ECDSA_UNSUPPORTED_CURVE); + S2N_NO_ALERT(S2N_ERR_ECDHE_SERIALIZING); + S2N_NO_ALERT(S2N_ERR_KEM_UNSUPPORTED_PARAMS); + S2N_NO_ALERT(S2N_ERR_SHUTDOWN_RECORD_TYPE); + S2N_NO_ALERT(S2N_ERR_SHUTDOWN_CLOSED); + S2N_NO_ALERT(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + S2N_NO_ALERT(S2N_ERR_RECORD_LIMIT); + S2N_NO_ALERT(S2N_ERR_CERT_INTENT_INVALID); + S2N_NO_ALERT(S2N_ERR_CRL_LOOKUP_FAILED); + S2N_NO_ALERT(S2N_ERR_CRL_SIGNATURE); + S2N_NO_ALERT(S2N_ERR_CRL_ISSUER); + S2N_NO_ALERT(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); + S2N_NO_ALERT(S2N_ERR_CRL_INVALID_THIS_UPDATE); + S2N_NO_ALERT(S2N_ERR_CRL_INVALID_NEXT_UPDATE); + S2N_NO_ALERT(S2N_ERR_CRL_NOT_YET_VALID); + S2N_NO_ALERT(S2N_ERR_CRL_EXPIRED); + S2N_NO_ALERT(S2N_ERR_INVALID_MAX_FRAG_LEN); + S2N_NO_ALERT(S2N_ERR_MAX_FRAG_LEN_MISMATCH); + S2N_NO_ALERT(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + S2N_NO_ALERT(S2N_ERR_BAD_KEY_SHARE); + S2N_NO_ALERT(S2N_ERR_CANCELLED); + S2N_NO_ALERT(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + S2N_NO_ALERT(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_SIZE); + S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + S2N_NO_ALERT(S2N_ERR_UNSUPPORTED_EXTENSION); + S2N_NO_ALERT(S2N_ERR_DUPLICATE_EXTENSION); + S2N_NO_ALERT(S2N_ERR_MAX_EARLY_DATA_SIZE); + S2N_NO_ALERT(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); + } + + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static bool s2n_alerts_supported(struct s2n_connection *conn) +{ + /* If running in QUIC mode, QUIC handles alerting. + * S2N should not send or receive alerts. */ + return !s2n_connection_is_quic_enabled(conn); +} + +/* In TLS1.3 all Alerts + *= https://www.rfc-editor.org/rfc/rfc8446#section-6 + *# MUST be treated as error alerts when received + *# regardless of the AlertLevel in the message. + */ +static bool s2n_process_as_warning(struct s2n_connection *conn, uint8_t level, uint8_t type) +{ + /* Only TLS1.2 considers the alert level. The alert level field is + * considered deprecated in TLS1.3. If the protocol version has not + * been negotiated yet, we allow for warnings to avoid premature + * handshake failures before we know the protocol version. */ + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13 || !conn->actual_protocol_version_established) { + return level == S2N_TLS_ALERT_LEVEL_WARNING + && conn->config->alert_behavior == S2N_ALERT_IGNORE_WARNINGS; + } + + /* user_canceled is the only alert currently treated as a warning in TLS1.3. + * We need to treat it as a warning regardless of alert_behavior to avoid marking + * correctly-closed connections as failed. */ + return type == S2N_TLS_ALERT_USER_CANCELED; +} + +int s2n_error_get_alert(int error, uint8_t *alert) +{ + int error_type = s2n_error_get_type(error); + + POSIX_ENSURE_REF(alert); + + switch (error_type) { + case S2N_ERR_T_OK: + case S2N_ERR_T_CLOSED: + case S2N_ERR_T_BLOCKED: + case S2N_ERR_T_USAGE: + case S2N_ERR_T_ALERT: + POSIX_BAIL(S2N_ERR_NO_ALERT); + break; + case S2N_ERR_T_PROTO: + POSIX_GUARD_RESULT(s2n_translate_protocol_error_to_alert(error, alert)); + break; + case S2N_ERR_T_IO: + case S2N_ERR_T_INTERNAL: + *alert = S2N_TLS_ALERT_INTERNAL_ERROR; + break; + } + + return S2N_SUCCESS; +} + +int s2n_process_alert_fragment(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) == 2, S2N_ERR_ALERT_PRESENT); + POSIX_ENSURE(s2n_alerts_supported(conn), S2N_ERR_BAD_MESSAGE); + + while (s2n_stuffer_data_available(&conn->in)) { + uint8_t bytes_required = 2; + + /* Alerts are two bytes long, but can still be fragmented or coalesced */ + if (s2n_stuffer_data_available(&conn->alert_in) == 1) { + bytes_required = 1; + } + + int bytes_to_read = S2N_MIN(bytes_required, s2n_stuffer_data_available(&conn->in)); + + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->alert_in, bytes_to_read)); + + if (s2n_stuffer_data_available(&conn->alert_in) == 2) { + /* Close notifications are handled as shutdowns */ + if (conn->alert_in_data[1] == S2N_TLS_ALERT_CLOSE_NOTIFY) { + s2n_atomic_flag_set(&conn->read_closed); + s2n_atomic_flag_set(&conn->close_notify_received); + return 0; + } + + /* Ignore warning-level alerts if we're in warning-tolerant mode */ + if (s2n_process_as_warning(conn, conn->alert_in_data[0], conn->alert_in_data[1])) { + POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); + return 0; + } + + /* RFC 5077 5.1 - Expire any cached session on an error alert */ + if (s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { + conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); + } + + /* All other alerts are treated as fatal errors. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-6 + *# Unknown Alert types MUST be treated as error alerts. + */ + POSIX_GUARD_RESULT(s2n_connection_set_closed(conn)); + s2n_atomic_flag_set(&conn->error_alert_received); + POSIX_BAIL(S2N_ERR_ALERT); + } + } + + return 0; +} + +static S2N_RESULT s2n_queue_reader_alert(struct s2n_connection *conn, s2n_tls_alert_code code) +{ + RESULT_ENSURE_REF(conn); + if (!conn->reader_alert_out) { + conn->reader_alert_out = code; + } + return S2N_RESULT_OK; +} + +int s2n_queue_reader_unsupported_protocol_version_alert(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_PROTOCOL_VERSION)); + return S2N_SUCCESS; +} + +int s2n_queue_reader_handshake_failure_alert(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_HANDSHAKE_FAILURE)); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_queue_reader_no_renegotiation_alert(struct s2n_connection *conn) +{ + /** + *= https://www.rfc-editor.org/rfc/rfc5746#4.5 + *# SSLv3 does not define the "no_renegotiation" alert (and does + *# not offer a way to indicate a refusal to renegotiate at a "warning" + *# level). SSLv3 clients that refuse renegotiation SHOULD use a fatal + *# handshake_failure alert. + **/ + if (s2n_connection_get_protocol_version(conn) == S2N_SSLv3) { + RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); + } + + if (!conn->reader_warning_out) { + conn->reader_warning_out = S2N_TLS_ALERT_NO_RENEGOTIATION; + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_alerts_write_error_or_close_notify(struct s2n_connection *conn) +{ + if (!s2n_alerts_supported(conn)) { + return S2N_RESULT_OK; + } + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *= type=exception + *= reason=Specific alerts could expose a side-channel attack vector. + *# The phrases "terminate the connection with an X + *# alert" and "abort the handshake with an X alert" mean that the + *# implementation MUST send alert X if it sends any alert. + * + * By default, s2n-tls sends a generic close_notify alert, even in + * response to fatal errors. This is done to avoid potential + * side-channel attacks since specific alerts could reveal information + * about why the error occurred. + */ + uint8_t code = S2N_TLS_ALERT_CLOSE_NOTIFY; + uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; + + /* s2n-tls sends a very small subset of more specific error alerts. + * Since either the reader or the writer can produce one of these alerts, + * but only a single alert can be reported, we prioritize writer alerts. + */ + if (conn->writer_alert_out) { + code = conn->writer_alert_out; + level = S2N_TLS_ALERT_LEVEL_FATAL; + } else if (conn->reader_alert_out) { + code = conn->reader_alert_out; + level = S2N_TLS_ALERT_LEVEL_FATAL; + } + + struct s2n_blob alert = { 0 }; + uint8_t alert_bytes[] = { level, code }; + RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); + + RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); + conn->alert_sent = true; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_alerts_write_warning(struct s2n_connection *conn) +{ + if (!s2n_alerts_supported(conn)) { + return S2N_RESULT_OK; + } + + uint8_t code = conn->reader_warning_out; + uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; + + struct s2n_blob alert = { 0 }; + uint8_t alert_bytes[] = { level, code }; + RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); + + RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); + return S2N_RESULT_OK; +} diff --git a/tls/s2n_async_pkey.h b/tls/s2n_async_pkey.h index 7bea58e8553..19779cfba36 100644 --- a/tls/s2n_async_pkey.h +++ b/tls/s2n_async_pkey.h @@ -1,88 +1,107 @@ -/* - * 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 - -#include "crypto/s2n_signature.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_result.h" - -struct s2n_connection; - -typedef int (*s2n_async_pkey_sign_complete)(struct s2n_connection *conn, struct s2n_blob *signature); -typedef int (*s2n_async_pkey_decrypt_complete)(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted); - -struct s2n_async_pkey_op; - -/* Guard to handle async states inside handler which uses async pkey operations. If async operation was not invoked - * it means that we enter this handler for the first time and handler may or may not use async operation, so we let it - * continue. If async operation is invoking or was invoked, but yet to be complete, we error out of the handler to let - * s2n_handle_retry_state try again. If async operation was complete we clear the state and let s2n_handle_retry_state - * proceed to the next handler */ -#define S2N_ASYNC_PKEY_GUARD(conn) \ - do { \ - __typeof(conn) __tmp_conn = (conn); \ - POSIX_GUARD_PTR(__tmp_conn); \ - switch (conn->handshake.async_state) { \ - case S2N_ASYNC_NOT_INVOKED: \ - break; \ - \ - case S2N_ASYNC_INVOKED: \ - POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ - \ - case S2N_ASYNC_COMPLETE: \ - /* clean up state and return a success from handler */ \ - __tmp_conn->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ - return S2N_SUCCESS; \ - } \ - } while (0) - -/* Macros for safe exection of async sign/decrypt. - * - * When operation is done asynchronously, we drop to s2n_negotiate loop with S2N_ERR_ASYNC_BLOCKED error and do not - * perform any of the operations to follow after s2n_async* call. To enforce that there are no operations after the - * call, we use a macro which directly returns the result of s2n_async* operation forcing compiler to error out on - * unreachable code and forcing developer to use on_complete function instead */ -#define S2N_ASYNC_PKEY_DECRYPT(conn, encrypted, init_decrypted, on_complete) \ - return s2n_result_is_ok(s2n_async_pkey_decrypt(conn, encrypted, init_decrypted, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; - -#define S2N_ASYNC_PKEY_SIGN(conn, sig_alg, digest, on_complete) \ - return s2n_result_is_ok(s2n_async_pkey_sign(conn, sig_alg, digest, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; - -int s2n_async_pkey_op_perform(struct s2n_async_pkey_op *op, s2n_cert_private_key *key); -int s2n_async_pkey_op_apply(struct s2n_async_pkey_op *op, struct s2n_connection *conn); -int s2n_async_pkey_op_free(struct s2n_async_pkey_op *op); - -int s2n_async_pkey_op_get_op_type(struct s2n_async_pkey_op *op, s2n_async_pkey_op_type *type); -int s2n_async_pkey_op_get_input_size(struct s2n_async_pkey_op *op, uint32_t *data_len); -int s2n_async_pkey_op_get_input(struct s2n_async_pkey_op *op, uint8_t *data, uint32_t data_len); -int s2n_async_pkey_op_set_output(struct s2n_async_pkey_op *op, const uint8_t *data, uint32_t data_len); -int s2n_async_pkey_op_set_validation_mode(struct s2n_async_pkey_op *op, s2n_async_pkey_validation_mode mode); - -S2N_RESULT s2n_async_pkey_verify_signature(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *digest, struct s2n_blob *signature); -S2N_RESULT s2n_async_pkey_decrypt(struct s2n_connection *conn, struct s2n_blob *encrypted, struct s2n_blob *init_decrypted, - s2n_async_pkey_decrypt_complete on_complete); -S2N_RESULT s2n_async_pkey_sign(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, struct s2n_hash_state *digest, - s2n_async_pkey_sign_complete on_complete); - -struct s2n_async_pkey_verify_data { - struct s2n_hash_state digest; - s2n_signature_algorithm sig_alg; - struct s2n_blob signature; -}; - -int s2n_async_pkey_verify(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *digest, struct s2n_blob *signature); +/* + * 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 + +#include "crypto/s2n_signature.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" + +struct s2n_connection; + +typedef int (*s2n_async_pkey_sign_complete)(struct s2n_connection *conn, struct s2n_blob *signature); +typedef int (*s2n_async_pkey_decrypt_complete)(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted); + +struct s2n_async_pkey_op; + +/* Guard to handle async states inside handler which uses async pkey operations. If async operation was not invoked + * it means that we enter this handler for the first time and handler may or may not use async operation, so we let it + * continue. If async operation is invoking or was invoked, but yet to be complete, we error out of the handler to let + * s2n_handle_retry_state try again. If async operation was complete we clear the state and let s2n_handle_retry_state + * proceed to the next handler */ +#if defined(_MSC_VER) +#define S2N_ASYNC_PKEY_GUARD(conn) \ + do { \ + POSIX_GUARD_PTR((conn)); \ + switch ((conn)->handshake.async_state) { \ + case S2N_ASYNC_NOT_INVOKED: \ + break; \ + \ + case S2N_ASYNC_INVOKED: \ + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ + \ + case S2N_ASYNC_COMPLETE: \ + /* clean up state and return a success from handler */ \ + (conn)->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ + return S2N_SUCCESS; \ + } \ + } while (0) +#else +#define S2N_ASYNC_PKEY_GUARD(conn) \ + do { \ + __typeof(conn) __tmp_conn = (conn); \ + POSIX_GUARD_PTR(__tmp_conn); \ + switch (conn->handshake.async_state) { \ + case S2N_ASYNC_NOT_INVOKED: \ + break; \ + \ + case S2N_ASYNC_INVOKED: \ + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ + \ + case S2N_ASYNC_COMPLETE: \ + /* clean up state and return a success from handler */ \ + __tmp_conn->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ + return S2N_SUCCESS; \ + } \ + } while (0) +#endif + +/* Macros for safe exection of async sign/decrypt. + * + * When operation is done asynchronously, we drop to s2n_negotiate loop with S2N_ERR_ASYNC_BLOCKED error and do not + * perform any of the operations to follow after s2n_async* call. To enforce that there are no operations after the + * call, we use a macro which directly returns the result of s2n_async* operation forcing compiler to error out on + * unreachable code and forcing developer to use on_complete function instead */ +#define S2N_ASYNC_PKEY_DECRYPT(conn, encrypted, init_decrypted, on_complete) \ + return s2n_result_is_ok(s2n_async_pkey_decrypt(conn, encrypted, init_decrypted, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; + +#define S2N_ASYNC_PKEY_SIGN(conn, sig_alg, digest, on_complete) \ + return s2n_result_is_ok(s2n_async_pkey_sign(conn, sig_alg, digest, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; + +int s2n_async_pkey_op_perform(struct s2n_async_pkey_op *op, s2n_cert_private_key *key); +int s2n_async_pkey_op_apply(struct s2n_async_pkey_op *op, struct s2n_connection *conn); +int s2n_async_pkey_op_free(struct s2n_async_pkey_op *op); + +int s2n_async_pkey_op_get_op_type(struct s2n_async_pkey_op *op, s2n_async_pkey_op_type *type); +int s2n_async_pkey_op_get_input_size(struct s2n_async_pkey_op *op, uint32_t *data_len); +int s2n_async_pkey_op_get_input(struct s2n_async_pkey_op *op, uint8_t *data, uint32_t data_len); +int s2n_async_pkey_op_set_output(struct s2n_async_pkey_op *op, const uint8_t *data, uint32_t data_len); +int s2n_async_pkey_op_set_validation_mode(struct s2n_async_pkey_op *op, s2n_async_pkey_validation_mode mode); + +S2N_RESULT s2n_async_pkey_verify_signature(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature); +S2N_RESULT s2n_async_pkey_decrypt(struct s2n_connection *conn, struct s2n_blob *encrypted, struct s2n_blob *init_decrypted, + s2n_async_pkey_decrypt_complete on_complete); +S2N_RESULT s2n_async_pkey_sign(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, struct s2n_hash_state *digest, + s2n_async_pkey_sign_complete on_complete); + +struct s2n_async_pkey_verify_data { + struct s2n_hash_state digest; + s2n_signature_algorithm sig_alg; + struct s2n_blob signature; +}; + +int s2n_async_pkey_verify(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature); diff --git a/tls/s2n_cbc.c b/tls/s2n_cbc.c index 3f4754e431e..e5d3d530f15 100644 --- a/tls/s2n_cbc.c +++ b/tls/s2n_cbc.c @@ -1,103 +1,104 @@ -/* - * 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 "crypto/s2n_hmac.h" -#include "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* A TLS CBC record looks like .. - * - * [ Payload data ] [ HMAC ] [ Padding ] [ Padding length byte ] - * - * Each byte in the padding is expected to be set to the same value - * as the padding length byte. So if the padding length byte is '2' - * then the padding will be [ '2', '2' ] (there'll be three bytes - * set to that value if you include the padding length byte). - * - * The goal of s2n_verify_cbc() is to verify that the padding and hmac - * are correct, without leaking (via timing) how much padding there - * actually is: as this is considered secret. - * - * In addition to our efforts here though, s2n also wraps any CBC - * verification error (or record parsing error in general) with - * a randomized delay of between 1ms and 10 seconds. See s2n_connection.c. - * This amount of delay randomization is sufficient to increase the - * complexity of attack for even a 1 microsecond timing leak (which - * is quite large) by a factor of around 83 trillion. - */ -int s2n_verify_cbc(struct s2n_connection *conn, struct s2n_hmac_state *hmac, struct s2n_blob *decrypted) -{ - uint8_t mac_digest_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(hmac->alg, &mac_digest_size)); - - /* The record has to be at least big enough to contain the MAC, - * plus the padding length byte */ - POSIX_ENSURE_GT(decrypted->size, mac_digest_size); - - int payload_and_padding_size = decrypted->size - mac_digest_size; - - /* Determine what the padding length is */ - uint8_t padding_length = decrypted->data[decrypted->size - 1]; - - int payload_length = S2N_MAX(payload_and_padding_size - padding_length - 1, 0); - - /* Update the MAC */ - POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, payload_length)); - int currently_in_hash_block = hmac->currently_in_hash_block; - - /* Check the MAC */ - uint8_t check_digest[S2N_MAX_DIGEST_LEN]; - POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); - POSIX_GUARD(s2n_hmac_digest_two_compression_rounds(hmac, check_digest, mac_digest_size)); - - int mismatches = s2n_constant_time_equals(decrypted->data + payload_length, check_digest, mac_digest_size) ^ 1; - - /* Compute a MAC on the rest of the data so that we perform the same number of hash operations. - * Include the partial hash block from the first MAC to ensure we use the same number of blocks. - */ - POSIX_GUARD(s2n_hmac_reset(hmac)); - POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, currently_in_hash_block)); - POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data + payload_length + mac_digest_size, decrypted->size - payload_length - mac_digest_size - 1)); - - /* SSLv3 doesn't specify what the padding should actually be, so - * padding bytes are not verified. This is the vector for the POODLE - * attack (CVE-2014-3566). SSLv3 is disabled by default and not - * recommended. If SSLv3 must be used, the blinding feature (enabled - * by default) helps mitigates this issue as well. - */ - if (conn->actual_protocol_version == S2N_SSLv3) { - return 0 - mismatches; - } - - /* Check the maximum amount that could theoretically be padding */ - uint32_t check = S2N_MIN(255, (payload_and_padding_size - 1)); - - POSIX_ENSURE_GTE(check, padding_length); - - uint32_t cutoff = check - padding_length; - for (size_t i = 0, j = decrypted->size - 1 - check; i < check && j < decrypted->size; i++, j++) { - uint8_t mask = ~(0xff << ((i >= cutoff) * 8)); - mismatches |= (decrypted->data[j] ^ padding_length) & mask; - } - - S2N_ERROR_IF(mismatches, S2N_ERR_CBC_VERIFY); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hmac.h" +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* A TLS CBC record looks like .. + * + * [ Payload data ] [ HMAC ] [ Padding ] [ Padding length byte ] + * + * Each byte in the padding is expected to be set to the same value + * as the padding length byte. So if the padding length byte is '2' + * then the padding will be [ '2', '2' ] (there'll be three bytes + * set to that value if you include the padding length byte). + * + * The goal of s2n_verify_cbc() is to verify that the padding and hmac + * are correct, without leaking (via timing) how much padding there + * actually is: as this is considered secret. + * + * In addition to our efforts here though, s2n also wraps any CBC + * verification error (or record parsing error in general) with + * a randomized delay of between 1ms and 10 seconds. See s2n_connection.c. + * This amount of delay randomization is sufficient to increase the + * complexity of attack for even a 1 microsecond timing leak (which + * is quite large) by a factor of around 83 trillion. + */ +int s2n_verify_cbc(struct s2n_connection *conn, struct s2n_hmac_state *hmac, struct s2n_blob *decrypted) +{ + uint8_t mac_digest_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(hmac->alg, &mac_digest_size)); + + /* The record has to be at least big enough to contain the MAC, + * plus the padding length byte */ + POSIX_ENSURE_GT(decrypted->size, mac_digest_size); + + int payload_and_padding_size = decrypted->size - mac_digest_size; + + /* Determine what the padding length is */ + uint8_t padding_length = decrypted->data[decrypted->size - 1]; + + int payload_length = S2N_MAX(payload_and_padding_size - padding_length - 1, 0); + + /* Update the MAC */ + POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, payload_length)); + int currently_in_hash_block = hmac->currently_in_hash_block; + + /* Check the MAC */ + uint8_t check_digest[S2N_MAX_DIGEST_LEN]; + POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); + POSIX_GUARD(s2n_hmac_digest_two_compression_rounds(hmac, check_digest, mac_digest_size)); + + int mismatches = s2n_constant_time_equals(decrypted->data + payload_length, check_digest, mac_digest_size) ^ 1; + + /* Compute a MAC on the rest of the data so that we perform the same number of hash operations. + * Include the partial hash block from the first MAC to ensure we use the same number of blocks. + */ + POSIX_GUARD(s2n_hmac_reset(hmac)); + POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, currently_in_hash_block)); + POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data + payload_length + mac_digest_size, decrypted->size - payload_length - mac_digest_size - 1)); + + /* SSLv3 doesn't specify what the padding should actually be, so + * padding bytes are not verified. This is the vector for the POODLE + * attack (CVE-2014-3566). SSLv3 is disabled by default and not + * recommended. If SSLv3 must be used, the blinding feature (enabled + * by default) helps mitigates this issue as well. + */ + if (conn->actual_protocol_version == S2N_SSLv3) { + return 0 - mismatches; + } + + /* Check the maximum amount that could theoretically be padding */ + uint32_t check = S2N_MIN(255, (payload_and_padding_size - 1)); + + POSIX_ENSURE_GTE(check, padding_length); + + uint32_t cutoff = check - padding_length; + for (size_t i = 0, j = decrypted->size - 1 - check; i < check && j < decrypted->size; i++, j++) { + uint8_t mask = ~(0xff << ((i >= cutoff) * 8)); + mismatches |= (decrypted->data[j] ^ padding_length) & mask; + } + + S2N_ERROR_IF(mismatches, S2N_ERR_CBC_VERIFY); + + return 0; +} diff --git a/tls/s2n_cipher_preferences.c b/tls/s2n_cipher_preferences.c index d1d0dcb1572..b60d4f55695 100644 --- a/tls/s2n_cipher_preferences.c +++ b/tls/s2n_cipher_preferences.c @@ -1,2117 +1,2119 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_cipher_preferences.h" - -#include -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "tls/s2n_config.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_kex.h" -#include "utils/s2n_safety.h" - -/* clang-format off */ -/* TLS 1.3 cipher suites, in order of preference. - * Can be added to other ciphers suite lists to enable - * TLS1.3 compatibility. */ -#define S2N_TLS13_CIPHER_SUITES_20190801 \ - &s2n_tls13_aes_256_gcm_sha384, \ - &s2n_tls13_aes_128_gcm_sha256, \ - &s2n_tls13_chacha20_poly1305_sha256 - -#define S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 \ - &s2n_tls13_aes_128_gcm_sha256, \ - &s2n_tls13_aes_256_gcm_sha384, \ - &s2n_tls13_chacha20_poly1305_sha256 - -/* s2n's list of cipher suites, in order of preferences, as of 2019-08-01 */ -struct s2n_cipher_suite *cipher_suites_20190801[] = { - S2N_TLS13_CIPHER_SUITES_20190801, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20190801 = { - .count = s2n_array_len(cipher_suites_20190801), - .suites = cipher_suites_20190801, - .allow_chacha20_boosting = false, -}; - -/* Same as 20190801, but with ECDSA for TLS 1.2 added */ -struct s2n_cipher_suite *cipher_suites_20210831[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20210831 = { - .count = s2n_array_len(cipher_suites_20210831), - .suites = cipher_suites_20210831, - .allow_chacha20_boosting = false, -}; - -/* - * These cipher suites were chosen based on the following specification: - * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf - */ -struct s2n_cipher_suite *cipher_suites_default_fips[] = { - /* tls1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - /* tls1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_default_fips = { - .count = s2n_array_len(cipher_suites_default_fips), - .suites = cipher_suites_default_fips, - .allow_chacha20_boosting = false, -}; - -/* s2n's list of cipher suites, in order of preference, as of 2014-06-01 */ -struct s2n_cipher_suite *cipher_suites_20140601[] = { - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_20140601 = { - .count = s2n_array_len(cipher_suites_20140601), - .suites = cipher_suites_20140601, - .allow_chacha20_boosting = false, -}; - -/* Disable SSLv3 due to POODLE */ -const struct s2n_cipher_preferences cipher_preferences_20141001 = { - .count = s2n_array_len(cipher_suites_20140601), - .suites = cipher_suites_20140601, - .allow_chacha20_boosting = false, -}; - -/* Disable RC4 */ -struct s2n_cipher_suite *cipher_suites_20150202[] = { - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20150202 = { - .count = s2n_array_len(cipher_suites_20150202), - .suites = cipher_suites_20150202, - .allow_chacha20_boosting = false, -}; - -/* Support AES-GCM modes */ -struct s2n_cipher_suite *cipher_suites_20150214[] = { - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20150214 = { - .count = s2n_array_len(cipher_suites_20150214), - .suites = cipher_suites_20150214, - .allow_chacha20_boosting = false, -}; - -/* Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients */ -struct s2n_cipher_suite *cipher_suites_20160411[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20160411 = { - .count = s2n_array_len(cipher_suites_20160411), - .suites = cipher_suites_20160411, - .allow_chacha20_boosting = false, -}; - -/* Use ECDHE instead of plain DHE. Prioritize ECDHE in favour of non ECDHE; GCM in favour of CBC; AES128 in favour of AES256. */ -struct s2n_cipher_suite *cipher_suites_20150306[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20150306 = { - .count = s2n_array_len(cipher_suites_20150306), - .suites = cipher_suites_20150306, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20160804[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20160804 = { - .count = s2n_array_len(cipher_suites_20160804), - .suites = cipher_suites_20160804, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20160824[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20160824 = { - .count = s2n_array_len(cipher_suites_20160824), - .suites = cipher_suites_20160824, - .allow_chacha20_boosting = false, -}; - -/* Add ChaCha20 suite */ -struct s2n_cipher_suite *cipher_suites_20170210[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20170210 = { - .count = s2n_array_len(cipher_suites_20170210), - .suites = cipher_suites_20170210, - .allow_chacha20_boosting = false, -}; - -/* - * TLS1.3 support. - * FIPS compliant. - * No DHE (would require extra setup with s2n_config_add_dhparams) - */ -struct s2n_cipher_suite *cipher_suites_20230317[] = { - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - - /* TLS1.3 */ - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20230317 = { - .count = s2n_array_len(cipher_suites_20230317), - .suites = cipher_suites_20230317, - .allow_chacha20_boosting = false, -}; - -/* - * TLS1.3 support. - */ -struct s2n_cipher_suite *cipher_suites_20251014[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251014 = { - .count = s2n_array_len(cipher_suites_20251014), - .suites = cipher_suites_20251014, - .allow_chacha20_boosting = false, -}; - -/* - * FIPS - * TLS1.3 support. - * Same as 20251014 but without chachapoly - */ -struct s2n_cipher_suite *cipher_suites_20251015[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251015 = { - .count = s2n_array_len(cipher_suites_20251015), - .suites = cipher_suites_20251015, - .allow_chacha20_boosting = false, -}; - -/* - * No TLS1.3 support. - * FIPS compliant. - * No DHE (would require extra setup with s2n_config_add_dhparams) - */ -struct s2n_cipher_suite *cipher_suites_20240331[] = { - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20240331 = { - .count = s2n_array_len(cipher_suites_20240331), - .suites = cipher_suites_20240331, - .allow_chacha20_boosting = false, -}; - -/* Same as 20160411, but with ChaCha20 added as 1st in Preference List */ -struct s2n_cipher_suite *cipher_suites_20190122[] = { - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190122 = { - .count = s2n_array_len(cipher_suites_20190122), - .suites = cipher_suites_20190122, - .allow_chacha20_boosting = false, -}; - -/* Same as 20160804, but with ChaCha20 added as 2nd in Preference List */ -struct s2n_cipher_suite *cipher_suites_20190121[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20190121 = { - .count = s2n_array_len(cipher_suites_20190121), - .suites = cipher_suites_20190121, - .allow_chacha20_boosting = false, -}; - -/* Same as 20160411, but with ChaCha20 in 3rd Place after CBC and GCM */ -struct s2n_cipher_suite *cipher_suites_20190120[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190120 = { - .count = s2n_array_len(cipher_suites_20190120), - .suites = cipher_suites_20190120, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for interop, includes ECDSA priortitized. DHE and 3DES are added(at the lowest preference). */ -struct s2n_cipher_suite *cipher_suites_20190214[] = { - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190214 = { - .count = s2n_array_len(cipher_suites_20190214), - .suites = cipher_suites_20190214, - .allow_chacha20_boosting = false, -}; - -/* 20190214 with aes-gcm prioritized above aes-cbc */ -struct s2n_cipher_suite *cipher_suites_20190214_gcm[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190214_gcm = { - .count = s2n_array_len(cipher_suites_20190214_gcm), - .suites = cipher_suites_20190214_gcm, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20190214, but with TLS 1.3 Ciphers */ -struct s2n_cipher_suite *cipher_suites_20210825[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210825 = { - .count = s2n_array_len(cipher_suites_20210825), - .suites = cipher_suites_20210825, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20190214_gcm, but with TLS 1.3 Ciphers */ -struct s2n_cipher_suite *cipher_suites_20210825_gcm[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210825_gcm = { - .count = s2n_array_len(cipher_suites_20210825_gcm), - .suites = cipher_suites_20210825_gcm, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20210825, but with 3DES removed */ -struct s2n_cipher_suite *cipher_suites_20241008[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20241008 = { - .count = s2n_array_len(cipher_suites_20241008), - .suites = cipher_suites_20241008, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20210825_gcm and cipher_suites_pq_tls_1_0_2021_05_26, but with 3DES removed */ -struct s2n_cipher_suite *cipher_suites_20241008_gcm[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20241008_gcm = { - .count = s2n_array_len(cipher_suites_20241008_gcm), - .suites = cipher_suites_20241008_gcm, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20241009[] = { - S2N_TLS13_CIPHER_SUITES_20190801, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20241009 = { - .count = s2n_array_len(cipher_suites_20241009), - .suites = cipher_suites_20241009, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_null[] = { - &s2n_null_cipher_suite -}; - -const struct s2n_cipher_preferences cipher_preferences_null = { - .count = s2n_array_len(cipher_suites_null), - .suites = cipher_suites_null, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for interop. DHE and 3DES are added(at the lowest preference). */ -struct s2n_cipher_suite *cipher_suites_20170328[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170328 = { - .count = s2n_array_len(cipher_suites_20170328), - .suites = cipher_suites_20170328, - .allow_chacha20_boosting = false, -}; - -/* Equivalent to cipher_suites_20170328 with aes-gcm prioritized above aes-cbc */ -struct s2n_cipher_suite *cipher_suites_20170328_gcm[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170328_gcm = { - .count = s2n_array_len(cipher_suites_20170328_gcm), - .suites = cipher_suites_20170328_gcm, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for FIPS compatibility. */ -struct s2n_cipher_suite *cipher_suites_20170405[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170405 = { - .count = s2n_array_len(cipher_suites_20170405), - .suites = cipher_suites_20170405, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for FIPS compatibility with GCM prioritized */ -struct s2n_cipher_suite *cipher_suites_20170405_gcm[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170405_gcm = { - .count = s2n_array_len(cipher_suites_20170405_gcm), - .suites = cipher_suites_20170405_gcm, - .allow_chacha20_boosting = false, -}; - -/* Equivalent to cipher_suite_20160411 with 3DES removed. - * Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients. */ -struct s2n_cipher_suite *cipher_suites_20170718[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170718 = { - .count = s2n_array_len(cipher_suites_20170718), - .suites = cipher_suites_20170718, - .allow_chacha20_boosting = false, -}; - -/* Equivalent to cipher_suites_20170718 with aes-gcm prioritized above aes-cbc */ -struct s2n_cipher_suite *cipher_suites_20170718_gcm[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170718_gcm = { - .count = s2n_array_len(cipher_suites_20170718_gcm), - .suites = cipher_suites_20170718_gcm, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_2015_04[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_2015_04 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_2015_04), - .suites = cipher_suites_elb_security_policy_2015_04, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_2016_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_2016_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_2016_08), - .suites = cipher_suites_elb_security_policy_2016_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_2017_01[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls_1_2_2017_01 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_2017_01), - .suites = cipher_suites_elb_security_policy_tls_1_2_2017_01, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_1_2017_01[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls_1_1_2017_01 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_1_2017_01), - .suites = cipher_suites_elb_security_policy_tls_1_1_2017_01, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_ext_2018_06[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls_1_2_ext_2018_06 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_ext_2018_06), - .suites = cipher_suites_elb_security_policy_tls_1_2_ext_2018_06, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_2018_06[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_2018_06 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_2018_06), - .suites = cipher_suites_elb_security_policy_fs_2018_06, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_2019_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_1_2_2019_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_2019_08), - .suites = cipher_suites_elb_security_policy_fs_1_2_2019_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_1_2019_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_1_1_2019_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_1_2019_08), - .suites = cipher_suites_elb_security_policy_fs_1_1_2019_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_Res_2019_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_1_2_Res_2019_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_Res_2019_08), - .suites = cipher_suites_elb_security_policy_fs_1_2_Res_2019_08, - .allow_chacha20_boosting = false, -}; - -/* - * S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 on top of cipher_suites_elb_security_policy_tls_1_2_ext_2018_06 -*/ -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls13_1_2_Ext2_2021_06 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06), - .suites = cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream), - .suites = cipher_suites_cloudfront_upstream, - .allow_chacha20_boosting = false, -}; - -/* CloudFront viewer facing (with TLS 1.3) */ -struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3 = { - .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3), - .suites = cipher_suites_cloudfront_ssl_v_3, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014), - .suites = cipher_suites_cloudfront_tls_1_0_2014, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_sha256[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_sha256 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_sha256), - .suites = cipher_suites_cloudfront_tls_1_0_2014_sha256, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016), - .suites = cipher_suites_cloudfront_tls_1_0_2016, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016), - .suites = cipher_suites_cloudfront_tls_1_1_2016, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2017[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2017 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2017), - .suites = cipher_suites_cloudfront_tls_1_2_2017, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018), - .suites = cipher_suites_cloudfront_tls_1_2_2018, - .allow_chacha20_boosting = false, -}; - -/* TLS 1.3 beta policy */ -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_beta[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_beta = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_beta), - .suites = cipher_suites_cloudfront_tls_1_2_2018_beta, -}; - -/* CloudFront viewer facing legacy TLS 1.2 policies */ -struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3_legacy), - .suites = cipher_suites_cloudfront_ssl_v_3_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_legacy), - .suites = cipher_suites_cloudfront_tls_1_0_2014_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016_legacy), - .suites = cipher_suites_cloudfront_tls_1_0_2016_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016_legacy), - .suites = cipher_suites_cloudfront_tls_1_1_2016_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_legacy), - .suites = cipher_suites_cloudfront_tls_1_2_2018_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019_legacy), - .suites = cipher_suites_cloudfront_tls_1_2_2019_legacy, - .allow_chacha20_boosting = false, -}; - -/* CloudFront upstream */ -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls10[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls10 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls10), - .suites = cipher_suites_cloudfront_upstream_tls10, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls11[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls11 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls11), - .suites = cipher_suites_cloudfront_upstream_tls11, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls12[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls12 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls12), - .suites = cipher_suites_cloudfront_upstream_tls12, - .allow_chacha20_boosting = false, -}; - -/* CloudFront upstream with TLS 1.3 enabled */ -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08), - .suites = cipher_suites_cloudfront_upstream_2025_08_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08_tls13[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08_tls13 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08_tls13), - .suites = cipher_suites_cloudfront_upstream_2025_08_08_tls13, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019), - .suites = cipher_suites_cloudfront_tls_1_2_2019, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2021[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), - .suites = cipher_suites_cloudfront_tls_1_2_2021, - .allow_chacha20_boosting = false, -}; - -/* Duplicate of cipher_preferences_cloudfront_tls_1_2_2021 but with allow_chacha20_boosting enabled */ -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021_chacha20_boosted = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), - .suites = cipher_suites_cloudfront_tls_1_2_2021, - .allow_chacha20_boosting = true, -}; - -/* FIPS 140-3 compliant version of cipher_preferences_cloudfront_tls_1_2_2021 */ -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2025[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2025 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2025), - .suites = cipher_suites_cloudfront_tls_1_2_2025, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_3_2025[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_3_2025 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_3_2025), - .suites = cipher_suites_cloudfront_tls_1_3_2025, - .allow_chacha20_boosting = false, -}; - -/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256, DES-CBC3-SHA, and - * RC4-MD5 added for compatibility. */ -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_ssl_v3[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_ssl_v3 = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_ssl_v3), - .suites = cipher_suites_aws_crt_sdk_ssl_v3, - .allow_chacha20_boosting = false, -}; - -/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256 added for - * compatibility. */ -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_default[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_default = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_default), - .suites = cipher_suites_aws_crt_sdk_default, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_2025[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_2025 = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_2025), - .suites = cipher_suites_aws_crt_sdk_2025, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_tls_13[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_tls_13 = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_tls_13), - .suites = cipher_suites_aws_crt_sdk_tls_13, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2018_10[] = { - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2018_10 = { - .count = s2n_array_len(cipher_suites_kms_tls_1_0_2018_10), - .suites = cipher_suites_kms_tls_1_0_2018_10, - .allow_chacha20_boosting = false, -}; - - -struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2021_08[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2021_08 = { - .count = s2n_array_len(cipher_suites_kms_tls_1_0_2021_08), - .suites = cipher_suites_kms_tls_1_0_2021_08, - .allow_chacha20_boosting = false, -}; - - -struct s2n_cipher_suite *cipher_suites_20231213[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20231213 = { - .count = s2n_array_len(cipher_suites_20231213), - .suites = cipher_suites_20231213, -}; - -struct s2n_cipher_suite *cipher_suites_20231214[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20231214 = { - .count = s2n_array_len(cipher_suites_20231214), - .suites = cipher_suites_20231214, -}; - -struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2018_10[] = { - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2018_10 = { - .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2018_10), - .suites = cipher_suites_kms_fips_tls_1_2_2018_10, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2021_08[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2021_08 = { - .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2021_08), - .suites = cipher_suites_kms_fips_tls_1_2_2021_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20210816[] = { - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210816 = { - .count = s2n_array_len(cipher_suites_20210816), - .suites = cipher_suites_20210816, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20210816_gcm[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210816_gcm = { - .count = s2n_array_len(cipher_suites_20210816_gcm), - .suites = cipher_suites_20210816_gcm, - .allow_chacha20_boosting = false, -}; - -/* Cipher suite options for backwards compatibility with older clients, - * while prioritizing forward secret key exchange and ECDSA certificates. - */ -struct s2n_cipher_suite *cipher_suites_20240603[] = { - /* TLS1.3 suites */ - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_chacha20_poly1305_sha256, - - /* Preferred ECDHE + ECDSA suites */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - - /* Preferred ECDHE + RSA suites */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - - /* Legacy ECDHE suites */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - - /* DHE suites */ - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - - /* 3DES suites */ - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - - /* RSA kex suites */ - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20240603 = { - .count = s2n_array_len(cipher_suites_20240603), - .suites = cipher_suites_20240603, - .allow_chacha20_boosting = true, -}; - -struct s2n_cipher_suite *cipher_suites_20250429[] = { - /* TLS1.2 */ - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20250429 = { - .count = s2n_array_len(cipher_suites_20250429), - .suites = cipher_suites_20250429, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251013[] = { - /* TLS1.2 */ - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251013 = { - .count = s2n_array_len(cipher_suites_20251013), - .suites = cipher_suites_20251013, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20250211[] = { - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20250211 = { - .count = s2n_array_len(cipher_suites_20250211), - .suites = cipher_suites_20250211, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251113[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251113 = { - .count = s2n_array_len(cipher_suites_20251113), - .suites = cipher_suites_20251113, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251114[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251114 = { - .count = s2n_array_len(cipher_suites_20251114), - .suites = cipher_suites_20251114, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251115[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251115 = { - .count = s2n_array_len(cipher_suites_20251115), - .suites = cipher_suites_20251115, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251116[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251116 = { - .count = s2n_array_len(cipher_suites_20251116), - .suites = cipher_suites_20251116, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251117[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251117 = { - .count = s2n_array_len(cipher_suites_20251117), - .suites = cipher_suites_20251117, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20260220[] = { - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, - - /* TLS1.2 */ - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20260220 = { - .count = s2n_array_len(cipher_suites_20260220), - .suites = cipher_suites_20260220, - .allow_chacha20_boosting = false, -}; - -/* clang-format on */ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_cipher_preferences.h" + +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "tls/s2n_config.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_kex.h" +#include "utils/s2n_safety.h" + +/* clang-format off */ +/* TLS 1.3 cipher suites, in order of preference. + * Can be added to other ciphers suite lists to enable + * TLS1.3 compatibility. */ +#define S2N_TLS13_CIPHER_SUITES_20190801 \ + &s2n_tls13_aes_256_gcm_sha384, \ + &s2n_tls13_aes_128_gcm_sha256, \ + &s2n_tls13_chacha20_poly1305_sha256 + +#define S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 \ + &s2n_tls13_aes_128_gcm_sha256, \ + &s2n_tls13_aes_256_gcm_sha384, \ + &s2n_tls13_chacha20_poly1305_sha256 + +/* s2n's list of cipher suites, in order of preferences, as of 2019-08-01 */ +struct s2n_cipher_suite *cipher_suites_20190801[] = { + S2N_TLS13_CIPHER_SUITES_20190801, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20190801 = { + .count = s2n_array_len(cipher_suites_20190801), + .suites = cipher_suites_20190801, + .allow_chacha20_boosting = false, +}; + +/* Same as 20190801, but with ECDSA for TLS 1.2 added */ +struct s2n_cipher_suite *cipher_suites_20210831[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20210831 = { + .count = s2n_array_len(cipher_suites_20210831), + .suites = cipher_suites_20210831, + .allow_chacha20_boosting = false, +}; + +/* + * These cipher suites were chosen based on the following specification: + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf + */ +struct s2n_cipher_suite *cipher_suites_default_fips[] = { + /* tls1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* tls1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_default_fips = { + .count = s2n_array_len(cipher_suites_default_fips), + .suites = cipher_suites_default_fips, + .allow_chacha20_boosting = false, +}; + +/* s2n's list of cipher suites, in order of preference, as of 2014-06-01 */ +struct s2n_cipher_suite *cipher_suites_20140601[] = { + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_20140601 = { + .count = s2n_array_len(cipher_suites_20140601), + .suites = cipher_suites_20140601, + .allow_chacha20_boosting = false, +}; + +/* Disable SSLv3 due to POODLE */ +const struct s2n_cipher_preferences cipher_preferences_20141001 = { + .count = s2n_array_len(cipher_suites_20140601), + .suites = cipher_suites_20140601, + .allow_chacha20_boosting = false, +}; + +/* Disable RC4 */ +struct s2n_cipher_suite *cipher_suites_20150202[] = { + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20150202 = { + .count = s2n_array_len(cipher_suites_20150202), + .suites = cipher_suites_20150202, + .allow_chacha20_boosting = false, +}; + +/* Support AES-GCM modes */ +struct s2n_cipher_suite *cipher_suites_20150214[] = { + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20150214 = { + .count = s2n_array_len(cipher_suites_20150214), + .suites = cipher_suites_20150214, + .allow_chacha20_boosting = false, +}; + +/* Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients */ +struct s2n_cipher_suite *cipher_suites_20160411[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20160411 = { + .count = s2n_array_len(cipher_suites_20160411), + .suites = cipher_suites_20160411, + .allow_chacha20_boosting = false, +}; + +/* Use ECDHE instead of plain DHE. Prioritize ECDHE in favour of non ECDHE; GCM in favour of CBC; AES128 in favour of AES256. */ +struct s2n_cipher_suite *cipher_suites_20150306[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20150306 = { + .count = s2n_array_len(cipher_suites_20150306), + .suites = cipher_suites_20150306, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20160804[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20160804 = { + .count = s2n_array_len(cipher_suites_20160804), + .suites = cipher_suites_20160804, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20160824[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20160824 = { + .count = s2n_array_len(cipher_suites_20160824), + .suites = cipher_suites_20160824, + .allow_chacha20_boosting = false, +}; + +/* Add ChaCha20 suite */ +struct s2n_cipher_suite *cipher_suites_20170210[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20170210 = { + .count = s2n_array_len(cipher_suites_20170210), + .suites = cipher_suites_20170210, + .allow_chacha20_boosting = false, +}; + +/* + * TLS1.3 support. + * FIPS compliant. + * No DHE (would require extra setup with s2n_config_add_dhparams) + */ +struct s2n_cipher_suite *cipher_suites_20230317[] = { + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + + /* TLS1.3 */ + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20230317 = { + .count = s2n_array_len(cipher_suites_20230317), + .suites = cipher_suites_20230317, + .allow_chacha20_boosting = false, +}; + +/* + * TLS1.3 support. + */ +struct s2n_cipher_suite *cipher_suites_20251014[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251014 = { + .count = s2n_array_len(cipher_suites_20251014), + .suites = cipher_suites_20251014, + .allow_chacha20_boosting = false, +}; + +/* + * FIPS + * TLS1.3 support. + * Same as 20251014 but without chachapoly + */ +struct s2n_cipher_suite *cipher_suites_20251015[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251015 = { + .count = s2n_array_len(cipher_suites_20251015), + .suites = cipher_suites_20251015, + .allow_chacha20_boosting = false, +}; + +/* + * No TLS1.3 support. + * FIPS compliant. + * No DHE (would require extra setup with s2n_config_add_dhparams) + */ +struct s2n_cipher_suite *cipher_suites_20240331[] = { + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20240331 = { + .count = s2n_array_len(cipher_suites_20240331), + .suites = cipher_suites_20240331, + .allow_chacha20_boosting = false, +}; + +/* Same as 20160411, but with ChaCha20 added as 1st in Preference List */ +struct s2n_cipher_suite *cipher_suites_20190122[] = { + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190122 = { + .count = s2n_array_len(cipher_suites_20190122), + .suites = cipher_suites_20190122, + .allow_chacha20_boosting = false, +}; + +/* Same as 20160804, but with ChaCha20 added as 2nd in Preference List */ +struct s2n_cipher_suite *cipher_suites_20190121[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20190121 = { + .count = s2n_array_len(cipher_suites_20190121), + .suites = cipher_suites_20190121, + .allow_chacha20_boosting = false, +}; + +/* Same as 20160411, but with ChaCha20 in 3rd Place after CBC and GCM */ +struct s2n_cipher_suite *cipher_suites_20190120[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190120 = { + .count = s2n_array_len(cipher_suites_20190120), + .suites = cipher_suites_20190120, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for interop, includes ECDSA priortitized. DHE and 3DES are added(at the lowest preference). */ +struct s2n_cipher_suite *cipher_suites_20190214[] = { + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190214 = { + .count = s2n_array_len(cipher_suites_20190214), + .suites = cipher_suites_20190214, + .allow_chacha20_boosting = false, +}; + +/* 20190214 with aes-gcm prioritized above aes-cbc */ +struct s2n_cipher_suite *cipher_suites_20190214_gcm[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190214_gcm = { + .count = s2n_array_len(cipher_suites_20190214_gcm), + .suites = cipher_suites_20190214_gcm, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20190214, but with TLS 1.3 Ciphers */ +struct s2n_cipher_suite *cipher_suites_20210825[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210825 = { + .count = s2n_array_len(cipher_suites_20210825), + .suites = cipher_suites_20210825, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20190214_gcm, but with TLS 1.3 Ciphers */ +struct s2n_cipher_suite *cipher_suites_20210825_gcm[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210825_gcm = { + .count = s2n_array_len(cipher_suites_20210825_gcm), + .suites = cipher_suites_20210825_gcm, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20210825, but with 3DES removed */ +struct s2n_cipher_suite *cipher_suites_20241008[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20241008 = { + .count = s2n_array_len(cipher_suites_20241008), + .suites = cipher_suites_20241008, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20210825_gcm and cipher_suites_pq_tls_1_0_2021_05_26, but with 3DES removed */ +struct s2n_cipher_suite *cipher_suites_20241008_gcm[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20241008_gcm = { + .count = s2n_array_len(cipher_suites_20241008_gcm), + .suites = cipher_suites_20241008_gcm, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20241009[] = { + S2N_TLS13_CIPHER_SUITES_20190801, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20241009 = { + .count = s2n_array_len(cipher_suites_20241009), + .suites = cipher_suites_20241009, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_null[] = { + &s2n_null_cipher_suite +}; + +const struct s2n_cipher_preferences cipher_preferences_null = { + .count = s2n_array_len(cipher_suites_null), + .suites = cipher_suites_null, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for interop. DHE and 3DES are added(at the lowest preference). */ +struct s2n_cipher_suite *cipher_suites_20170328[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170328 = { + .count = s2n_array_len(cipher_suites_20170328), + .suites = cipher_suites_20170328, + .allow_chacha20_boosting = false, +}; + +/* Equivalent to cipher_suites_20170328 with aes-gcm prioritized above aes-cbc */ +struct s2n_cipher_suite *cipher_suites_20170328_gcm[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170328_gcm = { + .count = s2n_array_len(cipher_suites_20170328_gcm), + .suites = cipher_suites_20170328_gcm, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for FIPS compatibility. */ +struct s2n_cipher_suite *cipher_suites_20170405[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170405 = { + .count = s2n_array_len(cipher_suites_20170405), + .suites = cipher_suites_20170405, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for FIPS compatibility with GCM prioritized */ +struct s2n_cipher_suite *cipher_suites_20170405_gcm[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170405_gcm = { + .count = s2n_array_len(cipher_suites_20170405_gcm), + .suites = cipher_suites_20170405_gcm, + .allow_chacha20_boosting = false, +}; + +/* Equivalent to cipher_suite_20160411 with 3DES removed. + * Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients. */ +struct s2n_cipher_suite *cipher_suites_20170718[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170718 = { + .count = s2n_array_len(cipher_suites_20170718), + .suites = cipher_suites_20170718, + .allow_chacha20_boosting = false, +}; + +/* Equivalent to cipher_suites_20170718 with aes-gcm prioritized above aes-cbc */ +struct s2n_cipher_suite *cipher_suites_20170718_gcm[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170718_gcm = { + .count = s2n_array_len(cipher_suites_20170718_gcm), + .suites = cipher_suites_20170718_gcm, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_2015_04[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_2015_04 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_2015_04), + .suites = cipher_suites_elb_security_policy_2015_04, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_2016_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_2016_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_2016_08), + .suites = cipher_suites_elb_security_policy_2016_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_2017_01[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls_1_2_2017_01 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_2017_01), + .suites = cipher_suites_elb_security_policy_tls_1_2_2017_01, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_1_2017_01[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls_1_1_2017_01 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_1_2017_01), + .suites = cipher_suites_elb_security_policy_tls_1_1_2017_01, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_ext_2018_06[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls_1_2_ext_2018_06 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_ext_2018_06), + .suites = cipher_suites_elb_security_policy_tls_1_2_ext_2018_06, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_2018_06[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_2018_06 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_2018_06), + .suites = cipher_suites_elb_security_policy_fs_2018_06, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_2019_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_1_2_2019_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_2019_08), + .suites = cipher_suites_elb_security_policy_fs_1_2_2019_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_1_2019_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_1_1_2019_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_1_2019_08), + .suites = cipher_suites_elb_security_policy_fs_1_1_2019_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_Res_2019_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_1_2_Res_2019_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_Res_2019_08), + .suites = cipher_suites_elb_security_policy_fs_1_2_Res_2019_08, + .allow_chacha20_boosting = false, +}; + +/* + * S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 on top of cipher_suites_elb_security_policy_tls_1_2_ext_2018_06 +*/ +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls13_1_2_Ext2_2021_06 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06), + .suites = cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream), + .suites = cipher_suites_cloudfront_upstream, + .allow_chacha20_boosting = false, +}; + +/* CloudFront viewer facing (with TLS 1.3) */ +struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3 = { + .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3), + .suites = cipher_suites_cloudfront_ssl_v_3, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014), + .suites = cipher_suites_cloudfront_tls_1_0_2014, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_sha256[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_sha256 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_sha256), + .suites = cipher_suites_cloudfront_tls_1_0_2014_sha256, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016), + .suites = cipher_suites_cloudfront_tls_1_0_2016, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016), + .suites = cipher_suites_cloudfront_tls_1_1_2016, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2017[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2017 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2017), + .suites = cipher_suites_cloudfront_tls_1_2_2017, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018), + .suites = cipher_suites_cloudfront_tls_1_2_2018, + .allow_chacha20_boosting = false, +}; + +/* TLS 1.3 beta policy */ +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_beta[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_beta = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_beta), + .suites = cipher_suites_cloudfront_tls_1_2_2018_beta, +}; + +/* CloudFront viewer facing legacy TLS 1.2 policies */ +struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3_legacy), + .suites = cipher_suites_cloudfront_ssl_v_3_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_legacy), + .suites = cipher_suites_cloudfront_tls_1_0_2014_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016_legacy), + .suites = cipher_suites_cloudfront_tls_1_0_2016_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016_legacy), + .suites = cipher_suites_cloudfront_tls_1_1_2016_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_legacy), + .suites = cipher_suites_cloudfront_tls_1_2_2018_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019_legacy), + .suites = cipher_suites_cloudfront_tls_1_2_2019_legacy, + .allow_chacha20_boosting = false, +}; + +/* CloudFront upstream */ +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls10[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls10 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls10), + .suites = cipher_suites_cloudfront_upstream_tls10, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls11[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls11 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls11), + .suites = cipher_suites_cloudfront_upstream_tls11, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls12[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls12 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls12), + .suites = cipher_suites_cloudfront_upstream_tls12, + .allow_chacha20_boosting = false, +}; + +/* CloudFront upstream with TLS 1.3 enabled */ +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08), + .suites = cipher_suites_cloudfront_upstream_2025_08_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08_tls13[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08_tls13 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08_tls13), + .suites = cipher_suites_cloudfront_upstream_2025_08_08_tls13, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019), + .suites = cipher_suites_cloudfront_tls_1_2_2019, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2021[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), + .suites = cipher_suites_cloudfront_tls_1_2_2021, + .allow_chacha20_boosting = false, +}; + +/* Duplicate of cipher_preferences_cloudfront_tls_1_2_2021 but with allow_chacha20_boosting enabled */ +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021_chacha20_boosted = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), + .suites = cipher_suites_cloudfront_tls_1_2_2021, + .allow_chacha20_boosting = true, +}; + +/* FIPS 140-3 compliant version of cipher_preferences_cloudfront_tls_1_2_2021 */ +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2025[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2025 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2025), + .suites = cipher_suites_cloudfront_tls_1_2_2025, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_3_2025[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_3_2025 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_3_2025), + .suites = cipher_suites_cloudfront_tls_1_3_2025, + .allow_chacha20_boosting = false, +}; + +/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256, DES-CBC3-SHA, and + * RC4-MD5 added for compatibility. */ +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_ssl_v3[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_ssl_v3 = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_ssl_v3), + .suites = cipher_suites_aws_crt_sdk_ssl_v3, + .allow_chacha20_boosting = false, +}; + +/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256 added for + * compatibility. */ +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_default[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_default = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_default), + .suites = cipher_suites_aws_crt_sdk_default, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_2025[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_2025 = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_2025), + .suites = cipher_suites_aws_crt_sdk_2025, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_tls_13[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_tls_13 = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_tls_13), + .suites = cipher_suites_aws_crt_sdk_tls_13, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2018_10[] = { + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2018_10 = { + .count = s2n_array_len(cipher_suites_kms_tls_1_0_2018_10), + .suites = cipher_suites_kms_tls_1_0_2018_10, + .allow_chacha20_boosting = false, +}; + + +struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2021_08[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2021_08 = { + .count = s2n_array_len(cipher_suites_kms_tls_1_0_2021_08), + .suites = cipher_suites_kms_tls_1_0_2021_08, + .allow_chacha20_boosting = false, +}; + + +struct s2n_cipher_suite *cipher_suites_20231213[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20231213 = { + .count = s2n_array_len(cipher_suites_20231213), + .suites = cipher_suites_20231213, +}; + +struct s2n_cipher_suite *cipher_suites_20231214[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20231214 = { + .count = s2n_array_len(cipher_suites_20231214), + .suites = cipher_suites_20231214, +}; + +struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2018_10[] = { + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2018_10 = { + .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2018_10), + .suites = cipher_suites_kms_fips_tls_1_2_2018_10, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2021_08[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2021_08 = { + .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2021_08), + .suites = cipher_suites_kms_fips_tls_1_2_2021_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20210816[] = { + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210816 = { + .count = s2n_array_len(cipher_suites_20210816), + .suites = cipher_suites_20210816, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20210816_gcm[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210816_gcm = { + .count = s2n_array_len(cipher_suites_20210816_gcm), + .suites = cipher_suites_20210816_gcm, + .allow_chacha20_boosting = false, +}; + +/* Cipher suite options for backwards compatibility with older clients, + * while prioritizing forward secret key exchange and ECDSA certificates. + */ +struct s2n_cipher_suite *cipher_suites_20240603[] = { + /* TLS1.3 suites */ + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_chacha20_poly1305_sha256, + + /* Preferred ECDHE + ECDSA suites */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + + /* Preferred ECDHE + RSA suites */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + + /* Legacy ECDHE suites */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + + /* DHE suites */ + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + + /* 3DES suites */ + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + + /* RSA kex suites */ + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20240603 = { + .count = s2n_array_len(cipher_suites_20240603), + .suites = cipher_suites_20240603, + .allow_chacha20_boosting = true, +}; + +struct s2n_cipher_suite *cipher_suites_20250429[] = { + /* TLS1.2 */ + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20250429 = { + .count = s2n_array_len(cipher_suites_20250429), + .suites = cipher_suites_20250429, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251013[] = { + /* TLS1.2 */ + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251013 = { + .count = s2n_array_len(cipher_suites_20251013), + .suites = cipher_suites_20251013, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20250211[] = { + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20250211 = { + .count = s2n_array_len(cipher_suites_20250211), + .suites = cipher_suites_20250211, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251113[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251113 = { + .count = s2n_array_len(cipher_suites_20251113), + .suites = cipher_suites_20251113, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251114[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251114 = { + .count = s2n_array_len(cipher_suites_20251114), + .suites = cipher_suites_20251114, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251115[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251115 = { + .count = s2n_array_len(cipher_suites_20251115), + .suites = cipher_suites_20251115, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251116[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251116 = { + .count = s2n_array_len(cipher_suites_20251116), + .suites = cipher_suites_20251116, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251117[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251117 = { + .count = s2n_array_len(cipher_suites_20251117), + .suites = cipher_suites_20251117, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20260220[] = { + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, + + /* TLS1.2 */ + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20260220 = { + .count = s2n_array_len(cipher_suites_20260220), + .suites = cipher_suites_20260220, + .allow_chacha20_boosting = false, +}; + +/* clang-format on */ diff --git a/tls/s2n_client_hello.c b/tls/s2n_client_hello.c index c45879b8bc1..530f159c4ba 100644 --- a/tls/s2n_client_hello.c +++ b/tls/s2n_client_hello.c @@ -1,1106 +1,1107 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_client_hello.h" - -#include -#include -#include - -#include "api/unstable/fingerprint.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hash.h" -#include "crypto/s2n_pq.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/extensions/s2n_client_server_name.h" -#include "tls/extensions/s2n_client_supported_groups.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_auth_selection.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake_type.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_signature_algorithms.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn) -{ - if (conn->client_hello.parsed != 1) { - return NULL; - } - - return &conn->client_hello; -} - -static uint32_t min_size(struct s2n_blob *blob, uint32_t max_length) -{ - return blob->size < max_length ? blob->size : max_length; -} - -static S2N_RESULT s2n_generate_client_session_id(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - /* Session id already generated - no-op */ - if (conn->session_id_len) { - return S2N_RESULT_OK; - } - - /* Only generate the session id if using tickets */ - bool generate = conn->config->use_tickets; - - /* TLS1.3 doesn't require session ids. The field is actually renamed to legacy_session_id. - * However, we still set a session id if dealing with troublesome middleboxes - * (middlebox compatibility mode) or if trying to use a TLS1.2 ticket. - */ - if (conn->client_protocol_version >= S2N_TLS13) { - generate = s2n_is_middlebox_compat_enabled(conn) || conn->resume_protocol_version; - } - - /* Session id not needed - no-op */ - if (!generate) { - return S2N_RESULT_OK; - } - - /* QUIC should not allow session ids for any reason. - * - *= https://www.rfc-editor.org/rfc/rfc9001#section-8.4 - *# A server SHOULD treat the receipt of a TLS ClientHello with a non-empty - *# legacy_session_id field as a connection error of type PROTOCOL_VIOLATION. - */ - RESULT_ENSURE(!conn->quic_enabled, S2N_ERR_UNSUPPORTED_WITH_QUIC); - - struct s2n_blob session_id = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); - RESULT_GUARD(s2n_get_public_random_data(&session_id)); - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - return S2N_RESULT_OK; -} - -ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client_hello *ch) -{ - POSIX_ENSURE_REF(ch); - - return ch->raw_message.size; -} - -ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - - uint32_t len = min_size(&ch->raw_message, max_length); - POSIX_CHECKED_MEMCPY(out, ch->raw_message.data, len); - return len; -} - -ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch) -{ - POSIX_ENSURE_REF(ch); - - return ch->cipher_suites.size; -} - -int s2n_client_hello_cb_done(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE(conn->config->client_hello_cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); - POSIX_ENSURE(conn->client_hello.callback_invoked == 1, S2N_ERR_ASYNC_NOT_PERFORMED); - POSIX_ENSURE(conn->client_hello.parsed == 1, S2N_ERR_INVALID_STATE); - - conn->client_hello.callback_async_blocked = 0; - conn->client_hello.callback_async_done = 1; - - return S2N_SUCCESS; -} - -ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(ch->cipher_suites.data); - - uint32_t len = min_size(&ch->cipher_suites, max_length); - - POSIX_CHECKED_MEMCPY(out, ch->cipher_suites.data, len); - - return len; -} - -ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch) -{ - POSIX_ENSURE_REF(ch); - - return ch->extensions.raw.size; -} - -ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(ch->extensions.raw.data); - - uint32_t len = min_size(&ch->extensions.raw, max_length); - - POSIX_CHECKED_MEMCPY(out, ch->extensions.raw.data, len); - - return len; -} - -int s2n_client_hello_get_random(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - - POSIX_ENSURE(max_length >= S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(out, ch->random, S2N_TLS_RANDOM_DATA_LEN); - return S2N_SUCCESS; -} - -int s2n_client_hello_free_raw_message(struct s2n_client_hello *client_hello) -{ - POSIX_ENSURE_REF(client_hello); - - POSIX_GUARD(s2n_free(&client_hello->raw_message)); - - /* These point to data in the raw_message stuffer, - so we don't need to free them */ - client_hello->cipher_suites.data = NULL; - client_hello->extensions.raw.data = NULL; - - return 0; -} - -int s2n_client_hello_free(struct s2n_client_hello **ch) -{ - POSIX_ENSURE_REF(ch); - if (*ch == NULL) { - return S2N_SUCCESS; - } - - POSIX_ENSURE((*ch)->alloced, S2N_ERR_INVALID_ARGUMENT); - POSIX_GUARD(s2n_client_hello_free_raw_message(*ch)); - POSIX_GUARD(s2n_free_object((uint8_t **) ch, sizeof(struct s2n_client_hello))); - *ch = NULL; - return S2N_SUCCESS; -} - -int s2n_collect_client_hello(struct s2n_client_hello *ch, struct s2n_stuffer *source) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(source); - - uint32_t size = s2n_stuffer_data_available(source); - S2N_ERROR_IF(size == 0, S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_realloc(&ch->raw_message, size)); - POSIX_GUARD(s2n_stuffer_read(source, &ch->raw_message)); - - return 0; -} - -static S2N_RESULT s2n_client_hello_verify_for_retry(struct s2n_connection *conn, - struct s2n_client_hello *old_ch, struct s2n_client_hello *new_ch, - uint8_t *previous_client_random) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(old_ch); - RESULT_ENSURE_REF(new_ch); - RESULT_ENSURE_REF(previous_client_random); - - if (!s2n_is_hello_retry_handshake(conn)) { - return S2N_RESULT_OK; - } - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# The client will also send a - *# ClientHello when the server has responded to its ClientHello with a - *# HelloRetryRequest. In that case, the client MUST send the same - *# ClientHello without modification, except as follows: - * - * All of the exceptions that follow are extensions. - */ - RESULT_ENSURE(old_ch->legacy_version == new_ch->legacy_version, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(old_ch->compression_methods.size == new_ch->compression_methods.size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals(old_ch->compression_methods.data, new_ch->compression_methods.data, - new_ch->compression_methods.size), - S2N_ERR_BAD_MESSAGE); - - /* Some clients are not compliant with TLS 1.3 RFC, and send mismatching values in their second - * ClientHello. For increased compatibility, these checks are skipped outside of tests. The - * checks are still included in tests to ensure the s2n-tls client remains compliant. - */ - if (s2n_in_test()) { - /* In the past, the s2n-tls client updated the client random in the second ClientHello - * which is not allowed by RFC8446: https://github.com/aws/s2n-tls/pull/3311. Although the - * issue was addressed, its existence means that old versions of the s2n-tls client will - * fail this validation. - */ - RESULT_ENSURE(s2n_constant_time_equals( - previous_client_random, - conn->client_hello.random, - S2N_TLS_RANDOM_DATA_LEN), - S2N_ERR_BAD_MESSAGE); - - /* Some clients have been found to send a mismatching legacy session ID. */ - RESULT_ENSURE(old_ch->session_id.size == new_ch->session_id.size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals(old_ch->session_id.data, new_ch->session_id.data, - new_ch->session_id.size), - S2N_ERR_BAD_MESSAGE); - - /* Some clients have been found to send a mismatching cipher suite list. */ - RESULT_ENSURE(old_ch->cipher_suites.size == new_ch->cipher_suites.size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals(old_ch->cipher_suites.data, new_ch->cipher_suites.data, - new_ch->cipher_suites.size), - S2N_ERR_BAD_MESSAGE); - } - - /* - * Now enforce that the extensions also exactly match, - * except for the exceptions described in the RFC. - */ - for (size_t i = 0; i < s2n_array_len(s2n_supported_extensions); i++) { - s2n_parsed_extension *old_extension = &old_ch->extensions.parsed_extensions[i]; - uint32_t old_size = old_extension->extension.size; - s2n_parsed_extension *new_extension = &new_ch->extensions.parsed_extensions[i]; - uint32_t new_size = new_extension->extension.size; - - /* The extension type is only set if the extension is present. - * Look for a non-zero-length extension. - */ - uint16_t extension_type = 0; - if (old_size != 0) { - extension_type = old_extension->extension_type; - } else if (new_size != 0) { - extension_type = new_extension->extension_type; - } else { - continue; - } - - switch (extension_type) { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - If a "key_share" extension was supplied in the HelloRetryRequest, - *# replacing the list of shares with a list containing a single - *# KeyShareEntry from the indicated group. - */ - case TLS_EXTENSION_KEY_SHARE: - /* Handled when parsing the key share extension */ - break; - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - Removing the "early_data" extension (Section 4.2.10) if one was - *# present. Early data is not permitted after a HelloRetryRequest. - */ - case TLS_EXTENSION_EARLY_DATA: - RESULT_ENSURE(new_size == 0, S2N_ERR_BAD_MESSAGE); - break; - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - Including a "cookie" extension if one was provided in the - *# HelloRetryRequest. - */ - case TLS_EXTENSION_COOKIE: - /* Handled when parsing the cookie extension */ - break; - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - Updating the "pre_shared_key" extension if present by recomputing - *# the "obfuscated_ticket_age" and binder values and (optionally) - *# removing any PSKs which are incompatible with the server's - *# indicated cipher suite. - */ - case TLS_EXTENSION_PRE_SHARED_KEY: - /* Handled when parsing the psk extension */ - break; - - /* Some clients have been found to send mismatching supported versions in their second - * ClientHello. The extension isn't compared byte-for-byte for increased compatibility - * with these clients. - */ - case TLS_EXTENSION_SUPPORTED_VERSIONS: - /* Additional HRR validation for the supported versions extension is performed when - * parsing the extension. - */ - break; - - /* - * No more exceptions. - * All other extensions must match. - */ - default: - RESULT_ENSURE(old_size == new_size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals( - new_extension->extension.data, - old_extension->extension.data, - old_size), - S2N_ERR_BAD_MESSAGE); - } - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_client_hello_parse_raw(struct s2n_client_hello *client_hello) -{ - RESULT_ENSURE_REF(client_hello); - - struct s2n_stuffer in_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&in_stuffer, &client_hello->raw_message)); - struct s2n_stuffer *in = &in_stuffer; - - /** - * https://tools.ietf.org/rfc/rfc8446#4.1.2 - * Structure of this message: - * - * uint16 ProtocolVersion; - * opaque Random[32]; - * - * uint8 CipherSuite[2]; - * - * struct { - * ProtocolVersion legacy_version = 0x0303; - * Random random; - * opaque legacy_session_id<0..32>; - * CipherSuite cipher_suites<2..2^16-2>; - * opaque legacy_compression_methods<1..2^8-1>; - * Extension extensions<8..2^16-1>; - * } ClientHello; - **/ - - /* legacy_version */ - uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(in, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - /* Encode the version as a 1 byte representation of the two protocol version bytes, with the - * major version in the tens place and the minor version in the ones place. For example, the - * TLS 1.2 protocol version is 0x0303, which is encoded as S2N_TLS12 (33). - */ - client_hello->legacy_version = (client_protocol_version[0] * 10) + client_protocol_version[1]; - - /* random - read and store it, erasing from raw message */ - RESULT_GUARD_POSIX(s2n_stuffer_erase_and_read_bytes(in, client_hello->random, S2N_TLS_RANDOM_DATA_LEN)); - - /* legacy_session_id */ - uint8_t session_id_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &session_id_len)); - RESULT_ENSURE(session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); - uint8_t *session_id = s2n_stuffer_raw_read(in, session_id_len); - RESULT_ENSURE(session_id != NULL, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->session_id, session_id, session_id_len)); - - /* cipher suites */ - uint16_t cipher_suites_length = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(in, &cipher_suites_length)); - RESULT_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(cipher_suites_length % S2N_TLS_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); - uint8_t *cipher_suites = s2n_stuffer_raw_read(in, cipher_suites_length); - RESULT_ENSURE(cipher_suites != NULL, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->cipher_suites, cipher_suites, cipher_suites_length)); - - /* legacy_compression_methods */ - uint8_t compression_methods_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &compression_methods_len)); - uint8_t *compression_methods = s2n_stuffer_raw_read(in, compression_methods_len); - RESULT_ENSURE(compression_methods != NULL, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->compression_methods, compression_methods, compression_methods_len)); - - /* extensions */ - RESULT_GUARD_POSIX(s2n_extension_list_parse(in, &client_hello->extensions)); - - return S2N_RESULT_OK; -} - -int s2n_parse_client_hello(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* SSLv2 ClientHellos are not allowed during a HelloRetryRequest */ - if (s2n_is_hello_retry_handshake(conn)) { - POSIX_ENSURE(!conn->client_hello.sslv2, S2N_ERR_BAD_MESSAGE); - } - - /* If a retry, move the old version of the client hello - * somewhere safe so we can compare it to the new client hello later. - */ - DEFER_CLEANUP(struct s2n_client_hello previous_hello_retry = conn->client_hello, - s2n_client_hello_free_raw_message); - - /* Save the client random before clearing for retry validation */ - uint8_t previous_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - POSIX_CHECKED_MEMCPY(previous_client_random, conn->client_hello.random, - S2N_TLS_RANDOM_DATA_LEN); - - if (s2n_is_hello_retry_handshake(conn)) { - POSIX_CHECKED_MEMSET(&conn->client_hello, 0, sizeof(struct s2n_client_hello)); - } - - POSIX_GUARD(s2n_collect_client_hello(&conn->client_hello, &conn->handshake.io)); - - if (conn->client_hello.sslv2) { - POSIX_GUARD(s2n_sslv2_client_hello_parse(conn)); - return S2N_SUCCESS; - } - - /* Parse raw, collected client hello */ - POSIX_GUARD_RESULT(s2n_client_hello_parse_raw(&conn->client_hello)); - - /* Protocol version in the ClientHello is fixed at 0x0303(TLS 1.2) for - * future versions of TLS. Therefore, we will negotiate down if a client sends - * an unexpected value above 0x0303. - */ - conn->client_protocol_version = S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); - - /* Copy the session id to the connection. */ - conn->session_id_len = conn->client_hello.session_id.size; - POSIX_CHECKED_MEMCPY(conn->session_id, conn->client_hello.session_id.data, conn->session_id_len); - - POSIX_GUARD_RESULT(s2n_client_hello_verify_for_retry(conn, - &previous_hello_retry, &conn->client_hello, previous_client_random)); - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_client_hello_parse_message_impl(struct s2n_client_hello **result, - const uint8_t *raw_message, uint32_t raw_message_size) -{ - RESULT_ENSURE_REF(result); - - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_client_hello))); - RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); - - DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); - client_hello = (struct s2n_client_hello *) (void *) mem.data; - client_hello->alloced = true; - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - RESULT_GUARD_POSIX(s2n_stuffer_alloc(&in, raw_message_size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&in, raw_message, raw_message_size)); - - uint8_t message_type = 0; - uint32_t message_len = 0; - RESULT_GUARD(s2n_handshake_parse_header(&in, &message_type, &message_len)); - RESULT_ENSURE(message_type == TLS_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(message_len == s2n_stuffer_data_available(&in), S2N_ERR_BAD_MESSAGE); - - RESULT_GUARD_POSIX(s2n_collect_client_hello(client_hello, &in)); - RESULT_ENSURE(s2n_stuffer_data_available(&in) == 0, S2N_ERR_BAD_MESSAGE); - - RESULT_GUARD(s2n_client_hello_parse_raw(client_hello)); - - *result = client_hello; - ZERO_TO_DISABLE_DEFER_CLEANUP(client_hello); - return S2N_RESULT_OK; -} - -struct s2n_client_hello *s2n_client_hello_parse_message(const uint8_t *raw_message, uint32_t raw_message_size) -{ - struct s2n_client_hello *result = NULL; - PTR_GUARD_RESULT(s2n_client_hello_parse_message_impl(&result, raw_message, raw_message_size)); - return result; -} - -int s2n_process_client_hello(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - /* Client hello is parsed and config is finalized. - * Negotiate protocol version, cipher suite, ALPN, select a cert, etc. */ - struct s2n_client_hello *client_hello = &conn->client_hello; - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - - if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { - conn->server_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); - conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); - } - - /* Set default key exchange curve. - * This is going to be our fallback if the client has no preference. - * - * P-256 is our preferred fallback option because the TLS1.3 RFC requires - * all implementations to support it: - * - * https://tools.ietf.org/rfc/rfc8446#section-9.1 - * A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256) - * and SHOULD support key exchange with X25519 [RFC7748] - * - *= https://www.rfc-editor.org/rfc/rfc4492#section-4 - *# A client that proposes ECC cipher suites may choose not to include these extensions. - *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. - * - */ - const struct s2n_ecc_preferences *ecc_pref = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - POSIX_ENSURE_REF(ecc_pref); - - if (ecc_pref->count == 0) { - conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; - } else if (s2n_ecc_preferences_includes_curve(ecc_pref, TLS_EC_CURVE_SECP_256_R1)) { - conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; - } else { - /* If P-256 isn't allowed by the current security policy, choose the first / most preferred curve. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - } - - POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &conn->client_hello.extensions)); - - /* After parsing extensions, select a curve and corresponding keyshare to use */ - if (conn->actual_protocol_version >= S2N_TLS13) { - POSIX_GUARD(s2n_extensions_server_key_share_select(conn)); - } - - /* for pre TLS 1.3 connections, protocol selection is not done in supported_versions extensions, so do it here */ - if (conn->actual_protocol_version < S2N_TLS13) { - conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); - } - - if (conn->client_protocol_version < security_policy->minimum_protocol_version) { - POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); - POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - if (s2n_connection_is_quic_enabled(conn)) { - POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - /* Find potential certificate matches before we choose the cipher. */ - POSIX_GUARD(s2n_conn_find_name_matching_certs(conn)); - - /* Save the previous cipher suite */ - uint8_t previous_cipher_suite_iana[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - POSIX_CHECKED_MEMCPY(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN); - - /* Now choose the ciphers we have certs for. */ - if (conn->client_hello.sslv2) { - POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, - client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN)); - } else { - POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, - client_hello->cipher_suites.size / 2)); - } - - /* Check if this is the second client hello in a hello retry handshake */ - if (s2n_is_hello_retry_handshake(conn) && conn->handshake.message_number > 0) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Servers MUST ensure that they negotiate the - *# same cipher suite when receiving a conformant updated ClientHello (if - *# the server selects the cipher suite as the first step in the - *# negotiation, then this will happen automatically). - **/ - POSIX_ENSURE(s2n_constant_time_equals(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, - S2N_TLS_CIPHER_SUITE_LEN), - S2N_ERR_BAD_MESSAGE); - } - - /* If we're using a PSK, we don't need to choose a signature algorithm or certificate, - * because no additional auth is required. */ - if (conn->psk_params.chosen_psk != NULL) { - return S2N_SUCCESS; - } - - /* And set the signature and hash algorithm used for key exchange signatures */ - POSIX_GUARD_RESULT(s2n_signature_algorithm_select(conn)); - - /* And finally, set the certs specified by the final auth + sig_alg combo. */ - POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &conn->handshake_params.our_chain_and_key)); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_client_hello_process_cb_response(struct s2n_connection *conn, int rc) -{ - if (rc < 0) { - goto fail; - } - switch (conn->config->client_hello_cb_mode) { - case S2N_CLIENT_HELLO_CB_BLOCKING: { - if (rc) { - conn->server_name_used = 1; - } - return S2N_RESULT_OK; - } - case S2N_CLIENT_HELLO_CB_NONBLOCKING: { - if (conn->client_hello.callback_async_done) { - return S2N_RESULT_OK; - } - conn->client_hello.callback_async_blocked = 1; - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - } -fail: - /* rc < 0 */ - RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); - RESULT_BAIL(S2N_ERR_CANCELLED); -} - -int s2n_client_hello_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE(!conn->client_hello.callback_async_blocked, S2N_ERR_ASYNC_BLOCKED); - - /* Only parse the ClientHello once */ - if (!conn->client_hello.parsed) { - POSIX_GUARD(s2n_parse_client_hello(conn)); - /* Mark the collected client hello as available when parsing is done and before the client hello callback */ - conn->client_hello.parsed = true; - } - - /* Only invoke the ClientHello callback once. - * This means that we do NOT invoke the callback again on the second ClientHello - * in a TLS1.3 retry handshake. We explicitly check for a retry because the - * callback state may have been cleared while parsing the second ClientHello. - */ - if (!conn->client_hello.callback_invoked && !IS_HELLO_RETRY_HANDSHAKE(conn)) { - /* Mark the client hello callback as invoked to avoid calling it again. */ - conn->client_hello.callback_invoked = true; - - /* Do NOT move this null check. A test exists to assert that a server connection can get - * as far as the client hello callback without using its config. To do this we need a - * specific error for a null config just before the client hello callback. The test's - * assertions are weakened if this check is moved. */ - POSIX_ENSURE(conn->config, S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); - - /* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */ - if (conn->config->client_hello_cb) { - int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx); - POSIX_GUARD_RESULT(s2n_client_hello_process_cb_response(conn, rc)); - } - } - - POSIX_GUARD(s2n_process_client_hello(conn)); - - return 0; -} - -S2N_RESULT s2n_cipher_suite_validate_available(struct s2n_connection *conn, struct s2n_cipher_suite *cipher) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(cipher); - RESULT_ENSURE_EQ(cipher->available, true); - RESULT_ENSURE_LTE(cipher->minimum_required_tls_version, conn->client_protocol_version); - if (s2n_connection_is_quic_enabled(conn)) { - RESULT_ENSURE_GTE(cipher->minimum_required_tls_version, S2N_TLS13); - } - return S2N_RESULT_OK; -} - -int s2n_client_hello_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - - const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; - POSIX_ENSURE_REF(cipher_preferences); - - if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { - conn->client_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); - conn->actual_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); - } - - struct s2n_stuffer *out = &conn->handshake.io; - uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - - uint8_t reported_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); - conn->client_hello.legacy_version = reported_protocol_version; - client_protocol_version[0] = reported_protocol_version / 10; - client_protocol_version[1] = reported_protocol_version % 10; - POSIX_GUARD(s2n_stuffer_write_bytes(out, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - struct s2n_blob client_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); - if (!s2n_is_hello_retry_handshake(conn)) { - /* Only generate the random data for our first client hello. - * If we retry, we'll reuse the value. */ - POSIX_GUARD_RESULT(s2n_get_public_random_data(&client_random)); - } - POSIX_GUARD(s2n_stuffer_write(out, &client_random)); - - POSIX_GUARD_RESULT(s2n_generate_client_session_id(conn)); - POSIX_GUARD(s2n_stuffer_write_uint8(out, conn->session_id_len)); - if (conn->session_id_len > 0) { - POSIX_GUARD(s2n_stuffer_write_bytes(out, conn->session_id, conn->session_id_len)); - } - - /* Reserve space for size of the list of available ciphers */ - struct s2n_stuffer_reservation available_cipher_suites_size; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &available_cipher_suites_size)); - - /* Now, write the IANA values of every available cipher suite in our list */ - struct s2n_cipher_suite *cipher = NULL; - bool tls12_is_possible = false; - for (size_t i = 0; i < security_policy->cipher_preferences->count; i++) { - cipher = cipher_preferences->suites[i]; - if (s2n_result_is_error(s2n_cipher_suite_validate_available(conn, cipher))) { - continue; - } - if (cipher->minimum_required_tls_version < S2N_TLS13) { - tls12_is_possible = true; - } - POSIX_GUARD(s2n_stuffer_write_bytes(out, cipher->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - } - - /** - * For initial handshakes: - *= https://www.rfc-editor.org/rfc/rfc5746#3.4 - *# o The client MUST include either an empty "renegotiation_info" - *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling - *# cipher suite value in the ClientHello. Including both is NOT - *# RECOMMENDED. - * For maximum backwards compatibility, we choose to use the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite - * rather than the "renegotiation_info" extension. - * - * For renegotiation handshakes: - *= https://www.rfc-editor.org/rfc/rfc5746#3.5 - *# The SCSV MUST NOT be included. - */ - if (tls12_is_possible && !s2n_handshake_is_renegotiation(conn)) { - uint8_t renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; - POSIX_GUARD(s2n_stuffer_write_bytes(out, renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN)); - } - - /* Write size of the list of available ciphers */ - uint32_t ciphers_size = 0; - POSIX_GUARD(s2n_stuffer_get_vector_size(&available_cipher_suites_size, &ciphers_size)); - POSIX_ENSURE(ciphers_size > 0, S2N_ERR_INVALID_CIPHER_PREFERENCES); - POSIX_GUARD(s2n_stuffer_write_reservation(&available_cipher_suites_size, ciphers_size)); - - /* Zero compression methods */ - POSIX_GUARD(s2n_stuffer_write_uint8(out, 1)); - POSIX_GUARD(s2n_stuffer_write_uint8(out, 0)); - - /* Write the extensions */ - POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, out)); - - /* Once the message is complete, finish calculating the PSK binders. - * - * The PSK binders require all the sizes in the ClientHello to be written correctly, - * including the extension size and extension list size, and therefore have - * to be calculated AFTER we finish writing the entire extension list. */ - POSIX_GUARD_RESULT(s2n_finish_psk_extension(conn)); - - /* If early data was not requested as part of the ClientHello, it never will be. */ - if (conn->early_data_state == S2N_UNKNOWN_EARLY_DATA_STATE) { - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_NOT_REQUESTED)); - } - - return S2N_SUCCESS; -} - -/* - * s2n-tls does NOT support SSLv2. However, it does support SSLv2 ClientHellos. - * Clients may send SSLv2 ClientHellos advertising higher protocol versions for - * backwards compatibility reasons. See https://tools.ietf.org/rfc/rfc2246 Appendix E. - * - * In this case, conn->client_hello.legacy_version and conn->client_protocol_version - * will be higher than SSLv2. - * - * See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html Section 2.5 - * for a description of the expected SSLv2 format. - * Alternatively, the TLS1.0 RFC includes a more modern description of the format: - * https://tools.ietf.org/rfc/rfc2246 Appendix E.1 - */ -int s2n_sslv2_client_hello_parse(struct s2n_connection *conn) -{ - struct s2n_client_hello *client_hello = &conn->client_hello; - conn->client_protocol_version = S2N_MIN(client_hello->legacy_version, S2N_TLS12); - - struct s2n_stuffer in_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&in_stuffer, &client_hello->raw_message)); - POSIX_GUARD(s2n_stuffer_skip_write(&in_stuffer, client_hello->raw_message.size)); - struct s2n_stuffer *in = &in_stuffer; - - /* We start 5 bytes into the record */ - uint16_t cipher_suites_length = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); - POSIX_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(cipher_suites_length % S2N_SSLv2_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); - - uint16_t session_id_length = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &session_id_length)); - - uint16_t challenge_length = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &challenge_length)); - - S2N_ERROR_IF(challenge_length > S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_BAD_MESSAGE); - - client_hello->cipher_suites.size = cipher_suites_length; - client_hello->cipher_suites.data = s2n_stuffer_raw_read(in, cipher_suites_length); - POSIX_ENSURE_REF(client_hello->cipher_suites.data); - - S2N_ERROR_IF(session_id_length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_blob_init(&client_hello->session_id, s2n_stuffer_raw_read(in, session_id_length), session_id_length)); - if (session_id_length > 0 && session_id_length <= S2N_TLS_SESSION_ID_MAX_LEN) { - POSIX_CHECKED_MEMCPY(conn->session_id, client_hello->session_id.data, session_id_length); - conn->session_id_len = (uint8_t) session_id_length; - } - - struct s2n_blob b = { 0 }; - POSIX_GUARD(s2n_blob_init(&b, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); - - b.data += S2N_TLS_RANDOM_DATA_LEN - challenge_length; - b.size -= S2N_TLS_RANDOM_DATA_LEN - challenge_length; - - POSIX_GUARD(s2n_stuffer_read(in, &b)); - - return 0; -} - -int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type, - s2n_parsed_extensions_list *parsed_extension_list, s2n_parsed_extension **parsed_extension) -{ - POSIX_ENSURE_REF(parsed_extension_list); - POSIX_ENSURE_REF(parsed_extension); - - s2n_extension_type_id extension_type_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type, &extension_type_id)); - - s2n_parsed_extension *found_parsed_extension = &parsed_extension_list->parsed_extensions[extension_type_id]; - POSIX_ENSURE(found_parsed_extension->extension.data, S2N_ERR_EXTENSION_NOT_RECEIVED); - POSIX_ENSURE(found_parsed_extension->extension_type == extension_type, S2N_ERR_INVALID_PARSED_EXTENSIONS); - - *parsed_extension = found_parsed_extension; - return S2N_SUCCESS; -} - -ssize_t s2n_client_hello_get_extension_length(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type) -{ - POSIX_ENSURE_REF(ch); - - s2n_parsed_extension *parsed_extension = NULL; - if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { - return 0; - } - - return parsed_extension->extension.size; -} - -ssize_t s2n_client_hello_get_extension_by_id(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - - s2n_parsed_extension *parsed_extension = NULL; - if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { - return 0; - } - - uint32_t len = min_size(&parsed_extension->extension, max_length); - POSIX_CHECKED_MEMCPY(out, parsed_extension->extension.data, len); - return len; -} - -int s2n_client_hello_get_session_id_length(struct s2n_client_hello *ch, uint32_t *out_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out_length); - *out_length = ch->session_id.size; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(out_length); - - uint32_t len = min_size(&ch->session_id, max_length); - POSIX_CHECKED_MEMCPY(out, ch->session_id.data, len); - *out_length = len; - - return S2N_SUCCESS; -} - -int s2n_client_hello_get_compression_methods_length(struct s2n_client_hello *ch, uint32_t *out_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out_length); - *out_length = ch->compression_methods.size; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_compression_methods(struct s2n_client_hello *ch, uint8_t *list, uint32_t list_length, uint32_t *out_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(list); - POSIX_ENSURE_REF(out_length); - - POSIX_ENSURE(list_length >= ch->compression_methods.size, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(list, ch->compression_methods.data, ch->compression_methods.size); - *out_length = ch->compression_methods.size; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_legacy_protocol_version(struct s2n_client_hello *ch, uint8_t *out) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - *out = ch->legacy_version; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_legacy_record_version(struct s2n_client_hello *ch, uint8_t *out) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE(ch->record_version_recorded, S2N_ERR_INVALID_ARGUMENT); - *out = ch->legacy_record_version; - return S2N_SUCCESS; -} - -S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, - struct s2n_blob *raw_extensions, struct s2n_blob *extension) -{ - RESULT_ENSURE_REF(raw_extensions); - RESULT_ENSURE_REF(extension); - - *extension = (struct s2n_blob){ 0 }; - - struct s2n_stuffer raw_extensions_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&raw_extensions_stuffer, raw_extensions)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&raw_extensions_stuffer, raw_extensions->size)); - - while (s2n_stuffer_data_available(&raw_extensions_stuffer) > 0) { - uint16_t extension_type = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_type)); - - uint16_t extension_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_size)); - - uint8_t *extension_data = s2n_stuffer_raw_read(&raw_extensions_stuffer, extension_size); - RESULT_ENSURE_REF(extension_data); - - if (extension_iana == extension_type) { - RESULT_GUARD_POSIX(s2n_blob_init(extension, extension_data, extension_size)); - return S2N_RESULT_OK; - } - } - return S2N_RESULT_OK; -} - -int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extension_iana, bool *exists) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(exists); - - *exists = false; - - s2n_extension_type_id extension_type_id = s2n_unsupported_extension; - if (s2n_extension_supported_iana_value_to_id(extension_iana, &extension_type_id) == S2N_SUCCESS) { - s2n_parsed_extension *parsed_extension = NULL; - if (s2n_client_hello_get_parsed_extension(extension_iana, &ch->extensions, &parsed_extension) == S2N_SUCCESS) { - *exists = true; - } - return S2N_SUCCESS; - } - - struct s2n_blob extension = { 0 }; - POSIX_GUARD_RESULT(s2n_client_hello_get_raw_extension(extension_iana, &ch->extensions.raw, &extension)); - if (extension.data != NULL) { - *exists = true; - } - return S2N_SUCCESS; -} - -int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups, - uint16_t groups_count_max, uint16_t *groups_count_out) -{ - POSIX_ENSURE_REF(groups_count_out); - *groups_count_out = 0; - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(groups); - - s2n_parsed_extension *supported_groups_extension = NULL; - POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS, &ch->extensions, &supported_groups_extension)); - POSIX_ENSURE_REF(supported_groups_extension); - - struct s2n_stuffer extension_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &supported_groups_extension->extension)); - - uint16_t supported_groups_count = 0; - POSIX_GUARD_RESULT(s2n_supported_groups_parse_count(&extension_stuffer, &supported_groups_count)); - POSIX_ENSURE(supported_groups_count <= groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); - - for (size_t i = 0; i < supported_groups_count; i++) { - /* s2n_stuffer_read_uint16 is used to read each of the supported groups in network-order - * endianness. - */ - POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &groups[i])); - } - - *groups_count_out = supported_groups_count; - - return S2N_SUCCESS; -} - -int s2n_client_hello_get_server_name_length(struct s2n_client_hello *ch, uint16_t *length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(length); - *length = 0; - - s2n_parsed_extension *server_name_extension = NULL; - POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); - POSIX_ENSURE_REF(server_name_extension); - - struct s2n_stuffer extension_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); - - struct s2n_blob blob = { 0 }; - POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); - *length = blob.size; - - return S2N_SUCCESS; -} - -int s2n_client_hello_get_server_name(struct s2n_client_hello *ch, uint8_t *server_name, uint16_t length, uint16_t *out_length) -{ - POSIX_ENSURE_REF(out_length); - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(server_name); - *out_length = 0; - - s2n_parsed_extension *server_name_extension = NULL; - POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); - POSIX_ENSURE_REF(server_name_extension); - - struct s2n_stuffer extension_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); - - struct s2n_blob blob = { 0 }; - POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); - POSIX_ENSURE_LTE(blob.size, length); - POSIX_CHECKED_MEMCPY(server_name, blob.data, blob.size); - - *out_length = blob.size; - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_client_hello.h" + +#include +#include +#include + +#include "api/unstable/fingerprint.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_pq.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_client_server_name.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_auth_selection.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake_type.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_signature_algorithms.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn) +{ + if (conn->client_hello.parsed != 1) { + return NULL; + } + + return &conn->client_hello; +} + +static uint32_t min_size(struct s2n_blob *blob, uint32_t max_length) +{ + return blob->size < max_length ? blob->size : max_length; +} + +static S2N_RESULT s2n_generate_client_session_id(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + /* Session id already generated - no-op */ + if (conn->session_id_len) { + return S2N_RESULT_OK; + } + + /* Only generate the session id if using tickets */ + bool generate = conn->config->use_tickets; + + /* TLS1.3 doesn't require session ids. The field is actually renamed to legacy_session_id. + * However, we still set a session id if dealing with troublesome middleboxes + * (middlebox compatibility mode) or if trying to use a TLS1.2 ticket. + */ + if (conn->client_protocol_version >= S2N_TLS13) { + generate = s2n_is_middlebox_compat_enabled(conn) || conn->resume_protocol_version; + } + + /* Session id not needed - no-op */ + if (!generate) { + return S2N_RESULT_OK; + } + + /* QUIC should not allow session ids for any reason. + * + *= https://www.rfc-editor.org/rfc/rfc9001#section-8.4 + *# A server SHOULD treat the receipt of a TLS ClientHello with a non-empty + *# legacy_session_id field as a connection error of type PROTOCOL_VIOLATION. + */ + RESULT_ENSURE(!conn->quic_enabled, S2N_ERR_UNSUPPORTED_WITH_QUIC); + + struct s2n_blob session_id = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + RESULT_GUARD(s2n_get_public_random_data(&session_id)); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + return S2N_RESULT_OK; +} + +ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client_hello *ch) +{ + POSIX_ENSURE_REF(ch); + + return ch->raw_message.size; +} + +ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + + uint32_t len = min_size(&ch->raw_message, max_length); + POSIX_CHECKED_MEMCPY(out, ch->raw_message.data, len); + return len; +} + +ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch) +{ + POSIX_ENSURE_REF(ch); + + return ch->cipher_suites.size; +} + +int s2n_client_hello_cb_done(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE(conn->config->client_hello_cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(conn->client_hello.callback_invoked == 1, S2N_ERR_ASYNC_NOT_PERFORMED); + POSIX_ENSURE(conn->client_hello.parsed == 1, S2N_ERR_INVALID_STATE); + + conn->client_hello.callback_async_blocked = 0; + conn->client_hello.callback_async_done = 1; + + return S2N_SUCCESS; +} + +ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(ch->cipher_suites.data); + + uint32_t len = min_size(&ch->cipher_suites, max_length); + + POSIX_CHECKED_MEMCPY(out, ch->cipher_suites.data, len); + + return len; +} + +ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch) +{ + POSIX_ENSURE_REF(ch); + + return ch->extensions.raw.size; +} + +ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(ch->extensions.raw.data); + + uint32_t len = min_size(&ch->extensions.raw, max_length); + + POSIX_CHECKED_MEMCPY(out, ch->extensions.raw.data, len); + + return len; +} + +int s2n_client_hello_get_random(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + + POSIX_ENSURE(max_length >= S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(out, ch->random, S2N_TLS_RANDOM_DATA_LEN); + return S2N_SUCCESS; +} + +int s2n_client_hello_free_raw_message(struct s2n_client_hello *client_hello) +{ + POSIX_ENSURE_REF(client_hello); + + POSIX_GUARD(s2n_free(&client_hello->raw_message)); + + /* These point to data in the raw_message stuffer, + so we don't need to free them */ + client_hello->cipher_suites.data = NULL; + client_hello->extensions.raw.data = NULL; + + return 0; +} + +int s2n_client_hello_free(struct s2n_client_hello **ch) +{ + POSIX_ENSURE_REF(ch); + if (*ch == NULL) { + return S2N_SUCCESS; + } + + POSIX_ENSURE((*ch)->alloced, S2N_ERR_INVALID_ARGUMENT); + POSIX_GUARD(s2n_client_hello_free_raw_message(*ch)); + POSIX_GUARD(s2n_free_object((uint8_t **) ch, sizeof(struct s2n_client_hello))); + *ch = NULL; + return S2N_SUCCESS; +} + +int s2n_collect_client_hello(struct s2n_client_hello *ch, struct s2n_stuffer *source) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(source); + + uint32_t size = s2n_stuffer_data_available(source); + S2N_ERROR_IF(size == 0, S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_realloc(&ch->raw_message, size)); + POSIX_GUARD(s2n_stuffer_read(source, &ch->raw_message)); + + return 0; +} + +static S2N_RESULT s2n_client_hello_verify_for_retry(struct s2n_connection *conn, + struct s2n_client_hello *old_ch, struct s2n_client_hello *new_ch, + uint8_t *previous_client_random) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(old_ch); + RESULT_ENSURE_REF(new_ch); + RESULT_ENSURE_REF(previous_client_random); + + if (!s2n_is_hello_retry_handshake(conn)) { + return S2N_RESULT_OK; + } + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# The client will also send a + *# ClientHello when the server has responded to its ClientHello with a + *# HelloRetryRequest. In that case, the client MUST send the same + *# ClientHello without modification, except as follows: + * + * All of the exceptions that follow are extensions. + */ + RESULT_ENSURE(old_ch->legacy_version == new_ch->legacy_version, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(old_ch->compression_methods.size == new_ch->compression_methods.size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals(old_ch->compression_methods.data, new_ch->compression_methods.data, + new_ch->compression_methods.size), + S2N_ERR_BAD_MESSAGE); + + /* Some clients are not compliant with TLS 1.3 RFC, and send mismatching values in their second + * ClientHello. For increased compatibility, these checks are skipped outside of tests. The + * checks are still included in tests to ensure the s2n-tls client remains compliant. + */ + if (s2n_in_test()) { + /* In the past, the s2n-tls client updated the client random in the second ClientHello + * which is not allowed by RFC8446: https://github.com/aws/s2n-tls/pull/3311. Although the + * issue was addressed, its existence means that old versions of the s2n-tls client will + * fail this validation. + */ + RESULT_ENSURE(s2n_constant_time_equals( + previous_client_random, + conn->client_hello.random, + S2N_TLS_RANDOM_DATA_LEN), + S2N_ERR_BAD_MESSAGE); + + /* Some clients have been found to send a mismatching legacy session ID. */ + RESULT_ENSURE(old_ch->session_id.size == new_ch->session_id.size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals(old_ch->session_id.data, new_ch->session_id.data, + new_ch->session_id.size), + S2N_ERR_BAD_MESSAGE); + + /* Some clients have been found to send a mismatching cipher suite list. */ + RESULT_ENSURE(old_ch->cipher_suites.size == new_ch->cipher_suites.size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals(old_ch->cipher_suites.data, new_ch->cipher_suites.data, + new_ch->cipher_suites.size), + S2N_ERR_BAD_MESSAGE); + } + + /* + * Now enforce that the extensions also exactly match, + * except for the exceptions described in the RFC. + */ + for (size_t i = 0; i < s2n_array_len(s2n_supported_extensions); i++) { + s2n_parsed_extension *old_extension = &old_ch->extensions.parsed_extensions[i]; + uint32_t old_size = old_extension->extension.size; + s2n_parsed_extension *new_extension = &new_ch->extensions.parsed_extensions[i]; + uint32_t new_size = new_extension->extension.size; + + /* The extension type is only set if the extension is present. + * Look for a non-zero-length extension. + */ + uint16_t extension_type = 0; + if (old_size != 0) { + extension_type = old_extension->extension_type; + } else if (new_size != 0) { + extension_type = new_extension->extension_type; + } else { + continue; + } + + switch (extension_type) { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - If a "key_share" extension was supplied in the HelloRetryRequest, + *# replacing the list of shares with a list containing a single + *# KeyShareEntry from the indicated group. + */ + case TLS_EXTENSION_KEY_SHARE: + /* Handled when parsing the key share extension */ + break; + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - Removing the "early_data" extension (Section 4.2.10) if one was + *# present. Early data is not permitted after a HelloRetryRequest. + */ + case TLS_EXTENSION_EARLY_DATA: + RESULT_ENSURE(new_size == 0, S2N_ERR_BAD_MESSAGE); + break; + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - Including a "cookie" extension if one was provided in the + *# HelloRetryRequest. + */ + case TLS_EXTENSION_COOKIE: + /* Handled when parsing the cookie extension */ + break; + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - Updating the "pre_shared_key" extension if present by recomputing + *# the "obfuscated_ticket_age" and binder values and (optionally) + *# removing any PSKs which are incompatible with the server's + *# indicated cipher suite. + */ + case TLS_EXTENSION_PRE_SHARED_KEY: + /* Handled when parsing the psk extension */ + break; + + /* Some clients have been found to send mismatching supported versions in their second + * ClientHello. The extension isn't compared byte-for-byte for increased compatibility + * with these clients. + */ + case TLS_EXTENSION_SUPPORTED_VERSIONS: + /* Additional HRR validation for the supported versions extension is performed when + * parsing the extension. + */ + break; + + /* + * No more exceptions. + * All other extensions must match. + */ + default: + RESULT_ENSURE(old_size == new_size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals( + new_extension->extension.data, + old_extension->extension.data, + old_size), + S2N_ERR_BAD_MESSAGE); + } + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_client_hello_parse_raw(struct s2n_client_hello *client_hello) +{ + RESULT_ENSURE_REF(client_hello); + + struct s2n_stuffer in_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&in_stuffer, &client_hello->raw_message)); + struct s2n_stuffer *in = &in_stuffer; + + /** + * https://tools.ietf.org/rfc/rfc8446#4.1.2 + * Structure of this message: + * + * uint16 ProtocolVersion; + * opaque Random[32]; + * + * uint8 CipherSuite[2]; + * + * struct { + * ProtocolVersion legacy_version = 0x0303; + * Random random; + * opaque legacy_session_id<0..32>; + * CipherSuite cipher_suites<2..2^16-2>; + * opaque legacy_compression_methods<1..2^8-1>; + * Extension extensions<8..2^16-1>; + * } ClientHello; + **/ + + /* legacy_version */ + uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(in, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + /* Encode the version as a 1 byte representation of the two protocol version bytes, with the + * major version in the tens place and the minor version in the ones place. For example, the + * TLS 1.2 protocol version is 0x0303, which is encoded as S2N_TLS12 (33). + */ + client_hello->legacy_version = (client_protocol_version[0] * 10) + client_protocol_version[1]; + + /* random - read and store it, erasing from raw message */ + RESULT_GUARD_POSIX(s2n_stuffer_erase_and_read_bytes(in, client_hello->random, S2N_TLS_RANDOM_DATA_LEN)); + + /* legacy_session_id */ + uint8_t session_id_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &session_id_len)); + RESULT_ENSURE(session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); + uint8_t *session_id = s2n_stuffer_raw_read(in, session_id_len); + RESULT_ENSURE(session_id != NULL, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->session_id, session_id, session_id_len)); + + /* cipher suites */ + uint16_t cipher_suites_length = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(in, &cipher_suites_length)); + RESULT_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(cipher_suites_length % S2N_TLS_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); + uint8_t *cipher_suites = s2n_stuffer_raw_read(in, cipher_suites_length); + RESULT_ENSURE(cipher_suites != NULL, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->cipher_suites, cipher_suites, cipher_suites_length)); + + /* legacy_compression_methods */ + uint8_t compression_methods_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &compression_methods_len)); + uint8_t *compression_methods = s2n_stuffer_raw_read(in, compression_methods_len); + RESULT_ENSURE(compression_methods != NULL, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->compression_methods, compression_methods, compression_methods_len)); + + /* extensions */ + RESULT_GUARD_POSIX(s2n_extension_list_parse(in, &client_hello->extensions)); + + return S2N_RESULT_OK; +} + +int s2n_parse_client_hello(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* SSLv2 ClientHellos are not allowed during a HelloRetryRequest */ + if (s2n_is_hello_retry_handshake(conn)) { + POSIX_ENSURE(!conn->client_hello.sslv2, S2N_ERR_BAD_MESSAGE); + } + + /* If a retry, move the old version of the client hello + * somewhere safe so we can compare it to the new client hello later. + */ + DEFER_CLEANUP(struct s2n_client_hello previous_hello_retry = conn->client_hello, + s2n_client_hello_free_raw_message); + + /* Save the client random before clearing for retry validation */ + uint8_t previous_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + POSIX_CHECKED_MEMCPY(previous_client_random, conn->client_hello.random, + S2N_TLS_RANDOM_DATA_LEN); + + if (s2n_is_hello_retry_handshake(conn)) { + POSIX_CHECKED_MEMSET(&conn->client_hello, 0, sizeof(struct s2n_client_hello)); + } + + POSIX_GUARD(s2n_collect_client_hello(&conn->client_hello, &conn->handshake.io)); + + if (conn->client_hello.sslv2) { + POSIX_GUARD(s2n_sslv2_client_hello_parse(conn)); + return S2N_SUCCESS; + } + + /* Parse raw, collected client hello */ + POSIX_GUARD_RESULT(s2n_client_hello_parse_raw(&conn->client_hello)); + + /* Protocol version in the ClientHello is fixed at 0x0303(TLS 1.2) for + * future versions of TLS. Therefore, we will negotiate down if a client sends + * an unexpected value above 0x0303. + */ + conn->client_protocol_version = S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); + + /* Copy the session id to the connection. */ + conn->session_id_len = conn->client_hello.session_id.size; + POSIX_CHECKED_MEMCPY(conn->session_id, conn->client_hello.session_id.data, conn->session_id_len); + + POSIX_GUARD_RESULT(s2n_client_hello_verify_for_retry(conn, + &previous_hello_retry, &conn->client_hello, previous_client_random)); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_client_hello_parse_message_impl(struct s2n_client_hello **result, + const uint8_t *raw_message, uint32_t raw_message_size) +{ + RESULT_ENSURE_REF(result); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_client_hello))); + RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + client_hello = (struct s2n_client_hello *) (void *) mem.data; + client_hello->alloced = true; + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_alloc(&in, raw_message_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&in, raw_message, raw_message_size)); + + uint8_t message_type = 0; + uint32_t message_len = 0; + RESULT_GUARD(s2n_handshake_parse_header(&in, &message_type, &message_len)); + RESULT_ENSURE(message_type == TLS_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(message_len == s2n_stuffer_data_available(&in), S2N_ERR_BAD_MESSAGE); + + RESULT_GUARD_POSIX(s2n_collect_client_hello(client_hello, &in)); + RESULT_ENSURE(s2n_stuffer_data_available(&in) == 0, S2N_ERR_BAD_MESSAGE); + + RESULT_GUARD(s2n_client_hello_parse_raw(client_hello)); + + *result = client_hello; + ZERO_TO_DISABLE_DEFER_CLEANUP(client_hello); + return S2N_RESULT_OK; +} + +struct s2n_client_hello *s2n_client_hello_parse_message(const uint8_t *raw_message, uint32_t raw_message_size) +{ + struct s2n_client_hello *result = NULL; + PTR_GUARD_RESULT(s2n_client_hello_parse_message_impl(&result, raw_message, raw_message_size)); + return result; +} + +int s2n_process_client_hello(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + /* Client hello is parsed and config is finalized. + * Negotiate protocol version, cipher suite, ALPN, select a cert, etc. */ + struct s2n_client_hello *client_hello = &conn->client_hello; + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + + if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { + conn->server_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); + conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); + } + + /* Set default key exchange curve. + * This is going to be our fallback if the client has no preference. + * + * P-256 is our preferred fallback option because the TLS1.3 RFC requires + * all implementations to support it: + * + * https://tools.ietf.org/rfc/rfc8446#section-9.1 + * A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256) + * and SHOULD support key exchange with X25519 [RFC7748] + * + *= https://www.rfc-editor.org/rfc/rfc4492#section-4 + *# A client that proposes ECC cipher suites may choose not to include these extensions. + *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. + * + */ + const struct s2n_ecc_preferences *ecc_pref = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + POSIX_ENSURE_REF(ecc_pref); + + if (ecc_pref->count == 0) { + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + } else if (s2n_ecc_preferences_includes_curve(ecc_pref, TLS_EC_CURVE_SECP_256_R1)) { + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + } else { + /* If P-256 isn't allowed by the current security policy, choose the first / most preferred curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + } + + POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &conn->client_hello.extensions)); + + /* After parsing extensions, select a curve and corresponding keyshare to use */ + if (conn->actual_protocol_version >= S2N_TLS13) { + POSIX_GUARD(s2n_extensions_server_key_share_select(conn)); + } + + /* for pre TLS 1.3 connections, protocol selection is not done in supported_versions extensions, so do it here */ + if (conn->actual_protocol_version < S2N_TLS13) { + conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); + } + + if (conn->client_protocol_version < security_policy->minimum_protocol_version) { + POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); + POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + if (s2n_connection_is_quic_enabled(conn)) { + POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + /* Find potential certificate matches before we choose the cipher. */ + POSIX_GUARD(s2n_conn_find_name_matching_certs(conn)); + + /* Save the previous cipher suite */ + uint8_t previous_cipher_suite_iana[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + POSIX_CHECKED_MEMCPY(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN); + + /* Now choose the ciphers we have certs for. */ + if (conn->client_hello.sslv2) { + POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, + client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN)); + } else { + POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, + client_hello->cipher_suites.size / 2)); + } + + /* Check if this is the second client hello in a hello retry handshake */ + if (s2n_is_hello_retry_handshake(conn) && conn->handshake.message_number > 0) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + POSIX_ENSURE(s2n_constant_time_equals(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, + S2N_TLS_CIPHER_SUITE_LEN), + S2N_ERR_BAD_MESSAGE); + } + + /* If we're using a PSK, we don't need to choose a signature algorithm or certificate, + * because no additional auth is required. */ + if (conn->psk_params.chosen_psk != NULL) { + return S2N_SUCCESS; + } + + /* And set the signature and hash algorithm used for key exchange signatures */ + POSIX_GUARD_RESULT(s2n_signature_algorithm_select(conn)); + + /* And finally, set the certs specified by the final auth + sig_alg combo. */ + POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &conn->handshake_params.our_chain_and_key)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_client_hello_process_cb_response(struct s2n_connection *conn, int rc) +{ + if (rc < 0) { + goto fail; + } + switch (conn->config->client_hello_cb_mode) { + case S2N_CLIENT_HELLO_CB_BLOCKING: { + if (rc) { + conn->server_name_used = 1; + } + return S2N_RESULT_OK; + } + case S2N_CLIENT_HELLO_CB_NONBLOCKING: { + if (conn->client_hello.callback_async_done) { + return S2N_RESULT_OK; + } + conn->client_hello.callback_async_blocked = 1; + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + } +fail: + /* rc < 0 */ + RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); + RESULT_BAIL(S2N_ERR_CANCELLED); +} + +int s2n_client_hello_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE(!conn->client_hello.callback_async_blocked, S2N_ERR_ASYNC_BLOCKED); + + /* Only parse the ClientHello once */ + if (!conn->client_hello.parsed) { + POSIX_GUARD(s2n_parse_client_hello(conn)); + /* Mark the collected client hello as available when parsing is done and before the client hello callback */ + conn->client_hello.parsed = true; + } + + /* Only invoke the ClientHello callback once. + * This means that we do NOT invoke the callback again on the second ClientHello + * in a TLS1.3 retry handshake. We explicitly check for a retry because the + * callback state may have been cleared while parsing the second ClientHello. + */ + if (!conn->client_hello.callback_invoked && !IS_HELLO_RETRY_HANDSHAKE(conn)) { + /* Mark the client hello callback as invoked to avoid calling it again. */ + conn->client_hello.callback_invoked = true; + + /* Do NOT move this null check. A test exists to assert that a server connection can get + * as far as the client hello callback without using its config. To do this we need a + * specific error for a null config just before the client hello callback. The test's + * assertions are weakened if this check is moved. */ + POSIX_ENSURE(conn->config, S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); + + /* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */ + if (conn->config->client_hello_cb) { + int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx); + POSIX_GUARD_RESULT(s2n_client_hello_process_cb_response(conn, rc)); + } + } + + POSIX_GUARD(s2n_process_client_hello(conn)); + + return 0; +} + +S2N_RESULT s2n_cipher_suite_validate_available(struct s2n_connection *conn, struct s2n_cipher_suite *cipher) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(cipher); + RESULT_ENSURE_EQ(cipher->available, true); + RESULT_ENSURE_LTE(cipher->minimum_required_tls_version, conn->client_protocol_version); + if (s2n_connection_is_quic_enabled(conn)) { + RESULT_ENSURE_GTE(cipher->minimum_required_tls_version, S2N_TLS13); + } + return S2N_RESULT_OK; +} + +int s2n_client_hello_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + + const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; + POSIX_ENSURE_REF(cipher_preferences); + + if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { + conn->client_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); + conn->actual_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); + } + + struct s2n_stuffer *out = &conn->handshake.io; + uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + + uint8_t reported_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); + conn->client_hello.legacy_version = reported_protocol_version; + client_protocol_version[0] = reported_protocol_version / 10; + client_protocol_version[1] = reported_protocol_version % 10; + POSIX_GUARD(s2n_stuffer_write_bytes(out, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + struct s2n_blob client_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); + if (!s2n_is_hello_retry_handshake(conn)) { + /* Only generate the random data for our first client hello. + * If we retry, we'll reuse the value. */ + POSIX_GUARD_RESULT(s2n_get_public_random_data(&client_random)); + } + POSIX_GUARD(s2n_stuffer_write(out, &client_random)); + + POSIX_GUARD_RESULT(s2n_generate_client_session_id(conn)); + POSIX_GUARD(s2n_stuffer_write_uint8(out, conn->session_id_len)); + if (conn->session_id_len > 0) { + POSIX_GUARD(s2n_stuffer_write_bytes(out, conn->session_id, conn->session_id_len)); + } + + /* Reserve space for size of the list of available ciphers */ + struct s2n_stuffer_reservation available_cipher_suites_size; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &available_cipher_suites_size)); + + /* Now, write the IANA values of every available cipher suite in our list */ + struct s2n_cipher_suite *cipher = NULL; + bool tls12_is_possible = false; + for (size_t i = 0; i < security_policy->cipher_preferences->count; i++) { + cipher = cipher_preferences->suites[i]; + if (s2n_result_is_error(s2n_cipher_suite_validate_available(conn, cipher))) { + continue; + } + if (cipher->minimum_required_tls_version < S2N_TLS13) { + tls12_is_possible = true; + } + POSIX_GUARD(s2n_stuffer_write_bytes(out, cipher->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + } + + /** + * For initial handshakes: + *= https://www.rfc-editor.org/rfc/rfc5746#3.4 + *# o The client MUST include either an empty "renegotiation_info" + *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling + *# cipher suite value in the ClientHello. Including both is NOT + *# RECOMMENDED. + * For maximum backwards compatibility, we choose to use the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite + * rather than the "renegotiation_info" extension. + * + * For renegotiation handshakes: + *= https://www.rfc-editor.org/rfc/rfc5746#3.5 + *# The SCSV MUST NOT be included. + */ + if (tls12_is_possible && !s2n_handshake_is_renegotiation(conn)) { + uint8_t renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; + POSIX_GUARD(s2n_stuffer_write_bytes(out, renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN)); + } + + /* Write size of the list of available ciphers */ + uint32_t ciphers_size = 0; + POSIX_GUARD(s2n_stuffer_get_vector_size(&available_cipher_suites_size, &ciphers_size)); + POSIX_ENSURE(ciphers_size > 0, S2N_ERR_INVALID_CIPHER_PREFERENCES); + POSIX_GUARD(s2n_stuffer_write_reservation(&available_cipher_suites_size, ciphers_size)); + + /* Zero compression methods */ + POSIX_GUARD(s2n_stuffer_write_uint8(out, 1)); + POSIX_GUARD(s2n_stuffer_write_uint8(out, 0)); + + /* Write the extensions */ + POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, out)); + + /* Once the message is complete, finish calculating the PSK binders. + * + * The PSK binders require all the sizes in the ClientHello to be written correctly, + * including the extension size and extension list size, and therefore have + * to be calculated AFTER we finish writing the entire extension list. */ + POSIX_GUARD_RESULT(s2n_finish_psk_extension(conn)); + + /* If early data was not requested as part of the ClientHello, it never will be. */ + if (conn->early_data_state == S2N_UNKNOWN_EARLY_DATA_STATE) { + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_NOT_REQUESTED)); + } + + return S2N_SUCCESS; +} + +/* + * s2n-tls does NOT support SSLv2. However, it does support SSLv2 ClientHellos. + * Clients may send SSLv2 ClientHellos advertising higher protocol versions for + * backwards compatibility reasons. See https://tools.ietf.org/rfc/rfc2246 Appendix E. + * + * In this case, conn->client_hello.legacy_version and conn->client_protocol_version + * will be higher than SSLv2. + * + * See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html Section 2.5 + * for a description of the expected SSLv2 format. + * Alternatively, the TLS1.0 RFC includes a more modern description of the format: + * https://tools.ietf.org/rfc/rfc2246 Appendix E.1 + */ +int s2n_sslv2_client_hello_parse(struct s2n_connection *conn) +{ + struct s2n_client_hello *client_hello = &conn->client_hello; + conn->client_protocol_version = S2N_MIN(client_hello->legacy_version, S2N_TLS12); + + struct s2n_stuffer in_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&in_stuffer, &client_hello->raw_message)); + POSIX_GUARD(s2n_stuffer_skip_write(&in_stuffer, client_hello->raw_message.size)); + struct s2n_stuffer *in = &in_stuffer; + + /* We start 5 bytes into the record */ + uint16_t cipher_suites_length = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); + POSIX_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(cipher_suites_length % S2N_SSLv2_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); + + uint16_t session_id_length = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &session_id_length)); + + uint16_t challenge_length = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &challenge_length)); + + S2N_ERROR_IF(challenge_length > S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_BAD_MESSAGE); + + client_hello->cipher_suites.size = cipher_suites_length; + client_hello->cipher_suites.data = s2n_stuffer_raw_read(in, cipher_suites_length); + POSIX_ENSURE_REF(client_hello->cipher_suites.data); + + S2N_ERROR_IF(session_id_length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_blob_init(&client_hello->session_id, s2n_stuffer_raw_read(in, session_id_length), session_id_length)); + if (session_id_length > 0 && session_id_length <= S2N_TLS_SESSION_ID_MAX_LEN) { + POSIX_CHECKED_MEMCPY(conn->session_id, client_hello->session_id.data, session_id_length); + conn->session_id_len = (uint8_t) session_id_length; + } + + struct s2n_blob b = { 0 }; + POSIX_GUARD(s2n_blob_init(&b, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); + + b.data += S2N_TLS_RANDOM_DATA_LEN - challenge_length; + b.size -= S2N_TLS_RANDOM_DATA_LEN - challenge_length; + + POSIX_GUARD(s2n_stuffer_read(in, &b)); + + return 0; +} + +int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type, + s2n_parsed_extensions_list *parsed_extension_list, s2n_parsed_extension **parsed_extension) +{ + POSIX_ENSURE_REF(parsed_extension_list); + POSIX_ENSURE_REF(parsed_extension); + + s2n_extension_type_id extension_type_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type, &extension_type_id)); + + s2n_parsed_extension *found_parsed_extension = &parsed_extension_list->parsed_extensions[extension_type_id]; + POSIX_ENSURE(found_parsed_extension->extension.data, S2N_ERR_EXTENSION_NOT_RECEIVED); + POSIX_ENSURE(found_parsed_extension->extension_type == extension_type, S2N_ERR_INVALID_PARSED_EXTENSIONS); + + *parsed_extension = found_parsed_extension; + return S2N_SUCCESS; +} + +ssize_t s2n_client_hello_get_extension_length(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type) +{ + POSIX_ENSURE_REF(ch); + + s2n_parsed_extension *parsed_extension = NULL; + if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { + return 0; + } + + return parsed_extension->extension.size; +} + +ssize_t s2n_client_hello_get_extension_by_id(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + + s2n_parsed_extension *parsed_extension = NULL; + if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { + return 0; + } + + uint32_t len = min_size(&parsed_extension->extension, max_length); + POSIX_CHECKED_MEMCPY(out, parsed_extension->extension.data, len); + return len; +} + +int s2n_client_hello_get_session_id_length(struct s2n_client_hello *ch, uint32_t *out_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out_length); + *out_length = ch->session_id.size; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(out_length); + + uint32_t len = min_size(&ch->session_id, max_length); + POSIX_CHECKED_MEMCPY(out, ch->session_id.data, len); + *out_length = len; + + return S2N_SUCCESS; +} + +int s2n_client_hello_get_compression_methods_length(struct s2n_client_hello *ch, uint32_t *out_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out_length); + *out_length = ch->compression_methods.size; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_compression_methods(struct s2n_client_hello *ch, uint8_t *list, uint32_t list_length, uint32_t *out_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(list); + POSIX_ENSURE_REF(out_length); + + POSIX_ENSURE(list_length >= ch->compression_methods.size, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(list, ch->compression_methods.data, ch->compression_methods.size); + *out_length = ch->compression_methods.size; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_legacy_protocol_version(struct s2n_client_hello *ch, uint8_t *out) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + *out = ch->legacy_version; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_legacy_record_version(struct s2n_client_hello *ch, uint8_t *out) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE(ch->record_version_recorded, S2N_ERR_INVALID_ARGUMENT); + *out = ch->legacy_record_version; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, + struct s2n_blob *raw_extensions, struct s2n_blob *extension) +{ + RESULT_ENSURE_REF(raw_extensions); + RESULT_ENSURE_REF(extension); + + *extension = (struct s2n_blob){ 0 }; + + struct s2n_stuffer raw_extensions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&raw_extensions_stuffer, raw_extensions)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&raw_extensions_stuffer, raw_extensions->size)); + + while (s2n_stuffer_data_available(&raw_extensions_stuffer) > 0) { + uint16_t extension_type = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_type)); + + uint16_t extension_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_size)); + + uint8_t *extension_data = s2n_stuffer_raw_read(&raw_extensions_stuffer, extension_size); + RESULT_ENSURE_REF(extension_data); + + if (extension_iana == extension_type) { + RESULT_GUARD_POSIX(s2n_blob_init(extension, extension_data, extension_size)); + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extension_iana, bool *exists) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(exists); + + *exists = false; + + s2n_extension_type_id extension_type_id = s2n_unsupported_extension; + if (s2n_extension_supported_iana_value_to_id(extension_iana, &extension_type_id) == S2N_SUCCESS) { + s2n_parsed_extension *parsed_extension = NULL; + if (s2n_client_hello_get_parsed_extension(extension_iana, &ch->extensions, &parsed_extension) == S2N_SUCCESS) { + *exists = true; + } + return S2N_SUCCESS; + } + + struct s2n_blob extension = { 0 }; + POSIX_GUARD_RESULT(s2n_client_hello_get_raw_extension(extension_iana, &ch->extensions.raw, &extension)); + if (extension.data != NULL) { + *exists = true; + } + return S2N_SUCCESS; +} + +int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups, + uint16_t groups_count_max, uint16_t *groups_count_out) +{ + POSIX_ENSURE_REF(groups_count_out); + *groups_count_out = 0; + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(groups); + + s2n_parsed_extension *supported_groups_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS, &ch->extensions, &supported_groups_extension)); + POSIX_ENSURE_REF(supported_groups_extension); + + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &supported_groups_extension->extension)); + + uint16_t supported_groups_count = 0; + POSIX_GUARD_RESULT(s2n_supported_groups_parse_count(&extension_stuffer, &supported_groups_count)); + POSIX_ENSURE(supported_groups_count <= groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); + + for (size_t i = 0; i < supported_groups_count; i++) { + /* s2n_stuffer_read_uint16 is used to read each of the supported groups in network-order + * endianness. + */ + POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &groups[i])); + } + + *groups_count_out = supported_groups_count; + + return S2N_SUCCESS; +} + +int s2n_client_hello_get_server_name_length(struct s2n_client_hello *ch, uint16_t *length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(length); + *length = 0; + + s2n_parsed_extension *server_name_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); + POSIX_ENSURE_REF(server_name_extension); + + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); + + struct s2n_blob blob = { 0 }; + POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); + *length = blob.size; + + return S2N_SUCCESS; +} + +int s2n_client_hello_get_server_name(struct s2n_client_hello *ch, uint8_t *server_name, uint16_t length, uint16_t *out_length) +{ + POSIX_ENSURE_REF(out_length); + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(server_name); + *out_length = 0; + + s2n_parsed_extension *server_name_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); + POSIX_ENSURE_REF(server_name_extension); + + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); + + struct s2n_blob blob = { 0 }; + POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); + POSIX_ENSURE_LTE(blob.size, length); + POSIX_CHECKED_MEMCPY(server_name, blob.data, blob.size); + + *out_length = blob.size; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_client_key_exchange.c b/tls/s2n_client_key_exchange.c index d316523db2d..c40239a8658 100644 --- a/tls/s2n_client_key_exchange.c +++ b/tls/s2n_client_key_exchange.c @@ -1,347 +1,348 @@ -/* - * 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 "crypto/s2n_dhe.h" -#include "crypto/s2n_pkey.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_async_pkey.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_kex.h" -#include "tls/s2n_key_log.h" -#include "tls/s2n_resume.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -typedef S2N_RESULT s2n_kex_client_key_method(const struct s2n_kex *kex, struct s2n_connection *conn, struct s2n_blob *shared_key); -typedef void *s2n_stuffer_action(struct s2n_stuffer *stuffer, uint32_t data_len); - -static int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *shared_key); - -/* - *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 - *# client_version - *# The latest (newest) version supported by the client. This is - *# used to detect version rollback attacks. - * - * However, TLS1.2 rsa kex does not account for the existence of TLS1.3. - * Therefore "latest" actually means "latest up to TLS1.2". - */ -static S2N_RESULT s2n_client_key_exchange_get_rsa_client_version(struct s2n_connection *conn, - uint8_t client_version[S2N_TLS_PROTOCOL_VERSION_LEN]) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(client_version); - uint8_t client_version_for_rsa = S2N_MIN(conn->client_protocol_version, S2N_TLS12); - client_version[0] = client_version_for_rsa / 10; - client_version[1] = client_version_for_rsa % 10; - return S2N_RESULT_OK; -} - -static int s2n_hybrid_client_action(struct s2n_connection *conn, struct s2n_blob *combined_shared_key, - s2n_kex_client_key_method kex_method, uint32_t *cursor, s2n_stuffer_action stuffer_action) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(kex_method); - POSIX_ENSURE_REF(stuffer_action); - - struct s2n_stuffer *io = &conn->handshake.io; - const struct s2n_kex *hybrid_kex_0 = conn->secure->cipher_suite->key_exchange_alg->hybrid[0]; - const struct s2n_kex *hybrid_kex_1 = conn->secure->cipher_suite->key_exchange_alg->hybrid[1]; - - /* Keep a copy to the start of the entire hybrid client key exchange message for the hybrid PRF */ - struct s2n_blob *client_key_exchange_message = &conn->kex_params.client_key_exchange_message; - client_key_exchange_message->data = stuffer_action(io, 0); - POSIX_ENSURE_REF(client_key_exchange_message->data); - const uint32_t start_cursor = *cursor; - - DEFER_CLEANUP(struct s2n_blob shared_key_0 = { 0 }, s2n_free); - POSIX_GUARD_RESULT(kex_method(hybrid_kex_0, conn, &shared_key_0)); - - struct s2n_blob *shared_key_1 = &(conn->kex_params.kem_params.shared_secret); - POSIX_GUARD_RESULT(kex_method(hybrid_kex_1, conn, shared_key_1)); - - const uint32_t end_cursor = *cursor; - POSIX_ENSURE_GTE(end_cursor, start_cursor); - client_key_exchange_message->size = end_cursor - start_cursor; - - POSIX_GUARD(s2n_alloc(combined_shared_key, shared_key_0.size + shared_key_1->size)); - struct s2n_stuffer stuffer_combiner = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, combined_shared_key)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &shared_key_0)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, shared_key_1)); - - POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); - - return 0; -} - -static int s2n_calculate_keys(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - /* Turn the pre-master secret into a master secret */ - POSIX_GUARD_RESULT(s2n_kex_tls_prf(conn->secure->cipher_suite->key_exchange_alg, conn, shared_key)); - - /* Expand the keys */ - POSIX_GUARD(s2n_prf_key_expansion(conn)); - /* Save the master secret in the cache. - * Failing to cache the session should not affect the current handshake. - */ - if (s2n_allowed_to_cache_connection(conn)) { - s2n_result_ignore(s2n_store_to_cache(conn)); - } - /* log the secret, if needed */ - s2n_result_ignore(s2n_key_log_tls12_secret(conn)); - return 0; -} - -int s2n_rsa_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - /* Set shared_key before async guard to pass the proper shared_key to the caller upon async completion */ - POSIX_ENSURE_REF(shared_key); - shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; - shared_key->size = S2N_TLS_SECRET_LEN; - - S2N_ASYNC_PKEY_GUARD(conn); - - struct s2n_stuffer *in = &conn->handshake.io; - uint16_t length = 0; - - if (conn->actual_protocol_version == S2N_SSLv3) { - length = s2n_stuffer_data_available(in); - } else { - POSIX_GUARD(s2n_stuffer_read_uint16(in, &length)); - } - - S2N_ERROR_IF(length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); - - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); - - /* Decrypt the pre-master secret */ - struct s2n_blob encrypted = { 0 }; - POSIX_GUARD(s2n_blob_init(&encrypted, s2n_stuffer_raw_read(in, length), length)); - POSIX_ENSURE_REF(encrypted.data); - POSIX_ENSURE_GT(encrypted.size, 0); - - /* First: use a random pre-master secret */ - POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); - conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; - conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; - - S2N_ASYNC_PKEY_DECRYPT(conn, &encrypted, shared_key, s2n_rsa_client_key_recv_complete); -} - -int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) -{ - S2N_ERROR_IF(decrypted->size != S2N_TLS_SECRET_LEN, S2N_ERR_SIZE_MISMATCH); - - /* Avoid copying the same buffer for the case where async pkey is not used */ - if (conn->secrets.version.tls12.rsa_premaster_secret != decrypted->data) { - /* Copy (maybe) decrypted data into shared key */ - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, decrypted->data, S2N_TLS_SECRET_LEN); - } - - /* Get client hello protocol version for comparison with decrypted data */ - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); - - conn->handshake.rsa_failed = rsa_failed; - - /* Set rsa_failed to true, if it isn't already, if the protocol version isn't what we expect */ - conn->handshake.rsa_failed |= !s2n_constant_time_equals(protocol_version, - conn->secrets.version.tls12.rsa_premaster_secret, S2N_TLS_PROTOCOL_VERSION_LEN); - - /* Required to protect against Bleichenbacher attack. - * See https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 - * We choose the first option: always setting the version in the rsa_premaster_secret - * from our local view of the client_hello value. - */ - conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; - conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; - - return 0; -} - -int s2n_dhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *in = &conn->handshake.io; - - /* Get the shared key */ - POSIX_GUARD(s2n_dh_compute_shared_secret_as_server(&conn->kex_params.server_dh_params, in, shared_key)); - /* We don't need the server params any more */ - POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); - return 0; -} - -int s2n_ecdhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *in = &conn->handshake.io; - - /* Get the shared key */ - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_server(&conn->kex_params.server_ecc_evp_params, in, shared_key)); - /* We don't need the server params any more */ - POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); - return 0; -} - -int s2n_kem_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - /* s2n_kem_recv_ciphertext() writes the KEM shared secret directly to - * conn->kex_params.kem_params. However, the calling function - * likely expects *shared_key to point to the shared secret. We - * can't reassign *shared_key to point to kem_params.shared_secret, - * because that would require us to take struct s2n_blob **shared_key - * as the argument, but we can't (easily) change the function signature - * because it has to be consistent with what is defined in s2n_kex. - * - * So, we assert that the caller already has *shared_key pointing - * to kem_params.shared_secret. */ - POSIX_ENSURE_REF(shared_key); - S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); - conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed. */ - - POSIX_GUARD(s2n_kem_recv_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); - - return 0; -} - -int s2n_hybrid_client_key_recv(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) -{ - return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_recv, &conn->handshake.io.read_cursor, - &s2n_stuffer_raw_read); -} - -int s2n_client_key_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; - DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); - POSIX_GUARD_RESULT(s2n_kex_client_key_recv(key_exchange, conn, &shared_key)); - - POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); - return 0; -} - -int s2n_dhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *out = &conn->handshake.io; - POSIX_GUARD(s2n_dh_compute_shared_secret_as_client(&conn->kex_params.server_dh_params, out, shared_key)); - - /* We don't need the server params any more */ - POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); - return 0; -} - -int s2n_ecdhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *out = &conn->handshake.io; - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_client(&conn->kex_params.server_ecc_evp_params, out, shared_key)); - - /* We don't need the server params any more */ - POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); - return 0; -} - -int s2n_rsa_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); - - shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; - shared_key->size = S2N_TLS_SECRET_LEN; - - POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); - - /* Over-write the first two bytes with the client hello version, per RFC2246/RFC4346/RFC5246 7.4.7.1. - * The latest version supported by client (as seen from the the client hello version) are <= TLS1.2 - * for all clients, because TLS 1.3 clients freezes the TLS1.2 legacy version in client hello. - */ - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN); - - uint32_t encrypted_size = 0; - POSIX_GUARD_RESULT(s2n_pkey_size(&conn->handshake_params.server_public_key, &encrypted_size)); - S2N_ERROR_IF(encrypted_size > 0xffff, S2N_ERR_SIZE_MISMATCH); - - if (conn->actual_protocol_version > S2N_SSLv3) { - POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, encrypted_size)); - } - - struct s2n_blob encrypted = { 0 }; - encrypted.data = s2n_stuffer_raw_write(&conn->handshake.io, encrypted_size); - encrypted.size = encrypted_size; - POSIX_ENSURE_REF(encrypted.data); - - /* Encrypt the secret and send it on */ - POSIX_GUARD(s2n_pkey_encrypt(&conn->handshake_params.server_public_key, shared_key, &encrypted)); - - /* We don't need the key any more, so free it */ - POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); - return 0; -} - -int s2n_kem_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - /* s2n_kem_send_ciphertext() writes the KEM shared secret directly to - * conn->kex_params.kem_params. However, the calling function - * likely expects *shared_key to point to the shared secret. We - * can't reassign *shared_key to point to kem_params.shared_secret, - * because that would require us to take struct s2n_blob **shared_key - * as the argument, but we can't (easily) change the function signature - * because it has to be consistent with what is defined in s2n_kex. - * - * So, we assert that the caller already has *shared_key pointing - * to kem_params.shared_secret. */ - POSIX_ENSURE_REF(shared_key); - S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); - - conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed */ - - POSIX_GUARD(s2n_kem_send_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); - - return 0; -} - -int s2n_hybrid_client_key_send(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) -{ - return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_send, &conn->handshake.io.write_cursor, - s2n_stuffer_raw_write); -} - -int s2n_client_key_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; - DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); - - POSIX_GUARD_RESULT(s2n_kex_client_key_send(key_exchange, conn, &shared_key)); - - POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_dhe.h" +#include "crypto/s2n_pkey.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_key_log.h" +#include "tls/s2n_resume.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +typedef S2N_RESULT s2n_kex_client_key_method(const struct s2n_kex *kex, struct s2n_connection *conn, struct s2n_blob *shared_key); +typedef void *s2n_stuffer_action(struct s2n_stuffer *stuffer, uint32_t data_len); + +static int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *shared_key); + +/* + *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 + *# client_version + *# The latest (newest) version supported by the client. This is + *# used to detect version rollback attacks. + * + * However, TLS1.2 rsa kex does not account for the existence of TLS1.3. + * Therefore "latest" actually means "latest up to TLS1.2". + */ +static S2N_RESULT s2n_client_key_exchange_get_rsa_client_version(struct s2n_connection *conn, + uint8_t client_version[S2N_TLS_PROTOCOL_VERSION_LEN]) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(client_version); + uint8_t client_version_for_rsa = S2N_MIN(conn->client_protocol_version, S2N_TLS12); + client_version[0] = client_version_for_rsa / 10; + client_version[1] = client_version_for_rsa % 10; + return S2N_RESULT_OK; +} + +static int s2n_hybrid_client_action(struct s2n_connection *conn, struct s2n_blob *combined_shared_key, + s2n_kex_client_key_method kex_method, uint32_t *cursor, s2n_stuffer_action stuffer_action) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(kex_method); + POSIX_ENSURE_REF(stuffer_action); + + struct s2n_stuffer *io = &conn->handshake.io; + const struct s2n_kex *hybrid_kex_0 = conn->secure->cipher_suite->key_exchange_alg->hybrid[0]; + const struct s2n_kex *hybrid_kex_1 = conn->secure->cipher_suite->key_exchange_alg->hybrid[1]; + + /* Keep a copy to the start of the entire hybrid client key exchange message for the hybrid PRF */ + struct s2n_blob *client_key_exchange_message = &conn->kex_params.client_key_exchange_message; + client_key_exchange_message->data = stuffer_action(io, 0); + POSIX_ENSURE_REF(client_key_exchange_message->data); + const uint32_t start_cursor = *cursor; + + DEFER_CLEANUP(struct s2n_blob shared_key_0 = { 0 }, s2n_free); + POSIX_GUARD_RESULT(kex_method(hybrid_kex_0, conn, &shared_key_0)); + + struct s2n_blob *shared_key_1 = &(conn->kex_params.kem_params.shared_secret); + POSIX_GUARD_RESULT(kex_method(hybrid_kex_1, conn, shared_key_1)); + + const uint32_t end_cursor = *cursor; + POSIX_ENSURE_GTE(end_cursor, start_cursor); + client_key_exchange_message->size = end_cursor - start_cursor; + + POSIX_GUARD(s2n_alloc(combined_shared_key, shared_key_0.size + shared_key_1->size)); + struct s2n_stuffer stuffer_combiner = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, combined_shared_key)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &shared_key_0)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, shared_key_1)); + + POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); + + return 0; +} + +static int s2n_calculate_keys(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + /* Turn the pre-master secret into a master secret */ + POSIX_GUARD_RESULT(s2n_kex_tls_prf(conn->secure->cipher_suite->key_exchange_alg, conn, shared_key)); + + /* Expand the keys */ + POSIX_GUARD(s2n_prf_key_expansion(conn)); + /* Save the master secret in the cache. + * Failing to cache the session should not affect the current handshake. + */ + if (s2n_allowed_to_cache_connection(conn)) { + s2n_result_ignore(s2n_store_to_cache(conn)); + } + /* log the secret, if needed */ + s2n_result_ignore(s2n_key_log_tls12_secret(conn)); + return 0; +} + +int s2n_rsa_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + /* Set shared_key before async guard to pass the proper shared_key to the caller upon async completion */ + POSIX_ENSURE_REF(shared_key); + shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; + shared_key->size = S2N_TLS_SECRET_LEN; + + S2N_ASYNC_PKEY_GUARD(conn); + + struct s2n_stuffer *in = &conn->handshake.io; + uint16_t length = 0; + + if (conn->actual_protocol_version == S2N_SSLv3) { + length = s2n_stuffer_data_available(in); + } else { + POSIX_GUARD(s2n_stuffer_read_uint16(in, &length)); + } + + S2N_ERROR_IF(length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); + + /* Decrypt the pre-master secret */ + struct s2n_blob encrypted = { 0 }; + POSIX_GUARD(s2n_blob_init(&encrypted, s2n_stuffer_raw_read(in, length), length)); + POSIX_ENSURE_REF(encrypted.data); + POSIX_ENSURE_GT(encrypted.size, 0); + + /* First: use a random pre-master secret */ + POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); + conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; + conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; + + S2N_ASYNC_PKEY_DECRYPT(conn, &encrypted, shared_key, s2n_rsa_client_key_recv_complete); +} + +int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) +{ + S2N_ERROR_IF(decrypted->size != S2N_TLS_SECRET_LEN, S2N_ERR_SIZE_MISMATCH); + + /* Avoid copying the same buffer for the case where async pkey is not used */ + if (conn->secrets.version.tls12.rsa_premaster_secret != decrypted->data) { + /* Copy (maybe) decrypted data into shared key */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, decrypted->data, S2N_TLS_SECRET_LEN); + } + + /* Get client hello protocol version for comparison with decrypted data */ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); + + conn->handshake.rsa_failed = rsa_failed; + + /* Set rsa_failed to true, if it isn't already, if the protocol version isn't what we expect */ + conn->handshake.rsa_failed |= !s2n_constant_time_equals(protocol_version, + conn->secrets.version.tls12.rsa_premaster_secret, S2N_TLS_PROTOCOL_VERSION_LEN); + + /* Required to protect against Bleichenbacher attack. + * See https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 + * We choose the first option: always setting the version in the rsa_premaster_secret + * from our local view of the client_hello value. + */ + conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; + conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; + + return 0; +} + +int s2n_dhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *in = &conn->handshake.io; + + /* Get the shared key */ + POSIX_GUARD(s2n_dh_compute_shared_secret_as_server(&conn->kex_params.server_dh_params, in, shared_key)); + /* We don't need the server params any more */ + POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); + return 0; +} + +int s2n_ecdhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *in = &conn->handshake.io; + + /* Get the shared key */ + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_server(&conn->kex_params.server_ecc_evp_params, in, shared_key)); + /* We don't need the server params any more */ + POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + return 0; +} + +int s2n_kem_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + /* s2n_kem_recv_ciphertext() writes the KEM shared secret directly to + * conn->kex_params.kem_params. However, the calling function + * likely expects *shared_key to point to the shared secret. We + * can't reassign *shared_key to point to kem_params.shared_secret, + * because that would require us to take struct s2n_blob **shared_key + * as the argument, but we can't (easily) change the function signature + * because it has to be consistent with what is defined in s2n_kex. + * + * So, we assert that the caller already has *shared_key pointing + * to kem_params.shared_secret. */ + POSIX_ENSURE_REF(shared_key); + S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); + conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed. */ + + POSIX_GUARD(s2n_kem_recv_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); + + return 0; +} + +int s2n_hybrid_client_key_recv(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) +{ + return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_recv, &conn->handshake.io.read_cursor, + &s2n_stuffer_raw_read); +} + +int s2n_client_key_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; + DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); + POSIX_GUARD_RESULT(s2n_kex_client_key_recv(key_exchange, conn, &shared_key)); + + POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); + return 0; +} + +int s2n_dhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *out = &conn->handshake.io; + POSIX_GUARD(s2n_dh_compute_shared_secret_as_client(&conn->kex_params.server_dh_params, out, shared_key)); + + /* We don't need the server params any more */ + POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); + return 0; +} + +int s2n_ecdhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *out = &conn->handshake.io; + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_client(&conn->kex_params.server_ecc_evp_params, out, shared_key)); + + /* We don't need the server params any more */ + POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + return 0; +} + +int s2n_rsa_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); + + shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; + shared_key->size = S2N_TLS_SECRET_LEN; + + POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); + + /* Over-write the first two bytes with the client hello version, per RFC2246/RFC4346/RFC5246 7.4.7.1. + * The latest version supported by client (as seen from the the client hello version) are <= TLS1.2 + * for all clients, because TLS 1.3 clients freezes the TLS1.2 legacy version in client hello. + */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN); + + uint32_t encrypted_size = 0; + POSIX_GUARD_RESULT(s2n_pkey_size(&conn->handshake_params.server_public_key, &encrypted_size)); + S2N_ERROR_IF(encrypted_size > 0xffff, S2N_ERR_SIZE_MISMATCH); + + if (conn->actual_protocol_version > S2N_SSLv3) { + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, encrypted_size)); + } + + struct s2n_blob encrypted = { 0 }; + encrypted.data = s2n_stuffer_raw_write(&conn->handshake.io, encrypted_size); + encrypted.size = encrypted_size; + POSIX_ENSURE_REF(encrypted.data); + + /* Encrypt the secret and send it on */ + POSIX_GUARD(s2n_pkey_encrypt(&conn->handshake_params.server_public_key, shared_key, &encrypted)); + + /* We don't need the key any more, so free it */ + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); + return 0; +} + +int s2n_kem_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + /* s2n_kem_send_ciphertext() writes the KEM shared secret directly to + * conn->kex_params.kem_params. However, the calling function + * likely expects *shared_key to point to the shared secret. We + * can't reassign *shared_key to point to kem_params.shared_secret, + * because that would require us to take struct s2n_blob **shared_key + * as the argument, but we can't (easily) change the function signature + * because it has to be consistent with what is defined in s2n_kex. + * + * So, we assert that the caller already has *shared_key pointing + * to kem_params.shared_secret. */ + POSIX_ENSURE_REF(shared_key); + S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); + + conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed */ + + POSIX_GUARD(s2n_kem_send_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); + + return 0; +} + +int s2n_hybrid_client_key_send(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) +{ + return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_send, &conn->handshake.io.write_cursor, + s2n_stuffer_raw_write); +} + +int s2n_client_key_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; + DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); + + POSIX_GUARD_RESULT(s2n_kex_client_key_send(key_exchange, conn, &shared_key)); + + POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); + return 0; +} diff --git a/tls/s2n_config.c b/tls/s2n_config.c index f9768c44143..b69af707f95 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -1,1338 +1,1345 @@ -/* - * 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. - */ - -#ifndef _S2N_PRELUDE_INCLUDED - /* make sure s2n_prelude.h is includes as part of the compiler flags, if not then fail the build */ - #error "Expected s2n_prelude.h to be included as part of the compiler flags" -#endif - -#include -#include - -#include "api/unstable/custom_x509_extensions.h" -#include "api/unstable/npn.h" -#include "crypto/s2n_certificate.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hkdf.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_pq.h" -#include "error/s2n_errno.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_map.h" -#include "utils/s2n_safety.h" - -#if defined(CLOCK_MONOTONIC_RAW) - #define S2N_CLOCK_HW CLOCK_MONOTONIC_RAW -#else - #define S2N_CLOCK_HW CLOCK_MONOTONIC -#endif - -#define S2N_CLOCK_SYS CLOCK_REALTIME - -int s2n_default_monotonic_clock(void *unused_data, uint64_t *nanoseconds) -{ - struct timespec current_time = { 0 }; - - POSIX_GUARD(clock_gettime(S2N_CLOCK_HW, ¤t_time)); - - *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; - *nanoseconds += current_time.tv_nsec; - - return 0; -} - -static int wall_clock(void *data, uint64_t *nanoseconds) -{ - struct timespec current_time = { 0 }; - - POSIX_GUARD(clock_gettime(S2N_CLOCK_SYS, ¤t_time)); - - *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; - *nanoseconds += current_time.tv_nsec; - - return 0; -} - -static struct s2n_config s2n_default_config = { 0 }; -static struct s2n_config s2n_default_fips_config = { 0 }; -static struct s2n_config s2n_default_tls13_config = { 0 }; - -static int s2n_config_setup_default(struct s2n_config *config) -{ - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default")); - return S2N_SUCCESS; -} - -static int s2n_config_setup_tls13(struct s2n_config *config) -{ - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_tls13")); - return S2N_SUCCESS; -} - -static int s2n_config_setup_fips(struct s2n_config *config) -{ - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_fips")); - return S2N_SUCCESS; -} - -static int s2n_config_init(struct s2n_config *config) -{ - config->wall_clock = wall_clock; - config->monotonic_clock = s2n_default_monotonic_clock; - config->ct_type = S2N_CT_SUPPORT_NONE; - config->mfl_code = S2N_TLS_MAX_FRAG_LEN_EXT_NONE; - config->alert_behavior = S2N_ALERT_FAIL_ON_WARNINGS; - config->session_state_lifetime_in_nanos = S2N_STATE_LIFETIME_IN_NANOS; - config->encrypt_decrypt_key_lifetime_in_nanos = S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS; - config->decrypt_key_lifetime_in_nanos = S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS; - config->async_pkey_validation_mode = S2N_ASYNC_PKEY_VALIDATION_FAST; - config->check_ocsp = 1; - - config->client_hello_cb_mode = S2N_CLIENT_HELLO_CB_BLOCKING; - - POSIX_GUARD(s2n_config_setup_default(config)); - if (s2n_use_default_tls13_config()) { - POSIX_GUARD(s2n_config_setup_tls13(config)); - } else if (s2n_is_in_fips_mode()) { - POSIX_GUARD(s2n_config_setup_fips(config)); - } - - POSIX_GUARD_PTR(config->domain_name_to_cert_map = s2n_map_new_with_initial_capacity(1)); - POSIX_GUARD_RESULT(s2n_map_complete(config->domain_name_to_cert_map)); - - s2n_x509_trust_store_init_empty(&config->trust_store); - - return 0; -} - -static int s2n_config_cleanup(struct s2n_config *config) -{ - s2n_x509_trust_store_wipe(&config->trust_store); - config->check_ocsp = 0; - - if (config->custom_x509_extension_oids) { - sk_ASN1_OBJECT_pop_free(config->custom_x509_extension_oids, ASN1_OBJECT_free); - config->custom_x509_extension_oids = NULL; - } - - POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); - POSIX_GUARD(s2n_config_free_cert_chain_and_key(config)); - POSIX_GUARD(s2n_config_free_dhparams(config)); - POSIX_GUARD(s2n_free(&config->application_protocols)); - POSIX_GUARD(s2n_free(&config->cert_authorities)); - POSIX_GUARD_RESULT(s2n_map_free(config->domain_name_to_cert_map)); - - POSIX_CHECKED_MEMSET(config, 0, sizeof(struct s2n_config)); - - return 0; -} - -static int s2n_config_update_domain_name_to_cert_map(struct s2n_config *config, - struct s2n_blob *name, - struct s2n_cert_chain_and_key *cert_key_pair) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(name); - - struct s2n_map *domain_name_to_cert_map = config->domain_name_to_cert_map; - /* s2n_map does not allow zero-size key */ - if (name->size == 0) { - return 0; - } - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); - struct s2n_blob s2n_map_value = { 0 }; - bool key_found = false; - POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, name, &s2n_map_value, &key_found)); - if (!key_found) { - struct certs_by_type value = { { 0 } }; - value.certs[cert_type] = cert_key_pair; - s2n_map_value.data = (uint8_t *) &value; - s2n_map_value.size = sizeof(struct certs_by_type); - - POSIX_GUARD_RESULT(s2n_map_unlock(domain_name_to_cert_map)); - - int add_result = s2n_result_is_ok(s2n_map_add(domain_name_to_cert_map, name, &s2n_map_value)) ? S2N_SUCCESS : S2N_FAILURE; - - /* The map must always be re-completed (made immutable) after an unlock, - * even if the add failed. Otherwise s2n_map_lookup, called on every - * ClientHello for SNI matching, will fail its RESULT_ENSURE(map->immutable) - * check, silently disabling SNI-based certificate selection for the - * lifetime of this config. - */ - POSIX_GUARD_RESULT(s2n_map_complete(domain_name_to_cert_map)); - POSIX_GUARD(add_result); - } else { - struct certs_by_type *value = (void *) s2n_map_value.data; - if (value->certs[cert_type] == NULL) { - value->certs[cert_type] = cert_key_pair; - } else if (config->cert_tiebreak_cb) { - /* There's an existing certificate for this (domain_name, auth_method). - * Run the application's tiebreaking callback to decide which cert should be used. - * An application may have some context specific logic to resolve ties that are based - * on factors like trust, expiry, etc. - */ - struct s2n_cert_chain_and_key *winner = config->cert_tiebreak_cb( - value->certs[cert_type], - cert_key_pair, - name->data, - name->size); - if (winner) { - value->certs[cert_type] = winner; - } - } - } - - return 0; -} - -int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) -{ - uint32_t cn_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->cn_names, &cn_len)); - uint32_t san_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->san_names, &san_len)); - - if (san_len == 0) { - for (uint32_t i = 0; i < cn_len; i++) { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->cn_names, i, (void **) &cn_name)); - POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, cn_name, cert_key_pair)); - } - } else { - for (uint32_t i = 0; i < san_len; i++) { - struct s2n_blob *san_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->san_names, i, (void **) &san_name)); - POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, san_name, cert_key_pair)); - } - } - - return 0; -} - -struct s2n_config *s2n_fetch_default_config(void) -{ - if (s2n_use_default_tls13_config()) { - return &s2n_default_tls13_config; - } - if (s2n_is_in_fips_mode()) { - return &s2n_default_fips_config; - } - return &s2n_default_config; -} - -int s2n_config_set_unsafe_for_testing(struct s2n_config *config) -{ - POSIX_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); - config->check_ocsp = 0; - config->disable_x509_validation = 1; - - return S2N_SUCCESS; -} - -int s2n_config_defaults_init(void) -{ - /* Set up fips defaults */ - if (s2n_is_in_fips_mode()) { - POSIX_GUARD(s2n_config_init(&s2n_default_fips_config)); - POSIX_GUARD(s2n_config_setup_fips(&s2n_default_fips_config)); - POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_fips_config)); - } else { - /* Set up default */ - POSIX_GUARD(s2n_config_init(&s2n_default_config)); - POSIX_GUARD(s2n_config_setup_default(&s2n_default_config)); - POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_config)); - } - - /* TLS 1.3 default config is only used in tests so avoid initialization costs in applications */ - POSIX_GUARD(s2n_config_init(&s2n_default_tls13_config)); - POSIX_GUARD(s2n_config_setup_tls13(&s2n_default_tls13_config)); - - return S2N_SUCCESS; -} - -void s2n_wipe_static_configs(void) -{ - s2n_config_cleanup(&s2n_default_fips_config); - s2n_config_cleanup(&s2n_default_config); - s2n_config_cleanup(&s2n_default_tls13_config); -} - -int s2n_config_load_system_certs(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - struct s2n_x509_trust_store *store = &config->trust_store; - POSIX_ENSURE(!store->loaded_system_certs, S2N_ERR_X509_TRUST_STORE); - - if (!store->trust_store) { - store->trust_store = X509_STORE_new(); - POSIX_ENSURE_REF(store->trust_store); - } - - int err_code = X509_STORE_set_default_paths(store->trust_store); - if (!err_code) { - s2n_x509_trust_store_wipe(store); - POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); - } - store->loaded_system_certs = true; - - return S2N_SUCCESS; -} - -struct s2n_config *s2n_config_new_minimal(void) -{ - struct s2n_blob allocator = { 0 }; - struct s2n_config *new_config = NULL; - - PTR_GUARD_POSIX(s2n_alloc(&allocator, sizeof(struct s2n_config))); - PTR_GUARD_POSIX(s2n_blob_zero(&allocator)); - - new_config = (struct s2n_config *) (void *) allocator.data; - if (s2n_config_init(new_config) != S2N_SUCCESS) { - s2n_free(&allocator); - return NULL; - } - - return new_config; -} - -struct s2n_config *s2n_config_new(void) -{ - struct s2n_config *new_config = s2n_config_new_minimal(); - PTR_ENSURE_REF(new_config); - - /* For backwards compatibility, s2n_config_new loads system certs by default. */ - PTR_GUARD_POSIX(s2n_config_load_system_certs(new_config)); - - return new_config; -} - -int s2n_config_init_session_ticket_keys(struct s2n_config *config) -{ - if (config->ticket_keys == NULL) { - POSIX_ENSURE_REF(config->ticket_keys = s2n_array_new_with_capacity(sizeof(struct s2n_ticket_key), S2N_MAX_TICKET_KEYS)); - } - - return 0; -} - -int s2n_config_free_session_ticket_keys(struct s2n_config *config) -{ - if (config->ticket_keys != NULL) { - POSIX_GUARD_RESULT(s2n_array_free_p(&config->ticket_keys)); - } - - return 0; -} - -int s2n_config_free_cert_chain_and_key(struct s2n_config *config) -{ - /* We track certificate ownership on the config itself because a config - * CANNOT use a combination of owned and unowned chains. - * If it does, and the unowned chains are freed before the config is, - * then iterating over the chains to determine which are owned and need to be freed - * will mean also reading the invalid, freed memory of any unowned certificates. - * As of now, some tests free chains before the config, so that pattern may also - * appear in application code. - */ - if (config->cert_ownership != S2N_LIB_OWNED) { - return S2N_SUCCESS; - } - - /* Free the cert_chain_and_key since the application has no reference - * to it. This is necessary until s2n_config_add_cert_chain_and_key is deprecated. */ - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - s2n_cert_chain_and_key_free(config->default_certs_by_type.certs[i]); - config->default_certs_by_type.certs[i] = NULL; - } - - config->cert_ownership = S2N_NOT_OWNED; - return S2N_SUCCESS; -} - -int s2n_config_free_dhparams(struct s2n_config *config) -{ - if (config->dhparams) { - POSIX_GUARD(s2n_dh_params_free(config->dhparams)); - } - - POSIX_GUARD(s2n_free_object((uint8_t **) &config->dhparams, sizeof(struct s2n_dh_params))); - return 0; -} - -S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config) -{ - RESULT_ENSURE_REF(config); - RESULT_GUARD_POSIX(s2n_config_free(*config)); - *config = NULL; - return S2N_RESULT_OK; -} - -int s2n_config_free(struct s2n_config *config) -{ - s2n_config_cleanup(config); - - POSIX_GUARD(s2n_free_object((uint8_t **) &config, sizeof(struct s2n_config))); - return 0; -} - -int s2n_config_get_client_auth_type(struct s2n_config *config, s2n_cert_auth_type *client_auth_type) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(client_auth_type); - *client_auth_type = config->client_cert_auth_type; - return 0; -} - -int s2n_config_set_client_auth_type(struct s2n_config *config, s2n_cert_auth_type client_auth_type) -{ - POSIX_ENSURE_REF(config); - config->client_cert_auth_type_overridden = 1; - config->client_cert_auth_type = client_auth_type; - return 0; -} - -int s2n_config_set_ct_support_level(struct s2n_config *config, s2n_ct_support_level type) -{ - POSIX_ENSURE_REF(config); - config->ct_type = type; - - return 0; -} - -int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_behavior alert_behavior) -{ - POSIX_ENSURE_REF(config); - - switch (alert_behavior) { - case S2N_ALERT_FAIL_ON_WARNINGS: - case S2N_ALERT_IGNORE_WARNINGS: - config->alert_behavior = alert_behavior; - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); - } - - return 0; -} - -int s2n_config_set_verify_host_callback(struct s2n_config *config, s2n_verify_host_fn verify_host_fn, void *data) -{ - POSIX_ENSURE_REF(config); - config->verify_host_fn = verify_host_fn; - config->data_for_verify_host = data; - return 0; -} - -int s2n_config_set_check_stapled_ocsp_response(struct s2n_config *config, uint8_t check_ocsp) -{ - POSIX_ENSURE_REF(config); - S2N_ERROR_IF(check_ocsp && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); - config->check_ocsp = check_ocsp; - return 0; -} - -int s2n_config_disable_x509_time_verification(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - config->disable_x509_time_validation = true; - return S2N_SUCCESS; -} - -int s2n_config_disable_x509_intent_verification(struct s2n_config *config) -{ - POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); - config->disable_x509_intent_verification = true; - return S2N_SUCCESS; -} - -int s2n_config_disable_x509_verification(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - s2n_x509_trust_store_wipe(&config->trust_store); - config->disable_x509_validation = 1; - return 0; -} - -int s2n_config_set_max_cert_chain_depth(struct s2n_config *config, uint16_t max_depth) -{ - POSIX_ENSURE_REF(config); - S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); - - config->max_verify_cert_chain_depth = max_depth; - config->max_verify_cert_chain_depth_set = 1; - return 0; -} - -int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type) -{ - S2N_ERROR_IF(type == S2N_STATUS_REQUEST_OCSP && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); - - POSIX_ENSURE_REF(config); - config->ocsp_status_requested_by_user = (type == S2N_STATUS_REQUEST_OCSP); - - /* Reset the ocsp_status_requested_by_s2n flag if OCSP status requests were disabled. */ - if (type == S2N_STATUS_REQUEST_NONE) { - config->ocsp_status_requested_by_s2n = false; - } - - return 0; -} - -int s2n_config_wipe_trust_store(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - s2n_x509_trust_store_wipe(&config->trust_store); - - return S2N_SUCCESS; -} - -int s2n_config_add_pem_to_trust_store(struct s2n_config *config, const char *pem) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(pem); - - POSIX_GUARD(s2n_x509_trust_store_add_pem(&config->trust_store, pem)); - - return 0; -} - -int s2n_config_set_verification_ca_location(struct s2n_config *config, const char *ca_pem_filename, const char *ca_dir) -{ - POSIX_ENSURE_REF(config); - int err_code = s2n_x509_trust_store_from_ca_file(&config->trust_store, ca_pem_filename, ca_dir); - - if (!err_code) { - config->ocsp_status_requested_by_s2n = s2n_x509_ocsp_stapling_supported() ? S2N_STATUS_REQUEST_OCSP : S2N_STATUS_REQUEST_NONE; - } - - return err_code; -} - -static int s2n_config_add_cert_chain_and_key_impl(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) -{ - POSIX_ENSURE_REF(config->domain_name_to_cert_map); - POSIX_ENSURE_REF(cert_key_pair); - - POSIX_GUARD_RESULT(s2n_security_policy_validate_certificate_chain(config->security_policy, cert_key_pair)); - - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); - config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); - - POSIX_GUARD(s2n_config_build_domain_name_to_cert_map(config, cert_key_pair)); - - if (!config->default_certs_are_explicit) { - POSIX_ENSURE(cert_type >= 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); - POSIX_ENSURE(cert_type < S2N_CERT_TYPE_COUNT, S2N_ERR_CERT_TYPE_UNSUPPORTED); - /* Attempt to auto set default based on ordering. ie: first RSA cert is the default, first ECDSA cert is the - * default, etc. */ - if (config->default_certs_by_type.certs[cert_type] == NULL) { - config->default_certs_by_type.certs[cert_type] = cert_key_pair; - } else { - /* Because library-owned certificates are tracked and cleaned up via the - * default_certs_by_type mapping, library-owned chains MUST be set as the default - * to avoid a memory leak. If they're not the default, they're not freed. - */ - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, - S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - } - } - - if (s2n_pkey_check_key_exists(cert_key_pair->private_key) != S2N_SUCCESS) { - config->no_signing_key = true; - } - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_config_validate_loaded_certificates(const struct s2n_config *config, - const struct s2n_security_policy *security_policy) -{ - RESULT_ENSURE_REF(config); - RESULT_ENSURE_REF(security_policy); - - if (security_policy->certificate_key_preferences == NULL - && security_policy->certificate_signature_preferences == NULL) { - return S2N_RESULT_OK; - } - - /* Duplicates a check in s2n_security_policy_validate_certificate_chain. - * If a large number of certificates are configured, even iterating - * over the chains to call s2n_security_policy_validate_certificate_chain - * could be prohibitively expensive. - */ - if (!security_policy->certificate_preferences_apply_locally) { - return S2N_RESULT_OK; - } - - /* validate the default certs */ - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - struct s2n_cert_chain_and_key *cert = config->default_certs_by_type.certs[i]; - if (cert == NULL) { - continue; - } - RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); - } - - /* validate the certs in the domain map */ - if (config->domain_name_to_cert_map == NULL) { - return S2N_RESULT_OK; - } - - struct s2n_map_iterator iter = { 0 }; - RESULT_GUARD(s2n_map_iterator_init(&iter, config->domain_name_to_cert_map)); - - while (s2n_map_iterator_has_next(&iter)) { - struct s2n_blob value = { 0 }; - RESULT_GUARD(s2n_map_iterator_next(&iter, &value)); - - struct certs_by_type *domain_certs = (void *) value.data; - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - struct s2n_cert_chain_and_key *cert = domain_certs->certs[i]; - if (cert == NULL) { - continue; - } - RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); - } - } - return S2N_RESULT_OK; -} - -/* Deprecated. Superseded by s2n_config_add_cert_chain_and_key_to_store */ -int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); - config->cert_ownership = S2N_LIB_OWNED; - - ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); - return S2N_SUCCESS; -} - -/* Only used in the Rust bindings. Superseded by s2n_config_add_cert_chain_and_key_to_store */ -int s2n_config_add_cert_chain(struct s2n_config *config, - uint8_t *cert_chain_pem, uint32_t cert_chain_pem_size) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, - cert_chain_pem, cert_chain_pem_size)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); - config->cert_ownership = S2N_LIB_OWNED; - - ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); - return S2N_SUCCESS; -} - -int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - POSIX_ENSURE_REF(cert_key_pair); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, cert_key_pair)); - config->cert_ownership = S2N_APP_OWNED; - - return S2N_SUCCESS; -} - -int s2n_config_set_async_offload_callback(struct s2n_config *config, uint32_t allow_list, s2n_async_offload_cb fn, void *ctx) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(fn); - POSIX_ENSURE(allow_list != 0, S2N_ERR_INVALID_ARGUMENT); - - config->async_offload_allow_list = allow_list; - config->async_offload_cb = fn; - config->async_offload_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn) -{ - POSIX_ENSURE_REF(config); - - config->async_pkey_cb = fn; - - return S2N_SUCCESS; -} - -static int s2n_config_clear_default_certificates(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - /* Clearing library-owned chains would lead to a memory leak. - * See s2n_config_free_cert_chain_and_key. - */ - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - config->default_certs_by_type.certs[i] = NULL; - } - config->cert_ownership = S2N_NOT_OWNED; - return 0; -} - -int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, - struct s2n_cert_chain_and_key **cert_key_pairs, - uint32_t num_cert_key_pairs) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(cert_key_pairs); - S2N_ERROR_IF(num_cert_key_pairs < 1 || num_cert_key_pairs > S2N_CERT_TYPE_COUNT, - S2N_ERR_NUM_DEFAULT_CERTIFICATES); - - /* This method will set application-owned chains, so we must not already be using - * any library owned chains. See s2n_config_free_cert_chain_and_key. - */ - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - /* Validate certs being set before clearing auto-chosen defaults or previously set defaults */ - struct certs_by_type new_defaults = { { 0 } }; - for (size_t i = 0; i < num_cert_key_pairs; i++) { - POSIX_ENSURE_REF(cert_key_pairs[i]); - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); - S2N_ERROR_IF(new_defaults.certs[cert_type] != NULL, S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - new_defaults.certs[cert_type] = cert_key_pairs[i]; - } - - POSIX_GUARD(s2n_config_clear_default_certificates(config)); - for (size_t i = 0; i < num_cert_key_pairs; i++) { - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); - config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); - config->default_certs_by_type.certs[cert_type] = cert_key_pairs[i]; - } - - config->default_certs_are_explicit = 1; - config->cert_ownership = S2N_APP_OWNED; - return 0; -} - -int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem) -{ - DEFER_CLEANUP(struct s2n_stuffer dhparams_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer dhparams_out_stuffer = { 0 }, s2n_stuffer_free); - struct s2n_blob dhparams_blob = { 0 }; - struct s2n_blob mem = { 0 }; - - /* Allocate the memory for the chain and key struct */ - POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_dh_params))); - config->dhparams = (struct s2n_dh_params *) (void *) mem.data; - - if (s2n_stuffer_alloc_ro_from_string(&dhparams_in_stuffer, dhparams_pem) != S2N_SUCCESS) { - s2n_free(&mem); - S2N_ERROR_PRESERVE_ERRNO(); - } - if (s2n_stuffer_growable_alloc(&dhparams_out_stuffer, strlen(dhparams_pem)) != S2N_SUCCESS) { - s2n_free(&mem); - S2N_ERROR_PRESERVE_ERRNO(); - } - - /* Convert pem to asn1 and asn1 to the private key */ - POSIX_GUARD(s2n_stuffer_dhparams_from_pem(&dhparams_in_stuffer, &dhparams_out_stuffer)); - - dhparams_blob.size = s2n_stuffer_data_available(&dhparams_out_stuffer); - dhparams_blob.data = s2n_stuffer_raw_read(&dhparams_out_stuffer, dhparams_blob.size); - POSIX_ENSURE_REF(dhparams_blob.data); - - POSIX_GUARD(s2n_pkcs3_to_dh_params(config->dhparams, &dhparams_blob)); - - return 0; -} - -int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) -{ - POSIX_ENSURE_REF(clock_fn); - - config->wall_clock = clock_fn; - config->sys_clock_ctx = ctx; - - return 0; -} - -int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) -{ - POSIX_ENSURE_REF(clock_fn); - - config->monotonic_clock = clock_fn; - config->monotonic_clock_ctx = ctx; - - return 0; -} - -int s2n_config_set_cache_store_callback(struct s2n_config *config, s2n_cache_store_callback cache_store_callback, void *data) -{ - POSIX_ENSURE_REF(cache_store_callback); - - config->cache_store = cache_store_callback; - config->cache_store_data = data; - - return 0; -} - -int s2n_config_set_cache_retrieve_callback(struct s2n_config *config, s2n_cache_retrieve_callback cache_retrieve_callback, void *data) -{ - POSIX_ENSURE_REF(cache_retrieve_callback); - - config->cache_retrieve = cache_retrieve_callback; - config->cache_retrieve_data = data; - - return 0; -} - -int s2n_config_set_cache_delete_callback(struct s2n_config *config, s2n_cache_delete_callback cache_delete_callback, void *data) -{ - POSIX_ENSURE_REF(cache_delete_callback); - - config->cache_delete = cache_delete_callback; - config->cache_delete_data = data; - - return 0; -} - -int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_type type, const uint8_t *data, uint32_t length) -{ - POSIX_ENSURE_REF(config); - - if (s2n_config_get_num_default_certs(config) == 0) { - POSIX_BAIL(S2N_ERR_UPDATING_EXTENSION); - } - struct s2n_cert_chain_and_key *config_chain_and_key = s2n_config_get_single_default_cert(config); - POSIX_ENSURE_REF(config_chain_and_key); - POSIX_ENSURE(config->cert_ownership == S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - switch (type) { - case S2N_EXTENSION_CERTIFICATE_TRANSPARENCY: - POSIX_GUARD(s2n_cert_chain_and_key_set_sct_list(config_chain_and_key, data, length)); - break; - case S2N_EXTENSION_OCSP_STAPLING: - POSIX_GUARD(s2n_cert_chain_and_key_set_ocsp_data(config_chain_and_key, data, length)); - break; - default: - POSIX_BAIL(S2N_ERR_UNRECOGNIZED_EXTENSION); - } - - return 0; -} - -int s2n_config_add_custom_x509_extension(struct s2n_config *config, uint8_t *extension_oid, uint32_t extension_oid_len) -{ - POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(extension_oid, S2N_ERR_INVALID_ARGUMENT); - - POSIX_ENSURE(s2n_libcrypto_supports_custom_oid(), S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO); - - if (config->custom_x509_extension_oids == NULL) { - config->custom_x509_extension_oids = sk_ASN1_OBJECT_new_null(); - } - POSIX_ENSURE_REF(config->custom_x509_extension_oids); - - DEFER_CLEANUP(struct s2n_blob oid_buffer = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&oid_buffer, extension_oid_len + 1)); - - POSIX_GUARD(s2n_blob_zero(&oid_buffer)); - POSIX_CHECKED_MEMCPY(oid_buffer.data, extension_oid, extension_oid_len); - oid_buffer.data[extension_oid_len] = '\0'; - - const char *oid_string = (const char *) oid_buffer.data; - POSIX_ENSURE_REF(oid_string); - - ASN1_OBJECT *critical_oid = OBJ_txt2obj(oid_string, 1); - POSIX_ENSURE_REF(critical_oid); - POSIX_ENSURE(sk_ASN1_OBJECT_push(config->custom_x509_extension_oids, critical_oid) > 0, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - return S2N_SUCCESS; -} - -int s2n_config_set_cert_request_callback(struct s2n_config *config, s2n_cert_request_callback cert_request_cb, void *ctx) -{ - POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); - - config->cert_request_cb = cert_request_cb; - config->cert_request_cb_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_set_client_hello_cb(struct s2n_config *config, s2n_client_hello_fn client_hello_cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - - config->client_hello_cb = client_hello_cb; - config->client_hello_cb_ctx = ctx; - return 0; -} - -int s2n_config_set_client_hello_cb_mode(struct s2n_config *config, s2n_client_hello_cb_mode cb_mode) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(cb_mode == S2N_CLIENT_HELLO_CB_BLOCKING || cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); - - config->client_hello_cb_mode = cb_mode; - return S2N_SUCCESS; -} - -int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code) -{ - POSIX_ENSURE_REF(config); - - S2N_ERROR_IF(mfl_code > S2N_TLS_MAX_FRAG_LEN_4096, S2N_ERR_INVALID_MAX_FRAG_LEN); - - config->mfl_code = mfl_code; - - return 0; -} - -int s2n_config_accept_max_fragment_length(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - config->accept_mfl = 1; - - return 0; -} - -int s2n_config_set_session_state_lifetime(struct s2n_config *config, - uint64_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(config); - - config->session_state_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); - return 0; -} - -int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled) -{ - POSIX_ENSURE_REF(config); - - if (config->use_tickets == enabled) { - return 0; - } - - config->use_tickets = enabled; - - if (config->initial_tickets_to_send == 0) { - /* Normally initial_tickets_to_send is set via s2n_config_set_initial_ticket_count. - * However, s2n_config_set_initial_ticket_count calls this method. - * So we set initial_tickets_to_send directly to avoid infinite recursion. */ - config->initial_tickets_to_send = 1; - } - - /* session ticket || session id is enabled */ - if (enabled) { - POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); - } else if (!config->use_session_cache) { - POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); - } - - return 0; -} - -int s2n_config_set_session_cache_onoff(struct s2n_config *config, uint8_t enabled) -{ - POSIX_ENSURE_REF(config); - if (enabled && config->cache_store && config->cache_retrieve && config->cache_delete) { - POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); - config->use_session_cache = 1; - } else { - if (!config->use_tickets) { - POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); - } - config->use_session_cache = 0; - } - return 0; -} - -int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, - uint64_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(config); - - config->encrypt_decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); - return 0; -} - -int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, - uint64_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(config); - - config->decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); - return 0; -} - -int s2n_config_add_ticket_crypto_key(struct s2n_config *config, - const uint8_t *name, uint32_t name_len, - uint8_t *key, uint32_t key_len, - uint64_t intro_time_in_seconds_from_epoch) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(name); - POSIX_ENSURE_REF(key); - - /* both session ticket and session cache encryption/decryption can use the same key mechanism */ - if (!config->use_tickets && !config->use_session_cache) { - return 0; - } - - POSIX_GUARD(s2n_config_wipe_expired_ticket_crypto_keys(config, -1)); - - POSIX_ENSURE(key_len != 0, S2N_ERR_INVALID_TICKET_KEY_LENGTH); - - uint32_t ticket_keys_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - POSIX_ENSURE(ticket_keys_len < S2N_MAX_TICKET_KEYS, S2N_ERR_TICKET_KEY_LIMIT); - - POSIX_ENSURE(name_len != 0, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - POSIX_ENSURE(name_len <= S2N_TICKET_KEY_NAME_LEN, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Copy the name into a zero-padded array. */ - /* This ensures that all ticket names are equal in length, as the serialized name is fixed length */ - uint8_t name_data[S2N_TICKET_KEY_NAME_LEN] = { 0 }; - POSIX_CHECKED_MEMCPY(name_data, name, name_len); - - uint8_t output_pad[S2N_AES256_KEY_LEN + S2N_TICKET_AAD_IMPLICIT_LEN] = { 0 }; - struct s2n_blob out_key = { 0 }; - POSIX_GUARD(s2n_blob_init(&out_key, output_pad, s2n_array_len(output_pad))); - struct s2n_blob in_key = { 0 }; - POSIX_GUARD(s2n_blob_init(&in_key, key, key_len)); - struct s2n_blob salt = { 0 }; - POSIX_GUARD(s2n_blob_init(&salt, NULL, 0)); - struct s2n_blob info = { 0 }; - POSIX_GUARD(s2n_blob_init(&info, NULL, 0)); - - struct s2n_ticket_key *session_ticket_key = { 0 }; - DEFER_CLEANUP(struct s2n_blob allocator = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&allocator, sizeof(struct s2n_ticket_key))); - session_ticket_key = (struct s2n_ticket_key *) (void *) allocator.data; - - DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free); - - POSIX_GUARD(s2n_hmac_new(&hmac)); - POSIX_GUARD(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &in_key, &info, &out_key)); - - POSIX_CHECKED_MEMCPY(session_ticket_key->key_name, name_data, s2n_array_len(name_data)); - POSIX_CHECKED_MEMCPY(session_ticket_key->aes_key, out_key.data, S2N_AES256_KEY_LEN); - out_key.data = output_pad + S2N_AES256_KEY_LEN; - POSIX_CHECKED_MEMCPY(session_ticket_key->implicit_aad, out_key.data, S2N_TICKET_AAD_IMPLICIT_LEN); - - if (intro_time_in_seconds_from_epoch == 0) { - uint64_t now = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - session_ticket_key->intro_timestamp = now; - } else { - session_ticket_key->intro_timestamp = (intro_time_in_seconds_from_epoch * ONE_SEC_IN_NANOS); - } - - POSIX_GUARD(s2n_config_store_ticket_key(config, session_ticket_key)); - - return 0; -} - -int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled) -{ - POSIX_ENSURE_REF(config); - - config->ticket_forward_secrecy = enabled; - - return S2N_SUCCESS; -} - -int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb) -{ - config->cert_tiebreak_cb = cert_tiebreak_cb; - return 0; -} - -struct s2n_cert_chain_and_key *s2n_config_get_single_default_cert(struct s2n_config *config) -{ - PTR_ENSURE_REF(config); - struct s2n_cert_chain_and_key *cert = NULL; - - for (int i = S2N_CERT_TYPE_COUNT - 1; i >= 0; i--) { - if (config->default_certs_by_type.certs[i] != NULL) { - cert = config->default_certs_by_type.certs[i]; - } - } - return cert; -} - -int s2n_config_get_num_default_certs(const struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - int num_certs = 0; - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - if (config->default_certs_by_type.certs[i] != NULL) { - num_certs++; - } - } - - return num_certs; -} - -int s2n_config_enable_cert_req_dss_legacy_compat(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - config->cert_req_dss_legacy_compat_enabled = 1; - return S2N_SUCCESS; -} - -int s2n_config_set_psk_selection_callback(struct s2n_config *config, s2n_psk_selection_callback cb, void *context) -{ - POSIX_ENSURE_REF(config); - config->psk_selection_cb = cb; - config->psk_selection_ctx = context; - return S2N_SUCCESS; -} - -int s2n_config_set_key_log_cb(struct s2n_config *config, s2n_key_log_fn callback, void *ctx) -{ - POSIX_ENSURE_MUT(config); - - config->key_log_cb = callback; - config->key_log_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_set_async_pkey_validation_mode(struct s2n_config *config, s2n_async_pkey_validation_mode mode) -{ - POSIX_ENSURE_REF(config); - - switch (mode) { - case S2N_ASYNC_PKEY_VALIDATION_FAST: - case S2N_ASYNC_PKEY_VALIDATION_STRICT: - config->async_pkey_validation_mode = mode; - return S2N_SUCCESS; - } - - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); -} - -int s2n_config_set_ctx(struct s2n_config *config, void *ctx) -{ - POSIX_ENSURE_REF(config); - - config->context = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_get_ctx(struct s2n_config *config, void **ctx) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(ctx); - - *ctx = config->context; - - return S2N_SUCCESS; -} - -int s2n_config_set_send_buffer_size(struct s2n_config *config, uint32_t size) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(size >= S2N_MIN_SEND_BUFFER_SIZE, S2N_ERR_INVALID_ARGUMENT); - config->send_buffer_size_override = size; - return S2N_SUCCESS; -} - -int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after_sign mode) -{ - POSIX_ENSURE_REF(config); - switch (mode) { - case S2N_VERIFY_AFTER_SIGN_DISABLED: - config->verify_after_sign = false; - break; - case S2N_VERIFY_AFTER_SIGN_ENABLED: - config->verify_after_sign = true; - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); - } - return S2N_SUCCESS; -} - -/* - *= https://www.rfc-editor.org/rfc/rfc5746#5 - *# TLS implementations SHOULD provide a mechanism to disable and enable - *# renegotiation. - */ -int s2n_config_set_renegotiate_request_cb(struct s2n_config *config, s2n_renegotiate_request_cb cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - - /* This feature cannot be used with serialization currently */ - POSIX_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE, S2N_ERR_INVALID_STATE); - - config->renegotiate_request_cb = cb; - config->renegotiate_request_ctx = ctx; - return S2N_SUCCESS; -} - -int s2n_config_set_npn(struct s2n_config *config, bool enable) -{ - POSIX_ENSURE_REF(config); - - config->npn_supported = enable; - - return S2N_SUCCESS; -} - -/* - * Wrapper for wall_clock callback. This wrapper will ensure right return of s2n_errno everytime wall_clock - * callback is called. - */ -S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output) -{ - RESULT_ENSURE_REF(config); - RESULT_ENSURE(config->wall_clock(config->sys_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); - return S2N_RESULT_OK; -} - -/* - * Get the indicated time from the monotonic clock. - * - * This callback ensures that the correct errno is set in the case of failure. - */ -S2N_RESULT s2n_config_monotonic_clock(struct s2n_config *config, uint64_t *output) -{ - RESULT_ENSURE_REF(config); - RESULT_ENSURE(config->monotonic_clock(config->monotonic_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); - return S2N_RESULT_OK; -} - -int s2n_config_set_crl_lookup_cb(struct s2n_config *config, s2n_crl_lookup_callback cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - config->crl_lookup_cb = cb; - config->crl_lookup_ctx = ctx; - return S2N_SUCCESS; -} - -int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled) -{ - POSIX_ENSURE_REF(config); - - config->recv_multi_record = enabled; - - return S2N_SUCCESS; -} - -int s2n_config_set_cert_validation_cb(struct s2n_config *config, s2n_cert_validation_callback cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - - config->cert_validation_cb = cb; - config->cert_validation_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, - uint16_t *groups_count_out) -{ - POSIX_ENSURE_REF(groups_count_out); - *groups_count_out = 0; - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(groups); - - const struct s2n_security_policy *security_policy = config->security_policy; - POSIX_ENSURE_REF(security_policy); - const struct s2n_kem_preferences *kem_preferences = security_policy->kem_preferences; - POSIX_ENSURE_REF(kem_preferences); - const struct s2n_ecc_preferences *ecc_preferences = security_policy->ecc_preferences; - POSIX_ENSURE_REF(ecc_preferences); - - uint16_t groups_count = 0; - for (uint8_t i = 0; i < kem_preferences->tls13_kem_group_count; i++) { - const struct s2n_kem_group *kem_group = kem_preferences->tls13_kem_groups[i]; - POSIX_ENSURE_REF(kem_group); - if (!s2n_kem_group_is_available(kem_group)) { - continue; - } - - POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); - groups[groups_count] = kem_group->iana_id; - groups_count += 1; - } - - for (uint8_t i = 0; i < ecc_preferences->count; i++) { - const struct s2n_ecc_named_curve *ecc_curve = ecc_preferences->ecc_curves[i]; - POSIX_ENSURE_REF(ecc_curve); - - POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); - groups[groups_count] = ecc_curve->iana_id; - groups_count += 1; - } - - *groups_count_out = groups_count; - - return S2N_SUCCESS; -} - -int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version) -{ - POSIX_ENSURE_REF(config); - - /* This feature cannot be used with renegotiation currently */ - POSIX_ENSURE(config->renegotiate_request_cb == NULL, S2N_ERR_INVALID_STATE); - - /* Currently there is only one format version supported */ - POSIX_ENSURE_EQ(version, S2N_SERIALIZED_CONN_V1); - config->serialized_connection_version = version; - - return S2N_SUCCESS; -} - -int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds) -{ - POSIX_ENSURE_REF(config); - - config->custom_blinding_set = 1; - config->max_blinding = seconds; - - return S2N_SUCCESS; -} - -int s2n_config_set_subscriber(struct s2n_config *config, void *subscriber) -{ - POSIX_ENSURE_REF(config); - config->subscriber = subscriber; - return S2N_SUCCESS; -} - -int s2n_config_set_handshake_event(struct s2n_config *config, s2n_event_on_handshake_cb callback) -{ - POSIX_ENSURE_REF(config); - config->on_handshake_event = callback; - return S2N_SUCCESS; -} +/* + * 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. + */ + +#ifndef _S2N_PRELUDE_INCLUDED + #if !defined(_MSC_VER) + /* make sure s2n_prelude.h is includes as part of the compiler flags, if not then fail the build */ + #error "Expected s2n_prelude.h to be included as part of the compiler flags" + #endif +#endif + +#if !defined(_MSC_VER) +#include +#endif +#include + +#include "api/unstable/custom_x509_extensions.h" +#include "api/unstable/npn.h" +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hkdf.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_pq.h" +#include "error/s2n_errno.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_map.h" +#include "utils/s2n_safety.h" + +#if defined(_MSC_VER) + #define S2N_CLOCK_HW 0 + #define S2N_CLOCK_SYS 0 +#elif defined(CLOCK_MONOTONIC_RAW) + #define S2N_CLOCK_HW CLOCK_MONOTONIC_RAW + #define S2N_CLOCK_SYS CLOCK_REALTIME +#else + #define S2N_CLOCK_HW CLOCK_MONOTONIC + #define S2N_CLOCK_SYS CLOCK_REALTIME +#endif + +int s2n_default_monotonic_clock(void *unused_data, uint64_t *nanoseconds) +{ + struct timespec current_time = { 0 }; + + POSIX_GUARD(clock_gettime(S2N_CLOCK_HW, ¤t_time)); + + *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; + *nanoseconds += current_time.tv_nsec; + + return 0; +} + +static int wall_clock(void *data, uint64_t *nanoseconds) +{ + struct timespec current_time = { 0 }; + + POSIX_GUARD(clock_gettime(S2N_CLOCK_SYS, ¤t_time)); + + *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; + *nanoseconds += current_time.tv_nsec; + + return 0; +} + +static struct s2n_config s2n_default_config = { 0 }; +static struct s2n_config s2n_default_fips_config = { 0 }; +static struct s2n_config s2n_default_tls13_config = { 0 }; + +static int s2n_config_setup_default(struct s2n_config *config) +{ + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default")); + return S2N_SUCCESS; +} + +static int s2n_config_setup_tls13(struct s2n_config *config) +{ + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_tls13")); + return S2N_SUCCESS; +} + +static int s2n_config_setup_fips(struct s2n_config *config) +{ + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_fips")); + return S2N_SUCCESS; +} + +static int s2n_config_init(struct s2n_config *config) +{ + config->wall_clock = wall_clock; + config->monotonic_clock = s2n_default_monotonic_clock; + config->ct_type = S2N_CT_SUPPORT_NONE; + config->mfl_code = S2N_TLS_MAX_FRAG_LEN_EXT_NONE; + config->alert_behavior = S2N_ALERT_FAIL_ON_WARNINGS; + config->session_state_lifetime_in_nanos = S2N_STATE_LIFETIME_IN_NANOS; + config->encrypt_decrypt_key_lifetime_in_nanos = S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS; + config->decrypt_key_lifetime_in_nanos = S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS; + config->async_pkey_validation_mode = S2N_ASYNC_PKEY_VALIDATION_FAST; + config->check_ocsp = 1; + + config->client_hello_cb_mode = S2N_CLIENT_HELLO_CB_BLOCKING; + + POSIX_GUARD(s2n_config_setup_default(config)); + if (s2n_use_default_tls13_config()) { + POSIX_GUARD(s2n_config_setup_tls13(config)); + } else if (s2n_is_in_fips_mode()) { + POSIX_GUARD(s2n_config_setup_fips(config)); + } + + POSIX_GUARD_PTR(config->domain_name_to_cert_map = s2n_map_new_with_initial_capacity(1)); + POSIX_GUARD_RESULT(s2n_map_complete(config->domain_name_to_cert_map)); + + s2n_x509_trust_store_init_empty(&config->trust_store); + + return 0; +} + +static int s2n_config_cleanup(struct s2n_config *config) +{ + s2n_x509_trust_store_wipe(&config->trust_store); + config->check_ocsp = 0; + + if (config->custom_x509_extension_oids) { + sk_ASN1_OBJECT_pop_free(config->custom_x509_extension_oids, ASN1_OBJECT_free); + config->custom_x509_extension_oids = NULL; + } + + POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); + POSIX_GUARD(s2n_config_free_cert_chain_and_key(config)); + POSIX_GUARD(s2n_config_free_dhparams(config)); + POSIX_GUARD(s2n_free(&config->application_protocols)); + POSIX_GUARD(s2n_free(&config->cert_authorities)); + POSIX_GUARD_RESULT(s2n_map_free(config->domain_name_to_cert_map)); + + POSIX_CHECKED_MEMSET(config, 0, sizeof(struct s2n_config)); + + return 0; +} + +static int s2n_config_update_domain_name_to_cert_map(struct s2n_config *config, + struct s2n_blob *name, + struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(name); + + struct s2n_map *domain_name_to_cert_map = config->domain_name_to_cert_map; + /* s2n_map does not allow zero-size key */ + if (name->size == 0) { + return 0; + } + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); + struct s2n_blob s2n_map_value = { 0 }; + bool key_found = false; + POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, name, &s2n_map_value, &key_found)); + if (!key_found) { + struct certs_by_type value = { { 0 } }; + value.certs[cert_type] = cert_key_pair; + s2n_map_value.data = (uint8_t *) &value; + s2n_map_value.size = sizeof(struct certs_by_type); + + POSIX_GUARD_RESULT(s2n_map_unlock(domain_name_to_cert_map)); + + int add_result = s2n_result_is_ok(s2n_map_add(domain_name_to_cert_map, name, &s2n_map_value)) ? S2N_SUCCESS : S2N_FAILURE; + + /* The map must always be re-completed (made immutable) after an unlock, + * even if the add failed. Otherwise s2n_map_lookup, called on every + * ClientHello for SNI matching, will fail its RESULT_ENSURE(map->immutable) + * check, silently disabling SNI-based certificate selection for the + * lifetime of this config. + */ + POSIX_GUARD_RESULT(s2n_map_complete(domain_name_to_cert_map)); + POSIX_GUARD(add_result); + } else { + struct certs_by_type *value = (void *) s2n_map_value.data; + if (value->certs[cert_type] == NULL) { + value->certs[cert_type] = cert_key_pair; + } else if (config->cert_tiebreak_cb) { + /* There's an existing certificate for this (domain_name, auth_method). + * Run the application's tiebreaking callback to decide which cert should be used. + * An application may have some context specific logic to resolve ties that are based + * on factors like trust, expiry, etc. + */ + struct s2n_cert_chain_and_key *winner = config->cert_tiebreak_cb( + value->certs[cert_type], + cert_key_pair, + name->data, + name->size); + if (winner) { + value->certs[cert_type] = winner; + } + } + } + + return 0; +} + +int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + uint32_t cn_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->cn_names, &cn_len)); + uint32_t san_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->san_names, &san_len)); + + if (san_len == 0) { + for (uint32_t i = 0; i < cn_len; i++) { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->cn_names, i, (void **) &cn_name)); + POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, cn_name, cert_key_pair)); + } + } else { + for (uint32_t i = 0; i < san_len; i++) { + struct s2n_blob *san_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->san_names, i, (void **) &san_name)); + POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, san_name, cert_key_pair)); + } + } + + return 0; +} + +struct s2n_config *s2n_fetch_default_config(void) +{ + if (s2n_use_default_tls13_config()) { + return &s2n_default_tls13_config; + } + if (s2n_is_in_fips_mode()) { + return &s2n_default_fips_config; + } + return &s2n_default_config; +} + +int s2n_config_set_unsafe_for_testing(struct s2n_config *config) +{ + POSIX_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); + config->check_ocsp = 0; + config->disable_x509_validation = 1; + + return S2N_SUCCESS; +} + +int s2n_config_defaults_init(void) +{ + /* Set up fips defaults */ + if (s2n_is_in_fips_mode()) { + POSIX_GUARD(s2n_config_init(&s2n_default_fips_config)); + POSIX_GUARD(s2n_config_setup_fips(&s2n_default_fips_config)); + POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_fips_config)); + } else { + /* Set up default */ + POSIX_GUARD(s2n_config_init(&s2n_default_config)); + POSIX_GUARD(s2n_config_setup_default(&s2n_default_config)); + POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_config)); + } + + /* TLS 1.3 default config is only used in tests so avoid initialization costs in applications */ + POSIX_GUARD(s2n_config_init(&s2n_default_tls13_config)); + POSIX_GUARD(s2n_config_setup_tls13(&s2n_default_tls13_config)); + + return S2N_SUCCESS; +} + +void s2n_wipe_static_configs(void) +{ + s2n_config_cleanup(&s2n_default_fips_config); + s2n_config_cleanup(&s2n_default_config); + s2n_config_cleanup(&s2n_default_tls13_config); +} + +int s2n_config_load_system_certs(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + struct s2n_x509_trust_store *store = &config->trust_store; + POSIX_ENSURE(!store->loaded_system_certs, S2N_ERR_X509_TRUST_STORE); + + if (!store->trust_store) { + store->trust_store = X509_STORE_new(); + POSIX_ENSURE_REF(store->trust_store); + } + + int err_code = X509_STORE_set_default_paths(store->trust_store); + if (!err_code) { + s2n_x509_trust_store_wipe(store); + POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); + } + store->loaded_system_certs = true; + + return S2N_SUCCESS; +} + +struct s2n_config *s2n_config_new_minimal(void) +{ + struct s2n_blob allocator = { 0 }; + struct s2n_config *new_config = NULL; + + PTR_GUARD_POSIX(s2n_alloc(&allocator, sizeof(struct s2n_config))); + PTR_GUARD_POSIX(s2n_blob_zero(&allocator)); + + new_config = (struct s2n_config *) (void *) allocator.data; + if (s2n_config_init(new_config) != S2N_SUCCESS) { + s2n_free(&allocator); + return NULL; + } + + return new_config; +} + +struct s2n_config *s2n_config_new(void) +{ + struct s2n_config *new_config = s2n_config_new_minimal(); + PTR_ENSURE_REF(new_config); + + /* For backwards compatibility, s2n_config_new loads system certs by default. */ + PTR_GUARD_POSIX(s2n_config_load_system_certs(new_config)); + + return new_config; +} + +int s2n_config_init_session_ticket_keys(struct s2n_config *config) +{ + if (config->ticket_keys == NULL) { + POSIX_ENSURE_REF(config->ticket_keys = s2n_array_new_with_capacity(sizeof(struct s2n_ticket_key), S2N_MAX_TICKET_KEYS)); + } + + return 0; +} + +int s2n_config_free_session_ticket_keys(struct s2n_config *config) +{ + if (config->ticket_keys != NULL) { + POSIX_GUARD_RESULT(s2n_array_free_p(&config->ticket_keys)); + } + + return 0; +} + +int s2n_config_free_cert_chain_and_key(struct s2n_config *config) +{ + /* We track certificate ownership on the config itself because a config + * CANNOT use a combination of owned and unowned chains. + * If it does, and the unowned chains are freed before the config is, + * then iterating over the chains to determine which are owned and need to be freed + * will mean also reading the invalid, freed memory of any unowned certificates. + * As of now, some tests free chains before the config, so that pattern may also + * appear in application code. + */ + if (config->cert_ownership != S2N_LIB_OWNED) { + return S2N_SUCCESS; + } + + /* Free the cert_chain_and_key since the application has no reference + * to it. This is necessary until s2n_config_add_cert_chain_and_key is deprecated. */ + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + s2n_cert_chain_and_key_free(config->default_certs_by_type.certs[i]); + config->default_certs_by_type.certs[i] = NULL; + } + + config->cert_ownership = S2N_NOT_OWNED; + return S2N_SUCCESS; +} + +int s2n_config_free_dhparams(struct s2n_config *config) +{ + if (config->dhparams) { + POSIX_GUARD(s2n_dh_params_free(config->dhparams)); + } + + POSIX_GUARD(s2n_free_object((uint8_t **) &config->dhparams, sizeof(struct s2n_dh_params))); + return 0; +} + +S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config) +{ + RESULT_ENSURE_REF(config); + RESULT_GUARD_POSIX(s2n_config_free(*config)); + *config = NULL; + return S2N_RESULT_OK; +} + +int s2n_config_free(struct s2n_config *config) +{ + s2n_config_cleanup(config); + + POSIX_GUARD(s2n_free_object((uint8_t **) &config, sizeof(struct s2n_config))); + return 0; +} + +int s2n_config_get_client_auth_type(struct s2n_config *config, s2n_cert_auth_type *client_auth_type) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(client_auth_type); + *client_auth_type = config->client_cert_auth_type; + return 0; +} + +int s2n_config_set_client_auth_type(struct s2n_config *config, s2n_cert_auth_type client_auth_type) +{ + POSIX_ENSURE_REF(config); + config->client_cert_auth_type_overridden = 1; + config->client_cert_auth_type = client_auth_type; + return 0; +} + +int s2n_config_set_ct_support_level(struct s2n_config *config, s2n_ct_support_level type) +{ + POSIX_ENSURE_REF(config); + config->ct_type = type; + + return 0; +} + +int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_behavior alert_behavior) +{ + POSIX_ENSURE_REF(config); + + switch (alert_behavior) { + case S2N_ALERT_FAIL_ON_WARNINGS: + case S2N_ALERT_IGNORE_WARNINGS: + config->alert_behavior = alert_behavior; + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + + return 0; +} + +int s2n_config_set_verify_host_callback(struct s2n_config *config, s2n_verify_host_fn verify_host_fn, void *data) +{ + POSIX_ENSURE_REF(config); + config->verify_host_fn = verify_host_fn; + config->data_for_verify_host = data; + return 0; +} + +int s2n_config_set_check_stapled_ocsp_response(struct s2n_config *config, uint8_t check_ocsp) +{ + POSIX_ENSURE_REF(config); + S2N_ERROR_IF(check_ocsp && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); + config->check_ocsp = check_ocsp; + return 0; +} + +int s2n_config_disable_x509_time_verification(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + config->disable_x509_time_validation = true; + return S2N_SUCCESS; +} + +int s2n_config_disable_x509_intent_verification(struct s2n_config *config) +{ + POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); + config->disable_x509_intent_verification = true; + return S2N_SUCCESS; +} + +int s2n_config_disable_x509_verification(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + s2n_x509_trust_store_wipe(&config->trust_store); + config->disable_x509_validation = 1; + return 0; +} + +int s2n_config_set_max_cert_chain_depth(struct s2n_config *config, uint16_t max_depth) +{ + POSIX_ENSURE_REF(config); + S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); + + config->max_verify_cert_chain_depth = max_depth; + config->max_verify_cert_chain_depth_set = 1; + return 0; +} + +int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type) +{ + S2N_ERROR_IF(type == S2N_STATUS_REQUEST_OCSP && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); + + POSIX_ENSURE_REF(config); + config->ocsp_status_requested_by_user = (type == S2N_STATUS_REQUEST_OCSP); + + /* Reset the ocsp_status_requested_by_s2n flag if OCSP status requests were disabled. */ + if (type == S2N_STATUS_REQUEST_NONE) { + config->ocsp_status_requested_by_s2n = false; + } + + return 0; +} + +int s2n_config_wipe_trust_store(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + s2n_x509_trust_store_wipe(&config->trust_store); + + return S2N_SUCCESS; +} + +int s2n_config_add_pem_to_trust_store(struct s2n_config *config, const char *pem) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(pem); + + POSIX_GUARD(s2n_x509_trust_store_add_pem(&config->trust_store, pem)); + + return 0; +} + +int s2n_config_set_verification_ca_location(struct s2n_config *config, const char *ca_pem_filename, const char *ca_dir) +{ + POSIX_ENSURE_REF(config); + int err_code = s2n_x509_trust_store_from_ca_file(&config->trust_store, ca_pem_filename, ca_dir); + + if (!err_code) { + config->ocsp_status_requested_by_s2n = s2n_x509_ocsp_stapling_supported() ? S2N_STATUS_REQUEST_OCSP : S2N_STATUS_REQUEST_NONE; + } + + return err_code; +} + +static int s2n_config_add_cert_chain_and_key_impl(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config->domain_name_to_cert_map); + POSIX_ENSURE_REF(cert_key_pair); + + POSIX_GUARD_RESULT(s2n_security_policy_validate_certificate_chain(config->security_policy, cert_key_pair)); + + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); + config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); + + POSIX_GUARD(s2n_config_build_domain_name_to_cert_map(config, cert_key_pair)); + + if (!config->default_certs_are_explicit) { + POSIX_ENSURE(cert_type >= 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); + POSIX_ENSURE(cert_type < S2N_CERT_TYPE_COUNT, S2N_ERR_CERT_TYPE_UNSUPPORTED); + /* Attempt to auto set default based on ordering. ie: first RSA cert is the default, first ECDSA cert is the + * default, etc. */ + if (config->default_certs_by_type.certs[cert_type] == NULL) { + config->default_certs_by_type.certs[cert_type] = cert_key_pair; + } else { + /* Because library-owned certificates are tracked and cleaned up via the + * default_certs_by_type mapping, library-owned chains MUST be set as the default + * to avoid a memory leak. If they're not the default, they're not freed. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + } + } + + if (s2n_pkey_check_key_exists(cert_key_pair->private_key) != S2N_SUCCESS) { + config->no_signing_key = true; + } + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_config_validate_loaded_certificates(const struct s2n_config *config, + const struct s2n_security_policy *security_policy) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE_REF(security_policy); + + if (security_policy->certificate_key_preferences == NULL + && security_policy->certificate_signature_preferences == NULL) { + return S2N_RESULT_OK; + } + + /* Duplicates a check in s2n_security_policy_validate_certificate_chain. + * If a large number of certificates are configured, even iterating + * over the chains to call s2n_security_policy_validate_certificate_chain + * could be prohibitively expensive. + */ + if (!security_policy->certificate_preferences_apply_locally) { + return S2N_RESULT_OK; + } + + /* validate the default certs */ + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + struct s2n_cert_chain_and_key *cert = config->default_certs_by_type.certs[i]; + if (cert == NULL) { + continue; + } + RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); + } + + /* validate the certs in the domain map */ + if (config->domain_name_to_cert_map == NULL) { + return S2N_RESULT_OK; + } + + struct s2n_map_iterator iter = { 0 }; + RESULT_GUARD(s2n_map_iterator_init(&iter, config->domain_name_to_cert_map)); + + while (s2n_map_iterator_has_next(&iter)) { + struct s2n_blob value = { 0 }; + RESULT_GUARD(s2n_map_iterator_next(&iter, &value)); + + struct certs_by_type *domain_certs = (void *) value.data; + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + struct s2n_cert_chain_and_key *cert = domain_certs->certs[i]; + if (cert == NULL) { + continue; + } + RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); + } + } + return S2N_RESULT_OK; +} + +/* Deprecated. Superseded by s2n_config_add_cert_chain_and_key_to_store */ +int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); + config->cert_ownership = S2N_LIB_OWNED; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); + return S2N_SUCCESS; +} + +/* Only used in the Rust bindings. Superseded by s2n_config_add_cert_chain_and_key_to_store */ +int s2n_config_add_cert_chain(struct s2n_config *config, + uint8_t *cert_chain_pem, uint32_t cert_chain_pem_size) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, + cert_chain_pem, cert_chain_pem_size)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); + config->cert_ownership = S2N_LIB_OWNED; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); + return S2N_SUCCESS; +} + +int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + POSIX_ENSURE_REF(cert_key_pair); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, cert_key_pair)); + config->cert_ownership = S2N_APP_OWNED; + + return S2N_SUCCESS; +} + +int s2n_config_set_async_offload_callback(struct s2n_config *config, uint32_t allow_list, s2n_async_offload_cb fn, void *ctx) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(fn); + POSIX_ENSURE(allow_list != 0, S2N_ERR_INVALID_ARGUMENT); + + config->async_offload_allow_list = allow_list; + config->async_offload_cb = fn; + config->async_offload_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn) +{ + POSIX_ENSURE_REF(config); + + config->async_pkey_cb = fn; + + return S2N_SUCCESS; +} + +static int s2n_config_clear_default_certificates(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + /* Clearing library-owned chains would lead to a memory leak. + * See s2n_config_free_cert_chain_and_key. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + config->default_certs_by_type.certs[i] = NULL; + } + config->cert_ownership = S2N_NOT_OWNED; + return 0; +} + +int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, + struct s2n_cert_chain_and_key **cert_key_pairs, + uint32_t num_cert_key_pairs) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(cert_key_pairs); + S2N_ERROR_IF(num_cert_key_pairs < 1 || num_cert_key_pairs > S2N_CERT_TYPE_COUNT, + S2N_ERR_NUM_DEFAULT_CERTIFICATES); + + /* This method will set application-owned chains, so we must not already be using + * any library owned chains. See s2n_config_free_cert_chain_and_key. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + /* Validate certs being set before clearing auto-chosen defaults or previously set defaults */ + struct certs_by_type new_defaults = { { 0 } }; + for (size_t i = 0; i < num_cert_key_pairs; i++) { + POSIX_ENSURE_REF(cert_key_pairs[i]); + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); + S2N_ERROR_IF(new_defaults.certs[cert_type] != NULL, S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + new_defaults.certs[cert_type] = cert_key_pairs[i]; + } + + POSIX_GUARD(s2n_config_clear_default_certificates(config)); + for (size_t i = 0; i < num_cert_key_pairs; i++) { + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); + config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); + config->default_certs_by_type.certs[cert_type] = cert_key_pairs[i]; + } + + config->default_certs_are_explicit = 1; + config->cert_ownership = S2N_APP_OWNED; + return 0; +} + +int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem) +{ + DEFER_CLEANUP(struct s2n_stuffer dhparams_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer dhparams_out_stuffer = { 0 }, s2n_stuffer_free); + struct s2n_blob dhparams_blob = { 0 }; + struct s2n_blob mem = { 0 }; + + /* Allocate the memory for the chain and key struct */ + POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_dh_params))); + config->dhparams = (struct s2n_dh_params *) (void *) mem.data; + + if (s2n_stuffer_alloc_ro_from_string(&dhparams_in_stuffer, dhparams_pem) != S2N_SUCCESS) { + s2n_free(&mem); + S2N_ERROR_PRESERVE_ERRNO(); + } + if (s2n_stuffer_growable_alloc(&dhparams_out_stuffer, strlen(dhparams_pem)) != S2N_SUCCESS) { + s2n_free(&mem); + S2N_ERROR_PRESERVE_ERRNO(); + } + + /* Convert pem to asn1 and asn1 to the private key */ + POSIX_GUARD(s2n_stuffer_dhparams_from_pem(&dhparams_in_stuffer, &dhparams_out_stuffer)); + + dhparams_blob.size = s2n_stuffer_data_available(&dhparams_out_stuffer); + dhparams_blob.data = s2n_stuffer_raw_read(&dhparams_out_stuffer, dhparams_blob.size); + POSIX_ENSURE_REF(dhparams_blob.data); + + POSIX_GUARD(s2n_pkcs3_to_dh_params(config->dhparams, &dhparams_blob)); + + return 0; +} + +int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) +{ + POSIX_ENSURE_REF(clock_fn); + + config->wall_clock = clock_fn; + config->sys_clock_ctx = ctx; + + return 0; +} + +int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) +{ + POSIX_ENSURE_REF(clock_fn); + + config->monotonic_clock = clock_fn; + config->monotonic_clock_ctx = ctx; + + return 0; +} + +int s2n_config_set_cache_store_callback(struct s2n_config *config, s2n_cache_store_callback cache_store_callback, void *data) +{ + POSIX_ENSURE_REF(cache_store_callback); + + config->cache_store = cache_store_callback; + config->cache_store_data = data; + + return 0; +} + +int s2n_config_set_cache_retrieve_callback(struct s2n_config *config, s2n_cache_retrieve_callback cache_retrieve_callback, void *data) +{ + POSIX_ENSURE_REF(cache_retrieve_callback); + + config->cache_retrieve = cache_retrieve_callback; + config->cache_retrieve_data = data; + + return 0; +} + +int s2n_config_set_cache_delete_callback(struct s2n_config *config, s2n_cache_delete_callback cache_delete_callback, void *data) +{ + POSIX_ENSURE_REF(cache_delete_callback); + + config->cache_delete = cache_delete_callback; + config->cache_delete_data = data; + + return 0; +} + +int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_type type, const uint8_t *data, uint32_t length) +{ + POSIX_ENSURE_REF(config); + + if (s2n_config_get_num_default_certs(config) == 0) { + POSIX_BAIL(S2N_ERR_UPDATING_EXTENSION); + } + struct s2n_cert_chain_and_key *config_chain_and_key = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(config_chain_and_key); + POSIX_ENSURE(config->cert_ownership == S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + switch (type) { + case S2N_EXTENSION_CERTIFICATE_TRANSPARENCY: + POSIX_GUARD(s2n_cert_chain_and_key_set_sct_list(config_chain_and_key, data, length)); + break; + case S2N_EXTENSION_OCSP_STAPLING: + POSIX_GUARD(s2n_cert_chain_and_key_set_ocsp_data(config_chain_and_key, data, length)); + break; + default: + POSIX_BAIL(S2N_ERR_UNRECOGNIZED_EXTENSION); + } + + return 0; +} + +int s2n_config_add_custom_x509_extension(struct s2n_config *config, uint8_t *extension_oid, uint32_t extension_oid_len) +{ + POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(extension_oid, S2N_ERR_INVALID_ARGUMENT); + + POSIX_ENSURE(s2n_libcrypto_supports_custom_oid(), S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO); + + if (config->custom_x509_extension_oids == NULL) { + config->custom_x509_extension_oids = sk_ASN1_OBJECT_new_null(); + } + POSIX_ENSURE_REF(config->custom_x509_extension_oids); + + DEFER_CLEANUP(struct s2n_blob oid_buffer = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&oid_buffer, extension_oid_len + 1)); + + POSIX_GUARD(s2n_blob_zero(&oid_buffer)); + POSIX_CHECKED_MEMCPY(oid_buffer.data, extension_oid, extension_oid_len); + oid_buffer.data[extension_oid_len] = '\0'; + + const char *oid_string = (const char *) oid_buffer.data; + POSIX_ENSURE_REF(oid_string); + + ASN1_OBJECT *critical_oid = OBJ_txt2obj(oid_string, 1); + POSIX_ENSURE_REF(critical_oid); + POSIX_ENSURE(sk_ASN1_OBJECT_push(config->custom_x509_extension_oids, critical_oid) > 0, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + return S2N_SUCCESS; +} + +int s2n_config_set_cert_request_callback(struct s2n_config *config, s2n_cert_request_callback cert_request_cb, void *ctx) +{ + POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); + + config->cert_request_cb = cert_request_cb; + config->cert_request_cb_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_set_client_hello_cb(struct s2n_config *config, s2n_client_hello_fn client_hello_cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + + config->client_hello_cb = client_hello_cb; + config->client_hello_cb_ctx = ctx; + return 0; +} + +int s2n_config_set_client_hello_cb_mode(struct s2n_config *config, s2n_client_hello_cb_mode cb_mode) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(cb_mode == S2N_CLIENT_HELLO_CB_BLOCKING || cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); + + config->client_hello_cb_mode = cb_mode; + return S2N_SUCCESS; +} + +int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code) +{ + POSIX_ENSURE_REF(config); + + S2N_ERROR_IF(mfl_code > S2N_TLS_MAX_FRAG_LEN_4096, S2N_ERR_INVALID_MAX_FRAG_LEN); + + config->mfl_code = mfl_code; + + return 0; +} + +int s2n_config_accept_max_fragment_length(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + config->accept_mfl = 1; + + return 0; +} + +int s2n_config_set_session_state_lifetime(struct s2n_config *config, + uint64_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(config); + + config->session_state_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); + return 0; +} + +int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled) +{ + POSIX_ENSURE_REF(config); + + if (config->use_tickets == enabled) { + return 0; + } + + config->use_tickets = enabled; + + if (config->initial_tickets_to_send == 0) { + /* Normally initial_tickets_to_send is set via s2n_config_set_initial_ticket_count. + * However, s2n_config_set_initial_ticket_count calls this method. + * So we set initial_tickets_to_send directly to avoid infinite recursion. */ + config->initial_tickets_to_send = 1; + } + + /* session ticket || session id is enabled */ + if (enabled) { + POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); + } else if (!config->use_session_cache) { + POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); + } + + return 0; +} + +int s2n_config_set_session_cache_onoff(struct s2n_config *config, uint8_t enabled) +{ + POSIX_ENSURE_REF(config); + if (enabled && config->cache_store && config->cache_retrieve && config->cache_delete) { + POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); + config->use_session_cache = 1; + } else { + if (!config->use_tickets) { + POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); + } + config->use_session_cache = 0; + } + return 0; +} + +int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, + uint64_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(config); + + config->encrypt_decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); + return 0; +} + +int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, + uint64_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(config); + + config->decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); + return 0; +} + +int s2n_config_add_ticket_crypto_key(struct s2n_config *config, + const uint8_t *name, uint32_t name_len, + uint8_t *key, uint32_t key_len, + uint64_t intro_time_in_seconds_from_epoch) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(name); + POSIX_ENSURE_REF(key); + + /* both session ticket and session cache encryption/decryption can use the same key mechanism */ + if (!config->use_tickets && !config->use_session_cache) { + return 0; + } + + POSIX_GUARD(s2n_config_wipe_expired_ticket_crypto_keys(config, -1)); + + POSIX_ENSURE(key_len != 0, S2N_ERR_INVALID_TICKET_KEY_LENGTH); + + uint32_t ticket_keys_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + POSIX_ENSURE(ticket_keys_len < S2N_MAX_TICKET_KEYS, S2N_ERR_TICKET_KEY_LIMIT); + + POSIX_ENSURE(name_len != 0, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + POSIX_ENSURE(name_len <= S2N_TICKET_KEY_NAME_LEN, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Copy the name into a zero-padded array. */ + /* This ensures that all ticket names are equal in length, as the serialized name is fixed length */ + uint8_t name_data[S2N_TICKET_KEY_NAME_LEN] = { 0 }; + POSIX_CHECKED_MEMCPY(name_data, name, name_len); + + uint8_t output_pad[S2N_AES256_KEY_LEN + S2N_TICKET_AAD_IMPLICIT_LEN] = { 0 }; + struct s2n_blob out_key = { 0 }; + POSIX_GUARD(s2n_blob_init(&out_key, output_pad, s2n_array_len(output_pad))); + struct s2n_blob in_key = { 0 }; + POSIX_GUARD(s2n_blob_init(&in_key, key, key_len)); + struct s2n_blob salt = { 0 }; + POSIX_GUARD(s2n_blob_init(&salt, NULL, 0)); + struct s2n_blob info = { 0 }; + POSIX_GUARD(s2n_blob_init(&info, NULL, 0)); + + struct s2n_ticket_key *session_ticket_key = { 0 }; + DEFER_CLEANUP(struct s2n_blob allocator = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&allocator, sizeof(struct s2n_ticket_key))); + session_ticket_key = (struct s2n_ticket_key *) (void *) allocator.data; + + DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free); + + POSIX_GUARD(s2n_hmac_new(&hmac)); + POSIX_GUARD(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &in_key, &info, &out_key)); + + POSIX_CHECKED_MEMCPY(session_ticket_key->key_name, name_data, s2n_array_len(name_data)); + POSIX_CHECKED_MEMCPY(session_ticket_key->aes_key, out_key.data, S2N_AES256_KEY_LEN); + out_key.data = output_pad + S2N_AES256_KEY_LEN; + POSIX_CHECKED_MEMCPY(session_ticket_key->implicit_aad, out_key.data, S2N_TICKET_AAD_IMPLICIT_LEN); + + if (intro_time_in_seconds_from_epoch == 0) { + uint64_t now = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + session_ticket_key->intro_timestamp = now; + } else { + session_ticket_key->intro_timestamp = (intro_time_in_seconds_from_epoch * ONE_SEC_IN_NANOS); + } + + POSIX_GUARD(s2n_config_store_ticket_key(config, session_ticket_key)); + + return 0; +} + +int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled) +{ + POSIX_ENSURE_REF(config); + + config->ticket_forward_secrecy = enabled; + + return S2N_SUCCESS; +} + +int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb) +{ + config->cert_tiebreak_cb = cert_tiebreak_cb; + return 0; +} + +struct s2n_cert_chain_and_key *s2n_config_get_single_default_cert(struct s2n_config *config) +{ + PTR_ENSURE_REF(config); + struct s2n_cert_chain_and_key *cert = NULL; + + for (int i = S2N_CERT_TYPE_COUNT - 1; i >= 0; i--) { + if (config->default_certs_by_type.certs[i] != NULL) { + cert = config->default_certs_by_type.certs[i]; + } + } + return cert; +} + +int s2n_config_get_num_default_certs(const struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + int num_certs = 0; + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + if (config->default_certs_by_type.certs[i] != NULL) { + num_certs++; + } + } + + return num_certs; +} + +int s2n_config_enable_cert_req_dss_legacy_compat(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + config->cert_req_dss_legacy_compat_enabled = 1; + return S2N_SUCCESS; +} + +int s2n_config_set_psk_selection_callback(struct s2n_config *config, s2n_psk_selection_callback cb, void *context) +{ + POSIX_ENSURE_REF(config); + config->psk_selection_cb = cb; + config->psk_selection_ctx = context; + return S2N_SUCCESS; +} + +int s2n_config_set_key_log_cb(struct s2n_config *config, s2n_key_log_fn callback, void *ctx) +{ + POSIX_ENSURE_MUT(config); + + config->key_log_cb = callback; + config->key_log_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_set_async_pkey_validation_mode(struct s2n_config *config, s2n_async_pkey_validation_mode mode) +{ + POSIX_ENSURE_REF(config); + + switch (mode) { + case S2N_ASYNC_PKEY_VALIDATION_FAST: + case S2N_ASYNC_PKEY_VALIDATION_STRICT: + config->async_pkey_validation_mode = mode; + return S2N_SUCCESS; + } + + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); +} + +int s2n_config_set_ctx(struct s2n_config *config, void *ctx) +{ + POSIX_ENSURE_REF(config); + + config->context = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_get_ctx(struct s2n_config *config, void **ctx) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(ctx); + + *ctx = config->context; + + return S2N_SUCCESS; +} + +int s2n_config_set_send_buffer_size(struct s2n_config *config, uint32_t size) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(size >= S2N_MIN_SEND_BUFFER_SIZE, S2N_ERR_INVALID_ARGUMENT); + config->send_buffer_size_override = size; + return S2N_SUCCESS; +} + +int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after_sign mode) +{ + POSIX_ENSURE_REF(config); + switch (mode) { + case S2N_VERIFY_AFTER_SIGN_DISABLED: + config->verify_after_sign = false; + break; + case S2N_VERIFY_AFTER_SIGN_ENABLED: + config->verify_after_sign = true; + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + return S2N_SUCCESS; +} + +/* + *= https://www.rfc-editor.org/rfc/rfc5746#5 + *# TLS implementations SHOULD provide a mechanism to disable and enable + *# renegotiation. + */ +int s2n_config_set_renegotiate_request_cb(struct s2n_config *config, s2n_renegotiate_request_cb cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + + /* This feature cannot be used with serialization currently */ + POSIX_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE, S2N_ERR_INVALID_STATE); + + config->renegotiate_request_cb = cb; + config->renegotiate_request_ctx = ctx; + return S2N_SUCCESS; +} + +int s2n_config_set_npn(struct s2n_config *config, bool enable) +{ + POSIX_ENSURE_REF(config); + + config->npn_supported = enable; + + return S2N_SUCCESS; +} + +/* + * Wrapper for wall_clock callback. This wrapper will ensure right return of s2n_errno everytime wall_clock + * callback is called. + */ +S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE(config->wall_clock(config->sys_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); + return S2N_RESULT_OK; +} + +/* + * Get the indicated time from the monotonic clock. + * + * This callback ensures that the correct errno is set in the case of failure. + */ +S2N_RESULT s2n_config_monotonic_clock(struct s2n_config *config, uint64_t *output) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE(config->monotonic_clock(config->monotonic_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); + return S2N_RESULT_OK; +} + +int s2n_config_set_crl_lookup_cb(struct s2n_config *config, s2n_crl_lookup_callback cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + config->crl_lookup_cb = cb; + config->crl_lookup_ctx = ctx; + return S2N_SUCCESS; +} + +int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled) +{ + POSIX_ENSURE_REF(config); + + config->recv_multi_record = enabled; + + return S2N_SUCCESS; +} + +int s2n_config_set_cert_validation_cb(struct s2n_config *config, s2n_cert_validation_callback cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + + config->cert_validation_cb = cb; + config->cert_validation_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, + uint16_t *groups_count_out) +{ + POSIX_ENSURE_REF(groups_count_out); + *groups_count_out = 0; + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(groups); + + const struct s2n_security_policy *security_policy = config->security_policy; + POSIX_ENSURE_REF(security_policy); + const struct s2n_kem_preferences *kem_preferences = security_policy->kem_preferences; + POSIX_ENSURE_REF(kem_preferences); + const struct s2n_ecc_preferences *ecc_preferences = security_policy->ecc_preferences; + POSIX_ENSURE_REF(ecc_preferences); + + uint16_t groups_count = 0; + for (uint8_t i = 0; i < kem_preferences->tls13_kem_group_count; i++) { + const struct s2n_kem_group *kem_group = kem_preferences->tls13_kem_groups[i]; + POSIX_ENSURE_REF(kem_group); + if (!s2n_kem_group_is_available(kem_group)) { + continue; + } + + POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); + groups[groups_count] = kem_group->iana_id; + groups_count += 1; + } + + for (uint8_t i = 0; i < ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *ecc_curve = ecc_preferences->ecc_curves[i]; + POSIX_ENSURE_REF(ecc_curve); + + POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); + groups[groups_count] = ecc_curve->iana_id; + groups_count += 1; + } + + *groups_count_out = groups_count; + + return S2N_SUCCESS; +} + +int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version) +{ + POSIX_ENSURE_REF(config); + + /* This feature cannot be used with renegotiation currently */ + POSIX_ENSURE(config->renegotiate_request_cb == NULL, S2N_ERR_INVALID_STATE); + + /* Currently there is only one format version supported */ + POSIX_ENSURE_EQ(version, S2N_SERIALIZED_CONN_V1); + config->serialized_connection_version = version; + + return S2N_SUCCESS; +} + +int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds) +{ + POSIX_ENSURE_REF(config); + + config->custom_blinding_set = 1; + config->max_blinding = seconds; + + return S2N_SUCCESS; +} + +int s2n_config_set_subscriber(struct s2n_config *config, void *subscriber) +{ + POSIX_ENSURE_REF(config); + config->subscriber = subscriber; + return S2N_SUCCESS; +} + +int s2n_config_set_handshake_event(struct s2n_config *config, s2n_event_on_handshake_cb callback) +{ + POSIX_ENSURE_REF(config); + config->on_handshake_event = callback; + return S2N_SUCCESS; +} diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index b69d2bbc492..01333216ca2 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -1,1890 +1,1895 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_connection.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -/* Required for s2n_connection_get_key_update_counts */ -#include "api/unstable/ktls.h" -#include "crypto/s2n_certificate.h" -#include "crypto/s2n_cipher.h" -#include "crypto/s2n_crypto.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_openssl_x509.h" -#include "error/s2n_errno.h" -#include "tls/extensions/s2n_client_server_name.h" -#include "tls/extensions/s2n_client_supported_versions.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_prf.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_atomic.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_compiler.h" -#include "utils/s2n_io.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" -#include "utils/s2n_timer.h" - -#define S2N_SET_KEY_SHARE_LIST_EMPTY(keyshares) (keyshares |= 1) -#define S2N_SET_KEY_SHARE_REQUEST(keyshares, i) (keyshares |= (1 << (i + 1))) - -static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, - const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type); - -/* Allocates and initializes memory for a new connection. - * - * Since customers can reuse a connection, ensure that values on the connection are - * initialized in `s2n_connection_wipe` where possible. */ -struct s2n_connection *s2n_connection_new(s2n_mode mode) -{ - struct s2n_blob blob = { 0 }; - PTR_GUARD_POSIX(s2n_alloc(&blob, sizeof(struct s2n_connection))); - PTR_GUARD_POSIX(s2n_blob_zero(&blob)); - - /* Cast 'through' void to acknowledge that we are changing alignment, - * which is ok, as blob.data is always aligned. - */ - struct s2n_connection *conn = (struct s2n_connection *) (void *) blob.data; - - PTR_GUARD_POSIX(s2n_connection_set_config(conn, s2n_fetch_default_config())); - - /* `mode` is initialized here since it's passed in as a parameter. */ - conn->mode = mode; - - /* Allocate the fixed-size stuffers */ - blob = (struct s2n_blob){ 0 }; - PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->alert_in_data, S2N_ALERT_LENGTH)); - PTR_GUARD_POSIX(s2n_stuffer_init(&conn->alert_in, &blob)); - - blob = (struct s2n_blob){ 0 }; - PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->ticket_ext_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); - PTR_GUARD_POSIX(s2n_stuffer_init(&conn->client_ticket_to_decrypt, &blob)); - - /* Allocate long term hash and HMAC memory */ - PTR_GUARD_RESULT(s2n_prf_new(conn)); - PTR_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); - - /* Initialize the growable stuffers. Zero length at first, but the resize - * in _wipe will fix that - */ - blob = (struct s2n_blob){ 0 }; - PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->header_in_data, S2N_TLS_RECORD_HEADER_LENGTH)); - PTR_GUARD_POSIX(s2n_stuffer_init(&conn->header_in, &blob)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->handshake.io, 0)); - PTR_GUARD_RESULT(s2n_timer_start(conn->config, &conn->write_timer)); - - /* NOTE: s2n_connection_wipe MUST be called last in this function. - * - * s2n_connection_wipe is used for initializing values but also used by customers to - * reset/reuse the connection. Calling it last ensures that s2n_connection_wipe is - * implemented correctly and safe. - */ - PTR_GUARD_POSIX(s2n_connection_wipe(conn)); - return conn; -} - -static int s2n_connection_zero(struct s2n_connection *conn, int mode, struct s2n_config *config) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(config); - - /* Zero the whole connection structure */ - POSIX_CHECKED_MEMSET(conn, 0, sizeof(struct s2n_connection)); - - conn->mode = mode; - conn->max_outgoing_fragment_length = S2N_DEFAULT_FRAGMENT_LENGTH; - conn->handshake.end_of_messages = APPLICATION_DATA; - s2n_connection_set_config(conn, config); - - return 0; -} - -S2N_RESULT s2n_connection_wipe_all_keyshares(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); - RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.client_ecc_evp_params)); - - RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.server_kem_group_params)); - RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.client_kem_group_params)); - - return S2N_RESULT_OK; -} - -static int s2n_connection_wipe_keys(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* Free any server key received (we may not have completed a - * handshake, so this may not have been free'd yet) */ - POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); - POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.server_public_key)); - POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.client_public_key)); - POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.client_public_key)); - s2n_x509_validator_wipe(&conn->x509_validator); - POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); - POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); - POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); - POSIX_GUARD(s2n_free(&conn->handshake_params.client_cert_chain)); - POSIX_GUARD(s2n_free(&conn->ct_response)); - - return 0; -} - -static int s2n_connection_free_managed_recv_io(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->managed_recv_io) { - POSIX_GUARD(s2n_free_object((uint8_t **) &conn->recv_io_context, sizeof(struct s2n_socket_read_io_context))); - conn->managed_recv_io = false; - conn->recv = NULL; - } - return S2N_SUCCESS; -} - -static int s2n_connection_free_managed_send_io(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->managed_send_io) { - POSIX_GUARD(s2n_free_object((uint8_t **) &conn->send_io_context, sizeof(struct s2n_socket_write_io_context))); - conn->managed_send_io = false; - conn->send = NULL; - } - return S2N_SUCCESS; -} - -static int s2n_connection_free_managed_io(struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); - POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); - return S2N_SUCCESS; -} - -static int s2n_connection_wipe_io(struct s2n_connection *conn) -{ - if (s2n_connection_is_managed_corked(conn) && conn->recv) { - POSIX_GUARD(s2n_socket_read_restore(conn)); - } - if (s2n_connection_is_managed_corked(conn) && conn->send) { - POSIX_GUARD(s2n_socket_write_restore(conn)); - } - - /* Remove all I/O-related members */ - POSIX_GUARD(s2n_connection_free_managed_io(conn)); - - return 0; -} - -static uint8_t s2n_default_verify_host(const char *host_name, size_t len, void *data) -{ - /* if present, match server_name of the connection using rules - * outlined in RFC6125 6.4. */ - - struct s2n_connection *conn = data; - - if (conn->server_name[0] == '\0') { - return 0; - } - - /* complete match */ - if (strlen(conn->server_name) == len && strncasecmp(conn->server_name, host_name, len) == 0) { - return 1; - } - - /* match 1 level of wildcard */ - if (len > 2 && host_name[0] == '*' && host_name[1] == '.') { - const char *suffix = strchr(conn->server_name, '.'); - - if (suffix == NULL) { - return 0; - } - - if (strlen(suffix) == len - 1 && strncasecmp(suffix, host_name + 1, len - 1) == 0) { - return 1; - } - } - - return 0; -} - -S2N_CLEANUP_RESULT s2n_connection_ptr_free(struct s2n_connection **conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_GUARD_POSIX(s2n_connection_free(*conn)); - *conn = NULL; - return S2N_RESULT_OK; -} - -int s2n_connection_free(struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_connection_wipe_keys(conn)); - POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); - - POSIX_GUARD_RESULT(s2n_prf_free(conn)); - POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); - - POSIX_GUARD(s2n_connection_free_managed_io(conn)); - - POSIX_GUARD(s2n_free(&conn->client_ticket)); - POSIX_GUARD(s2n_free(&conn->status_response)); - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->server_early_data_context)); - POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); - POSIX_GUARD(s2n_stuffer_free(&conn->buffer_in)); - POSIX_GUARD(s2n_stuffer_free(&conn->in)); - POSIX_GUARD(s2n_stuffer_free(&conn->out)); - POSIX_GUARD(s2n_stuffer_free(&conn->handshake.io)); - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - s2n_x509_validator_wipe(&conn->x509_validator); - POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); - POSIX_GUARD(s2n_client_hello_free_raw_message(&conn->client_hello)); - POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); - POSIX_GUARD(s2n_free(&conn->cookie)); - POSIX_GUARD(s2n_free(&conn->cert_authorities)); - POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); - POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->secure)); - POSIX_GUARD(s2n_free_object((uint8_t **) &conn, sizeof(struct s2n_connection))); - - return 0; -} - -int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *config) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(config); - - if (conn->config == config) { - return 0; - } - - /* s2n_config invariant: any s2n_config is always in a state that respects the - * config->security_policy certificate preferences. Therefore we only need to - * validate certificates here if the connection is using a security policy override. - */ - const struct s2n_security_policy *security_policy_override = conn->security_policy_override; - if (security_policy_override) { - POSIX_GUARD_RESULT(s2n_config_validate_loaded_certificates(config, security_policy_override)); - } - - /* We only support one client certificate */ - if (s2n_config_get_num_default_certs(config) > 1 && conn->mode == S2N_CLIENT) { - POSIX_BAIL(S2N_ERR_TOO_MANY_CERTIFICATES); - } - - s2n_x509_validator_wipe(&conn->x509_validator); - - 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)); - if (!conn->verify_host_fn_overridden) { - if (config->verify_host_fn != NULL) { - conn->verify_host_fn = config->verify_host_fn; - conn->data_for_verify_host = config->data_for_verify_host; - } else { - conn->verify_host_fn = s2n_default_verify_host; - conn->data_for_verify_host = conn; - } - } - - if (config->max_verify_cert_chain_depth_set) { - POSIX_GUARD(s2n_x509_validator_set_max_chain_depth(&conn->x509_validator, config->max_verify_cert_chain_depth)); - } - } - conn->tickets_to_send = config->initial_tickets_to_send; - - if (conn->psk_params.psk_list.len == 0 && !conn->psk_mode_overridden) { - POSIX_GUARD(s2n_connection_set_psk_mode(conn, config->psk_mode)); - conn->psk_mode_overridden = false; - } - - /* If at least one certificate does not have a private key configured, - * the config must provide an async pkey callback. - * The handshake could still fail if the callback doesn't offload the - * signature, but this at least catches configuration mistakes. - */ - if (config->no_signing_key) { - POSIX_ENSURE(config->async_pkey_cb, S2N_ERR_NO_PRIVATE_KEY); - } - - if (config->quic_enabled) { - /* If QUIC is ever enabled for a connection via the config, - * we should enforce that it can never be disabled by - * changing the config. - * - * Enabling QUIC indicates that the connection is being used by - * a QUIC implementation, which never changes. Disabling QUIC - * partially through a connection could also potentially be - * dangerous, as QUIC handles encryption. - */ - POSIX_GUARD(s2n_connection_enable_quic(conn)); - } - - if (config->send_buffer_size_override) { - conn->multirecord_send = true; - } - - /* Historically, calling s2n_config_set_verification_ca_location enabled OCSP stapling - * regardless of the value set by an application calling s2n_config_set_status_request_type. - * We maintain this behavior for backwards compatibility. - * - * However, the s2n_config_set_verification_ca_location behavior predates client authentication - * support for OCSP stapling, so could only affect whether clients requested OCSP stapling. We - * therefore only have to maintain the legacy behavior for clients, not servers. - * - * Note: The Rust bindings do not maintain the legacy behavior. - */ - conn->request_ocsp_status = config->ocsp_status_requested_by_user; - if (config->ocsp_status_requested_by_s2n && conn->mode == S2N_CLIENT) { - conn->request_ocsp_status = true; - } - - conn->config = config; - return S2N_SUCCESS; -} - -int s2n_connection_server_name_extension_used(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_INVALID_STATE); - POSIX_ENSURE(!(conn->handshake.client_hello_received), S2N_ERR_INVALID_STATE); - - conn->server_name_used = 1; - return S2N_SUCCESS; -} - -int s2n_connection_set_ctx(struct s2n_connection *conn, void *ctx) -{ - POSIX_ENSURE_REF(conn); - - conn->context = ctx; - return S2N_SUCCESS; -} - -void *s2n_connection_get_ctx(struct s2n_connection *conn) -{ - return conn->context; -} - -int s2n_connection_release_buffers(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_PRECONDITION(s2n_stuffer_validate(&conn->out)); - POSIX_PRECONDITION(s2n_stuffer_validate(&conn->in)); - - POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->out), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); - - POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - if (s2n_stuffer_is_consumed(&conn->buffer_in)) { - POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); - } - - POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->post_handshake.in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - - POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->out)); - POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->in)); - return S2N_SUCCESS; -} - -int s2n_connection_free_handshake(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* We are done with the handshake */ - POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); - POSIX_GUARD_RESULT(s2n_prf_free(conn)); - - /* All IO should use conn->secure after the handshake. - * However, if this method is called before the handshake completes, - * the connection may still be using conn->initial. - */ - if (conn->client != conn->initial && conn->server != conn->initial) { - POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); - } - - /* Wipe the buffers we are going to free */ - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); - - /* Truncate buffers to save memory, we are done with the handshake */ - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); - POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); - - /* We can free extension data we no longer need */ - POSIX_GUARD(s2n_free(&conn->client_ticket)); - POSIX_GUARD(s2n_free(&conn->status_response)); - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); - POSIX_GUARD(s2n_free(&conn->cookie)); - POSIX_GUARD(s2n_free(&conn->cert_authorities)); - - return 0; -} - -/* An idempotent operation which initializes values on the connection. - * - * Called in order to reuse a connection structure for a new connection. Should wipe - * any persistent memory, free any temporary memory, and set all fields back to their - * defaults. - */ -int s2n_connection_wipe(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* First make a copy of everything we'd like to save, which isn't very much. */ - int mode = conn->mode; - struct s2n_config *config = conn->config; - struct s2n_stuffer alert_in = { 0 }; - struct s2n_stuffer client_ticket_to_decrypt = { 0 }; - struct s2n_stuffer handshake_io = { 0 }; - struct s2n_stuffer header_in = { 0 }; - struct s2n_stuffer buffer_in = { 0 }; - struct s2n_stuffer out = { 0 }; - - /* Some required structures might have been freed to conserve memory between handshakes. - * Restore them. - */ - if (!conn->handshake.hashes) { - POSIX_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); - } - POSIX_GUARD_RESULT(s2n_handshake_hashes_wipe(conn->handshake.hashes)); - struct s2n_handshake_hashes *handshake_hashes = conn->handshake.hashes; - if (!conn->prf_space) { - POSIX_GUARD_RESULT(s2n_prf_new(conn)); - } - POSIX_GUARD_RESULT(s2n_prf_wipe(conn)); - struct s2n_prf_working_space *prf_workspace = conn->prf_space; - if (!conn->initial) { - POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->initial)); - } else { - POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->initial)); - } - struct s2n_crypto_parameters *initial = conn->initial; - if (!conn->secure) { - POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->secure)); - } else { - POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->secure)); - } - struct s2n_crypto_parameters *secure = conn->secure; - - /* Wipe all of the sensitive stuff */ - POSIX_GUARD(s2n_connection_wipe_keys(conn)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->client_ticket_to_decrypt)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->post_handshake.in)); - POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->header_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->buffer_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); - - /* Free stuffers we plan to just recreate */ - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - POSIX_GUARD(s2n_stuffer_free(&conn->in)); - - POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); - POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); - - /* Wipe the I/O-related info and restore the original socket if necessary */ - POSIX_GUARD(s2n_connection_wipe_io(conn)); - - POSIX_GUARD(s2n_free(&conn->client_ticket)); - POSIX_GUARD(s2n_free(&conn->status_response)); - POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->server_early_data_context)); - POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); - POSIX_GUARD(s2n_free(&conn->cookie)); - POSIX_GUARD(s2n_free(&conn->cert_authorities)); - - /* Allocate memory for handling handshakes */ - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, S2N_LARGE_RECORD_LENGTH)); - - /* Truncate the message buffers to save memory, we will dynamically resize it as needed */ - POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); - POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); - POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); - - /* Remove context associated with connection */ - conn->context = NULL; - conn->verify_host_fn_overridden = 0; - conn->verify_host_fn = NULL; - conn->data_for_verify_host = NULL; - - /* Clone the stuffers */ - /* ignore address warnings because dest is allocated on the stack */ -#ifdef S2N_DIAGNOSTICS_PUSH_SUPPORTED - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Waddress" -#endif - POSIX_CHECKED_MEMCPY(&alert_in, &conn->alert_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&client_ticket_to_decrypt, &conn->client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&buffer_in, &conn->buffer_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&out, &conn->out, sizeof(struct s2n_stuffer)); -#ifdef S2N_DIAGNOSTICS_POP_SUPPORTED - #pragma GCC diagnostic pop -#endif - - POSIX_GUARD(s2n_connection_zero(conn, mode, config)); - - POSIX_CHECKED_MEMCPY(&conn->alert_in, &alert_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->client_ticket_to_decrypt, &client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->buffer_in, &buffer_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->out, &out, sizeof(struct s2n_stuffer)); - - /* conn->in will eventually point to part of conn->buffer_in, but we initialize - * it as growable and allocated to support legacy tests. - */ - POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->in, 0)); - - conn->handshake.hashes = handshake_hashes; - conn->prf_space = prf_workspace; - conn->initial = initial; - conn->secure = secure; - conn->client = conn->initial; - conn->server = conn->initial; - conn->handshake_params.client_cert_sig_scheme = &s2n_null_sig_scheme; - conn->handshake_params.server_cert_sig_scheme = &s2n_null_sig_scheme; - - POSIX_GUARD_RESULT(s2n_psk_parameters_init(&conn->psk_params)); - conn->server_keying_material_lifetime = ONE_WEEK_IN_SEC; - - /* Require all handshakes hashes. This set can be reduced as the handshake progresses. */ - POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); - - if (conn->mode == S2N_SERVER) { - /* Start with the highest protocol version so that the highest common protocol version can be selected */ - /* during handshake. */ - conn->server_protocol_version = s2n_highest_protocol_version; - conn->client_protocol_version = s2n_unknown_protocol_version; - conn->actual_protocol_version = s2n_unknown_protocol_version; - } else { - /* For clients, also set actual_protocol_version. Record generation uses that value for the initial */ - /* ClientHello record version. Not all servers ignore the record version in ClientHello. */ - conn->server_protocol_version = s2n_unknown_protocol_version; - conn->client_protocol_version = s2n_highest_protocol_version; - conn->actual_protocol_version = s2n_highest_protocol_version; - } - - /* Initialize remaining values */ - conn->blinding = S2N_BUILT_IN_BLINDING; - conn->session_ticket_status = S2N_NO_TICKET; - - return 0; -} - -int s2n_connection_set_recv_ctx(struct s2n_connection *conn, void *ctx) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); - conn->recv_io_context = ctx; - return S2N_SUCCESS; -} - -int s2n_connection_set_send_ctx(struct s2n_connection *conn, void *ctx) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); - conn->send_io_context = ctx; - return S2N_SUCCESS; -} - -int s2n_connection_set_recv_cb(struct s2n_connection *conn, s2n_recv_fn recv) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); - conn->recv = recv; - return S2N_SUCCESS; -} - -int s2n_connection_set_send_cb(struct s2n_connection *conn, s2n_send_fn send) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); - conn->send = send; - return S2N_SUCCESS; -} - -int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t **cert_chain_out, uint32_t *cert_chain_len) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(cert_chain_out); - POSIX_ENSURE_REF(cert_chain_len); - POSIX_ENSURE_REF(conn->handshake_params.client_cert_chain.data); - - *cert_chain_out = conn->handshake_params.client_cert_chain.data; - *cert_chain_len = conn->handshake_params.client_cert_chain.size; - - return S2N_SUCCESS; -} - -int s2n_connection_get_cipher_preferences(struct s2n_connection *conn, const struct s2n_cipher_preferences **cipher_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(cipher_preferences); - - if (conn->security_policy_override != NULL) { - *cipher_preferences = conn->security_policy_override->cipher_preferences; - } else if (conn->config->security_policy != NULL) { - *cipher_preferences = conn->config->security_policy->cipher_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_CIPHER_PREFERENCES); - } - - POSIX_ENSURE_REF(*cipher_preferences); - return 0; -} - -int s2n_connection_get_certificate_match(struct s2n_connection *conn, s2n_cert_sni_match *match_status) -{ - POSIX_ENSURE(conn, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(match_status, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); - - /* Server must have gotten past certificate selection */ - POSIX_ENSURE(conn->handshake_params.our_chain_and_key, S2N_ERR_NO_CERT_FOUND); - - if (!s2n_server_received_server_name(conn)) { - *match_status = S2N_SNI_NONE; - } else if (conn->handshake_params.exact_sni_match_exists) { - *match_status = S2N_SNI_EXACT_MATCH; - } else if (conn->handshake_params.wc_sni_match_exists) { - *match_status = S2N_SNI_WILDCARD_MATCH; - } else { - *match_status = S2N_SNI_NO_MATCH; - } - - return S2N_SUCCESS; -} - -int s2n_connection_get_security_policy(struct s2n_connection *conn, const struct s2n_security_policy **security_policy) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(security_policy); - - if (conn->security_policy_override != NULL) { - *security_policy = conn->security_policy_override; - } else if (conn->config->security_policy != NULL) { - *security_policy = conn->config->security_policy; - } else { - POSIX_BAIL(S2N_ERR_INVALID_SECURITY_POLICY); - } - - POSIX_ENSURE_REF(*security_policy); - return 0; -} - -int s2n_connection_get_kem_preferences(struct s2n_connection *conn, const struct s2n_kem_preferences **kem_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(kem_preferences); - - if (conn->security_policy_override != NULL) { - *kem_preferences = conn->security_policy_override->kem_preferences; - } else if (conn->config->security_policy != NULL) { - *kem_preferences = conn->config->security_policy->kem_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_KEM_PREFERENCES); - } - - POSIX_ENSURE_REF(*kem_preferences); - return 0; -} - -int s2n_connection_get_signature_preferences(struct s2n_connection *conn, const struct s2n_signature_preferences **signature_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(signature_preferences); - - if (conn->security_policy_override != NULL) { - *signature_preferences = conn->security_policy_override->signature_preferences; - } else if (conn->config->security_policy != NULL) { - *signature_preferences = conn->config->security_policy->signature_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES); - } - - POSIX_ENSURE_REF(*signature_preferences); - return 0; -} - -int s2n_connection_get_ecc_preferences(struct s2n_connection *conn, const struct s2n_ecc_preferences **ecc_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(ecc_preferences); - - if (conn->security_policy_override != NULL) { - *ecc_preferences = conn->security_policy_override->ecc_preferences; - } else if (conn->config->security_policy != NULL) { - *ecc_preferences = conn->config->security_policy->ecc_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_ECC_PREFERENCES); - } - - POSIX_ENSURE_REF(*ecc_preferences); - return 0; -} - -int s2n_connection_get_protocol_preferences(struct s2n_connection *conn, struct s2n_blob **protocol_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(protocol_preferences); - - *protocol_preferences = NULL; - if (conn->application_protocols_overridden.size > 0) { - *protocol_preferences = &conn->application_protocols_overridden; - } else { - POSIX_ENSURE_REF(conn->config); - *protocol_preferences = &conn->config->application_protocols; - } - - POSIX_ENSURE_REF(*protocol_preferences); - return 0; -} - -static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, - const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(config); - RESULT_ENSURE_REF(client_cert_auth_type); - - if (conn->client_cert_auth_type_overridden) { - *client_cert_auth_type = conn->client_cert_auth_type; - } else if (config->client_cert_auth_type_overridden) { - *client_cert_auth_type = config->client_cert_auth_type; - } else if (conn->mode == S2N_CLIENT) { - /* Clients should default to "Optional" so that they handle any - * CertificateRequests sent by the server. - */ - *client_cert_auth_type = S2N_CERT_AUTH_OPTIONAL; - } else { - /* Servers should default to "None" so that they send no CertificateRequests. */ - *client_cert_auth_type = S2N_CERT_AUTH_NONE; - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_client_auth_type(struct s2n_connection *conn, - s2n_cert_auth_type *client_cert_auth_type) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_and_config_get_client_auth_type( - conn, conn->config, client_cert_auth_type)); - return S2N_SUCCESS; -} - -int s2n_connection_set_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type client_cert_auth_type) -{ - POSIX_ENSURE_REF(conn); - - conn->client_cert_auth_type_overridden = 1; - conn->client_cert_auth_type = client_cert_auth_type; - return 0; -} - -int s2n_connection_set_read_fd(struct s2n_connection *conn, int rfd) -{ - struct s2n_blob ctx_mem = { 0 }; - struct s2n_socket_read_io_context *peer_socket_ctx = NULL; - - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_read_io_context))); - POSIX_GUARD(s2n_blob_zero(&ctx_mem)); - - peer_socket_ctx = (struct s2n_socket_read_io_context *) (void *) ctx_mem.data; - peer_socket_ctx->fd = rfd; - - POSIX_GUARD(s2n_connection_set_recv_cb(conn, s2n_socket_read)); - POSIX_GUARD(s2n_connection_set_recv_ctx(conn, peer_socket_ctx)); - conn->managed_recv_io = true; - - /* This is only needed if the user is using corked io. - * Take the snapshot in case optimized io is enabled after setting the fd. - */ - POSIX_GUARD(s2n_socket_read_snapshot(conn)); - - return 0; -} - -int s2n_connection_get_read_fd(struct s2n_connection *conn, int *readfd) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(readfd); - POSIX_ENSURE((conn->managed_recv_io && conn->recv_io_context), S2N_ERR_INVALID_STATE); - - const struct s2n_socket_read_io_context *peer_socket_ctx = conn->recv_io_context; - *readfd = peer_socket_ctx->fd; - return S2N_SUCCESS; -} - -int s2n_connection_set_write_fd(struct s2n_connection *conn, int wfd) -{ - struct s2n_blob ctx_mem = { 0 }; - struct s2n_socket_write_io_context *peer_socket_ctx = NULL; - - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_write_io_context))); - - peer_socket_ctx = (struct s2n_socket_write_io_context *) (void *) ctx_mem.data; - peer_socket_ctx->fd = wfd; - - POSIX_GUARD(s2n_connection_set_send_cb(conn, s2n_socket_write)); - POSIX_GUARD(s2n_connection_set_send_ctx(conn, peer_socket_ctx)); - conn->managed_send_io = true; - - /* This is only needed if the user is using corked io. - * Take the snapshot in case optimized io is enabled after setting the fd. - */ - POSIX_GUARD(s2n_socket_write_snapshot(conn)); - - uint8_t ipv6 = 0; - if (0 == s2n_socket_is_ipv6(wfd, &ipv6)) { - conn->ipv6 = (ipv6 ? 1 : 0); - } - - conn->write_fd_broken = 0; - - return 0; -} - -int s2n_connection_get_write_fd(struct s2n_connection *conn, int *writefd) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(writefd); - POSIX_ENSURE((conn->managed_send_io && conn->send_io_context), S2N_ERR_INVALID_STATE); - - const struct s2n_socket_write_io_context *peer_socket_ctx = conn->send_io_context; - *writefd = peer_socket_ctx->fd; - return S2N_SUCCESS; -} -int s2n_connection_set_fd(struct s2n_connection *conn, int fd) -{ - POSIX_GUARD(s2n_connection_set_read_fd(conn, fd)); - POSIX_GUARD(s2n_connection_set_write_fd(conn, fd)); - return 0; -} - -int s2n_connection_use_corked_io(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* Caller shouldn't be trying to set s2n IO corked on non-s2n-managed IO */ - POSIX_ENSURE(conn->managed_send_io, S2N_ERR_CORK_SET_ON_UNMANAGED); - conn->corked_io = 1; - - return 0; -} - -uint64_t s2n_connection_get_wire_bytes_in(struct s2n_connection *conn) -{ - if (conn->ktls_recv_enabled) { - return 0; - } - return conn->wire_bytes_in; -} - -uint64_t s2n_connection_get_wire_bytes_out(struct s2n_connection *conn) -{ - if (conn->ktls_send_enabled) { - return 0; - } - return conn->wire_bytes_out; -} - -const char *s2n_connection_get_cipher(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_ENSURE_REF(conn->secure); - PTR_ENSURE_REF(conn->secure->cipher_suite); - - return conn->secure->cipher_suite->name; -} - -int s2n_connection_get_cipher_iana_value(struct s2n_connection *conn, uint8_t *first, uint8_t *second) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_MUT(first); - POSIX_ENSURE_MUT(second); - - /* ensure we've negotiated a cipher suite */ - POSIX_ENSURE(!s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, - s2n_null_cipher_suite.iana_value, sizeof(s2n_null_cipher_suite.iana_value)), - S2N_ERR_INVALID_STATE); - - const uint8_t *iana_value = conn->secure->cipher_suite->iana_value; - *first = iana_value[0]; - *second = iana_value[1]; - - return S2N_SUCCESS; -} - -const char *s2n_connection_get_curve(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_ENSURE_REF(conn->secure); - PTR_ENSURE_REF(conn->secure->cipher_suite); - - if (conn->kex_params.server_ecc_evp_params.negotiated_curve) { - /* TLS1.3 currently only uses ECC groups. */ - bool tls13 = conn->actual_protocol_version >= S2N_TLS13; - /* we check for a full handshake, because TLS 1.2 resumption does not perform - * an additional diffie-hellman exchange */ - bool ecdhe_cipher_negotiated = s2n_kex_includes(conn->secure->cipher_suite->key_exchange_alg, &s2n_ecdhe) - && IS_FULL_HANDSHAKE(conn); - if (tls13 || ecdhe_cipher_negotiated) { - return conn->kex_params.server_ecc_evp_params.negotiated_curve->name; - } - } - - return "NONE"; -} - -const char *s2n_connection_get_kem_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (!conn->kex_params.kem_params.kem) { - return "NONE"; - } - - return conn->kex_params.kem_params.kem->name; -} - -const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (conn->actual_protocol_version < S2N_TLS13 || !conn->kex_params.server_kem_group_params.kem_group) { - return "NONE"; - } - - return conn->kex_params.server_kem_group_params.kem_group->name; -} - -int s2n_connection_get_key_exchange_group(struct s2n_connection *conn, const char **group_name) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(group_name); - - /* s2n_connection_get_curve returns only the ECDH curve portion of a named group, even if - the negotiated group was a hybrid PQ key exchange also containing a KEM. Therefore, - we use the result of s2n_connection_get_kem_group_name if the connection supports PQ. */ - if (s2n_tls13_pq_hybrid_supported(conn)) { - *group_name = s2n_connection_get_kem_group_name(conn); - } else { - *group_name = s2n_connection_get_curve(conn); - } - - POSIX_ENSURE(*group_name != NULL && strcmp(*group_name, "NONE"), S2N_ERR_INVALID_STATE); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_connection_get_client_supported_version(struct s2n_connection *conn, - uint8_t *client_supported_version) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_EQ(conn->mode, S2N_SERVER); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); - RESULT_ENSURE_REF(client_hello); - - s2n_parsed_extension *supported_versions_extension = NULL; - RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_VERSIONS, &client_hello->extensions, - &supported_versions_extension)); - RESULT_ENSURE_REF(supported_versions_extension); - - struct s2n_stuffer supported_versions_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions_stuffer, &supported_versions_extension->extension)); - - uint8_t client_protocol_version = s2n_unknown_protocol_version; - uint8_t actual_protocol_version = s2n_unknown_protocol_version; - RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, &supported_versions_stuffer, - &client_protocol_version, &actual_protocol_version)); - - RESULT_ENSURE_NE(client_protocol_version, s2n_unknown_protocol_version); - - *client_supported_version = client_protocol_version; - - return S2N_RESULT_OK; -} - -int s2n_connection_get_client_protocol_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* For backwards compatibility, the client_protocol_version field isn't updated via the - * supported versions extension on TLS 1.2 servers. See - * https://github.com/aws/s2n-tls/issues/4240. - * - * The extension is processed here to ensure that TLS 1.2 servers report the same client - * protocol version to applications as TLS 1.3 servers. - */ - if (conn->mode == S2N_SERVER && conn->server_protocol_version <= S2N_TLS12) { - uint8_t client_supported_version = s2n_unknown_protocol_version; - s2n_result result = s2n_connection_get_client_supported_version(conn, &client_supported_version); - - /* If the extension wasn't received, or if a client protocol version couldn't be determined - * after processing the extension, the extension is ignored. - */ - if (s2n_result_is_ok(result)) { - return client_supported_version; - } - } - - return conn->client_protocol_version; -} - -int s2n_connection_get_server_protocol_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - return conn->server_protocol_version; -} - -int s2n_connection_get_actual_protocol_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - return conn->actual_protocol_version; -} - -int s2n_connection_get_client_hello_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->client_hello.sslv2) { - return S2N_SSLv2; - } else { - return S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); - } -} - -int s2n_connection_client_cert_used(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (IS_CLIENT_AUTH_HANDSHAKE(conn) && is_handshake_complete(conn)) { - if (IS_CLIENT_AUTH_NO_CERT(conn)) { - return 0; - } - return 1; - } - return 0; -} - -int s2n_connection_get_alert(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) != 2, S2N_ERR_NO_ALERT); - - /* Shallow copy the stuffer. We assume that multiple threads might call this - * function concurrently, so we must not mutate anything outside of the function scope */ - struct s2n_stuffer alert_stuffer = conn->alert_in; - uint8_t alert_code = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); - POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); - - return alert_code; -} - -int s2n_set_server_name(struct s2n_connection *conn, const char *server_name) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(server_name); - - S2N_ERROR_IF(conn->mode != S2N_CLIENT, S2N_ERR_CLIENT_MODE); - - int len = strlen(server_name); - S2N_ERROR_IF(len > S2N_MAX_SERVER_NAME, S2N_ERR_SERVER_NAME_TOO_LONG); - - POSIX_CHECKED_MEMCPY(conn->server_name, server_name, len); - - return 0; -} - -const char *s2n_get_server_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (conn->server_name[0]) { - return conn->server_name; - } - - PTR_GUARD_POSIX(s2n_extension_process(&s2n_client_server_name_extension, conn, &conn->client_hello.extensions)); - - if (!conn->server_name[0]) { - return NULL; - } - - return conn->server_name; -} - -const char *s2n_get_application_protocol(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (strlen(conn->application_protocol) == 0) { - return NULL; - } - - return conn->application_protocol; -} - -int s2n_connection_get_session_id_length(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - /* Stateful session resumption in TLS1.3 using session id is not yet supported. */ - if (conn->actual_protocol_version >= S2N_TLS13) { - return 0; - } - return conn->session_id_len; -} - -int s2n_connection_get_session_id(struct s2n_connection *conn, uint8_t *session_id, size_t max_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(session_id); - - const int session_id_len = s2n_connection_get_session_id_length(conn); - POSIX_GUARD(session_id_len); - - POSIX_ENSURE((size_t) session_id_len <= max_length, S2N_ERR_SESSION_ID_TOO_LONG); - - POSIX_CHECKED_MEMCPY(session_id, conn->session_id, session_id_len); - - return session_id_len; -} - -int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blinding) -{ - POSIX_ENSURE_REF(conn); - conn->blinding = blinding; - - return 0; -} - -#define ONE_S INT64_C(1000000000) - -static S2N_RESULT s2n_connection_get_delay_impl(struct s2n_connection *conn, uint64_t *delay) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(delay); - - if (!conn->delay) { - *delay = 0; - return S2N_RESULT_OK; - } - - uint64_t elapsed = 0; - RESULT_GUARD(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); - - if (elapsed > conn->delay) { - *delay = 0; - return S2N_RESULT_OK; - } - - *delay = conn->delay - elapsed; - - return S2N_RESULT_OK; -} - -uint64_t s2n_connection_get_delay(struct s2n_connection *conn) -{ - uint64_t delay = 0; - if (s2n_result_is_ok(s2n_connection_get_delay_impl(conn, &delay))) { - return delay; - } else { - return UINT64_MAX; - } -} - -/* s2n-tls has a random delay that will trigger for sensitive errors. This is a mitigation - * for possible timing sidechannels. - * - * The historical sidechannel that inspired s2n-tls blinding was the Lucky 13 attack, which takes - * advantage of potential timing differences when removing padding from a record encrypted in CBC mode. - * The attack is only theoretical in TLS; the attack criteria is unlikely to ever occur - * (See: Fardan, N. J. A., & Paterson, K. G. (2013, May 1). Lucky Thirteen: Breaking the TLS and - * DTLS Record Protocols.) However, we still include blinding to provide a defense in depth mitigation. - */ -S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(min); - RESULT_ENSURE_REF(max); - RESULT_ENSURE_REF(conn->config); - - /* - * The default delay is a random value between 10-30s. The rationale behind the range is that the - * floor is the fixed cost that an attacker must pay per attempt, in this case, 10s. The length of - * the range then affects the number of attempts that an attacker must perform in order to recover a - * byte of plaintext with a certain degree of confidence. - * - * A uniform distribution of the range [a, b] has a variance of ((b - a)^2)/12. Therefore, given a - * hypothetical timing difference of 1us, the number of attempts necessary to distinguish the correct - * byte from an incorrect byte in a Lucky13-style attack is (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion - * (note that we first have to convert from seconds to microseconds to match the unit of the timing difference.) - */ - *min = S2N_DEFAULT_BLINDING_MIN * ONE_S; - *max = S2N_DEFAULT_BLINDING_MAX * ONE_S; - - /* Setting the min to 1/3 of the max is an arbitrary ratio of fixed to variable delay. - * It is based on the ratio of our original default values. - */ - if (conn->config->custom_blinding_set) { - *max = conn->config->max_blinding * ONE_S; - *min = *max / 3; - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_GUARD(s2n_connection_set_closed(conn)); - - 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; - RESULT_GUARD(s2n_public_random(max - min, &rand_delay)); - - conn->delay = min + rand_delay; - - /* Restart the write timer */ - RESULT_GUARD(s2n_timer_start(conn->config, &conn->write_timer)); - - if (conn->blinding == S2N_BUILT_IN_BLINDING) { - struct timespec sleep_time = { .tv_sec = conn->delay / ONE_S, .tv_nsec = conn->delay % ONE_S }; - - int r = 0; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - } - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection **conn) -{ - RESULT_ENSURE_REF(conn); - if (*conn == NULL) { - return S2N_RESULT_OK; - } - - int error_code = s2n_errno; - int error_type = s2n_error_get_type(error_code); - - switch (error_type) { - case S2N_ERR_T_OK: - /* Ignore no error */ - return S2N_RESULT_OK; - case S2N_ERR_T_BLOCKED: - /* All blocking errors are retriable and should trigger no further action. */ - return S2N_RESULT_OK; - default: - break; - } - - /* Ensure that conn->in doesn't contain any leftover invalid or unauthenticated data. */ - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&(*conn)->in)); - - switch (error_code) { - /* Don't invoke blinding on some of the common errors. - * - * Be careful adding new errors here. Disabling blinding for an - * error that can be triggered by secret / encrypted values can - * potentially lead to a side channel attack. - * - * We may want to someday add an explicit error type for these errors. - */ - case S2N_ERR_CLOSED: - case S2N_ERR_CANCELLED: - case S2N_ERR_CIPHER_NOT_SUPPORTED: - case S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED: - case S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK: - RESULT_GUARD(s2n_connection_set_closed(*conn)); - break; - default: - /* Apply blinding to all other errors */ - RESULT_GUARD(s2n_connection_kill(*conn)); - break; - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_closed(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - s2n_atomic_flag_set(&conn->read_closed); - s2n_atomic_flag_set(&conn->write_closed); - return S2N_RESULT_OK; -} - -const uint8_t *s2n_connection_get_ocsp_response(struct s2n_connection *conn, uint32_t *length) -{ - PTR_ENSURE_REF(conn); - PTR_ENSURE_REF(length); - - *length = conn->status_response.size; - return conn->status_response.data; -} - -S2N_RESULT s2n_connection_set_max_fragment_length(struct s2n_connection *conn, uint16_t max_frag_length) -{ - RESULT_ENSURE_REF(conn); - - if (conn->negotiated_mfl_code) { - /* Respect the upper limit agreed on with the peer */ - RESULT_ENSURE_LT(conn->negotiated_mfl_code, s2n_array_len(mfl_code_to_length)); - conn->max_outgoing_fragment_length = S2N_MIN(mfl_code_to_length[conn->negotiated_mfl_code], max_frag_length); - } else { - conn->max_outgoing_fragment_length = max_frag_length; - } - - /* If no buffer has been initialized yet, no need to resize. - * The standard I/O logic will handle initializing the buffer. - */ - if (s2n_stuffer_is_freed(&conn->out)) { - return S2N_RESULT_OK; - } - - uint16_t max_wire_record_size = 0; - RESULT_GUARD(s2n_record_max_write_size(conn, conn->max_outgoing_fragment_length, &max_wire_record_size)); - if ((conn->out.blob.size < max_wire_record_size)) { - RESULT_GUARD_POSIX(s2n_realloc(&conn->out.blob, max_wire_record_size)); - } - - return S2N_RESULT_OK; -} - -int s2n_connection_prefer_throughput(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_LARGE_FRAGMENT_LENGTH)); - return S2N_SUCCESS; -} - -int s2n_connection_prefer_low_latency(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_SMALL_FRAGMENT_LENGTH)); - return S2N_SUCCESS; -} - -int s2n_connection_set_dynamic_buffers(struct s2n_connection *conn, bool enabled) -{ - POSIX_ENSURE_REF(conn); - conn->dynamic_buffers = enabled; - return S2N_SUCCESS; -} - -int s2n_connection_set_dynamic_record_threshold(struct s2n_connection *conn, uint32_t resize_threshold, uint16_t timeout_threshold) -{ - POSIX_ENSURE_REF(conn); - S2N_ERROR_IF(resize_threshold > S2N_TLS_MAX_RESIZE_THRESHOLD, S2N_ERR_INVALID_DYNAMIC_THRESHOLD); - - conn->dynamic_record_resize_threshold = resize_threshold; - conn->dynamic_record_timeout_threshold = timeout_threshold; - return 0; -} - -int s2n_connection_set_verify_host_callback(struct s2n_connection *conn, s2n_verify_host_fn verify_host_fn, void *data) -{ - POSIX_ENSURE_REF(conn); - - conn->verify_host_fn = verify_host_fn; - conn->data_for_verify_host = data; - conn->verify_host_fn_overridden = 1; - - return 0; -} - -int s2n_connection_recv_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) -{ - POSIX_ENSURE_REF(conn->recv); - /* Make sure we have enough space to write */ - POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, len)); - - int r = 0; - S2N_IO_RETRY_EINTR(r, - conn->recv(conn->recv_io_context, stuffer->blob.data + stuffer->write_cursor, len)); - POSIX_ENSURE(r >= 0, S2N_ERR_RECV_STUFFER_FROM_CONN); - - /* Record just how many bytes we have written */ - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, r)); - return r; -} - -int s2n_connection_send_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->send); - if (conn->write_fd_broken) { - POSIX_BAIL(S2N_ERR_SEND_STUFFER_TO_CONN); - } - /* Make sure we even have the data */ - S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) < len, S2N_ERR_STUFFER_OUT_OF_DATA); - - int w = 0; - S2N_IO_RETRY_EINTR(w, - conn->send(conn->send_io_context, stuffer->blob.data + stuffer->read_cursor, len)); - if (w < 0 && errno == EPIPE) { - conn->write_fd_broken = 1; - } - POSIX_ENSURE(w >= 0, S2N_ERR_SEND_STUFFER_TO_CONN); - - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, w)); - return w; -} - -int s2n_connection_is_managed_corked(const struct s2n_connection *s2n_connection) -{ - POSIX_ENSURE_REF(s2n_connection); - - return (s2n_connection->managed_send_io && s2n_connection->corked_io); -} - -const uint8_t *s2n_connection_get_sct_list(struct s2n_connection *conn, uint32_t *length) -{ - if (!length) { - return NULL; - } - - *length = conn->ct_response.size; - return conn->ct_response.data; -} - -int s2n_connection_is_client_auth_enabled(struct s2n_connection *s2n_connection) -{ - s2n_cert_auth_type auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(s2n_connection, &auth_type)); - - return (auth_type != S2N_CERT_AUTH_NONE); -} - -struct s2n_cert_chain_and_key *s2n_connection_get_selected_cert(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - return conn->handshake_params.our_chain_and_key; -} - -uint8_t s2n_connection_get_protocol_version(const struct s2n_connection *conn) -{ - if (conn == NULL) { - return S2N_UNKNOWN_PROTOCOL_VERSION; - } - - if (conn->actual_protocol_version != S2N_UNKNOWN_PROTOCOL_VERSION) { - return conn->actual_protocol_version; - } - - if (conn->mode == S2N_CLIENT) { - return conn->client_protocol_version; - } - return conn->server_protocol_version; -} - -DEFINE_POINTER_CLEANUP_FUNC(struct s2n_cert_chain *, s2n_cert_chain_free); - -int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain_and_key) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(cert_chain_and_key); - POSIX_ENSURE_REF(cert_chain_and_key->cert_chain); - - /* Ensure that cert_chain_and_key is empty BEFORE we modify it in any way. - * That includes before tying its cert_chain to DEFER_CLEANUP. - */ - POSIX_ENSURE(cert_chain_and_key->cert_chain->head == NULL, S2N_ERR_INVALID_ARGUMENT); - - DEFER_CLEANUP(struct s2n_cert_chain *cert_chain = cert_chain_and_key->cert_chain, s2n_cert_chain_free_pointer); - struct s2n_cert **insert = &cert_chain->head; - - const struct s2n_x509_validator *validator = &conn->x509_validator; - POSIX_ENSURE_REF(validator); - POSIX_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_CERT_NOT_VALIDATED); - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - POSIX_GUARD_RESULT(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - STACK_OF(X509) *cert_chain_validated = validated_cert_chain.stack; - POSIX_ENSURE_REF(cert_chain_validated); - - int cert_count = sk_X509_num(cert_chain_validated); - POSIX_ENSURE_GTE(cert_count, 0); - - for (size_t cert_idx = 0; cert_idx < (size_t) cert_count; cert_idx++) { - X509 *cert = sk_X509_value(cert_chain_validated, cert_idx); - POSIX_ENSURE_REF(cert); - DEFER_CLEANUP(uint8_t *cert_data = NULL, s2n_crypto_free); - int cert_size = i2d_X509(cert, &cert_data); - POSIX_ENSURE_GT(cert_size, 0); - - struct s2n_blob mem = { 0 }; - POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); - - struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; - POSIX_ENSURE_REF(new_node); - - new_node->next = NULL; - *insert = new_node; - insert = &new_node->next; - - POSIX_GUARD(s2n_alloc(&new_node->raw, cert_size)); - POSIX_CHECKED_MEMCPY(new_node->raw.data, cert_data, cert_size); - } - - ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_signature_scheme_to_tls_iana(const struct s2n_signature_scheme *sig_scheme, - s2n_tls_hash_algorithm *converted_scheme) -{ - RESULT_ENSURE_REF(sig_scheme); - RESULT_ENSURE_REF(converted_scheme); - *converted_scheme = S2N_TLS_HASH_NONE; - - switch (sig_scheme->hash_alg) { - case S2N_HASH_MD5: - *converted_scheme = S2N_TLS_HASH_MD5; - break; - case S2N_HASH_SHA1: - *converted_scheme = S2N_TLS_HASH_SHA1; - break; - case S2N_HASH_SHA224: - *converted_scheme = S2N_TLS_HASH_SHA224; - break; - case S2N_HASH_SHA256: - *converted_scheme = S2N_TLS_HASH_SHA256; - break; - case S2N_HASH_SHA384: - *converted_scheme = S2N_TLS_HASH_SHA384; - break; - case S2N_HASH_SHA512: - *converted_scheme = S2N_TLS_HASH_SHA512; - break; - case S2N_HASH_MD5_SHA1: - *converted_scheme = S2N_TLS_HASH_MD5_SHA1; - break; - case S2N_HASH_NONE: - case S2N_HASH_SHAKE256_64: - case S2N_HASH_ALGS_COUNT: - *converted_scheme = S2N_TLS_HASH_NONE; - break; - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_selected_digest_algorithm(struct s2n_connection *conn, - s2n_tls_hash_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( - conn->handshake_params.server_cert_sig_scheme, converted_scheme)); - - return S2N_SUCCESS; -} - -int s2n_connection_get_selected_client_cert_digest_algorithm(struct s2n_connection *conn, - s2n_tls_hash_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( - conn->handshake_params.client_cert_sig_scheme, converted_scheme)); - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_signature_scheme_to_signature_algorithm(const struct s2n_signature_scheme *sig_scheme, - s2n_tls_signature_algorithm *converted_scheme) -{ - RESULT_ENSURE_REF(sig_scheme); - RESULT_ENSURE_REF(converted_scheme); - *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; - - switch (sig_scheme->sig_alg) { - case S2N_SIGNATURE_RSA: - *converted_scheme = S2N_TLS_SIGNATURE_RSA; - break; - case S2N_SIGNATURE_ECDSA: - *converted_scheme = S2N_TLS_SIGNATURE_ECDSA; - break; - case S2N_SIGNATURE_RSA_PSS_RSAE: - *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_RSAE; - break; - case S2N_SIGNATURE_RSA_PSS_PSS: - *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_PSS; - break; - case S2N_SIGNATURE_MLDSA: - *converted_scheme = S2N_TLS_SIGNATURE_MLDSA; - break; - case S2N_SIGNATURE_ANONYMOUS: - *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; - break; - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_selected_signature_algorithm(struct s2n_connection *conn, - s2n_tls_signature_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( - conn->handshake_params.server_cert_sig_scheme, converted_scheme)); - - return S2N_SUCCESS; -} - -int s2n_connection_get_selected_client_cert_signature_algorithm(struct s2n_connection *conn, - s2n_tls_signature_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( - conn->handshake_params.client_cert_sig_scheme, converted_scheme)); - - return S2N_SUCCESS; -} - -int s2n_connection_get_signature_scheme(struct s2n_connection *conn, const char **scheme_name) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(scheme_name); - POSIX_ENSURE(IS_NEGOTIATED(conn), S2N_ERR_INVALID_STATE); - - const struct s2n_signature_scheme *scheme = conn->handshake_params.server_cert_sig_scheme; - /* The scheme should never be NULL. A "none" placeholder is used if no - * scheme has been negotiated. - */ - POSIX_ENSURE_REF(scheme); - - *scheme_name = scheme->name; - if (scheme->signature_curve) { - /* Some TLS1.2 and TLS1.3 signature schemes share an IANA value, - * but are NOT the same. The TLS1.3 version implies a specific curve. - */ - if (conn->actual_protocol_version >= S2N_TLS13) { - *scheme_name = scheme->tls13_name; - } else { - *scheme_name = scheme->legacy_name; - } - } - - POSIX_ENSURE_REF(*scheme_name); - return S2N_SUCCESS; -} - -/* - * Gets the config set on the connection. - */ -int s2n_connection_get_config(struct s2n_connection *conn, struct s2n_config **config) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(config); - - if (s2n_fetch_default_config() == conn->config) { - POSIX_BAIL(S2N_ERR_NULL); - } - - *config = conn->config; - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_connection_dynamic_free_out_buffer(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* free the out buffer if we're in dynamic mode and it's completely flushed */ - if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->out)) { - /* since outgoing buffers are already encrypted, the buffers don't need to be zeroed, which saves some overhead */ - RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->out)); - - /* reset the stuffer to its initial state */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_dynamic_free_in_buffer(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* free `buffer_in` if we're in dynamic mode and it's completely flushed */ - if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->buffer_in)) { - /* when copying the buffer into the application, we use `s2n_stuffer_erase_and_read`, which already zeroes the memory */ - RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->buffer_in)); - - /* reset the stuffer to its initial state */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); - } - - return S2N_RESULT_OK; -} - -bool s2n_connection_check_io_status(struct s2n_connection *conn, s2n_io_status status) -{ - if (!conn) { - return false; - } - - bool read_closed = s2n_atomic_flag_test(&conn->read_closed); - bool write_closed = s2n_atomic_flag_test(&conn->write_closed); - bool full_duplex = !read_closed && !write_closed; - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.1 - *# Note that this is a change from versions of TLS prior to TLS 1.3 in - *# which implementations were required to react to a "close_notify" by - *# discarding pending writes and sending an immediate "close_notify" - *# alert of their own. - */ - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - switch (status) { - case S2N_IO_WRITABLE: - case S2N_IO_READABLE: - case S2N_IO_FULL_DUPLEX: - return full_duplex; - case S2N_IO_CLOSED: - return !full_duplex; - } - } - - switch (status) { - case S2N_IO_WRITABLE: - return !write_closed; - case S2N_IO_READABLE: - return !read_closed; - case S2N_IO_FULL_DUPLEX: - return full_duplex; - case S2N_IO_CLOSED: - return read_closed && write_closed; - } - - return false; -} - -S2N_RESULT s2n_connection_get_secure_cipher(struct s2n_connection *conn, const struct s2n_cipher **cipher) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(cipher); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); - *cipher = conn->secure->cipher_suite->record_alg->cipher; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_get_sequence_number(struct s2n_connection *conn, - s2n_mode mode, struct s2n_blob *seq_num) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(seq_num); - RESULT_ENSURE_REF(conn->secure); - - switch (mode) { - case S2N_CLIENT: - RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->client_sequence_number, - sizeof(conn->secure->client_sequence_number))); - break; - case S2N_SERVER: - RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->server_sequence_number, - sizeof(conn->secure->server_sequence_number))); - break; - default: - RESULT_BAIL(S2N_ERR_SAFETY); - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_key_update_counts(struct s2n_connection *conn, - uint8_t *send_key_updates, uint8_t *recv_key_updates) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(send_key_updates); - POSIX_ENSURE_REF(recv_key_updates); - *send_key_updates = conn->send_key_updated; - *recv_key_updates = conn->recv_key_updated; - return S2N_SUCCESS; -} - -int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled) -{ - POSIX_ENSURE_REF(conn); - /* QUIC support is not currently compatible with recv_buffering */ - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_INVALID_STATE); - conn->recv_buffering = enabled; - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_connection.h" + +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +/* Required for s2n_connection_get_key_update_counts */ +#include "api/unstable/ktls.h" +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_crypto.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_openssl_x509.h" +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_client_server_name.h" +#include "tls/extensions/s2n_client_supported_versions.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_atomic.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_compiler.h" +#include "utils/s2n_io.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" +#include "utils/s2n_timer.h" + +#define S2N_SET_KEY_SHARE_LIST_EMPTY(keyshares) (keyshares |= 1) +#define S2N_SET_KEY_SHARE_REQUEST(keyshares, i) (keyshares |= (1 << (i + 1))) + +static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, + const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type); + +/* Allocates and initializes memory for a new connection. + * + * Since customers can reuse a connection, ensure that values on the connection are + * initialized in `s2n_connection_wipe` where possible. */ +struct s2n_connection *s2n_connection_new(s2n_mode mode) +{ + struct s2n_blob blob = { 0 }; + PTR_GUARD_POSIX(s2n_alloc(&blob, sizeof(struct s2n_connection))); + PTR_GUARD_POSIX(s2n_blob_zero(&blob)); + + /* Cast 'through' void to acknowledge that we are changing alignment, + * which is ok, as blob.data is always aligned. + */ + struct s2n_connection *conn = (struct s2n_connection *) (void *) blob.data; + + PTR_GUARD_POSIX(s2n_connection_set_config(conn, s2n_fetch_default_config())); + + /* `mode` is initialized here since it's passed in as a parameter. */ + conn->mode = mode; + + /* Allocate the fixed-size stuffers */ + blob = (struct s2n_blob){ 0 }; + PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->alert_in_data, S2N_ALERT_LENGTH)); + PTR_GUARD_POSIX(s2n_stuffer_init(&conn->alert_in, &blob)); + + blob = (struct s2n_blob){ 0 }; + PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->ticket_ext_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + PTR_GUARD_POSIX(s2n_stuffer_init(&conn->client_ticket_to_decrypt, &blob)); + + /* Allocate long term hash and HMAC memory */ + PTR_GUARD_RESULT(s2n_prf_new(conn)); + PTR_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); + + /* Initialize the growable stuffers. Zero length at first, but the resize + * in _wipe will fix that + */ + blob = (struct s2n_blob){ 0 }; + PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->header_in_data, S2N_TLS_RECORD_HEADER_LENGTH)); + PTR_GUARD_POSIX(s2n_stuffer_init(&conn->header_in, &blob)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->handshake.io, 0)); + PTR_GUARD_RESULT(s2n_timer_start(conn->config, &conn->write_timer)); + + /* NOTE: s2n_connection_wipe MUST be called last in this function. + * + * s2n_connection_wipe is used for initializing values but also used by customers to + * reset/reuse the connection. Calling it last ensures that s2n_connection_wipe is + * implemented correctly and safe. + */ + PTR_GUARD_POSIX(s2n_connection_wipe(conn)); + return conn; +} + +static int s2n_connection_zero(struct s2n_connection *conn, int mode, struct s2n_config *config) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(config); + + /* Zero the whole connection structure */ + POSIX_CHECKED_MEMSET(conn, 0, sizeof(struct s2n_connection)); + + conn->mode = mode; + conn->max_outgoing_fragment_length = S2N_DEFAULT_FRAGMENT_LENGTH; + conn->handshake.end_of_messages = APPLICATION_DATA; + s2n_connection_set_config(conn, config); + + return 0; +} + +S2N_RESULT s2n_connection_wipe_all_keyshares(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.client_ecc_evp_params)); + + RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.server_kem_group_params)); + RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.client_kem_group_params)); + + return S2N_RESULT_OK; +} + +static int s2n_connection_wipe_keys(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* Free any server key received (we may not have completed a + * handshake, so this may not have been free'd yet) */ + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); + POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.server_public_key)); + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.client_public_key)); + POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.client_public_key)); + s2n_x509_validator_wipe(&conn->x509_validator); + POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); + POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); + POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); + POSIX_GUARD(s2n_free(&conn->handshake_params.client_cert_chain)); + POSIX_GUARD(s2n_free(&conn->ct_response)); + + return 0; +} + +static int s2n_connection_free_managed_recv_io(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->managed_recv_io) { + POSIX_GUARD(s2n_free_object((uint8_t **) &conn->recv_io_context, sizeof(struct s2n_socket_read_io_context))); + conn->managed_recv_io = false; + conn->recv = NULL; + } + return S2N_SUCCESS; +} + +static int s2n_connection_free_managed_send_io(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->managed_send_io) { + POSIX_GUARD(s2n_free_object((uint8_t **) &conn->send_io_context, sizeof(struct s2n_socket_write_io_context))); + conn->managed_send_io = false; + conn->send = NULL; + } + return S2N_SUCCESS; +} + +static int s2n_connection_free_managed_io(struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); + POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); + return S2N_SUCCESS; +} + +static int s2n_connection_wipe_io(struct s2n_connection *conn) +{ + if (s2n_connection_is_managed_corked(conn) && conn->recv) { + POSIX_GUARD(s2n_socket_read_restore(conn)); + } + if (s2n_connection_is_managed_corked(conn) && conn->send) { + POSIX_GUARD(s2n_socket_write_restore(conn)); + } + + /* Remove all I/O-related members */ + POSIX_GUARD(s2n_connection_free_managed_io(conn)); + + return 0; +} + +static uint8_t s2n_default_verify_host(const char *host_name, size_t len, void *data) +{ + /* if present, match server_name of the connection using rules + * outlined in RFC6125 6.4. */ + + struct s2n_connection *conn = data; + + if (conn->server_name[0] == '\0') { + return 0; + } + + /* complete match */ + if (strlen(conn->server_name) == len && strncasecmp(conn->server_name, host_name, len) == 0) { + return 1; + } + + /* match 1 level of wildcard */ + if (len > 2 && host_name[0] == '*' && host_name[1] == '.') { + const char *suffix = strchr(conn->server_name, '.'); + + if (suffix == NULL) { + return 0; + } + + if (strlen(suffix) == len - 1 && strncasecmp(suffix, host_name + 1, len - 1) == 0) { + return 1; + } + } + + return 0; +} + +S2N_CLEANUP_RESULT s2n_connection_ptr_free(struct s2n_connection **conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_GUARD_POSIX(s2n_connection_free(*conn)); + *conn = NULL; + return S2N_RESULT_OK; +} + +int s2n_connection_free(struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_connection_wipe_keys(conn)); + POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); + + POSIX_GUARD_RESULT(s2n_prf_free(conn)); + POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); + + POSIX_GUARD(s2n_connection_free_managed_io(conn)); + + POSIX_GUARD(s2n_free(&conn->client_ticket)); + POSIX_GUARD(s2n_free(&conn->status_response)); + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->server_early_data_context)); + POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); + POSIX_GUARD(s2n_stuffer_free(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_free(&conn->in)); + POSIX_GUARD(s2n_stuffer_free(&conn->out)); + POSIX_GUARD(s2n_stuffer_free(&conn->handshake.io)); + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + s2n_x509_validator_wipe(&conn->x509_validator); + POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); + POSIX_GUARD(s2n_client_hello_free_raw_message(&conn->client_hello)); + POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); + POSIX_GUARD(s2n_free(&conn->cookie)); + POSIX_GUARD(s2n_free(&conn->cert_authorities)); + POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); + POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->secure)); + POSIX_GUARD(s2n_free_object((uint8_t **) &conn, sizeof(struct s2n_connection))); + + return 0; +} + +int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *config) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(config); + + if (conn->config == config) { + return 0; + } + + /* s2n_config invariant: any s2n_config is always in a state that respects the + * config->security_policy certificate preferences. Therefore we only need to + * validate certificates here if the connection is using a security policy override. + */ + const struct s2n_security_policy *security_policy_override = conn->security_policy_override; + if (security_policy_override) { + POSIX_GUARD_RESULT(s2n_config_validate_loaded_certificates(config, security_policy_override)); + } + + /* We only support one client certificate */ + if (s2n_config_get_num_default_certs(config) > 1 && conn->mode == S2N_CLIENT) { + POSIX_BAIL(S2N_ERR_TOO_MANY_CERTIFICATES); + } + + s2n_x509_validator_wipe(&conn->x509_validator); + + 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)); + if (!conn->verify_host_fn_overridden) { + if (config->verify_host_fn != NULL) { + conn->verify_host_fn = config->verify_host_fn; + conn->data_for_verify_host = config->data_for_verify_host; + } else { + conn->verify_host_fn = s2n_default_verify_host; + conn->data_for_verify_host = conn; + } + } + + if (config->max_verify_cert_chain_depth_set) { + POSIX_GUARD(s2n_x509_validator_set_max_chain_depth(&conn->x509_validator, config->max_verify_cert_chain_depth)); + } + } + conn->tickets_to_send = config->initial_tickets_to_send; + + if (conn->psk_params.psk_list.len == 0 && !conn->psk_mode_overridden) { + POSIX_GUARD(s2n_connection_set_psk_mode(conn, config->psk_mode)); + conn->psk_mode_overridden = false; + } + + /* If at least one certificate does not have a private key configured, + * the config must provide an async pkey callback. + * The handshake could still fail if the callback doesn't offload the + * signature, but this at least catches configuration mistakes. + */ + if (config->no_signing_key) { + POSIX_ENSURE(config->async_pkey_cb, S2N_ERR_NO_PRIVATE_KEY); + } + + if (config->quic_enabled) { + /* If QUIC is ever enabled for a connection via the config, + * we should enforce that it can never be disabled by + * changing the config. + * + * Enabling QUIC indicates that the connection is being used by + * a QUIC implementation, which never changes. Disabling QUIC + * partially through a connection could also potentially be + * dangerous, as QUIC handles encryption. + */ + POSIX_GUARD(s2n_connection_enable_quic(conn)); + } + + if (config->send_buffer_size_override) { + conn->multirecord_send = true; + } + + /* Historically, calling s2n_config_set_verification_ca_location enabled OCSP stapling + * regardless of the value set by an application calling s2n_config_set_status_request_type. + * We maintain this behavior for backwards compatibility. + * + * However, the s2n_config_set_verification_ca_location behavior predates client authentication + * support for OCSP stapling, so could only affect whether clients requested OCSP stapling. We + * therefore only have to maintain the legacy behavior for clients, not servers. + * + * Note: The Rust bindings do not maintain the legacy behavior. + */ + conn->request_ocsp_status = config->ocsp_status_requested_by_user; + if (config->ocsp_status_requested_by_s2n && conn->mode == S2N_CLIENT) { + conn->request_ocsp_status = true; + } + + conn->config = config; + return S2N_SUCCESS; +} + +int s2n_connection_server_name_extension_used(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(!(conn->handshake.client_hello_received), S2N_ERR_INVALID_STATE); + + conn->server_name_used = 1; + return S2N_SUCCESS; +} + +int s2n_connection_set_ctx(struct s2n_connection *conn, void *ctx) +{ + POSIX_ENSURE_REF(conn); + + conn->context = ctx; + return S2N_SUCCESS; +} + +void *s2n_connection_get_ctx(struct s2n_connection *conn) +{ + return conn->context; +} + +int s2n_connection_release_buffers(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_PRECONDITION(s2n_stuffer_validate(&conn->out)); + POSIX_PRECONDITION(s2n_stuffer_validate(&conn->in)); + + POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->out), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); + + POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + if (s2n_stuffer_is_consumed(&conn->buffer_in)) { + POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); + } + + POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->post_handshake.in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + + POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->out)); + POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->in)); + return S2N_SUCCESS; +} + +int s2n_connection_free_handshake(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* We are done with the handshake */ + POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); + POSIX_GUARD_RESULT(s2n_prf_free(conn)); + + /* All IO should use conn->secure after the handshake. + * However, if this method is called before the handshake completes, + * the connection may still be using conn->initial. + */ + if (conn->client != conn->initial && conn->server != conn->initial) { + POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); + } + + /* Wipe the buffers we are going to free */ + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); + + /* Truncate buffers to save memory, we are done with the handshake */ + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); + POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); + + /* We can free extension data we no longer need */ + POSIX_GUARD(s2n_free(&conn->client_ticket)); + POSIX_GUARD(s2n_free(&conn->status_response)); + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); + POSIX_GUARD(s2n_free(&conn->cookie)); + POSIX_GUARD(s2n_free(&conn->cert_authorities)); + + return 0; +} + +/* An idempotent operation which initializes values on the connection. + * + * Called in order to reuse a connection structure for a new connection. Should wipe + * any persistent memory, free any temporary memory, and set all fields back to their + * defaults. + */ +int s2n_connection_wipe(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* First make a copy of everything we'd like to save, which isn't very much. */ + int mode = conn->mode; + struct s2n_config *config = conn->config; + struct s2n_stuffer alert_in = { 0 }; + struct s2n_stuffer client_ticket_to_decrypt = { 0 }; + struct s2n_stuffer handshake_io = { 0 }; + struct s2n_stuffer header_in = { 0 }; + struct s2n_stuffer buffer_in = { 0 }; + struct s2n_stuffer out = { 0 }; + + /* Some required structures might have been freed to conserve memory between handshakes. + * Restore them. + */ + if (!conn->handshake.hashes) { + POSIX_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); + } + POSIX_GUARD_RESULT(s2n_handshake_hashes_wipe(conn->handshake.hashes)); + struct s2n_handshake_hashes *handshake_hashes = conn->handshake.hashes; + if (!conn->prf_space) { + POSIX_GUARD_RESULT(s2n_prf_new(conn)); + } + POSIX_GUARD_RESULT(s2n_prf_wipe(conn)); + struct s2n_prf_working_space *prf_workspace = conn->prf_space; + if (!conn->initial) { + POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->initial)); + } else { + POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->initial)); + } + struct s2n_crypto_parameters *initial = conn->initial; + if (!conn->secure) { + POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->secure)); + } else { + POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->secure)); + } + struct s2n_crypto_parameters *secure = conn->secure; + + /* Wipe all of the sensitive stuff */ + POSIX_GUARD(s2n_connection_wipe_keys(conn)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->client_ticket_to_decrypt)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->post_handshake.in)); + POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->header_in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); + + /* Free stuffers we plan to just recreate */ + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + POSIX_GUARD(s2n_stuffer_free(&conn->in)); + + POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); + POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); + + /* Wipe the I/O-related info and restore the original socket if necessary */ + POSIX_GUARD(s2n_connection_wipe_io(conn)); + + POSIX_GUARD(s2n_free(&conn->client_ticket)); + POSIX_GUARD(s2n_free(&conn->status_response)); + POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->server_early_data_context)); + POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); + POSIX_GUARD(s2n_free(&conn->cookie)); + POSIX_GUARD(s2n_free(&conn->cert_authorities)); + + /* Allocate memory for handling handshakes */ + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, S2N_LARGE_RECORD_LENGTH)); + + /* Truncate the message buffers to save memory, we will dynamically resize it as needed */ + POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); + POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); + POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); + + /* Remove context associated with connection */ + conn->context = NULL; + conn->verify_host_fn_overridden = 0; + conn->verify_host_fn = NULL; + conn->data_for_verify_host = NULL; + + /* Clone the stuffers */ + /* ignore address warnings because dest is allocated on the stack */ +#ifdef S2N_DIAGNOSTICS_PUSH_SUPPORTED + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Waddress" +#endif + POSIX_CHECKED_MEMCPY(&alert_in, &conn->alert_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&client_ticket_to_decrypt, &conn->client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&buffer_in, &conn->buffer_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&out, &conn->out, sizeof(struct s2n_stuffer)); +#ifdef S2N_DIAGNOSTICS_POP_SUPPORTED + #pragma GCC diagnostic pop +#endif + + POSIX_GUARD(s2n_connection_zero(conn, mode, config)); + + POSIX_CHECKED_MEMCPY(&conn->alert_in, &alert_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->client_ticket_to_decrypt, &client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->buffer_in, &buffer_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->out, &out, sizeof(struct s2n_stuffer)); + + /* conn->in will eventually point to part of conn->buffer_in, but we initialize + * it as growable and allocated to support legacy tests. + */ + POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->in, 0)); + + conn->handshake.hashes = handshake_hashes; + conn->prf_space = prf_workspace; + conn->initial = initial; + conn->secure = secure; + conn->client = conn->initial; + conn->server = conn->initial; + conn->handshake_params.client_cert_sig_scheme = &s2n_null_sig_scheme; + conn->handshake_params.server_cert_sig_scheme = &s2n_null_sig_scheme; + + POSIX_GUARD_RESULT(s2n_psk_parameters_init(&conn->psk_params)); + conn->server_keying_material_lifetime = ONE_WEEK_IN_SEC; + + /* Require all handshakes hashes. This set can be reduced as the handshake progresses. */ + POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); + + if (conn->mode == S2N_SERVER) { + /* Start with the highest protocol version so that the highest common protocol version can be selected */ + /* during handshake. */ + conn->server_protocol_version = s2n_highest_protocol_version; + conn->client_protocol_version = s2n_unknown_protocol_version; + conn->actual_protocol_version = s2n_unknown_protocol_version; + } else { + /* For clients, also set actual_protocol_version. Record generation uses that value for the initial */ + /* ClientHello record version. Not all servers ignore the record version in ClientHello. */ + conn->server_protocol_version = s2n_unknown_protocol_version; + conn->client_protocol_version = s2n_highest_protocol_version; + conn->actual_protocol_version = s2n_highest_protocol_version; + } + + /* Initialize remaining values */ + conn->blinding = S2N_BUILT_IN_BLINDING; + conn->session_ticket_status = S2N_NO_TICKET; + + return 0; +} + +int s2n_connection_set_recv_ctx(struct s2n_connection *conn, void *ctx) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); + conn->recv_io_context = ctx; + return S2N_SUCCESS; +} + +int s2n_connection_set_send_ctx(struct s2n_connection *conn, void *ctx) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); + conn->send_io_context = ctx; + return S2N_SUCCESS; +} + +int s2n_connection_set_recv_cb(struct s2n_connection *conn, s2n_recv_fn recv) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); + conn->recv = recv; + return S2N_SUCCESS; +} + +int s2n_connection_set_send_cb(struct s2n_connection *conn, s2n_send_fn send) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); + conn->send = send; + return S2N_SUCCESS; +} + +int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t **cert_chain_out, uint32_t *cert_chain_len) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(cert_chain_out); + POSIX_ENSURE_REF(cert_chain_len); + POSIX_ENSURE_REF(conn->handshake_params.client_cert_chain.data); + + *cert_chain_out = conn->handshake_params.client_cert_chain.data; + *cert_chain_len = conn->handshake_params.client_cert_chain.size; + + return S2N_SUCCESS; +} + +int s2n_connection_get_cipher_preferences(struct s2n_connection *conn, const struct s2n_cipher_preferences **cipher_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(cipher_preferences); + + if (conn->security_policy_override != NULL) { + *cipher_preferences = conn->security_policy_override->cipher_preferences; + } else if (conn->config->security_policy != NULL) { + *cipher_preferences = conn->config->security_policy->cipher_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_CIPHER_PREFERENCES); + } + + POSIX_ENSURE_REF(*cipher_preferences); + return 0; +} + +int s2n_connection_get_certificate_match(struct s2n_connection *conn, s2n_cert_sni_match *match_status) +{ + POSIX_ENSURE(conn, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(match_status, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); + + /* Server must have gotten past certificate selection */ + POSIX_ENSURE(conn->handshake_params.our_chain_and_key, S2N_ERR_NO_CERT_FOUND); + + if (!s2n_server_received_server_name(conn)) { + *match_status = S2N_SNI_NONE; + } else if (conn->handshake_params.exact_sni_match_exists) { + *match_status = S2N_SNI_EXACT_MATCH; + } else if (conn->handshake_params.wc_sni_match_exists) { + *match_status = S2N_SNI_WILDCARD_MATCH; + } else { + *match_status = S2N_SNI_NO_MATCH; + } + + return S2N_SUCCESS; +} + +int s2n_connection_get_security_policy(struct s2n_connection *conn, const struct s2n_security_policy **security_policy) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(security_policy); + + if (conn->security_policy_override != NULL) { + *security_policy = conn->security_policy_override; + } else if (conn->config->security_policy != NULL) { + *security_policy = conn->config->security_policy; + } else { + POSIX_BAIL(S2N_ERR_INVALID_SECURITY_POLICY); + } + + POSIX_ENSURE_REF(*security_policy); + return 0; +} + +int s2n_connection_get_kem_preferences(struct s2n_connection *conn, const struct s2n_kem_preferences **kem_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(kem_preferences); + + if (conn->security_policy_override != NULL) { + *kem_preferences = conn->security_policy_override->kem_preferences; + } else if (conn->config->security_policy != NULL) { + *kem_preferences = conn->config->security_policy->kem_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_KEM_PREFERENCES); + } + + POSIX_ENSURE_REF(*kem_preferences); + return 0; +} + +int s2n_connection_get_signature_preferences(struct s2n_connection *conn, const struct s2n_signature_preferences **signature_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(signature_preferences); + + if (conn->security_policy_override != NULL) { + *signature_preferences = conn->security_policy_override->signature_preferences; + } else if (conn->config->security_policy != NULL) { + *signature_preferences = conn->config->security_policy->signature_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES); + } + + POSIX_ENSURE_REF(*signature_preferences); + return 0; +} + +int s2n_connection_get_ecc_preferences(struct s2n_connection *conn, const struct s2n_ecc_preferences **ecc_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(ecc_preferences); + + if (conn->security_policy_override != NULL) { + *ecc_preferences = conn->security_policy_override->ecc_preferences; + } else if (conn->config->security_policy != NULL) { + *ecc_preferences = conn->config->security_policy->ecc_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_ECC_PREFERENCES); + } + + POSIX_ENSURE_REF(*ecc_preferences); + return 0; +} + +int s2n_connection_get_protocol_preferences(struct s2n_connection *conn, struct s2n_blob **protocol_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(protocol_preferences); + + *protocol_preferences = NULL; + if (conn->application_protocols_overridden.size > 0) { + *protocol_preferences = &conn->application_protocols_overridden; + } else { + POSIX_ENSURE_REF(conn->config); + *protocol_preferences = &conn->config->application_protocols; + } + + POSIX_ENSURE_REF(*protocol_preferences); + return 0; +} + +static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, + const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(config); + RESULT_ENSURE_REF(client_cert_auth_type); + + if (conn->client_cert_auth_type_overridden) { + *client_cert_auth_type = conn->client_cert_auth_type; + } else if (config->client_cert_auth_type_overridden) { + *client_cert_auth_type = config->client_cert_auth_type; + } else if (conn->mode == S2N_CLIENT) { + /* Clients should default to "Optional" so that they handle any + * CertificateRequests sent by the server. + */ + *client_cert_auth_type = S2N_CERT_AUTH_OPTIONAL; + } else { + /* Servers should default to "None" so that they send no CertificateRequests. */ + *client_cert_auth_type = S2N_CERT_AUTH_NONE; + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_client_auth_type(struct s2n_connection *conn, + s2n_cert_auth_type *client_cert_auth_type) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_and_config_get_client_auth_type( + conn, conn->config, client_cert_auth_type)); + return S2N_SUCCESS; +} + +int s2n_connection_set_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type client_cert_auth_type) +{ + POSIX_ENSURE_REF(conn); + + conn->client_cert_auth_type_overridden = 1; + conn->client_cert_auth_type = client_cert_auth_type; + return 0; +} + +int s2n_connection_set_read_fd(struct s2n_connection *conn, int rfd) +{ + struct s2n_blob ctx_mem = { 0 }; + struct s2n_socket_read_io_context *peer_socket_ctx = NULL; + + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_read_io_context))); + POSIX_GUARD(s2n_blob_zero(&ctx_mem)); + + peer_socket_ctx = (struct s2n_socket_read_io_context *) (void *) ctx_mem.data; + peer_socket_ctx->fd = rfd; + + POSIX_GUARD(s2n_connection_set_recv_cb(conn, s2n_socket_read)); + POSIX_GUARD(s2n_connection_set_recv_ctx(conn, peer_socket_ctx)); + conn->managed_recv_io = true; + + /* This is only needed if the user is using corked io. + * Take the snapshot in case optimized io is enabled after setting the fd. + */ + POSIX_GUARD(s2n_socket_read_snapshot(conn)); + + return 0; +} + +int s2n_connection_get_read_fd(struct s2n_connection *conn, int *readfd) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(readfd); + POSIX_ENSURE((conn->managed_recv_io && conn->recv_io_context), S2N_ERR_INVALID_STATE); + + const struct s2n_socket_read_io_context *peer_socket_ctx = conn->recv_io_context; + *readfd = peer_socket_ctx->fd; + return S2N_SUCCESS; +} + +int s2n_connection_set_write_fd(struct s2n_connection *conn, int wfd) +{ + struct s2n_blob ctx_mem = { 0 }; + struct s2n_socket_write_io_context *peer_socket_ctx = NULL; + + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_write_io_context))); + + peer_socket_ctx = (struct s2n_socket_write_io_context *) (void *) ctx_mem.data; + peer_socket_ctx->fd = wfd; + + POSIX_GUARD(s2n_connection_set_send_cb(conn, s2n_socket_write)); + POSIX_GUARD(s2n_connection_set_send_ctx(conn, peer_socket_ctx)); + conn->managed_send_io = true; + + /* This is only needed if the user is using corked io. + * Take the snapshot in case optimized io is enabled after setting the fd. + */ + POSIX_GUARD(s2n_socket_write_snapshot(conn)); + + uint8_t ipv6 = 0; + if (0 == s2n_socket_is_ipv6(wfd, &ipv6)) { + conn->ipv6 = (ipv6 ? 1 : 0); + } + + conn->write_fd_broken = 0; + + return 0; +} + +int s2n_connection_get_write_fd(struct s2n_connection *conn, int *writefd) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(writefd); + POSIX_ENSURE((conn->managed_send_io && conn->send_io_context), S2N_ERR_INVALID_STATE); + + const struct s2n_socket_write_io_context *peer_socket_ctx = conn->send_io_context; + *writefd = peer_socket_ctx->fd; + return S2N_SUCCESS; +} +int s2n_connection_set_fd(struct s2n_connection *conn, int fd) +{ + POSIX_GUARD(s2n_connection_set_read_fd(conn, fd)); + POSIX_GUARD(s2n_connection_set_write_fd(conn, fd)); + return 0; +} + +int s2n_connection_use_corked_io(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* Caller shouldn't be trying to set s2n IO corked on non-s2n-managed IO */ + POSIX_ENSURE(conn->managed_send_io, S2N_ERR_CORK_SET_ON_UNMANAGED); + conn->corked_io = 1; + + return 0; +} + +uint64_t s2n_connection_get_wire_bytes_in(struct s2n_connection *conn) +{ + if (conn->ktls_recv_enabled) { + return 0; + } + return conn->wire_bytes_in; +} + +uint64_t s2n_connection_get_wire_bytes_out(struct s2n_connection *conn) +{ + if (conn->ktls_send_enabled) { + return 0; + } + return conn->wire_bytes_out; +} + +const char *s2n_connection_get_cipher(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_ENSURE_REF(conn->secure); + PTR_ENSURE_REF(conn->secure->cipher_suite); + + return conn->secure->cipher_suite->name; +} + +int s2n_connection_get_cipher_iana_value(struct s2n_connection *conn, uint8_t *first, uint8_t *second) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_MUT(first); + POSIX_ENSURE_MUT(second); + + /* ensure we've negotiated a cipher suite */ + POSIX_ENSURE(!s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, + s2n_null_cipher_suite.iana_value, sizeof(s2n_null_cipher_suite.iana_value)), + S2N_ERR_INVALID_STATE); + + const uint8_t *iana_value = conn->secure->cipher_suite->iana_value; + *first = iana_value[0]; + *second = iana_value[1]; + + return S2N_SUCCESS; +} + +const char *s2n_connection_get_curve(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_ENSURE_REF(conn->secure); + PTR_ENSURE_REF(conn->secure->cipher_suite); + + if (conn->kex_params.server_ecc_evp_params.negotiated_curve) { + /* TLS1.3 currently only uses ECC groups. */ + bool tls13 = conn->actual_protocol_version >= S2N_TLS13; + /* we check for a full handshake, because TLS 1.2 resumption does not perform + * an additional diffie-hellman exchange */ + bool ecdhe_cipher_negotiated = s2n_kex_includes(conn->secure->cipher_suite->key_exchange_alg, &s2n_ecdhe) + && IS_FULL_HANDSHAKE(conn); + if (tls13 || ecdhe_cipher_negotiated) { + return conn->kex_params.server_ecc_evp_params.negotiated_curve->name; + } + } + + return "NONE"; +} + +const char *s2n_connection_get_kem_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (!conn->kex_params.kem_params.kem) { + return "NONE"; + } + + return conn->kex_params.kem_params.kem->name; +} + +const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (conn->actual_protocol_version < S2N_TLS13 || !conn->kex_params.server_kem_group_params.kem_group) { + return "NONE"; + } + + return conn->kex_params.server_kem_group_params.kem_group->name; +} + +int s2n_connection_get_key_exchange_group(struct s2n_connection *conn, const char **group_name) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(group_name); + + /* s2n_connection_get_curve returns only the ECDH curve portion of a named group, even if + the negotiated group was a hybrid PQ key exchange also containing a KEM. Therefore, + we use the result of s2n_connection_get_kem_group_name if the connection supports PQ. */ + if (s2n_tls13_pq_hybrid_supported(conn)) { + *group_name = s2n_connection_get_kem_group_name(conn); + } else { + *group_name = s2n_connection_get_curve(conn); + } + + POSIX_ENSURE(*group_name != NULL && strcmp(*group_name, "NONE"), S2N_ERR_INVALID_STATE); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_connection_get_client_supported_version(struct s2n_connection *conn, + uint8_t *client_supported_version) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_EQ(conn->mode, S2N_SERVER); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + RESULT_ENSURE_REF(client_hello); + + s2n_parsed_extension *supported_versions_extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_VERSIONS, &client_hello->extensions, + &supported_versions_extension)); + RESULT_ENSURE_REF(supported_versions_extension); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions_stuffer, &supported_versions_extension->extension)); + + uint8_t client_protocol_version = s2n_unknown_protocol_version; + uint8_t actual_protocol_version = s2n_unknown_protocol_version; + RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, &supported_versions_stuffer, + &client_protocol_version, &actual_protocol_version)); + + RESULT_ENSURE_NE(client_protocol_version, s2n_unknown_protocol_version); + + *client_supported_version = client_protocol_version; + + return S2N_RESULT_OK; +} + +int s2n_connection_get_client_protocol_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* For backwards compatibility, the client_protocol_version field isn't updated via the + * supported versions extension on TLS 1.2 servers. See + * https://github.com/aws/s2n-tls/issues/4240. + * + * The extension is processed here to ensure that TLS 1.2 servers report the same client + * protocol version to applications as TLS 1.3 servers. + */ + if (conn->mode == S2N_SERVER && conn->server_protocol_version <= S2N_TLS12) { + uint8_t client_supported_version = s2n_unknown_protocol_version; + s2n_result result = s2n_connection_get_client_supported_version(conn, &client_supported_version); + + /* If the extension wasn't received, or if a client protocol version couldn't be determined + * after processing the extension, the extension is ignored. + */ + if (s2n_result_is_ok(result)) { + return client_supported_version; + } + } + + return conn->client_protocol_version; +} + +int s2n_connection_get_server_protocol_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + return conn->server_protocol_version; +} + +int s2n_connection_get_actual_protocol_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + return conn->actual_protocol_version; +} + +int s2n_connection_get_client_hello_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->client_hello.sslv2) { + return S2N_SSLv2; + } else { + return S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); + } +} + +int s2n_connection_client_cert_used(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (IS_CLIENT_AUTH_HANDSHAKE(conn) && is_handshake_complete(conn)) { + if (IS_CLIENT_AUTH_NO_CERT(conn)) { + return 0; + } + return 1; + } + return 0; +} + +int s2n_connection_get_alert(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) != 2, S2N_ERR_NO_ALERT); + + /* Shallow copy the stuffer. We assume that multiple threads might call this + * function concurrently, so we must not mutate anything outside of the function scope */ + struct s2n_stuffer alert_stuffer = conn->alert_in; + uint8_t alert_code = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); + POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); + + return alert_code; +} + +int s2n_set_server_name(struct s2n_connection *conn, const char *server_name) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(server_name); + + S2N_ERROR_IF(conn->mode != S2N_CLIENT, S2N_ERR_CLIENT_MODE); + + int len = strlen(server_name); + S2N_ERROR_IF(len > S2N_MAX_SERVER_NAME, S2N_ERR_SERVER_NAME_TOO_LONG); + + POSIX_CHECKED_MEMCPY(conn->server_name, server_name, len); + + return 0; +} + +const char *s2n_get_server_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (conn->server_name[0]) { + return conn->server_name; + } + + PTR_GUARD_POSIX(s2n_extension_process(&s2n_client_server_name_extension, conn, &conn->client_hello.extensions)); + + if (!conn->server_name[0]) { + return NULL; + } + + return conn->server_name; +} + +const char *s2n_get_application_protocol(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (strlen(conn->application_protocol) == 0) { + return NULL; + } + + return conn->application_protocol; +} + +int s2n_connection_get_session_id_length(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + /* Stateful session resumption in TLS1.3 using session id is not yet supported. */ + if (conn->actual_protocol_version >= S2N_TLS13) { + return 0; + } + return conn->session_id_len; +} + +int s2n_connection_get_session_id(struct s2n_connection *conn, uint8_t *session_id, size_t max_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(session_id); + + const int session_id_len = s2n_connection_get_session_id_length(conn); + POSIX_GUARD(session_id_len); + + POSIX_ENSURE((size_t) session_id_len <= max_length, S2N_ERR_SESSION_ID_TOO_LONG); + + POSIX_CHECKED_MEMCPY(session_id, conn->session_id, session_id_len); + + return session_id_len; +} + +int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blinding) +{ + POSIX_ENSURE_REF(conn); + conn->blinding = blinding; + + return 0; +} + +#define ONE_S INT64_C(1000000000) + +static S2N_RESULT s2n_connection_get_delay_impl(struct s2n_connection *conn, uint64_t *delay) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(delay); + + if (!conn->delay) { + *delay = 0; + return S2N_RESULT_OK; + } + + uint64_t elapsed = 0; + RESULT_GUARD(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); + + if (elapsed > conn->delay) { + *delay = 0; + return S2N_RESULT_OK; + } + + *delay = conn->delay - elapsed; + + return S2N_RESULT_OK; +} + +uint64_t s2n_connection_get_delay(struct s2n_connection *conn) +{ + uint64_t delay = 0; + if (s2n_result_is_ok(s2n_connection_get_delay_impl(conn, &delay))) { + return delay; + } else { + return UINT64_MAX; + } +} + +/* s2n-tls has a random delay that will trigger for sensitive errors. This is a mitigation + * for possible timing sidechannels. + * + * The historical sidechannel that inspired s2n-tls blinding was the Lucky 13 attack, which takes + * advantage of potential timing differences when removing padding from a record encrypted in CBC mode. + * The attack is only theoretical in TLS; the attack criteria is unlikely to ever occur + * (See: Fardan, N. J. A., & Paterson, K. G. (2013, May 1). Lucky Thirteen: Breaking the TLS and + * DTLS Record Protocols.) However, we still include blinding to provide a defense in depth mitigation. + */ +S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(min); + RESULT_ENSURE_REF(max); + RESULT_ENSURE_REF(conn->config); + + /* + * The default delay is a random value between 10-30s. The rationale behind the range is that the + * floor is the fixed cost that an attacker must pay per attempt, in this case, 10s. The length of + * the range then affects the number of attempts that an attacker must perform in order to recover a + * byte of plaintext with a certain degree of confidence. + * + * A uniform distribution of the range [a, b] has a variance of ((b - a)^2)/12. Therefore, given a + * hypothetical timing difference of 1us, the number of attempts necessary to distinguish the correct + * byte from an incorrect byte in a Lucky13-style attack is (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion + * (note that we first have to convert from seconds to microseconds to match the unit of the timing difference.) + */ + *min = S2N_DEFAULT_BLINDING_MIN * ONE_S; + *max = S2N_DEFAULT_BLINDING_MAX * ONE_S; + + /* Setting the min to 1/3 of the max is an arbitrary ratio of fixed to variable delay. + * It is based on the ratio of our original default values. + */ + if (conn->config->custom_blinding_set) { + *max = conn->config->max_blinding * ONE_S; + *min = *max / 3; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_GUARD(s2n_connection_set_closed(conn)); + + 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; + RESULT_GUARD(s2n_public_random(max - min, &rand_delay)); + + conn->delay = min + rand_delay; + + /* Restart the write timer */ + RESULT_GUARD(s2n_timer_start(conn->config, &conn->write_timer)); + + if (conn->blinding == S2N_BUILT_IN_BLINDING) { + struct timespec sleep_time = { .tv_sec = conn->delay / ONE_S, .tv_nsec = conn->delay % ONE_S }; + + int r = 0; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + } + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection **conn) +{ + RESULT_ENSURE_REF(conn); + if (*conn == NULL) { + return S2N_RESULT_OK; + } + + int error_code = s2n_errno; + int error_type = s2n_error_get_type(error_code); + + switch (error_type) { + case S2N_ERR_T_OK: + /* Ignore no error */ + return S2N_RESULT_OK; + case S2N_ERR_T_BLOCKED: + /* All blocking errors are retriable and should trigger no further action. */ + return S2N_RESULT_OK; + default: + break; + } + + /* Ensure that conn->in doesn't contain any leftover invalid or unauthenticated data. */ + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&(*conn)->in)); + + switch (error_code) { + /* Don't invoke blinding on some of the common errors. + * + * Be careful adding new errors here. Disabling blinding for an + * error that can be triggered by secret / encrypted values can + * potentially lead to a side channel attack. + * + * We may want to someday add an explicit error type for these errors. + */ + case S2N_ERR_CLOSED: + case S2N_ERR_CANCELLED: + case S2N_ERR_CIPHER_NOT_SUPPORTED: + case S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED: + case S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK: + RESULT_GUARD(s2n_connection_set_closed(*conn)); + break; + default: + /* Apply blinding to all other errors */ + RESULT_GUARD(s2n_connection_kill(*conn)); + break; + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_closed(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + s2n_atomic_flag_set(&conn->read_closed); + s2n_atomic_flag_set(&conn->write_closed); + return S2N_RESULT_OK; +} + +const uint8_t *s2n_connection_get_ocsp_response(struct s2n_connection *conn, uint32_t *length) +{ + PTR_ENSURE_REF(conn); + PTR_ENSURE_REF(length); + + *length = conn->status_response.size; + return conn->status_response.data; +} + +S2N_RESULT s2n_connection_set_max_fragment_length(struct s2n_connection *conn, uint16_t max_frag_length) +{ + RESULT_ENSURE_REF(conn); + + if (conn->negotiated_mfl_code) { + /* Respect the upper limit agreed on with the peer */ + RESULT_ENSURE_LT(conn->negotiated_mfl_code, s2n_array_len(mfl_code_to_length)); + conn->max_outgoing_fragment_length = S2N_MIN(mfl_code_to_length[conn->negotiated_mfl_code], max_frag_length); + } else { + conn->max_outgoing_fragment_length = max_frag_length; + } + + /* If no buffer has been initialized yet, no need to resize. + * The standard I/O logic will handle initializing the buffer. + */ + if (s2n_stuffer_is_freed(&conn->out)) { + return S2N_RESULT_OK; + } + + uint16_t max_wire_record_size = 0; + RESULT_GUARD(s2n_record_max_write_size(conn, conn->max_outgoing_fragment_length, &max_wire_record_size)); + if ((conn->out.blob.size < max_wire_record_size)) { + RESULT_GUARD_POSIX(s2n_realloc(&conn->out.blob, max_wire_record_size)); + } + + return S2N_RESULT_OK; +} + +int s2n_connection_prefer_throughput(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_LARGE_FRAGMENT_LENGTH)); + return S2N_SUCCESS; +} + +int s2n_connection_prefer_low_latency(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_SMALL_FRAGMENT_LENGTH)); + return S2N_SUCCESS; +} + +int s2n_connection_set_dynamic_buffers(struct s2n_connection *conn, bool enabled) +{ + POSIX_ENSURE_REF(conn); + conn->dynamic_buffers = enabled; + return S2N_SUCCESS; +} + +int s2n_connection_set_dynamic_record_threshold(struct s2n_connection *conn, uint32_t resize_threshold, uint16_t timeout_threshold) +{ + POSIX_ENSURE_REF(conn); + S2N_ERROR_IF(resize_threshold > S2N_TLS_MAX_RESIZE_THRESHOLD, S2N_ERR_INVALID_DYNAMIC_THRESHOLD); + + conn->dynamic_record_resize_threshold = resize_threshold; + conn->dynamic_record_timeout_threshold = timeout_threshold; + return 0; +} + +int s2n_connection_set_verify_host_callback(struct s2n_connection *conn, s2n_verify_host_fn verify_host_fn, void *data) +{ + POSIX_ENSURE_REF(conn); + + conn->verify_host_fn = verify_host_fn; + conn->data_for_verify_host = data; + conn->verify_host_fn_overridden = 1; + + return 0; +} + +int s2n_connection_recv_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) +{ + POSIX_ENSURE_REF(conn->recv); + /* Make sure we have enough space to write */ + POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, len)); + + int r = 0; + S2N_IO_RETRY_EINTR(r, + conn->recv(conn->recv_io_context, stuffer->blob.data + stuffer->write_cursor, len)); + POSIX_ENSURE(r >= 0, S2N_ERR_RECV_STUFFER_FROM_CONN); + + /* Record just how many bytes we have written */ + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, r)); + return r; +} + +int s2n_connection_send_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->send); + if (conn->write_fd_broken) { + POSIX_BAIL(S2N_ERR_SEND_STUFFER_TO_CONN); + } + /* Make sure we even have the data */ + S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) < len, S2N_ERR_STUFFER_OUT_OF_DATA); + + int w = 0; + S2N_IO_RETRY_EINTR(w, + conn->send(conn->send_io_context, stuffer->blob.data + stuffer->read_cursor, len)); + if (w < 0 && errno == EPIPE) { + conn->write_fd_broken = 1; + } + POSIX_ENSURE(w >= 0, S2N_ERR_SEND_STUFFER_TO_CONN); + + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, w)); + return w; +} + +int s2n_connection_is_managed_corked(const struct s2n_connection *s2n_connection) +{ + POSIX_ENSURE_REF(s2n_connection); + + return (s2n_connection->managed_send_io && s2n_connection->corked_io); +} + +const uint8_t *s2n_connection_get_sct_list(struct s2n_connection *conn, uint32_t *length) +{ + if (!length) { + return NULL; + } + + *length = conn->ct_response.size; + return conn->ct_response.data; +} + +int s2n_connection_is_client_auth_enabled(struct s2n_connection *s2n_connection) +{ + s2n_cert_auth_type auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(s2n_connection, &auth_type)); + + return (auth_type != S2N_CERT_AUTH_NONE); +} + +struct s2n_cert_chain_and_key *s2n_connection_get_selected_cert(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + return conn->handshake_params.our_chain_and_key; +} + +uint8_t s2n_connection_get_protocol_version(const struct s2n_connection *conn) +{ + if (conn == NULL) { + return S2N_UNKNOWN_PROTOCOL_VERSION; + } + + if (conn->actual_protocol_version != S2N_UNKNOWN_PROTOCOL_VERSION) { + return conn->actual_protocol_version; + } + + if (conn->mode == S2N_CLIENT) { + return conn->client_protocol_version; + } + return conn->server_protocol_version; +} + +DEFINE_POINTER_CLEANUP_FUNC(struct s2n_cert_chain *, s2n_cert_chain_free); + +int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain_and_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(cert_chain_and_key); + POSIX_ENSURE_REF(cert_chain_and_key->cert_chain); + + /* Ensure that cert_chain_and_key is empty BEFORE we modify it in any way. + * That includes before tying its cert_chain to DEFER_CLEANUP. + */ + POSIX_ENSURE(cert_chain_and_key->cert_chain->head == NULL, S2N_ERR_INVALID_ARGUMENT); + + DEFER_CLEANUP(struct s2n_cert_chain *cert_chain = cert_chain_and_key->cert_chain, s2n_cert_chain_free_pointer); + struct s2n_cert **insert = &cert_chain->head; + + const struct s2n_x509_validator *validator = &conn->x509_validator; + POSIX_ENSURE_REF(validator); + POSIX_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_CERT_NOT_VALIDATED); + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + POSIX_GUARD_RESULT(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + STACK_OF(X509) *cert_chain_validated = validated_cert_chain.stack; + POSIX_ENSURE_REF(cert_chain_validated); + + int cert_count = sk_X509_num(cert_chain_validated); + POSIX_ENSURE_GTE(cert_count, 0); + + for (size_t cert_idx = 0; cert_idx < (size_t) cert_count; cert_idx++) { + X509 *cert = sk_X509_value(cert_chain_validated, cert_idx); + POSIX_ENSURE_REF(cert); + DEFER_CLEANUP(uint8_t *cert_data = NULL, s2n_crypto_free); + int cert_size = i2d_X509(cert, &cert_data); + POSIX_ENSURE_GT(cert_size, 0); + + struct s2n_blob mem = { 0 }; + POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); + + struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; + POSIX_ENSURE_REF(new_node); + + new_node->next = NULL; + *insert = new_node; + insert = &new_node->next; + + POSIX_GUARD(s2n_alloc(&new_node->raw, cert_size)); + POSIX_CHECKED_MEMCPY(new_node->raw.data, cert_data, cert_size); + } + + ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_signature_scheme_to_tls_iana(const struct s2n_signature_scheme *sig_scheme, + s2n_tls_hash_algorithm *converted_scheme) +{ + RESULT_ENSURE_REF(sig_scheme); + RESULT_ENSURE_REF(converted_scheme); + *converted_scheme = S2N_TLS_HASH_NONE; + + switch (sig_scheme->hash_alg) { + case S2N_HASH_MD5: + *converted_scheme = S2N_TLS_HASH_MD5; + break; + case S2N_HASH_SHA1: + *converted_scheme = S2N_TLS_HASH_SHA1; + break; + case S2N_HASH_SHA224: + *converted_scheme = S2N_TLS_HASH_SHA224; + break; + case S2N_HASH_SHA256: + *converted_scheme = S2N_TLS_HASH_SHA256; + break; + case S2N_HASH_SHA384: + *converted_scheme = S2N_TLS_HASH_SHA384; + break; + case S2N_HASH_SHA512: + *converted_scheme = S2N_TLS_HASH_SHA512; + break; + case S2N_HASH_MD5_SHA1: + *converted_scheme = S2N_TLS_HASH_MD5_SHA1; + break; + case S2N_HASH_NONE: + case S2N_HASH_SHAKE256_64: + case S2N_HASH_ALGS_COUNT: + *converted_scheme = S2N_TLS_HASH_NONE; + break; + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_selected_digest_algorithm(struct s2n_connection *conn, + s2n_tls_hash_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( + conn->handshake_params.server_cert_sig_scheme, converted_scheme)); + + return S2N_SUCCESS; +} + +int s2n_connection_get_selected_client_cert_digest_algorithm(struct s2n_connection *conn, + s2n_tls_hash_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( + conn->handshake_params.client_cert_sig_scheme, converted_scheme)); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_signature_scheme_to_signature_algorithm(const struct s2n_signature_scheme *sig_scheme, + s2n_tls_signature_algorithm *converted_scheme) +{ + RESULT_ENSURE_REF(sig_scheme); + RESULT_ENSURE_REF(converted_scheme); + *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; + + switch (sig_scheme->sig_alg) { + case S2N_SIGNATURE_RSA: + *converted_scheme = S2N_TLS_SIGNATURE_RSA; + break; + case S2N_SIGNATURE_ECDSA: + *converted_scheme = S2N_TLS_SIGNATURE_ECDSA; + break; + case S2N_SIGNATURE_RSA_PSS_RSAE: + *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_RSAE; + break; + case S2N_SIGNATURE_RSA_PSS_PSS: + *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_PSS; + break; + case S2N_SIGNATURE_MLDSA: + *converted_scheme = S2N_TLS_SIGNATURE_MLDSA; + break; + case S2N_SIGNATURE_ANONYMOUS: + *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; + break; + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_selected_signature_algorithm(struct s2n_connection *conn, + s2n_tls_signature_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( + conn->handshake_params.server_cert_sig_scheme, converted_scheme)); + + return S2N_SUCCESS; +} + +int s2n_connection_get_selected_client_cert_signature_algorithm(struct s2n_connection *conn, + s2n_tls_signature_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( + conn->handshake_params.client_cert_sig_scheme, converted_scheme)); + + return S2N_SUCCESS; +} + +int s2n_connection_get_signature_scheme(struct s2n_connection *conn, const char **scheme_name) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(scheme_name); + POSIX_ENSURE(IS_NEGOTIATED(conn), S2N_ERR_INVALID_STATE); + + const struct s2n_signature_scheme *scheme = conn->handshake_params.server_cert_sig_scheme; + /* The scheme should never be NULL. A "none" placeholder is used if no + * scheme has been negotiated. + */ + POSIX_ENSURE_REF(scheme); + + *scheme_name = scheme->name; + if (scheme->signature_curve) { + /* Some TLS1.2 and TLS1.3 signature schemes share an IANA value, + * but are NOT the same. The TLS1.3 version implies a specific curve. + */ + if (conn->actual_protocol_version >= S2N_TLS13) { + *scheme_name = scheme->tls13_name; + } else { + *scheme_name = scheme->legacy_name; + } + } + + POSIX_ENSURE_REF(*scheme_name); + return S2N_SUCCESS; +} + +/* + * Gets the config set on the connection. + */ +int s2n_connection_get_config(struct s2n_connection *conn, struct s2n_config **config) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(config); + + if (s2n_fetch_default_config() == conn->config) { + POSIX_BAIL(S2N_ERR_NULL); + } + + *config = conn->config; + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_connection_dynamic_free_out_buffer(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* free the out buffer if we're in dynamic mode and it's completely flushed */ + if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->out)) { + /* since outgoing buffers are already encrypted, the buffers don't need to be zeroed, which saves some overhead */ + RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->out)); + + /* reset the stuffer to its initial state */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_dynamic_free_in_buffer(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* free `buffer_in` if we're in dynamic mode and it's completely flushed */ + if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->buffer_in)) { + /* when copying the buffer into the application, we use `s2n_stuffer_erase_and_read`, which already zeroes the memory */ + RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->buffer_in)); + + /* reset the stuffer to its initial state */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); + } + + return S2N_RESULT_OK; +} + +bool s2n_connection_check_io_status(struct s2n_connection *conn, s2n_io_status status) +{ + if (!conn) { + return false; + } + + bool read_closed = s2n_atomic_flag_test(&conn->read_closed); + bool write_closed = s2n_atomic_flag_test(&conn->write_closed); + bool full_duplex = !read_closed && !write_closed; + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.1 + *# Note that this is a change from versions of TLS prior to TLS 1.3 in + *# which implementations were required to react to a "close_notify" by + *# discarding pending writes and sending an immediate "close_notify" + *# alert of their own. + */ + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + switch (status) { + case S2N_IO_WRITABLE: + case S2N_IO_READABLE: + case S2N_IO_FULL_DUPLEX: + return full_duplex; + case S2N_IO_CLOSED: + return !full_duplex; + } + } + + switch (status) { + case S2N_IO_WRITABLE: + return !write_closed; + case S2N_IO_READABLE: + return !read_closed; + case S2N_IO_FULL_DUPLEX: + return full_duplex; + case S2N_IO_CLOSED: + return read_closed && write_closed; + } + + return false; +} + +S2N_RESULT s2n_connection_get_secure_cipher(struct s2n_connection *conn, const struct s2n_cipher **cipher) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(cipher); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + *cipher = conn->secure->cipher_suite->record_alg->cipher; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_get_sequence_number(struct s2n_connection *conn, + s2n_mode mode, struct s2n_blob *seq_num) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(seq_num); + RESULT_ENSURE_REF(conn->secure); + + switch (mode) { + case S2N_CLIENT: + RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->client_sequence_number, + sizeof(conn->secure->client_sequence_number))); + break; + case S2N_SERVER: + RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->server_sequence_number, + sizeof(conn->secure->server_sequence_number))); + break; + default: + RESULT_BAIL(S2N_ERR_SAFETY); + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_key_update_counts(struct s2n_connection *conn, + uint8_t *send_key_updates, uint8_t *recv_key_updates) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(send_key_updates); + POSIX_ENSURE_REF(recv_key_updates); + *send_key_updates = conn->send_key_updated; + *recv_key_updates = conn->recv_key_updated; + return S2N_SUCCESS; +} + +int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled) +{ + POSIX_ENSURE_REF(conn); + /* QUIC support is not currently compatible with recv_buffering */ + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_INVALID_STATE); + conn->recv_buffering = enabled; + return S2N_SUCCESS; +} diff --git a/tls/s2n_early_data.c b/tls/s2n_early_data.c index d049f56dbab..1d97943075f 100644 --- a/tls/s2n_early_data.c +++ b/tls/s2n_early_data.c @@ -1,435 +1,436 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_early_data.h" - -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_psk.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -const s2n_early_data_state valid_previous_states[] = { - [S2N_EARLY_DATA_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, - [S2N_EARLY_DATA_NOT_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, - [S2N_EARLY_DATA_REJECTED] = S2N_EARLY_DATA_REQUESTED, - [S2N_EARLY_DATA_ACCEPTED] = S2N_EARLY_DATA_REQUESTED, - [S2N_END_OF_EARLY_DATA] = S2N_EARLY_DATA_ACCEPTED, -}; - -S2N_RESULT s2n_connection_set_early_data_state(struct s2n_connection *conn, s2n_early_data_state next_state) -{ - RESULT_ENSURE_REF(conn); - if (conn->early_data_state == next_state) { - return S2N_RESULT_OK; - } - RESULT_ENSURE(next_state < S2N_EARLY_DATA_STATES_COUNT, S2N_ERR_INVALID_EARLY_DATA_STATE); - RESULT_ENSURE(next_state != S2N_UNKNOWN_EARLY_DATA_STATE, S2N_ERR_INVALID_EARLY_DATA_STATE); - RESULT_ENSURE(conn->early_data_state == valid_previous_states[next_state], S2N_ERR_INVALID_EARLY_DATA_STATE); - conn->early_data_state = next_state; - return S2N_RESULT_OK; -} - -int s2n_connection_set_early_data_expected(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - conn->early_data_expected = true; - return S2N_SUCCESS; -} - -int s2n_connection_set_end_of_early_data(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - conn->early_data_expected = false; - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_early_data_validate(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# In order to accept early data, the server MUST have accepted a PSK - *# cipher suite and selected the first key offered in the client's - *# "pre_shared_key" extension. - **/ - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - RESULT_ENSURE_EQ(conn->psk_params.chosen_psk_wire_index, 0); - - struct s2n_early_data_config *config = &conn->psk_params.chosen_psk->early_data_config; - RESULT_ENSURE_GT(config->max_early_data_size, 0); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# In addition, it MUST verify that the - *# following values are the same as those associated with the - *# selected PSK: - *# - *# - The TLS version number - **/ - RESULT_ENSURE_EQ(config->protocol_version, s2n_connection_get_protocol_version(conn)); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# - The selected cipher suite - **/ - RESULT_ENSURE_EQ(config->cipher_suite, conn->secure->cipher_suite); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# - The selected ALPN [RFC7301] protocol, if any - **/ - const size_t app_protocol_size = strlen(conn->application_protocol); - if (app_protocol_size > 0 || config->application_protocol.size > 0) { - RESULT_ENSURE_EQ(config->application_protocol.size, app_protocol_size + 1 /* null-terminating char */); - RESULT_ENSURE(s2n_constant_time_equals(config->application_protocol.data, (uint8_t *) conn->application_protocol, app_protocol_size), S2N_ERR_SAFETY); - } - - return S2N_RESULT_OK; -} - -bool s2n_early_data_is_valid_for_connection(struct s2n_connection *conn) -{ - return s2n_result_is_ok(s2n_early_data_validate(conn)); -} - -S2N_RESULT s2n_early_data_accept_or_reject(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (conn->early_data_state != S2N_EARLY_DATA_REQUESTED) { - return S2N_RESULT_OK; - } - - if (conn->handshake.early_data_async_state.conn) { - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# If any of these checks fail, the server MUST NOT respond with the - *# extension - **/ - if (!s2n_early_data_is_valid_for_connection(conn)) { - RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - return S2N_RESULT_OK; - } - - /* Even if the connection is valid for early data, the client can't consider - * early data accepted until the server sends the early data indication. */ - if (conn->mode == S2N_CLIENT) { - return S2N_RESULT_OK; - } - - /* The server should reject early data if the application is not prepared to handle it. */ - if (!conn->early_data_expected) { - RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - return S2N_RESULT_OK; - } - - /* If early data would otherwise be accepted, let the application apply any additional restrictions. - * For example, an application could use this callback to implement anti-replay protections. - * - * This callback can be either synchronous or asynchronous. The handshake will not proceed until - * the application either accepts or rejects early data. - */ - RESULT_ENSURE_REF(conn->config); - if (conn->config->early_data_cb) { - conn->handshake.early_data_async_state.conn = conn; - RESULT_ENSURE(conn->config->early_data_cb(conn, &conn->handshake.early_data_async_state) >= S2N_SUCCESS, - S2N_ERR_CANCELLED); - if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - } else { - RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); - } - return S2N_RESULT_OK; -} - -int s2n_config_set_server_max_early_data_size(struct s2n_config *config, uint32_t max_early_data_size) -{ - POSIX_ENSURE_REF(config); - config->server_max_early_data_size = max_early_data_size; - return S2N_SUCCESS; -} - -int s2n_connection_set_server_max_early_data_size(struct s2n_connection *conn, uint32_t max_early_data_size) -{ - POSIX_ENSURE_REF(conn); - conn->server_max_early_data_size = max_early_data_size; - conn->server_max_early_data_size_overridden = true; - return S2N_SUCCESS; -} - -S2N_RESULT s2n_early_data_get_server_max_size(struct s2n_connection *conn, uint32_t *max_early_data_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(max_early_data_size); - if (conn->server_max_early_data_size_overridden) { - *max_early_data_size = conn->server_max_early_data_size; - } else { - RESULT_ENSURE_REF(conn->config); - *max_early_data_size = conn->config->server_max_early_data_size; - } - return S2N_RESULT_OK; -} - -int s2n_connection_set_server_early_data_context(struct s2n_connection *conn, const uint8_t *context, uint16_t context_size) -{ - POSIX_ENSURE_REF(conn); - if (context_size > 0) { - POSIX_ENSURE_REF(context); - } - - POSIX_GUARD(s2n_realloc(&conn->server_early_data_context, context_size)); - POSIX_CHECKED_MEMCPY(conn->server_early_data_context.data, context, context_size); - return S2N_SUCCESS; -} - -S2N_CLEANUP_RESULT s2n_early_data_config_free(struct s2n_early_data_config *config) -{ - if (config == NULL) { - return S2N_RESULT_OK; - } - RESULT_GUARD_POSIX(s2n_free(&config->application_protocol)); - RESULT_GUARD_POSIX(s2n_free(&config->context)); - return S2N_RESULT_OK; -} - -int s2n_psk_configure_early_data(struct s2n_psk *psk, uint32_t max_early_data_size, - uint8_t cipher_suite_first_byte, uint8_t cipher_suite_second_byte) -{ - POSIX_ENSURE_REF(psk); - - const uint8_t cipher_suite_iana[] = { cipher_suite_first_byte, cipher_suite_second_byte }; - struct s2n_cipher_suite *cipher_suite = NULL; - POSIX_GUARD_RESULT(s2n_cipher_suite_from_iana(cipher_suite_iana, sizeof(cipher_suite_iana), &cipher_suite)); - POSIX_ENSURE_REF(cipher_suite); - POSIX_ENSURE(cipher_suite->prf_alg == psk->hmac_alg, S2N_ERR_INVALID_ARGUMENT); - - psk->early_data_config.max_early_data_size = max_early_data_size; - psk->early_data_config.protocol_version = S2N_TLS13; - psk->early_data_config.cipher_suite = cipher_suite; - return S2N_SUCCESS; -} - -int s2n_psk_set_application_protocol(struct s2n_psk *psk, const uint8_t *application_protocol, uint8_t size) -{ - POSIX_ENSURE_REF(psk); - if (size > 0) { - POSIX_ENSURE_REF(application_protocol); - } - struct s2n_blob *protocol_blob = &psk->early_data_config.application_protocol; - POSIX_GUARD(s2n_realloc(protocol_blob, size)); - POSIX_CHECKED_MEMCPY(protocol_blob->data, application_protocol, size); - return S2N_SUCCESS; -} - -int s2n_psk_set_early_data_context(struct s2n_psk *psk, const uint8_t *context, uint16_t size) -{ - POSIX_ENSURE_REF(psk); - if (size > 0) { - POSIX_ENSURE_REF(context); - } - struct s2n_blob *context_blob = &psk->early_data_config.context; - POSIX_GUARD(s2n_realloc(context_blob, size)); - POSIX_CHECKED_MEMCPY(context_blob->data, context, size); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_early_data_config_clone(struct s2n_psk *new_psk, struct s2n_early_data_config *old_config) -{ - RESULT_ENSURE_REF(old_config); - RESULT_ENSURE_REF(new_psk); - - struct s2n_early_data_config config_copy = new_psk->early_data_config; - - /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ - new_psk->early_data_config = *old_config; - new_psk->early_data_config.application_protocol = config_copy.application_protocol; - new_psk->early_data_config.context = config_copy.context; - - /* Clone / realloc blobs */ - RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(new_psk, old_config->application_protocol.data, - old_config->application_protocol.size)); - RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(new_psk, old_config->context.data, - old_config->context.size)); - - return S2N_RESULT_OK; -} - -int s2n_connection_get_early_data_status(struct s2n_connection *conn, s2n_early_data_status_t *status) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(status); - - switch (conn->early_data_state) { - case S2N_EARLY_DATA_STATES_COUNT: - break; - case S2N_EARLY_DATA_NOT_REQUESTED: - *status = S2N_EARLY_DATA_STATUS_NOT_REQUESTED; - return S2N_SUCCESS; - case S2N_EARLY_DATA_REJECTED: - *status = S2N_EARLY_DATA_STATUS_REJECTED; - return S2N_SUCCESS; - case S2N_END_OF_EARLY_DATA: - *status = S2N_EARLY_DATA_STATUS_END; - return S2N_SUCCESS; - case S2N_UNKNOWN_EARLY_DATA_STATE: - case S2N_EARLY_DATA_REQUESTED: - case S2N_EARLY_DATA_ACCEPTED: - *status = S2N_EARLY_DATA_STATUS_OK; - return S2N_SUCCESS; - } - POSIX_BAIL(S2N_ERR_INVALID_EARLY_DATA_STATE); -} - -static S2N_RESULT s2n_get_remaining_early_data_bytes(struct s2n_connection *conn, uint32_t *early_data_allowed) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(early_data_allowed); - *early_data_allowed = 0; - - uint32_t max_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); - - RESULT_ENSURE(max_early_data_size >= conn->early_data_bytes, S2N_ERR_MAX_EARLY_DATA_SIZE); - *early_data_allowed = (max_early_data_size - conn->early_data_bytes); - - return S2N_RESULT_OK; -} - -int s2n_connection_get_remaining_early_data_size(struct s2n_connection *conn, uint32_t *allowed_early_data_size) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(allowed_early_data_size); - *allowed_early_data_size = 0; - - switch (conn->early_data_state) { - case S2N_EARLY_DATA_STATES_COUNT: - case S2N_EARLY_DATA_NOT_REQUESTED: - case S2N_EARLY_DATA_REJECTED: - case S2N_END_OF_EARLY_DATA: - *allowed_early_data_size = 0; - break; - case S2N_UNKNOWN_EARLY_DATA_STATE: - case S2N_EARLY_DATA_REQUESTED: - case S2N_EARLY_DATA_ACCEPTED: - POSIX_GUARD_RESULT(s2n_get_remaining_early_data_bytes(conn, allowed_early_data_size)); - break; - } - return S2N_SUCCESS; -} - -int s2n_connection_get_max_early_data_size(struct s2n_connection *conn, uint32_t *max_early_data_size) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(max_early_data_size); - *max_early_data_size = 0; - - uint32_t server_max_early_data_size = 0; - POSIX_GUARD_RESULT(s2n_early_data_get_server_max_size(conn, &server_max_early_data_size)); - - if (conn->psk_params.psk_list.len == 0) { - /* This method may be called by the server before loading its PSKs. - * The server can load its PSKs during the handshake, either via the PSK selection callback - * or by receiving a stateless session ticket. - * - * Before that happens, we should make an optimistic assumption of the early data size. - * That way, the max early data size always decreases (for example, it won't go from 0 -> UINT32_MAX - * after receiving a PSK in the ClientHello). - */ - if (conn->mode == S2N_SERVER && !IS_NEGOTIATED(conn)) { - *max_early_data_size = server_max_early_data_size; - } - return S2N_SUCCESS; - } - - struct s2n_psk *first_psk = NULL; - POSIX_GUARD_RESULT(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &first_psk)); - POSIX_ENSURE_REF(first_psk); - *max_early_data_size = first_psk->early_data_config.max_early_data_size; - - /* For the server, we should use the minimum of the limit retrieved from the ticket - * and the current limit being set for new tickets. - * - * This is defensive: even if more early data was previously allowed, the server may not be - * willing or able to handle that much early data now. - * - * We don't do this for external PSKs because the server has intentionally set the limit - * while setting up this connection, not during a previous connection. - */ - if (conn->mode == S2N_SERVER && first_psk->type == S2N_PSK_TYPE_RESUMPTION) { - *max_early_data_size = S2N_MIN(*max_early_data_size, server_max_early_data_size); - } - - return S2N_SUCCESS; -} - -int s2n_config_set_early_data_cb(struct s2n_config *config, s2n_early_data_cb cb) -{ - POSIX_ENSURE_REF(config); - config->early_data_cb = cb; - return S2N_SUCCESS; -} - -int s2n_offered_early_data_get_context_length(struct s2n_offered_early_data *early_data, uint16_t *context_len) -{ - POSIX_ENSURE_REF(context_len); - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->psk_params.chosen_psk); - struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; - - *context_len = early_data_config->context.size; - - return S2N_SUCCESS; -} - -int s2n_offered_early_data_get_context(struct s2n_offered_early_data *early_data, uint8_t *context, uint16_t max_len) -{ - POSIX_ENSURE_REF(context); - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->psk_params.chosen_psk); - struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; - - POSIX_ENSURE(early_data_config->context.size <= max_len, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(context, early_data_config->context.data, early_data_config->context.size); - - return S2N_SUCCESS; -} - -int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_data) -{ - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - return S2N_SUCCESS; -} - -int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data) -{ - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_early_data.h" + +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_psk.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +const s2n_early_data_state valid_previous_states[] = { + [S2N_EARLY_DATA_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, + [S2N_EARLY_DATA_NOT_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, + [S2N_EARLY_DATA_REJECTED] = S2N_EARLY_DATA_REQUESTED, + [S2N_EARLY_DATA_ACCEPTED] = S2N_EARLY_DATA_REQUESTED, + [S2N_END_OF_EARLY_DATA] = S2N_EARLY_DATA_ACCEPTED, +}; + +S2N_RESULT s2n_connection_set_early_data_state(struct s2n_connection *conn, s2n_early_data_state next_state) +{ + RESULT_ENSURE_REF(conn); + if (conn->early_data_state == next_state) { + return S2N_RESULT_OK; + } + RESULT_ENSURE(next_state < S2N_EARLY_DATA_STATES_COUNT, S2N_ERR_INVALID_EARLY_DATA_STATE); + RESULT_ENSURE(next_state != S2N_UNKNOWN_EARLY_DATA_STATE, S2N_ERR_INVALID_EARLY_DATA_STATE); + RESULT_ENSURE(conn->early_data_state == valid_previous_states[next_state], S2N_ERR_INVALID_EARLY_DATA_STATE); + conn->early_data_state = next_state; + return S2N_RESULT_OK; +} + +int s2n_connection_set_early_data_expected(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + conn->early_data_expected = true; + return S2N_SUCCESS; +} + +int s2n_connection_set_end_of_early_data(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + conn->early_data_expected = false; + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_early_data_validate(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# In order to accept early data, the server MUST have accepted a PSK + *# cipher suite and selected the first key offered in the client's + *# "pre_shared_key" extension. + **/ + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + RESULT_ENSURE_EQ(conn->psk_params.chosen_psk_wire_index, 0); + + struct s2n_early_data_config *config = &conn->psk_params.chosen_psk->early_data_config; + RESULT_ENSURE_GT(config->max_early_data_size, 0); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# In addition, it MUST verify that the + *# following values are the same as those associated with the + *# selected PSK: + *# + *# - The TLS version number + **/ + RESULT_ENSURE_EQ(config->protocol_version, s2n_connection_get_protocol_version(conn)); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# - The selected cipher suite + **/ + RESULT_ENSURE_EQ(config->cipher_suite, conn->secure->cipher_suite); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# - The selected ALPN [RFC7301] protocol, if any + **/ + const size_t app_protocol_size = strlen(conn->application_protocol); + if (app_protocol_size > 0 || config->application_protocol.size > 0) { + RESULT_ENSURE_EQ(config->application_protocol.size, app_protocol_size + 1 /* null-terminating char */); + RESULT_ENSURE(s2n_constant_time_equals(config->application_protocol.data, (uint8_t *) conn->application_protocol, app_protocol_size), S2N_ERR_SAFETY); + } + + return S2N_RESULT_OK; +} + +bool s2n_early_data_is_valid_for_connection(struct s2n_connection *conn) +{ + return s2n_result_is_ok(s2n_early_data_validate(conn)); +} + +S2N_RESULT s2n_early_data_accept_or_reject(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (conn->early_data_state != S2N_EARLY_DATA_REQUESTED) { + return S2N_RESULT_OK; + } + + if (conn->handshake.early_data_async_state.conn) { + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# If any of these checks fail, the server MUST NOT respond with the + *# extension + **/ + if (!s2n_early_data_is_valid_for_connection(conn)) { + RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + return S2N_RESULT_OK; + } + + /* Even if the connection is valid for early data, the client can't consider + * early data accepted until the server sends the early data indication. */ + if (conn->mode == S2N_CLIENT) { + return S2N_RESULT_OK; + } + + /* The server should reject early data if the application is not prepared to handle it. */ + if (!conn->early_data_expected) { + RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + return S2N_RESULT_OK; + } + + /* If early data would otherwise be accepted, let the application apply any additional restrictions. + * For example, an application could use this callback to implement anti-replay protections. + * + * This callback can be either synchronous or asynchronous. The handshake will not proceed until + * the application either accepts or rejects early data. + */ + RESULT_ENSURE_REF(conn->config); + if (conn->config->early_data_cb) { + conn->handshake.early_data_async_state.conn = conn; + RESULT_ENSURE(conn->config->early_data_cb(conn, &conn->handshake.early_data_async_state) >= S2N_SUCCESS, + S2N_ERR_CANCELLED); + if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + } else { + RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); + } + return S2N_RESULT_OK; +} + +int s2n_config_set_server_max_early_data_size(struct s2n_config *config, uint32_t max_early_data_size) +{ + POSIX_ENSURE_REF(config); + config->server_max_early_data_size = max_early_data_size; + return S2N_SUCCESS; +} + +int s2n_connection_set_server_max_early_data_size(struct s2n_connection *conn, uint32_t max_early_data_size) +{ + POSIX_ENSURE_REF(conn); + conn->server_max_early_data_size = max_early_data_size; + conn->server_max_early_data_size_overridden = true; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_early_data_get_server_max_size(struct s2n_connection *conn, uint32_t *max_early_data_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(max_early_data_size); + if (conn->server_max_early_data_size_overridden) { + *max_early_data_size = conn->server_max_early_data_size; + } else { + RESULT_ENSURE_REF(conn->config); + *max_early_data_size = conn->config->server_max_early_data_size; + } + return S2N_RESULT_OK; +} + +int s2n_connection_set_server_early_data_context(struct s2n_connection *conn, const uint8_t *context, uint16_t context_size) +{ + POSIX_ENSURE_REF(conn); + if (context_size > 0) { + POSIX_ENSURE_REF(context); + } + + POSIX_GUARD(s2n_realloc(&conn->server_early_data_context, context_size)); + POSIX_CHECKED_MEMCPY(conn->server_early_data_context.data, context, context_size); + return S2N_SUCCESS; +} + +S2N_CLEANUP_RESULT s2n_early_data_config_free(struct s2n_early_data_config *config) +{ + if (config == NULL) { + return S2N_RESULT_OK; + } + RESULT_GUARD_POSIX(s2n_free(&config->application_protocol)); + RESULT_GUARD_POSIX(s2n_free(&config->context)); + return S2N_RESULT_OK; +} + +int s2n_psk_configure_early_data(struct s2n_psk *psk, uint32_t max_early_data_size, + uint8_t cipher_suite_first_byte, uint8_t cipher_suite_second_byte) +{ + POSIX_ENSURE_REF(psk); + + const uint8_t cipher_suite_iana[] = { cipher_suite_first_byte, cipher_suite_second_byte }; + struct s2n_cipher_suite *cipher_suite = NULL; + POSIX_GUARD_RESULT(s2n_cipher_suite_from_iana(cipher_suite_iana, sizeof(cipher_suite_iana), &cipher_suite)); + POSIX_ENSURE_REF(cipher_suite); + POSIX_ENSURE(cipher_suite->prf_alg == psk->hmac_alg, S2N_ERR_INVALID_ARGUMENT); + + psk->early_data_config.max_early_data_size = max_early_data_size; + psk->early_data_config.protocol_version = S2N_TLS13; + psk->early_data_config.cipher_suite = cipher_suite; + return S2N_SUCCESS; +} + +int s2n_psk_set_application_protocol(struct s2n_psk *psk, const uint8_t *application_protocol, uint8_t size) +{ + POSIX_ENSURE_REF(psk); + if (size > 0) { + POSIX_ENSURE_REF(application_protocol); + } + struct s2n_blob *protocol_blob = &psk->early_data_config.application_protocol; + POSIX_GUARD(s2n_realloc(protocol_blob, size)); + POSIX_CHECKED_MEMCPY(protocol_blob->data, application_protocol, size); + return S2N_SUCCESS; +} + +int s2n_psk_set_early_data_context(struct s2n_psk *psk, const uint8_t *context, uint16_t size) +{ + POSIX_ENSURE_REF(psk); + if (size > 0) { + POSIX_ENSURE_REF(context); + } + struct s2n_blob *context_blob = &psk->early_data_config.context; + POSIX_GUARD(s2n_realloc(context_blob, size)); + POSIX_CHECKED_MEMCPY(context_blob->data, context, size); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_early_data_config_clone(struct s2n_psk *new_psk, struct s2n_early_data_config *old_config) +{ + RESULT_ENSURE_REF(old_config); + RESULT_ENSURE_REF(new_psk); + + struct s2n_early_data_config config_copy = new_psk->early_data_config; + + /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ + new_psk->early_data_config = *old_config; + new_psk->early_data_config.application_protocol = config_copy.application_protocol; + new_psk->early_data_config.context = config_copy.context; + + /* Clone / realloc blobs */ + RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(new_psk, old_config->application_protocol.data, + old_config->application_protocol.size)); + RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(new_psk, old_config->context.data, + old_config->context.size)); + + return S2N_RESULT_OK; +} + +int s2n_connection_get_early_data_status(struct s2n_connection *conn, s2n_early_data_status_t *status) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(status); + + switch (conn->early_data_state) { + case S2N_EARLY_DATA_STATES_COUNT: + break; + case S2N_EARLY_DATA_NOT_REQUESTED: + *status = S2N_EARLY_DATA_STATUS_NOT_REQUESTED; + return S2N_SUCCESS; + case S2N_EARLY_DATA_REJECTED: + *status = S2N_EARLY_DATA_STATUS_REJECTED; + return S2N_SUCCESS; + case S2N_END_OF_EARLY_DATA: + *status = S2N_EARLY_DATA_STATUS_END; + return S2N_SUCCESS; + case S2N_UNKNOWN_EARLY_DATA_STATE: + case S2N_EARLY_DATA_REQUESTED: + case S2N_EARLY_DATA_ACCEPTED: + *status = S2N_EARLY_DATA_STATUS_OK; + return S2N_SUCCESS; + } + POSIX_BAIL(S2N_ERR_INVALID_EARLY_DATA_STATE); +} + +static S2N_RESULT s2n_get_remaining_early_data_bytes(struct s2n_connection *conn, uint32_t *early_data_allowed) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(early_data_allowed); + *early_data_allowed = 0; + + uint32_t max_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); + + RESULT_ENSURE(max_early_data_size >= conn->early_data_bytes, S2N_ERR_MAX_EARLY_DATA_SIZE); + *early_data_allowed = (max_early_data_size - conn->early_data_bytes); + + return S2N_RESULT_OK; +} + +int s2n_connection_get_remaining_early_data_size(struct s2n_connection *conn, uint32_t *allowed_early_data_size) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(allowed_early_data_size); + *allowed_early_data_size = 0; + + switch (conn->early_data_state) { + case S2N_EARLY_DATA_STATES_COUNT: + case S2N_EARLY_DATA_NOT_REQUESTED: + case S2N_EARLY_DATA_REJECTED: + case S2N_END_OF_EARLY_DATA: + *allowed_early_data_size = 0; + break; + case S2N_UNKNOWN_EARLY_DATA_STATE: + case S2N_EARLY_DATA_REQUESTED: + case S2N_EARLY_DATA_ACCEPTED: + POSIX_GUARD_RESULT(s2n_get_remaining_early_data_bytes(conn, allowed_early_data_size)); + break; + } + return S2N_SUCCESS; +} + +int s2n_connection_get_max_early_data_size(struct s2n_connection *conn, uint32_t *max_early_data_size) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(max_early_data_size); + *max_early_data_size = 0; + + uint32_t server_max_early_data_size = 0; + POSIX_GUARD_RESULT(s2n_early_data_get_server_max_size(conn, &server_max_early_data_size)); + + if (conn->psk_params.psk_list.len == 0) { + /* This method may be called by the server before loading its PSKs. + * The server can load its PSKs during the handshake, either via the PSK selection callback + * or by receiving a stateless session ticket. + * + * Before that happens, we should make an optimistic assumption of the early data size. + * That way, the max early data size always decreases (for example, it won't go from 0 -> UINT32_MAX + * after receiving a PSK in the ClientHello). + */ + if (conn->mode == S2N_SERVER && !IS_NEGOTIATED(conn)) { + *max_early_data_size = server_max_early_data_size; + } + return S2N_SUCCESS; + } + + struct s2n_psk *first_psk = NULL; + POSIX_GUARD_RESULT(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &first_psk)); + POSIX_ENSURE_REF(first_psk); + *max_early_data_size = first_psk->early_data_config.max_early_data_size; + + /* For the server, we should use the minimum of the limit retrieved from the ticket + * and the current limit being set for new tickets. + * + * This is defensive: even if more early data was previously allowed, the server may not be + * willing or able to handle that much early data now. + * + * We don't do this for external PSKs because the server has intentionally set the limit + * while setting up this connection, not during a previous connection. + */ + if (conn->mode == S2N_SERVER && first_psk->type == S2N_PSK_TYPE_RESUMPTION) { + *max_early_data_size = S2N_MIN(*max_early_data_size, server_max_early_data_size); + } + + return S2N_SUCCESS; +} + +int s2n_config_set_early_data_cb(struct s2n_config *config, s2n_early_data_cb cb) +{ + POSIX_ENSURE_REF(config); + config->early_data_cb = cb; + return S2N_SUCCESS; +} + +int s2n_offered_early_data_get_context_length(struct s2n_offered_early_data *early_data, uint16_t *context_len) +{ + POSIX_ENSURE_REF(context_len); + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->psk_params.chosen_psk); + struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; + + *context_len = early_data_config->context.size; + + return S2N_SUCCESS; +} + +int s2n_offered_early_data_get_context(struct s2n_offered_early_data *early_data, uint8_t *context, uint16_t max_len) +{ + POSIX_ENSURE_REF(context); + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->psk_params.chosen_psk); + struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; + + POSIX_ENSURE(early_data_config->context.size <= max_len, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(context, early_data_config->context.data, early_data_config->context.size); + + return S2N_SUCCESS; +} + +int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + return S2N_SUCCESS; +} + +int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); + return S2N_SUCCESS; +} diff --git a/tls/s2n_early_data_io.c b/tls/s2n_early_data_io.c index c6a267a8625..232266c4bb4 100644 --- a/tls/s2n_early_data_io.c +++ b/tls/s2n_early_data_io.c @@ -1,276 +1,277 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_connection.h" -#include "tls/s2n_early_data.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -int s2n_end_of_early_data_send(struct s2n_connection *conn) -{ - if (conn->early_data_expected) { - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); - } - - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); - return S2N_SUCCESS; -} - -int s2n_end_of_early_data_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_BAD_MESSAGE); - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); - return S2N_SUCCESS; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# If the client attempts a 0-RTT handshake but the server - *# rejects it, the server will generally not have the 0-RTT record - *# protection keys and must instead use trial decryption (either with - *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in - *# the case of a HelloRetryRequest) to find the first non-0-RTT message. - */ -bool s2n_early_data_is_trial_decryption_allowed(struct s2n_connection *conn, uint8_t record_type) -{ - return conn && (conn->early_data_state == S2N_EARLY_DATA_REJECTED) - && record_type == TLS_APPLICATION_DATA - /* Only servers receive early data. */ - && (conn->mode == S2N_SERVER) - /* Early data is only expected during the handshake. */ - && (s2n_conn_get_current_message_type(conn) != APPLICATION_DATA); -} - -static bool s2n_is_early_data_io(struct s2n_connection *conn) -{ - if (s2n_conn_get_current_message_type(conn) == APPLICATION_DATA) { - return false; - } - - /* It would be more accurate to not include this check. - * However, before the early data feature was added, s2n_send and s2n_recv - * did not verify that they were being called after a complete handshake. - * Enforcing that broke several S2N tests, and might have broken customers too. - * - * Therefore, only consider this early data if the customer has indicated that - * they are aware of early data, either because early data is currently expected - * or early data is in a state that indicates that early data was previously expected. - */ - if (conn->early_data_expected - || (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) - || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED - || conn->early_data_state == S2N_END_OF_EARLY_DATA) { - return true; - } - return false; -} - -S2N_RESULT s2n_early_data_record_bytes(struct s2n_connection *conn, ssize_t data_len) -{ - RESULT_ENSURE_REF(conn); - if (data_len < 0 || !s2n_is_early_data_io(conn)) { - return S2N_RESULT_OK; - } - - /* Ensure the bytes read are within the bounds of what we can actually record. */ - if ((size_t) data_len > (UINT64_MAX - conn->early_data_bytes)) { - conn->early_data_bytes = UINT64_MAX; - RESULT_BAIL(S2N_ERR_INTEGER_OVERFLOW); - } - - /* Record the early data bytes read, even if they exceed the max_early_data_size. - * This will ensure that if this method is called again, it will fail again: - * Once we receive too many bytes, we can't proceed with the connection. */ - conn->early_data_bytes += data_len; - - uint32_t max_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); - RESULT_ENSURE(conn->early_data_bytes <= max_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_early_data_validate_send(struct s2n_connection *conn, uint32_t bytes_to_send) -{ - RESULT_ENSURE_REF(conn); - if (!s2n_is_early_data_io(conn)) { - return S2N_RESULT_OK; - } - - RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_REQUESTED - || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, - S2N_ERR_EARLY_DATA_NOT_ALLOWED); - - uint32_t allowed_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &allowed_early_data_size)); - RESULT_ENSURE(bytes_to_send <= allowed_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_early_data_validate_recv(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (!s2n_is_early_data_io(conn)) { - return S2N_RESULT_OK; - } - - RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == END_OF_EARLY_DATA, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - return S2N_RESULT_OK; -} - -static bool s2n_early_data_can_continue(struct s2n_connection *conn) -{ - uint32_t remaining_early_data_size = 0; - return s2n_connection_get_remaining_early_data_size(conn, &remaining_early_data_size) >= S2N_SUCCESS - && remaining_early_data_size > 0; -} - -S2N_RESULT s2n_send_early_data_impl(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len_signed, - ssize_t *data_sent, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_GTE(data_len_signed, 0); - size_t data_len = data_len_signed; - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(blocked); - *blocked = S2N_NOT_BLOCKED; - RESULT_ENSURE_REF(data_sent); - *data_sent = 0; - - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_SERVER_MODE); - RESULT_ENSURE(s2n_connection_supports_tls13(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - if (!s2n_early_data_can_continue(conn)) { - return S2N_RESULT_OK; - } - - /* Attempt to make progress in the handshake even if s2n_send eventually fails. - * We only care about the result of this call if it would prevent us from calling s2n_send. */ - int negotiate_result = s2n_negotiate(conn, blocked); - if (negotiate_result < S2N_SUCCESS) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - RESULT_GUARD_POSIX(negotiate_result); - } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA && *blocked != S2N_BLOCKED_ON_READ) { - RESULT_GUARD_POSIX(negotiate_result); - } - } - /* Save the error status for later */ - int negotiate_error = s2n_errno; - s2n_blocked_status negotiate_blocked = *blocked; - - /* Attempt to send the early data. - * We only care about the result of this call if it fails. */ - uint32_t early_data_to_send = 0; - RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &early_data_to_send)); - early_data_to_send = S2N_MIN(data_len, early_data_to_send); - if (early_data_to_send) { - ssize_t send_result = s2n_send(conn, data, early_data_to_send, blocked); - RESULT_GUARD_POSIX(send_result); - *data_sent = send_result; - } - *blocked = S2N_NOT_BLOCKED; - - /* Since the send was successful, report the result of the original negotiate call. - * If we got this far, the result must have been success or a blocking error. */ - if (negotiate_result < S2N_SUCCESS) { - RESULT_ENSURE_EQ(s2n_error_get_type(negotiate_error), S2N_ERR_T_BLOCKED); - if (negotiate_blocked == S2N_BLOCKED_ON_EARLY_DATA) { - return S2N_RESULT_OK; - } else if (s2n_early_data_can_continue(conn)) { - *blocked = negotiate_blocked; - RESULT_BAIL(negotiate_error); - } else { - return S2N_RESULT_OK; - } - } - return S2N_RESULT_OK; -} - -int s2n_send_early_data(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len, - ssize_t *data_sent, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - - /* Calling this method indicates that we expect early data. */ - POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); - - s2n_result result = s2n_send_early_data_impl(conn, data, data_len, data_sent, blocked); - - /* Unless s2n_send_early_data is called again (undoing this), we are done sending early data. - * If s2n_negotiate is called next, we could send the EndOfEarlyData message. */ - POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); - - POSIX_GUARD_RESULT(result); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_recv_early_data_impl(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, - ssize_t *data_received, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(blocked); - *blocked = S2N_NOT_BLOCKED; - RESULT_ENSURE_REF(data_received); - *data_received = 0; - - RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); - - if (!s2n_early_data_can_continue(conn)) { - return S2N_RESULT_OK; - } - - int negotiate_result = S2N_SUCCESS; - while ((negotiate_result = s2n_negotiate(conn, blocked)) != S2N_SUCCESS) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - RESULT_GUARD_POSIX(negotiate_result); - } else if (max_data_len <= *data_received) { - RESULT_GUARD_POSIX(negotiate_result); - } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA) { - if (s2n_early_data_can_continue(conn)) { - RESULT_GUARD_POSIX(negotiate_result); - } else { - *blocked = S2N_NOT_BLOCKED; - return S2N_RESULT_OK; - } - } - - ssize_t recv_result = s2n_recv(conn, data + *data_received, - max_data_len - *data_received, blocked); - RESULT_GUARD_POSIX(recv_result); - *data_received += recv_result; - } - return S2N_RESULT_OK; -} - -int s2n_recv_early_data(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, - ssize_t *data_received, s2n_blocked_status *blocked) -{ - /* Calling this method indicates that we expect early data. */ - POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); - - s2n_result result = s2n_recv_early_data_impl(conn, data, max_data_len, data_received, blocked); - - /* Unless s2n_recv_early_data is called again (undoing this), we are done accepting early data. */ - POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); - - POSIX_GUARD_RESULT(result); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_connection.h" +#include "tls/s2n_early_data.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +int s2n_end_of_early_data_send(struct s2n_connection *conn) +{ + if (conn->early_data_expected) { + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); + } + + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); + return S2N_SUCCESS; +} + +int s2n_end_of_early_data_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_BAD_MESSAGE); + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); + return S2N_SUCCESS; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# If the client attempts a 0-RTT handshake but the server + *# rejects it, the server will generally not have the 0-RTT record + *# protection keys and must instead use trial decryption (either with + *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in + *# the case of a HelloRetryRequest) to find the first non-0-RTT message. + */ +bool s2n_early_data_is_trial_decryption_allowed(struct s2n_connection *conn, uint8_t record_type) +{ + return conn && (conn->early_data_state == S2N_EARLY_DATA_REJECTED) + && record_type == TLS_APPLICATION_DATA + /* Only servers receive early data. */ + && (conn->mode == S2N_SERVER) + /* Early data is only expected during the handshake. */ + && (s2n_conn_get_current_message_type(conn) != APPLICATION_DATA); +} + +static bool s2n_is_early_data_io(struct s2n_connection *conn) +{ + if (s2n_conn_get_current_message_type(conn) == APPLICATION_DATA) { + return false; + } + + /* It would be more accurate to not include this check. + * However, before the early data feature was added, s2n_send and s2n_recv + * did not verify that they were being called after a complete handshake. + * Enforcing that broke several S2N tests, and might have broken customers too. + * + * Therefore, only consider this early data if the customer has indicated that + * they are aware of early data, either because early data is currently expected + * or early data is in a state that indicates that early data was previously expected. + */ + if (conn->early_data_expected + || (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) + || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED + || conn->early_data_state == S2N_END_OF_EARLY_DATA) { + return true; + } + return false; +} + +S2N_RESULT s2n_early_data_record_bytes(struct s2n_connection *conn, ssize_t data_len) +{ + RESULT_ENSURE_REF(conn); + if (data_len < 0 || !s2n_is_early_data_io(conn)) { + return S2N_RESULT_OK; + } + + /* Ensure the bytes read are within the bounds of what we can actually record. */ + if ((size_t) data_len > (UINT64_MAX - conn->early_data_bytes)) { + conn->early_data_bytes = UINT64_MAX; + RESULT_BAIL(S2N_ERR_INTEGER_OVERFLOW); + } + + /* Record the early data bytes read, even if they exceed the max_early_data_size. + * This will ensure that if this method is called again, it will fail again: + * Once we receive too many bytes, we can't proceed with the connection. */ + conn->early_data_bytes += data_len; + + uint32_t max_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); + RESULT_ENSURE(conn->early_data_bytes <= max_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_early_data_validate_send(struct s2n_connection *conn, uint32_t bytes_to_send) +{ + RESULT_ENSURE_REF(conn); + if (!s2n_is_early_data_io(conn)) { + return S2N_RESULT_OK; + } + + RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_REQUESTED + || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + uint32_t allowed_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &allowed_early_data_size)); + RESULT_ENSURE(bytes_to_send <= allowed_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_early_data_validate_recv(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (!s2n_is_early_data_io(conn)) { + return S2N_RESULT_OK; + } + + RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == END_OF_EARLY_DATA, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + return S2N_RESULT_OK; +} + +static bool s2n_early_data_can_continue(struct s2n_connection *conn) +{ + uint32_t remaining_early_data_size = 0; + return s2n_connection_get_remaining_early_data_size(conn, &remaining_early_data_size) >= S2N_SUCCESS + && remaining_early_data_size > 0; +} + +S2N_RESULT s2n_send_early_data_impl(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len_signed, + ssize_t *data_sent, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_GTE(data_len_signed, 0); + size_t data_len = data_len_signed; + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(blocked); + *blocked = S2N_NOT_BLOCKED; + RESULT_ENSURE_REF(data_sent); + *data_sent = 0; + + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_SERVER_MODE); + RESULT_ENSURE(s2n_connection_supports_tls13(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + if (!s2n_early_data_can_continue(conn)) { + return S2N_RESULT_OK; + } + + /* Attempt to make progress in the handshake even if s2n_send eventually fails. + * We only care about the result of this call if it would prevent us from calling s2n_send. */ + int negotiate_result = s2n_negotiate(conn, blocked); + if (negotiate_result < S2N_SUCCESS) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + RESULT_GUARD_POSIX(negotiate_result); + } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA && *blocked != S2N_BLOCKED_ON_READ) { + RESULT_GUARD_POSIX(negotiate_result); + } + } + /* Save the error status for later */ + int negotiate_error = s2n_errno; + s2n_blocked_status negotiate_blocked = *blocked; + + /* Attempt to send the early data. + * We only care about the result of this call if it fails. */ + uint32_t early_data_to_send = 0; + RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &early_data_to_send)); + early_data_to_send = S2N_MIN(data_len, early_data_to_send); + if (early_data_to_send) { + ssize_t send_result = s2n_send(conn, data, early_data_to_send, blocked); + RESULT_GUARD_POSIX(send_result); + *data_sent = send_result; + } + *blocked = S2N_NOT_BLOCKED; + + /* Since the send was successful, report the result of the original negotiate call. + * If we got this far, the result must have been success or a blocking error. */ + if (negotiate_result < S2N_SUCCESS) { + RESULT_ENSURE_EQ(s2n_error_get_type(negotiate_error), S2N_ERR_T_BLOCKED); + if (negotiate_blocked == S2N_BLOCKED_ON_EARLY_DATA) { + return S2N_RESULT_OK; + } else if (s2n_early_data_can_continue(conn)) { + *blocked = negotiate_blocked; + RESULT_BAIL(negotiate_error); + } else { + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +int s2n_send_early_data(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len, + ssize_t *data_sent, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + + /* Calling this method indicates that we expect early data. */ + POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); + + s2n_result result = s2n_send_early_data_impl(conn, data, data_len, data_sent, blocked); + + /* Unless s2n_send_early_data is called again (undoing this), we are done sending early data. + * If s2n_negotiate is called next, we could send the EndOfEarlyData message. */ + POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); + + POSIX_GUARD_RESULT(result); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_recv_early_data_impl(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, + ssize_t *data_received, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(blocked); + *blocked = S2N_NOT_BLOCKED; + RESULT_ENSURE_REF(data_received); + *data_received = 0; + + RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); + + if (!s2n_early_data_can_continue(conn)) { + return S2N_RESULT_OK; + } + + int negotiate_result = S2N_SUCCESS; + while ((negotiate_result = s2n_negotiate(conn, blocked)) != S2N_SUCCESS) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + RESULT_GUARD_POSIX(negotiate_result); + } else if (max_data_len <= *data_received) { + RESULT_GUARD_POSIX(negotiate_result); + } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA) { + if (s2n_early_data_can_continue(conn)) { + RESULT_GUARD_POSIX(negotiate_result); + } else { + *blocked = S2N_NOT_BLOCKED; + return S2N_RESULT_OK; + } + } + + ssize_t recv_result = s2n_recv(conn, data + *data_received, + max_data_len - *data_received, blocked); + RESULT_GUARD_POSIX(recv_result); + *data_received += recv_result; + } + return S2N_RESULT_OK; +} + +int s2n_recv_early_data(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, + ssize_t *data_received, s2n_blocked_status *blocked) +{ + /* Calling this method indicates that we expect early data. */ + POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); + + s2n_result result = s2n_recv_early_data_impl(conn, data, max_data_len, data_received, blocked); + + /* Unless s2n_recv_early_data is called again (undoing this), we are done accepting early data. */ + POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); + + POSIX_GUARD_RESULT(result); + return S2N_SUCCESS; +} diff --git a/tls/s2n_ecc_preferences.h b/tls/s2n_ecc_preferences.h index 6672c7fa2c0..704199bcd12 100644 --- a/tls/s2n_ecc_preferences.h +++ b/tls/s2n_ecc_preferences.h @@ -1,42 +1,44 @@ -/* - * 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 - -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_ecc_evp.h" - -struct s2n_ecc_preferences { - uint8_t count; - const struct s2n_ecc_named_curve *const *ecc_curves; -}; - -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240501; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20140601; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20200310; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20230623; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_default_fips; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20201021; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20210816; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240603; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20251113; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_test_all; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_null; - -int s2n_check_ecc_preferences_curves_list(const struct s2n_ecc_preferences *ecc_preferences); -bool s2n_ecc_preferences_includes_curve(const struct s2n_ecc_preferences *ecc_preferences, uint16_t query_iana_id); +/* + * 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 + +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_ecc_evp.h" + +struct s2n_ecc_preferences { + uint8_t count; + const struct s2n_ecc_named_curve *const *ecc_curves; +}; + +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240501; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20140601; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20200310; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20230623; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_default_fips; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20201021; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20210816; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240603; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20251113; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_test_all; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_null; + +int s2n_check_ecc_preferences_curves_list(const struct s2n_ecc_preferences *ecc_preferences); +bool s2n_ecc_preferences_includes_curve(const struct s2n_ecc_preferences *ecc_preferences, uint16_t query_iana_id); diff --git a/tls/s2n_fingerprint_ja4.c b/tls/s2n_fingerprint_ja4.c index cc3b21e4c16..8454b625d3e 100644 --- a/tls/s2n_fingerprint_ja4.c +++ b/tls/s2n_fingerprint_ja4.c @@ -1,601 +1,602 @@ -/* - * 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 "crypto/s2n_hash.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/extensions/s2n_client_supported_versions.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_client_hello.h" -#include "tls/s2n_fingerprint.h" -#include "tls/s2n_protocol_preferences.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -#define S2N_JA4_LIST_DIV ',' -#define S2N_JA4_PART_DIV '_' - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# 2 character number of cipher suites - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Same as counting ciphers. - */ -#define S2N_JA4_COUNT_SIZE 2 - -#define S2N_HEX_PER_BYTE 2 -#define S2N_JA4_DIGEST_HEX_CHAR_LIMIT 12 -#define S2N_JA4_DIGEST_BYTE_LIMIT (S2N_JA4_DIGEST_HEX_CHAR_LIMIT / S2N_HEX_PER_BYTE) - -#define S2N_JA4_A_SIZE 10 -#define S2N_JA4_B_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT -#define S2N_JA4_C_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT -#define S2N_JA4_SIZE (S2N_JA4_A_SIZE + 1 + S2N_JA4_B_SIZE + 1 + S2N_JA4_C_SIZE) - -#define S2N_JA4_LIST_LIMIT 99 -#define S2N_JA4_IANA_HEX_SIZE (S2N_HEX_PER_BYTE * sizeof(uint16_t)) -#define S2N_JA4_IANA_ENTRY_SIZE (S2N_JA4_IANA_HEX_SIZE + 1) -#define S2N_JA4_WORKSPACE_SIZE ((S2N_JA4_LIST_LIMIT * (S2N_JA4_IANA_ENTRY_SIZE))) - -const char *s2n_ja4_version_strings[] = { - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# 0x0304 = TLS 1.3 = “13” - *# 0x0303 = TLS 1.2 = “12” - *# 0x0302 = TLS 1.1 = “11” - *# 0x0301 = TLS 1.0 = “10” - */ - [0x0304] = "13", - [0x0303] = "12", - [0x0302] = "11", - [0x0301] = "10", - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# 0x0300 = SSL 3.0 = “s3” - *# 0x0002 = SSL 2.0 = “s2” - */ - [0x0300] = "s3", - [0x0002] = "s2", -}; - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# Unknown = “00” - */ -#define S2N_JA4_UNKNOWN_STR "00" - -DEFINE_POINTER_CLEANUP_FUNC(struct s2n_stuffer *, s2n_stuffer_wipe); - -static int s2n_fingerprint_ja4_iana_compare(const void *a, const void *b) -{ - const uint8_t *iana_a = (const uint8_t *) a; - const uint8_t *iana_b = (const uint8_t *) b; - for (size_t i = 0; i < S2N_JA4_IANA_HEX_SIZE; i++) { - if (iana_a[i] != iana_b[i]) { - return iana_a[i] - iana_b[i]; - } - } - return 0; -} - -static S2N_RESULT s2n_fingerprint_ja4_digest(struct s2n_fingerprint_hash *hash, - struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(hash); - if (!s2n_fingerprint_hash_do_digest(hash)) { - return S2N_RESULT_OK; - } - - /* Instead of hashing empty inputs, JA4 sets the output to a string of all zeroes. - * (Actually hashing an empty input doesn't produce a digest of all zeroes) - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash - *# If there are no ciphers in the sorted cipher list, then the value of - *# JA4_b is set to `000000000000` - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# If there are no extensions in the sorted extensions list, then the value of - *# JA4_c is set to `000000000000` - */ - uint64_t bytes = 0; - RESULT_GUARD_POSIX(s2n_hash_get_currently_in_hash_total(hash->hash, &bytes)); - if (bytes == 0) { - RESULT_GUARD_POSIX(s2n_stuffer_write_str(out, "000000000000")); - return S2N_RESULT_OK; - } - - uint8_t digest_bytes[SHA256_DIGEST_LENGTH] = { 0 }; - struct s2n_blob digest = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&digest, digest_bytes, sizeof(digest_bytes))); - RESULT_GUARD(s2n_fingerprint_hash_digest(hash, &digest)); - - /* JA4 digests are truncated */ - RESULT_ENSURE_LTE(S2N_JA4_DIGEST_BYTE_LIMIT, digest.size); - digest.size = S2N_JA4_DIGEST_BYTE_LIMIT; - RESULT_GUARD(s2n_stuffer_write_hex(out, &digest)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# 2 character number of cipher suites, so if there’s 6 cipher suites - *# in the hello packet, then the value should be “06”. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Same as counting ciphers. - */ -static S2N_RESULT s2n_fingerprint_ja4_count(struct s2n_blob *output, uint16_t count) -{ - RESULT_ENSURE_REF(output); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# If there’s > 99, which there should never be, then output “99”. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Same as counting ciphers. - */ - count = S2N_MIN(count, 99); - - RESULT_ENSURE_EQ(output->size, 2); - output->data[0] = (count / 10) + '0'; - output->data[1] = (count % 10) + '0'; - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_fingerprint_get_extension_version(struct s2n_client_hello *ch, - uint16_t *client_version) -{ - RESULT_ENSURE_REF(ch); - RESULT_ENSURE_REF(client_version); - - s2n_parsed_extension *extension = NULL; - RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension( - S2N_EXTENSION_SUPPORTED_VERSIONS, &ch->extensions, &extension)); - RESULT_ENSURE_REF(extension); - - struct s2n_stuffer supported_versions = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions, &extension->extension)); - - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&supported_versions, sizeof(uint8_t))); - while (s2n_stuffer_data_available(&supported_versions)) { - uint16_t version = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&supported_versions, &version)); - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# Remember to ignore GREASE values. - */ - if (s2n_fingerprint_is_grease_value(version)) { - continue; - } - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# If extension 0x002b exists (supported_versions), then the version is - *# the highest value in the extension. - */ - *client_version = S2N_MAX(*client_version, version); - } - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_fingerprint_ja4_version(struct s2n_stuffer *output, - struct s2n_client_hello *ch) -{ - uint16_t client_version = 0; - if (s2n_result_is_error(s2n_fingerprint_get_extension_version(ch, &client_version))) { - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# If the extension doesn’t exist, then the TLS version is the value of - *# the Protocol Version. - */ - RESULT_GUARD(s2n_fingerprint_get_legacy_version(ch, &client_version)); - } - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# Handshake version (located at the top of the packet) should be ignored. - */ - - const char *version_str = NULL; - if (client_version < s2n_array_len(s2n_ja4_version_strings)) { - version_str = s2n_ja4_version_strings[client_version]; - } - if (version_str == NULL) { - version_str = S2N_JA4_UNKNOWN_STR; - } - RESULT_GUARD_POSIX(s2n_stuffer_write_str(output, version_str)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_client_hello_get_first_alpn(struct s2n_client_hello *ch, struct s2n_blob *first) -{ - RESULT_ENSURE_REF(ch); - - s2n_parsed_extension *extension = NULL; - RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_ALPN, - &ch->extensions, &extension)); - RESULT_ENSURE_REF(extension); - - struct s2n_stuffer protocols = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&protocols, &extension->extension)); - - uint16_t list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&protocols, &list_size)); - - RESULT_GUARD(s2n_protocol_preferences_read(&protocols, first)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# The first and last alphanumeric characters of the ALPN (Application-Layer - *# Protocol Negotiation) first value. - */ -static S2N_RESULT s2n_fingerprint_ja4_alpn(struct s2n_stuffer *output, - struct s2n_client_hello *ch) -{ - struct s2n_blob protocol = { 0 }; - if (s2n_result_is_error(s2n_client_hello_get_first_alpn(ch, &protocol))) { - protocol.size = 0; - } - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# If there is no ALPN extension, no ALPN values, or the first ALPN value - *# is empty, then we print "00" as the value in the fingerprint. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# If the first ALPN value is only a single character, then that character - *# is treated as both the first and last character. - */ - uint8_t first_char = '0', last_char = '0'; - if (protocol.size > 0) { - first_char = protocol.data[0]; - last_char = protocol.data[protocol.size - 1]; - } - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# If the first or last byte of the first ALPN is non-alphanumeric (meaning - *# not `0x30-0x39`, `0x41-0x5A`, or `0x61-0x7A`), then we print the first and - *# last characters of the hex representation of the first ALPN instead. - */ - if (!isalnum(first_char) || !isalnum(last_char)) { - RESULT_GUARD(s2n_hex_digit((first_char >> 4), &first_char)); - RESULT_GUARD(s2n_hex_digit((last_char & 0x0F), &last_char)); - } - - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, first_char)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, last_char)); - return S2N_RESULT_OK; -} - -/* Part "a" of the fingerprint is a descriptive prefix. - * - * https://github.com/FoxIO-LLC/ja4/main/technical_details/JA4.md - *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) - *# (2 character TLS version) - *# (SNI=”d” or no SNI=”i”) - *# (2 character count of ciphers) - *# (2 character count of extensions) - *# (first and last characters of first ALPN extension value) - */ -static S2N_RESULT s2n_fingerprint_ja4_a(struct s2n_fingerprint *fingerprint, - struct s2n_stuffer *output, struct s2n_blob *ciphers_count, struct s2n_blob *extensions_count) -{ - RESULT_ENSURE_REF(fingerprint); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#quic-and-dtls - *# If the protocol is QUIC then the first character of the fingerprint is “q”, - *# if DTLS it is "d", else it is “t”. - * - * s2n-tls only supports TLS and QUIC. DTLS is not supported. - */ - bool is_quic = false; - RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, - TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS, &is_quic)); - char protocol_char = (is_quic) ? 'q' : 't'; - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, protocol_char)); - - RESULT_GUARD(s2n_fingerprint_ja4_version(output, fingerprint->client_hello)); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#sni - *# If the SNI extension (0x0000) exists, then the destination of the connection - *# is a domain, or “d” in the fingerprint. - *# If the SNI does not exist, then the destination is an IP address, or “i”. - */ - bool has_sni = false; - RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, - TLS_EXTENSION_SERVER_NAME, &has_sni)); - char sni_char = (has_sni) ? 'd' : 'i'; - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, sni_char)); - - /* Reserve two characters for the "count of ciphers". - * We'll calculate it later when we handle the cipher suite list for JA4_b. - */ - uint8_t *ciphers_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); - RESULT_GUARD_PTR(ciphers_count_mem); - RESULT_GUARD_POSIX(s2n_blob_init(ciphers_count, ciphers_count_mem, S2N_JA4_COUNT_SIZE)); - - /* Reserve two characters for the "count of extensions". - * We'll calculate it later when we handle the extensions list for JA4_c. - */ - uint8_t *extensions_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); - RESULT_GUARD_PTR(extensions_count_mem); - RESULT_GUARD_POSIX(s2n_blob_init(extensions_count, extensions_count_mem, S2N_JA4_COUNT_SIZE)); - - RESULT_GUARD(s2n_fingerprint_ja4_alpn(output, fingerprint->client_hello)); - - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash - *# The list is created using the 4 character hex values of the ciphers, - *# lower case, comma delimited, ignoring GREASE. - */ -static S2N_RESULT s2n_fingerprint_ja4_ciphers(struct s2n_fingerprint_hash *hash, - struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *ciphers_count) -{ - RESULT_ENSURE_REF(ch); - RESULT_ENSURE_REF(sort_space); - RESULT_ENSURE_REF(ciphers_count); - - struct s2n_stuffer cipher_suites = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&cipher_suites, &ch->cipher_suites)); - - DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); - while (s2n_stuffer_data_available(&cipher_suites)) { - uint16_t iana = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&cipher_suites, &iana)); - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# Remember, ignore GREASE values. They don’t count. - */ - if (s2n_fingerprint_is_grease_value(iana)) { - continue; - } - RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); - } - - size_t iana_list_size = s2n_stuffer_data_available(iana_list); - size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; - *ciphers_count = iana_count; - if (iana_count == 0) { - return S2N_RESULT_OK; - } - - uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); - RESULT_ENSURE_REF(ianas); - qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); - RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash - *# A 12 character truncated sha256 hash of the list of ciphers sorted in hex order, - *# first 12 characters. - */ -static S2N_RESULT s2n_fingerprint_ja4_b(struct s2n_fingerprint *fingerprint, - struct s2n_fingerprint_hash *hash, struct s2n_blob *ciphers_count, - struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(fingerprint); - - uint16_t ciphers_count_value = 0; - RESULT_GUARD(s2n_fingerprint_ja4_ciphers(hash, fingerprint->client_hello, - &fingerprint->workspace, &ciphers_count_value)); - - RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); - RESULT_GUARD(s2n_fingerprint_ja4_count(ciphers_count, ciphers_count_value)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# The extension list is created using the 4 character hex values of the extensions, - *# lower case, comma delimited, sorted (not in the order they appear). - */ -static S2N_RESULT s2n_fingerprint_ja4_extensions(struct s2n_fingerprint_hash *hash, - struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *extensions_count) -{ - RESULT_ENSURE_REF(ch); - RESULT_ENSURE_REF(sort_space); - RESULT_ENSURE_REF(extensions_count); - - struct s2n_stuffer extensions = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&extensions, &ch->extensions.raw)); - - DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); - while (s2n_stuffer_data_available(&extensions)) { - uint16_t iana = 0; - RESULT_GUARD(s2n_fingerprint_parse_extension(&extensions, &iana)); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Ignore GREASE. - */ - if (s2n_fingerprint_is_grease_value(iana)) { - continue; - } - - /* SNI and ALPN are included in the extension count, but not in the extension list. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# Ignore the SNI extension (0000) and the ALPN extension (0010) - *# as we’ve already captured them in the _a_ section of the fingerprint. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Include SNI and ALPN. - */ - (*extensions_count)++; - if (iana == TLS_EXTENSION_SERVER_NAME || iana == S2N_EXTENSION_ALPN) { - continue; - } - RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); - } - - size_t iana_list_size = s2n_stuffer_data_available(iana_list); - size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; - if (iana_count == 0) { - return S2N_RESULT_OK; - } - - uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); - RESULT_ENSURE_REF(ianas); - qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); - RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_fingerprint_ja4_sig_algs(struct s2n_fingerprint_hash *hash, - struct s2n_client_hello *ch) -{ - RESULT_ENSURE_REF(ch); - - s2n_parsed_extension *extension = NULL; - int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SIGNATURE_ALGORITHMS, - &ch->extensions, &extension); - if (result != S2N_SUCCESS) { - return S2N_RESULT_OK; - } - RESULT_ENSURE_REF(extension); - - struct s2n_stuffer sig_algs = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&sig_algs, &extension->extension)); - - uint8_t entry_bytes[S2N_JA4_IANA_ENTRY_SIZE] = { 0 }; - struct s2n_stuffer entry = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&entry.blob, entry_bytes, sizeof(entry_bytes))); - - bool is_first = true; - if (s2n_stuffer_skip_read(&sig_algs, sizeof(uint16_t)) != S2N_SUCCESS) { - return S2N_RESULT_OK; - } - while (s2n_stuffer_data_available(&sig_algs)) { - uint16_t iana = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&sig_algs, &iana)); - if (s2n_fingerprint_is_grease_value(iana)) { - continue; - } - if (is_first) { - RESULT_GUARD(s2n_fingerprint_hash_add_char(hash, S2N_JA4_PART_DIV)); - } else { - RESULT_GUARD_POSIX(s2n_stuffer_write_char(&entry, S2N_JA4_LIST_DIV)); - } - RESULT_GUARD(s2n_stuffer_write_uint16_hex(&entry, iana)); - RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, entry_bytes, - s2n_stuffer_data_available(&entry))); - RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&entry)); - is_first = false; - } - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# A 12 character truncated sha256 hash of the list of extensions, sorted by - *# hex value, followed by the list of signature algorithms, in the order that - *# they appear (not sorted). - */ -static S2N_RESULT s2n_fingerprint_ja4_c(struct s2n_fingerprint *fingerprint, - struct s2n_fingerprint_hash *hash, struct s2n_blob *extensions_count, - struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(fingerprint); - - uint16_t extensions_count_value = 0; - RESULT_GUARD(s2n_fingerprint_ja4_extensions(hash, fingerprint->client_hello, - &fingerprint->workspace, &extensions_count_value)); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# The signature algorithm hex values are then added to the end of the list - *# in the order that they appear (not sorted) with an underscore delimiting - *# the two lists. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# If there are no signature algorithms in the hello packet, - *# then the string ends without an underscore and is hashed. - * - * s2n_fingerprint_ja4_sig_algs handles writing the underscore because we - * need to skip writing it if there are no signature algorithms. - */ - RESULT_GUARD(s2n_fingerprint_ja4_sig_algs(hash, fingerprint->client_hello)); - - RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); - RESULT_GUARD(s2n_fingerprint_ja4_count(extensions_count, extensions_count_value)); - return S2N_RESULT_OK; -} - -/* JA4 fingerprints are basically of the form a_b_c: - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#ja4-algorithm - *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) - *# (2 character TLS version) - *# (SNI=”d” or no SNI=”i”) - *# (2 character count of ciphers) - *# (2 character count of extensions) - *# (first and last characters of first ALPN extension value) - *# _ - *# (sha256 hash of the list of cipher hex codes sorted in hex order, truncated to 12 characters) - *# _ - *# (sha256 hash of (the list of extension hex codes sorted in hex order)_(the list of signature algorithms), truncated to 12 characters) - *# - *# The end result is a fingerprint that looks like: - *# t13d1516h2_8daaf6152771_b186095e22b6 - */ -static S2N_RESULT s2n_fingerprint_ja4(struct s2n_fingerprint *fingerprint, - struct s2n_fingerprint_hash *hash, struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(fingerprint); - RESULT_ENSURE_REF(hash); - RESULT_ENSURE_REF(output); - - if (s2n_stuffer_is_freed(&fingerprint->workspace)) { - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&fingerprint->workspace, S2N_JA4_WORKSPACE_SIZE)); - } - - struct s2n_blob ciphers_count = { 0 }; - struct s2n_blob extensions_count = { 0 }; - RESULT_GUARD(s2n_fingerprint_ja4_a(fingerprint, output, &ciphers_count, &extensions_count)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); - RESULT_GUARD(s2n_fingerprint_ja4_b(fingerprint, hash, &ciphers_count, output)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); - RESULT_GUARD(s2n_fingerprint_ja4_c(fingerprint, hash, &extensions_count, output)); - - if (s2n_fingerprint_hash_do_digest(hash)) { - /* The extra two bytes are for the characters separating the parts */ - fingerprint->raw_size = hash->bytes_digested + S2N_JA4_A_SIZE + 2; - } else { - fingerprint->raw_size = s2n_stuffer_data_available(output); - } - - return S2N_RESULT_OK; -} - -struct s2n_fingerprint_method ja4_fingerprint = { - .hash = S2N_HASH_SHA256, - .hash_str_size = S2N_JA4_SIZE, - .fingerprint = s2n_fingerprint_ja4, -}; +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hash.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_client_supported_versions.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_fingerprint.h" +#include "tls/s2n_protocol_preferences.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +#define S2N_JA4_LIST_DIV ',' +#define S2N_JA4_PART_DIV '_' + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# 2 character number of cipher suites + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Same as counting ciphers. + */ +#define S2N_JA4_COUNT_SIZE 2 + +#define S2N_HEX_PER_BYTE 2 +#define S2N_JA4_DIGEST_HEX_CHAR_LIMIT 12 +#define S2N_JA4_DIGEST_BYTE_LIMIT (S2N_JA4_DIGEST_HEX_CHAR_LIMIT / S2N_HEX_PER_BYTE) + +#define S2N_JA4_A_SIZE 10 +#define S2N_JA4_B_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT +#define S2N_JA4_C_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT +#define S2N_JA4_SIZE (S2N_JA4_A_SIZE + 1 + S2N_JA4_B_SIZE + 1 + S2N_JA4_C_SIZE) + +#define S2N_JA4_LIST_LIMIT 99 +#define S2N_JA4_IANA_HEX_SIZE (S2N_HEX_PER_BYTE * sizeof(uint16_t)) +#define S2N_JA4_IANA_ENTRY_SIZE (S2N_JA4_IANA_HEX_SIZE + 1) +#define S2N_JA4_WORKSPACE_SIZE ((S2N_JA4_LIST_LIMIT * (S2N_JA4_IANA_ENTRY_SIZE))) + +const char *s2n_ja4_version_strings[] = { + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# 0x0304 = TLS 1.3 = “13” + *# 0x0303 = TLS 1.2 = “12” + *# 0x0302 = TLS 1.1 = “11” + *# 0x0301 = TLS 1.0 = “10” + */ + [0x0304] = "13", + [0x0303] = "12", + [0x0302] = "11", + [0x0301] = "10", + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# 0x0300 = SSL 3.0 = “s3” + *# 0x0002 = SSL 2.0 = “s2” + */ + [0x0300] = "s3", + [0x0002] = "s2", +}; + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# Unknown = “00” + */ +#define S2N_JA4_UNKNOWN_STR "00" + +DEFINE_POINTER_CLEANUP_FUNC(struct s2n_stuffer *, s2n_stuffer_wipe); + +static int s2n_fingerprint_ja4_iana_compare(const void *a, const void *b) +{ + const uint8_t *iana_a = (const uint8_t *) a; + const uint8_t *iana_b = (const uint8_t *) b; + for (size_t i = 0; i < S2N_JA4_IANA_HEX_SIZE; i++) { + if (iana_a[i] != iana_b[i]) { + return iana_a[i] - iana_b[i]; + } + } + return 0; +} + +static S2N_RESULT s2n_fingerprint_ja4_digest(struct s2n_fingerprint_hash *hash, + struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(hash); + if (!s2n_fingerprint_hash_do_digest(hash)) { + return S2N_RESULT_OK; + } + + /* Instead of hashing empty inputs, JA4 sets the output to a string of all zeroes. + * (Actually hashing an empty input doesn't produce a digest of all zeroes) + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash + *# If there are no ciphers in the sorted cipher list, then the value of + *# JA4_b is set to `000000000000` + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# If there are no extensions in the sorted extensions list, then the value of + *# JA4_c is set to `000000000000` + */ + uint64_t bytes = 0; + RESULT_GUARD_POSIX(s2n_hash_get_currently_in_hash_total(hash->hash, &bytes)); + if (bytes == 0) { + RESULT_GUARD_POSIX(s2n_stuffer_write_str(out, "000000000000")); + return S2N_RESULT_OK; + } + + uint8_t digest_bytes[SHA256_DIGEST_LENGTH] = { 0 }; + struct s2n_blob digest = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&digest, digest_bytes, sizeof(digest_bytes))); + RESULT_GUARD(s2n_fingerprint_hash_digest(hash, &digest)); + + /* JA4 digests are truncated */ + RESULT_ENSURE_LTE(S2N_JA4_DIGEST_BYTE_LIMIT, digest.size); + digest.size = S2N_JA4_DIGEST_BYTE_LIMIT; + RESULT_GUARD(s2n_stuffer_write_hex(out, &digest)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# 2 character number of cipher suites, so if there’s 6 cipher suites + *# in the hello packet, then the value should be “06”. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Same as counting ciphers. + */ +static S2N_RESULT s2n_fingerprint_ja4_count(struct s2n_blob *output, uint16_t count) +{ + RESULT_ENSURE_REF(output); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# If there’s > 99, which there should never be, then output “99”. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Same as counting ciphers. + */ + count = S2N_MIN(count, 99); + + RESULT_ENSURE_EQ(output->size, 2); + output->data[0] = (count / 10) + '0'; + output->data[1] = (count % 10) + '0'; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_get_extension_version(struct s2n_client_hello *ch, + uint16_t *client_version) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE_REF(client_version); + + s2n_parsed_extension *extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension( + S2N_EXTENSION_SUPPORTED_VERSIONS, &ch->extensions, &extension)); + RESULT_ENSURE_REF(extension); + + struct s2n_stuffer supported_versions = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions, &extension->extension)); + + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&supported_versions, sizeof(uint8_t))); + while (s2n_stuffer_data_available(&supported_versions)) { + uint16_t version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&supported_versions, &version)); + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# Remember to ignore GREASE values. + */ + if (s2n_fingerprint_is_grease_value(version)) { + continue; + } + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# If extension 0x002b exists (supported_versions), then the version is + *# the highest value in the extension. + */ + *client_version = S2N_MAX(*client_version, version); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_ja4_version(struct s2n_stuffer *output, + struct s2n_client_hello *ch) +{ + uint16_t client_version = 0; + if (s2n_result_is_error(s2n_fingerprint_get_extension_version(ch, &client_version))) { + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# If the extension doesn’t exist, then the TLS version is the value of + *# the Protocol Version. + */ + RESULT_GUARD(s2n_fingerprint_get_legacy_version(ch, &client_version)); + } + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# Handshake version (located at the top of the packet) should be ignored. + */ + + const char *version_str = NULL; + if (client_version < s2n_array_len(s2n_ja4_version_strings)) { + version_str = s2n_ja4_version_strings[client_version]; + } + if (version_str == NULL) { + version_str = S2N_JA4_UNKNOWN_STR; + } + RESULT_GUARD_POSIX(s2n_stuffer_write_str(output, version_str)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_client_hello_get_first_alpn(struct s2n_client_hello *ch, struct s2n_blob *first) +{ + RESULT_ENSURE_REF(ch); + + s2n_parsed_extension *extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_ALPN, + &ch->extensions, &extension)); + RESULT_ENSURE_REF(extension); + + struct s2n_stuffer protocols = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&protocols, &extension->extension)); + + uint16_t list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&protocols, &list_size)); + + RESULT_GUARD(s2n_protocol_preferences_read(&protocols, first)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# The first and last alphanumeric characters of the ALPN (Application-Layer + *# Protocol Negotiation) first value. + */ +static S2N_RESULT s2n_fingerprint_ja4_alpn(struct s2n_stuffer *output, + struct s2n_client_hello *ch) +{ + struct s2n_blob protocol = { 0 }; + if (s2n_result_is_error(s2n_client_hello_get_first_alpn(ch, &protocol))) { + protocol.size = 0; + } + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# If there is no ALPN extension, no ALPN values, or the first ALPN value + *# is empty, then we print "00" as the value in the fingerprint. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# If the first ALPN value is only a single character, then that character + *# is treated as both the first and last character. + */ + uint8_t first_char = '0', last_char = '0'; + if (protocol.size > 0) { + first_char = protocol.data[0]; + last_char = protocol.data[protocol.size - 1]; + } + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# If the first or last byte of the first ALPN is non-alphanumeric (meaning + *# not `0x30-0x39`, `0x41-0x5A`, or `0x61-0x7A`), then we print the first and + *# last characters of the hex representation of the first ALPN instead. + */ + if (!isalnum(first_char) || !isalnum(last_char)) { + RESULT_GUARD(s2n_hex_digit((first_char >> 4), &first_char)); + RESULT_GUARD(s2n_hex_digit((last_char & 0x0F), &last_char)); + } + + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, first_char)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, last_char)); + return S2N_RESULT_OK; +} + +/* Part "a" of the fingerprint is a descriptive prefix. + * + * https://github.com/FoxIO-LLC/ja4/main/technical_details/JA4.md + *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) + *# (2 character TLS version) + *# (SNI=”d” or no SNI=”i”) + *# (2 character count of ciphers) + *# (2 character count of extensions) + *# (first and last characters of first ALPN extension value) + */ +static S2N_RESULT s2n_fingerprint_ja4_a(struct s2n_fingerprint *fingerprint, + struct s2n_stuffer *output, struct s2n_blob *ciphers_count, struct s2n_blob *extensions_count) +{ + RESULT_ENSURE_REF(fingerprint); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#quic-and-dtls + *# If the protocol is QUIC then the first character of the fingerprint is “q”, + *# if DTLS it is "d", else it is “t”. + * + * s2n-tls only supports TLS and QUIC. DTLS is not supported. + */ + bool is_quic = false; + RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, + TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS, &is_quic)); + char protocol_char = (is_quic) ? 'q' : 't'; + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, protocol_char)); + + RESULT_GUARD(s2n_fingerprint_ja4_version(output, fingerprint->client_hello)); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#sni + *# If the SNI extension (0x0000) exists, then the destination of the connection + *# is a domain, or “d” in the fingerprint. + *# If the SNI does not exist, then the destination is an IP address, or “i”. + */ + bool has_sni = false; + RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, + TLS_EXTENSION_SERVER_NAME, &has_sni)); + char sni_char = (has_sni) ? 'd' : 'i'; + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, sni_char)); + + /* Reserve two characters for the "count of ciphers". + * We'll calculate it later when we handle the cipher suite list for JA4_b. + */ + uint8_t *ciphers_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); + RESULT_GUARD_PTR(ciphers_count_mem); + RESULT_GUARD_POSIX(s2n_blob_init(ciphers_count, ciphers_count_mem, S2N_JA4_COUNT_SIZE)); + + /* Reserve two characters for the "count of extensions". + * We'll calculate it later when we handle the extensions list for JA4_c. + */ + uint8_t *extensions_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); + RESULT_GUARD_PTR(extensions_count_mem); + RESULT_GUARD_POSIX(s2n_blob_init(extensions_count, extensions_count_mem, S2N_JA4_COUNT_SIZE)); + + RESULT_GUARD(s2n_fingerprint_ja4_alpn(output, fingerprint->client_hello)); + + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash + *# The list is created using the 4 character hex values of the ciphers, + *# lower case, comma delimited, ignoring GREASE. + */ +static S2N_RESULT s2n_fingerprint_ja4_ciphers(struct s2n_fingerprint_hash *hash, + struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *ciphers_count) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE_REF(sort_space); + RESULT_ENSURE_REF(ciphers_count); + + struct s2n_stuffer cipher_suites = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&cipher_suites, &ch->cipher_suites)); + + DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); + while (s2n_stuffer_data_available(&cipher_suites)) { + uint16_t iana = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&cipher_suites, &iana)); + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# Remember, ignore GREASE values. They don’t count. + */ + if (s2n_fingerprint_is_grease_value(iana)) { + continue; + } + RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); + } + + size_t iana_list_size = s2n_stuffer_data_available(iana_list); + size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; + *ciphers_count = iana_count; + if (iana_count == 0) { + return S2N_RESULT_OK; + } + + uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); + RESULT_ENSURE_REF(ianas); + qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); + RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash + *# A 12 character truncated sha256 hash of the list of ciphers sorted in hex order, + *# first 12 characters. + */ +static S2N_RESULT s2n_fingerprint_ja4_b(struct s2n_fingerprint *fingerprint, + struct s2n_fingerprint_hash *hash, struct s2n_blob *ciphers_count, + struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(fingerprint); + + uint16_t ciphers_count_value = 0; + RESULT_GUARD(s2n_fingerprint_ja4_ciphers(hash, fingerprint->client_hello, + &fingerprint->workspace, &ciphers_count_value)); + + RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); + RESULT_GUARD(s2n_fingerprint_ja4_count(ciphers_count, ciphers_count_value)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# The extension list is created using the 4 character hex values of the extensions, + *# lower case, comma delimited, sorted (not in the order they appear). + */ +static S2N_RESULT s2n_fingerprint_ja4_extensions(struct s2n_fingerprint_hash *hash, + struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *extensions_count) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE_REF(sort_space); + RESULT_ENSURE_REF(extensions_count); + + struct s2n_stuffer extensions = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&extensions, &ch->extensions.raw)); + + DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); + while (s2n_stuffer_data_available(&extensions)) { + uint16_t iana = 0; + RESULT_GUARD(s2n_fingerprint_parse_extension(&extensions, &iana)); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Ignore GREASE. + */ + if (s2n_fingerprint_is_grease_value(iana)) { + continue; + } + + /* SNI and ALPN are included in the extension count, but not in the extension list. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# Ignore the SNI extension (0000) and the ALPN extension (0010) + *# as we’ve already captured them in the _a_ section of the fingerprint. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Include SNI and ALPN. + */ + (*extensions_count)++; + if (iana == TLS_EXTENSION_SERVER_NAME || iana == S2N_EXTENSION_ALPN) { + continue; + } + RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); + } + + size_t iana_list_size = s2n_stuffer_data_available(iana_list); + size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; + if (iana_count == 0) { + return S2N_RESULT_OK; + } + + uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); + RESULT_ENSURE_REF(ianas); + qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); + RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_ja4_sig_algs(struct s2n_fingerprint_hash *hash, + struct s2n_client_hello *ch) +{ + RESULT_ENSURE_REF(ch); + + s2n_parsed_extension *extension = NULL; + int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SIGNATURE_ALGORITHMS, + &ch->extensions, &extension); + if (result != S2N_SUCCESS) { + return S2N_RESULT_OK; + } + RESULT_ENSURE_REF(extension); + + struct s2n_stuffer sig_algs = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&sig_algs, &extension->extension)); + + uint8_t entry_bytes[S2N_JA4_IANA_ENTRY_SIZE] = { 0 }; + struct s2n_stuffer entry = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&entry.blob, entry_bytes, sizeof(entry_bytes))); + + bool is_first = true; + if (s2n_stuffer_skip_read(&sig_algs, sizeof(uint16_t)) != S2N_SUCCESS) { + return S2N_RESULT_OK; + } + while (s2n_stuffer_data_available(&sig_algs)) { + uint16_t iana = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&sig_algs, &iana)); + if (s2n_fingerprint_is_grease_value(iana)) { + continue; + } + if (is_first) { + RESULT_GUARD(s2n_fingerprint_hash_add_char(hash, S2N_JA4_PART_DIV)); + } else { + RESULT_GUARD_POSIX(s2n_stuffer_write_char(&entry, S2N_JA4_LIST_DIV)); + } + RESULT_GUARD(s2n_stuffer_write_uint16_hex(&entry, iana)); + RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, entry_bytes, + s2n_stuffer_data_available(&entry))); + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&entry)); + is_first = false; + } + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# A 12 character truncated sha256 hash of the list of extensions, sorted by + *# hex value, followed by the list of signature algorithms, in the order that + *# they appear (not sorted). + */ +static S2N_RESULT s2n_fingerprint_ja4_c(struct s2n_fingerprint *fingerprint, + struct s2n_fingerprint_hash *hash, struct s2n_blob *extensions_count, + struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(fingerprint); + + uint16_t extensions_count_value = 0; + RESULT_GUARD(s2n_fingerprint_ja4_extensions(hash, fingerprint->client_hello, + &fingerprint->workspace, &extensions_count_value)); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# The signature algorithm hex values are then added to the end of the list + *# in the order that they appear (not sorted) with an underscore delimiting + *# the two lists. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# If there are no signature algorithms in the hello packet, + *# then the string ends without an underscore and is hashed. + * + * s2n_fingerprint_ja4_sig_algs handles writing the underscore because we + * need to skip writing it if there are no signature algorithms. + */ + RESULT_GUARD(s2n_fingerprint_ja4_sig_algs(hash, fingerprint->client_hello)); + + RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); + RESULT_GUARD(s2n_fingerprint_ja4_count(extensions_count, extensions_count_value)); + return S2N_RESULT_OK; +} + +/* JA4 fingerprints are basically of the form a_b_c: + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#ja4-algorithm + *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) + *# (2 character TLS version) + *# (SNI=”d” or no SNI=”i”) + *# (2 character count of ciphers) + *# (2 character count of extensions) + *# (first and last characters of first ALPN extension value) + *# _ + *# (sha256 hash of the list of cipher hex codes sorted in hex order, truncated to 12 characters) + *# _ + *# (sha256 hash of (the list of extension hex codes sorted in hex order)_(the list of signature algorithms), truncated to 12 characters) + *# + *# The end result is a fingerprint that looks like: + *# t13d1516h2_8daaf6152771_b186095e22b6 + */ +static S2N_RESULT s2n_fingerprint_ja4(struct s2n_fingerprint *fingerprint, + struct s2n_fingerprint_hash *hash, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(fingerprint); + RESULT_ENSURE_REF(hash); + RESULT_ENSURE_REF(output); + + if (s2n_stuffer_is_freed(&fingerprint->workspace)) { + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&fingerprint->workspace, S2N_JA4_WORKSPACE_SIZE)); + } + + struct s2n_blob ciphers_count = { 0 }; + struct s2n_blob extensions_count = { 0 }; + RESULT_GUARD(s2n_fingerprint_ja4_a(fingerprint, output, &ciphers_count, &extensions_count)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); + RESULT_GUARD(s2n_fingerprint_ja4_b(fingerprint, hash, &ciphers_count, output)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); + RESULT_GUARD(s2n_fingerprint_ja4_c(fingerprint, hash, &extensions_count, output)); + + if (s2n_fingerprint_hash_do_digest(hash)) { + /* The extra two bytes are for the characters separating the parts */ + fingerprint->raw_size = hash->bytes_digested + S2N_JA4_A_SIZE + 2; + } else { + fingerprint->raw_size = s2n_stuffer_data_available(output); + } + + return S2N_RESULT_OK; +} + +struct s2n_fingerprint_method ja4_fingerprint = { + .hash = S2N_HASH_SHA256, + .hash_str_size = S2N_JA4_SIZE, + .fingerprint = s2n_fingerprint_ja4, +}; diff --git a/tls/s2n_handshake.c b/tls/s2n_handshake.c index f959044378c..f8d471d5e7f 100644 --- a/tls/s2n_handshake.c +++ b/tls/s2n_handshake.c @@ -1,382 +1,383 @@ -/* - * 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 "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_map.h" -#include "utils/s2n_safety.h" - -int s2n_handshake_write_header(struct s2n_stuffer *out, uint8_t message_type) -{ - S2N_ERROR_IF(s2n_stuffer_data_available(out), S2N_ERR_HANDSHAKE_STATE); - - /* Write the message header */ - POSIX_GUARD(s2n_stuffer_write_uint8(out, message_type)); - - /* Leave the length blank for now */ - uint16_t length = 0; - POSIX_GUARD(s2n_stuffer_write_uint24(out, length)); - - return S2N_SUCCESS; -} - -int s2n_handshake_finish_header(struct s2n_stuffer *out) -{ - uint32_t length = s2n_stuffer_data_available(out); - S2N_ERROR_IF(length < TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); - - uint32_t payload = length - TLS_HANDSHAKE_HEADER_LENGTH; - - /* Write the message header */ - POSIX_GUARD(s2n_stuffer_rewrite(out)); - POSIX_GUARD(s2n_stuffer_skip_write(out, 1)); - POSIX_GUARD(s2n_stuffer_write_uint24(out, payload)); - POSIX_GUARD(s2n_stuffer_skip_write(out, payload)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_handshake_parse_header(struct s2n_stuffer *io, uint8_t *message_type, uint32_t *length) -{ - RESULT_ENSURE(s2n_stuffer_data_available(io) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); - - /* read the message header */ - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(io, message_type)); - RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(io, length)); - - return S2N_RESULT_OK; -} - -static int s2n_handshake_get_hash_state_ptr(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state **hash_state) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - switch (hash_alg) { - case S2N_HASH_MD5: - *hash_state = &conn->handshake.hashes->md5; - break; - case S2N_HASH_SHA1: - *hash_state = &conn->handshake.hashes->sha1; - break; - case S2N_HASH_SHA224: - *hash_state = &conn->handshake.hashes->sha224; - break; - case S2N_HASH_SHA256: - *hash_state = &conn->handshake.hashes->sha256; - break; - case S2N_HASH_SHA384: - *hash_state = &conn->handshake.hashes->sha384; - break; - case S2N_HASH_SHA512: - *hash_state = &conn->handshake.hashes->sha512; - break; - case S2N_HASH_MD5_SHA1: - *hash_state = &conn->handshake.hashes->md5_sha1; - break; - default: - POSIX_BAIL(S2N_ERR_HASH_INVALID_ALGORITHM); - break; - } - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg) -{ - struct s2n_hash_state *hash_state = NULL; - RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); - RESULT_GUARD_POSIX(s2n_hash_reset(hash_state)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_handshake_copy_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *copy) -{ - struct s2n_hash_state *hash_state = NULL; - RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); - RESULT_GUARD_POSIX(s2n_hash_copy(copy, hash_state)); - return S2N_RESULT_OK; -} - -int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake) -{ - memset(handshake->required_hash_algs, 1, sizeof(handshake->required_hash_algs)); - return S2N_SUCCESS; -} - -static int s2n_handshake_require_hash(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) -{ - handshake->required_hash_algs[hash_alg] = 1; - return S2N_SUCCESS; -} - -uint8_t s2n_handshake_is_hash_required(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) -{ - return handshake->required_hash_algs[hash_alg]; -} - -/* Update the required handshake hash algs depending on current handshake session state. - * This function must called at the end of a handshake message handler. Additionally it must be called after the - * ClientHello or ServerHello is processed in client and server mode respectively. The relevant handshake parameters - * are not available until those messages are processed. - */ -int s2n_conn_update_required_handshake_hashes(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - /* Clear all of the required hashes */ - memset(conn->handshake.required_hash_algs, 0, sizeof(conn->handshake.required_hash_algs)); - - if (conn->actual_protocol_version < S2N_TLS13) { - message_type_t handshake_message = s2n_conn_get_current_message_type(conn); - const uint8_t client_cert_verify_done = (handshake_message >= CLIENT_CERT_VERIFY) ? 1 : 0; - s2n_cert_auth_type client_cert_auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - - /* In TLS1.2 the transcript hash used in the client's certificate verify message - * is determined by the signature algorithm used to sign the certificate verify message. - * Therefore all hashes are needed until we're past CLIENT_CERT_VERIFY if client auth is possible. */ - if ((client_cert_auth_type != S2N_CERT_AUTH_NONE) && !client_cert_verify_done) { - POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); - return S2N_SUCCESS; - } - } - - /* We don't need all of the hashes. Set the hash alg(s) required for the PRF */ - switch (conn->actual_protocol_version) { - case S2N_SSLv3: - case S2N_TLS10: - case S2N_TLS11: - POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_MD5)); - POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_SHA1)); - break; - case S2N_TLS12: - /* fall through */ - case S2N_TLS13: { - /* For TLS 1.2 and TLS 1.3, the cipher suite defines the PRF hash alg */ - s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; - s2n_hash_algorithm hash_alg; - POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); - POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, hash_alg)); - break; - } - } - - return S2N_SUCCESS; -} - -/* - * Take a hostname and return a single "simple" wildcard domain name that matches it. - * The output wildcard representation is meant to be compared directly against a wildcard domain in a certificate. - * We take a restrictive definition of wildcard here to achieve a single unique wildcard representation - * given any input hostname. - * No embedded or trailing wildcards are supported. Additionally, we only support one level of wildcard matching. - * Thus the output should be a single wildcard character in the first(left-most) DNS label. - * - * Example: - * - my.domain.name -> *.domain.name - * - * Not supported: - * - my.domain.name -> m*.domain.name - * - my.domain.name -> my.*.name - * etc. - * - * The motivation for using a constrained definition of wildcard: - * - Support for issuing non-simple wildcard certificates is insignificant. - * - Certificate selection can be implemented with a constant number of lookups(two). - */ -int s2n_create_wildcard_hostname(struct s2n_stuffer *hostname_stuffer, struct s2n_stuffer *output) -{ - /* Find the end of the first label */ - POSIX_GUARD(s2n_stuffer_skip_to_char(hostname_stuffer, '.')); - - /* No first label found */ - if (s2n_stuffer_data_available(hostname_stuffer) == 0) { - return S2N_SUCCESS; - } - - /* Slap a single wildcard character to be the first label in output */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, '*')); - - /* Simply copy the rest of the input to the output. */ - POSIX_GUARD(s2n_stuffer_copy(hostname_stuffer, output, s2n_stuffer_data_available(hostname_stuffer))); - - return S2N_SUCCESS; -} - -static int s2n_find_cert_matches(struct s2n_map *domain_name_to_cert_map, - struct s2n_blob *dns_name, - struct s2n_cert_chain_and_key *matches[S2N_CERT_TYPE_COUNT], - uint8_t *match_exists) -{ - struct s2n_blob map_value = { 0 }; - bool key_found = false; - POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, dns_name, &map_value, &key_found)); - if (key_found) { - struct certs_by_type *value = (void *) map_value.data; - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - matches[i] = value->certs[i]; - } - *match_exists = 1; - } - - return S2N_SUCCESS; -} - -/* Find certificates that match the ServerName TLS extension sent by the client. - * For a given ServerName there can be multiple matching certificates based on the - * type of key in the certificate. - * - * A match is determined using s2n_map lookup by DNS name. - * Wildcards that have a single * in the left most label are supported. - */ -int s2n_conn_find_name_matching_certs(struct s2n_connection *conn) -{ - if (!s2n_server_received_server_name(conn)) { - return S2N_SUCCESS; - } - const char *name = conn->server_name; - struct s2n_blob hostname_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) name, strlen(name))); - POSIX_ENSURE_LTE(hostname_blob.size, S2N_MAX_SERVER_NAME); - char normalized_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; - POSIX_CHECKED_MEMCPY(normalized_hostname, hostname_blob.data, hostname_blob.size); - struct s2n_blob normalized_name = { 0 }; - POSIX_GUARD(s2n_blob_init(&normalized_name, (uint8_t *) normalized_hostname, hostname_blob.size)); - - POSIX_GUARD(s2n_blob_char_to_lower(&normalized_name)); - struct s2n_stuffer normalized_hostname_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&normalized_hostname_stuffer, &normalized_name)); - POSIX_GUARD(s2n_stuffer_skip_write(&normalized_hostname_stuffer, normalized_name.size)); - - /* Find the exact matches for the ServerName */ - POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, - &normalized_name, - conn->handshake_params.exact_sni_matches, - &(conn->handshake_params.exact_sni_match_exists))); - - if (!conn->handshake_params.exact_sni_match_exists) { - /* We have not yet found an exact domain match. Try to find wildcard matches. */ - char wildcard_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; - struct s2n_blob wildcard_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&wildcard_blob, (uint8_t *) wildcard_hostname, sizeof(wildcard_hostname))); - struct s2n_stuffer wildcard_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&wildcard_stuffer, &wildcard_blob)); - POSIX_GUARD(s2n_create_wildcard_hostname(&normalized_hostname_stuffer, &wildcard_stuffer)); - const uint32_t wildcard_len = s2n_stuffer_data_available(&wildcard_stuffer); - - /* Couldn't create a valid wildcard from the input */ - if (wildcard_len == 0) { - return S2N_SUCCESS; - } - - /* The client's SNI is wildcardified, do an exact match against the set of server certs. */ - wildcard_blob.size = wildcard_len; - POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, - &wildcard_blob, - conn->handshake_params.wc_sni_matches, - &(conn->handshake_params.wc_sni_match_exists))); - } - - /* If we found a suitable cert, we should send back the ServerName extension. - * Note that this may have already been set by the client hello callback, so we won't override its value - */ - conn->server_name_used = conn->server_name_used - || conn->handshake_params.exact_sni_match_exists - || conn->handshake_params.wc_sni_match_exists; - - return S2N_SUCCESS; -} - -/* Find the optimal certificate of a specific type. - * The priority of set of certificates to choose from: - * 1. Certificates that match the client's ServerName extension. - * 2. Default certificates - */ -struct s2n_cert_chain_and_key *s2n_get_compatible_cert_chain_and_key(struct s2n_connection *conn, const s2n_pkey_type cert_type) -{ - if (conn->handshake_params.exact_sni_match_exists) { - /* This may return NULL if there was an SNI match, but not a match the cipher_suite's authentication type. */ - return conn->handshake_params.exact_sni_matches[cert_type]; - } - if (conn->handshake_params.wc_sni_match_exists) { - return conn->handshake_params.wc_sni_matches[cert_type]; - } else { - /* We don't have any name matches. Use the default certificate that works with the key type. */ - return conn->config->default_certs_by_type.certs[cert_type]; - } -} - -/* This method will work when testing S2N, and for the EndOfEarlyData message. - * - * However, it will NOT work for arbitrary message types when potentially receiving records - * that contain multiple messages, like when talking to a non-S2N TLS implementation. If the "end_message" - * is not the first message in a multi-message record, negotiation will not stop. - * (This is not an issue for EndOfEarlyData because encryption and message order requirements force - * EndOfEarlyData to always be the first and only handshake message in its handshake record) - */ -S2N_RESULT s2n_negotiate_until_message(struct s2n_connection *conn, s2n_blocked_status *blocked, message_type_t end_message) -{ - RESULT_ENSURE_REF(conn); - conn->handshake.end_of_messages = end_message; - int r = s2n_negotiate(conn, blocked); - conn->handshake.end_of_messages = APPLICATION_DATA; - RESULT_GUARD_POSIX(r); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_handshake_validate(const struct s2n_handshake *s2n_handshake) -{ - RESULT_ENSURE_REF(s2n_handshake); - RESULT_DEBUG_ENSURE(s2n_handshake->handshake_type < 256, S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(s2n_handshake->message_number >= 0 && s2n_handshake->message_number < 32, S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_handshake_set_finished_len(struct s2n_connection *conn, uint8_t len) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_GT(len, 0); - RESULT_ENSURE_LTE(len, sizeof(conn->handshake.server_finished)); - RESULT_ENSURE_LTE(len, sizeof(conn->handshake.client_finished)); - - /* - * We maintain a version of the "finished" / "verify_data" field - * for both the client and server, so this method will be called - * once for the client version and once for the server version. - * - * The lengths of both versions must match, or something has - * gone wrong in our implementation. - */ - uint8_t *finished_length = &conn->handshake.finished_len; - if (*finished_length == 0) { - *finished_length = len; - } - RESULT_ENSURE_EQ(*finished_length, len); - - return S2N_RESULT_OK; -} - -bool s2n_handshake_is_renegotiation(struct s2n_connection *conn) -{ - return conn && conn->handshake.renegotiation; -} +#include "utils/s2n_prelude.h" +/* + * 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 "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_map.h" +#include "utils/s2n_safety.h" + +int s2n_handshake_write_header(struct s2n_stuffer *out, uint8_t message_type) +{ + S2N_ERROR_IF(s2n_stuffer_data_available(out), S2N_ERR_HANDSHAKE_STATE); + + /* Write the message header */ + POSIX_GUARD(s2n_stuffer_write_uint8(out, message_type)); + + /* Leave the length blank for now */ + uint16_t length = 0; + POSIX_GUARD(s2n_stuffer_write_uint24(out, length)); + + return S2N_SUCCESS; +} + +int s2n_handshake_finish_header(struct s2n_stuffer *out) +{ + uint32_t length = s2n_stuffer_data_available(out); + S2N_ERROR_IF(length < TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); + + uint32_t payload = length - TLS_HANDSHAKE_HEADER_LENGTH; + + /* Write the message header */ + POSIX_GUARD(s2n_stuffer_rewrite(out)); + POSIX_GUARD(s2n_stuffer_skip_write(out, 1)); + POSIX_GUARD(s2n_stuffer_write_uint24(out, payload)); + POSIX_GUARD(s2n_stuffer_skip_write(out, payload)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_handshake_parse_header(struct s2n_stuffer *io, uint8_t *message_type, uint32_t *length) +{ + RESULT_ENSURE(s2n_stuffer_data_available(io) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); + + /* read the message header */ + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(io, message_type)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(io, length)); + + return S2N_RESULT_OK; +} + +static int s2n_handshake_get_hash_state_ptr(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state **hash_state) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + switch (hash_alg) { + case S2N_HASH_MD5: + *hash_state = &conn->handshake.hashes->md5; + break; + case S2N_HASH_SHA1: + *hash_state = &conn->handshake.hashes->sha1; + break; + case S2N_HASH_SHA224: + *hash_state = &conn->handshake.hashes->sha224; + break; + case S2N_HASH_SHA256: + *hash_state = &conn->handshake.hashes->sha256; + break; + case S2N_HASH_SHA384: + *hash_state = &conn->handshake.hashes->sha384; + break; + case S2N_HASH_SHA512: + *hash_state = &conn->handshake.hashes->sha512; + break; + case S2N_HASH_MD5_SHA1: + *hash_state = &conn->handshake.hashes->md5_sha1; + break; + default: + POSIX_BAIL(S2N_ERR_HASH_INVALID_ALGORITHM); + break; + } + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg) +{ + struct s2n_hash_state *hash_state = NULL; + RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); + RESULT_GUARD_POSIX(s2n_hash_reset(hash_state)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_handshake_copy_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *copy) +{ + struct s2n_hash_state *hash_state = NULL; + RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); + RESULT_GUARD_POSIX(s2n_hash_copy(copy, hash_state)); + return S2N_RESULT_OK; +} + +int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake) +{ + memset(handshake->required_hash_algs, 1, sizeof(handshake->required_hash_algs)); + return S2N_SUCCESS; +} + +static int s2n_handshake_require_hash(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) +{ + handshake->required_hash_algs[hash_alg] = 1; + return S2N_SUCCESS; +} + +uint8_t s2n_handshake_is_hash_required(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) +{ + return handshake->required_hash_algs[hash_alg]; +} + +/* Update the required handshake hash algs depending on current handshake session state. + * This function must called at the end of a handshake message handler. Additionally it must be called after the + * ClientHello or ServerHello is processed in client and server mode respectively. The relevant handshake parameters + * are not available until those messages are processed. + */ +int s2n_conn_update_required_handshake_hashes(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + /* Clear all of the required hashes */ + memset(conn->handshake.required_hash_algs, 0, sizeof(conn->handshake.required_hash_algs)); + + if (conn->actual_protocol_version < S2N_TLS13) { + message_type_t handshake_message = s2n_conn_get_current_message_type(conn); + const uint8_t client_cert_verify_done = (handshake_message >= CLIENT_CERT_VERIFY) ? 1 : 0; + s2n_cert_auth_type client_cert_auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + /* In TLS1.2 the transcript hash used in the client's certificate verify message + * is determined by the signature algorithm used to sign the certificate verify message. + * Therefore all hashes are needed until we're past CLIENT_CERT_VERIFY if client auth is possible. */ + if ((client_cert_auth_type != S2N_CERT_AUTH_NONE) && !client_cert_verify_done) { + POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); + return S2N_SUCCESS; + } + } + + /* We don't need all of the hashes. Set the hash alg(s) required for the PRF */ + switch (conn->actual_protocol_version) { + case S2N_SSLv3: + case S2N_TLS10: + case S2N_TLS11: + POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_MD5)); + POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_SHA1)); + break; + case S2N_TLS12: + /* fall through */ + case S2N_TLS13: { + /* For TLS 1.2 and TLS 1.3, the cipher suite defines the PRF hash alg */ + s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, hash_alg)); + break; + } + } + + return S2N_SUCCESS; +} + +/* + * Take a hostname and return a single "simple" wildcard domain name that matches it. + * The output wildcard representation is meant to be compared directly against a wildcard domain in a certificate. + * We take a restrictive definition of wildcard here to achieve a single unique wildcard representation + * given any input hostname. + * No embedded or trailing wildcards are supported. Additionally, we only support one level of wildcard matching. + * Thus the output should be a single wildcard character in the first(left-most) DNS label. + * + * Example: + * - my.domain.name -> *.domain.name + * + * Not supported: + * - my.domain.name -> m*.domain.name + * - my.domain.name -> my.*.name + * etc. + * + * The motivation for using a constrained definition of wildcard: + * - Support for issuing non-simple wildcard certificates is insignificant. + * - Certificate selection can be implemented with a constant number of lookups(two). + */ +int s2n_create_wildcard_hostname(struct s2n_stuffer *hostname_stuffer, struct s2n_stuffer *output) +{ + /* Find the end of the first label */ + POSIX_GUARD(s2n_stuffer_skip_to_char(hostname_stuffer, '.')); + + /* No first label found */ + if (s2n_stuffer_data_available(hostname_stuffer) == 0) { + return S2N_SUCCESS; + } + + /* Slap a single wildcard character to be the first label in output */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, '*')); + + /* Simply copy the rest of the input to the output. */ + POSIX_GUARD(s2n_stuffer_copy(hostname_stuffer, output, s2n_stuffer_data_available(hostname_stuffer))); + + return S2N_SUCCESS; +} + +static int s2n_find_cert_matches(struct s2n_map *domain_name_to_cert_map, + struct s2n_blob *dns_name, + struct s2n_cert_chain_and_key *matches[S2N_CERT_TYPE_COUNT], + uint8_t *match_exists) +{ + struct s2n_blob map_value = { 0 }; + bool key_found = false; + POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, dns_name, &map_value, &key_found)); + if (key_found) { + struct certs_by_type *value = (void *) map_value.data; + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + matches[i] = value->certs[i]; + } + *match_exists = 1; + } + + return S2N_SUCCESS; +} + +/* Find certificates that match the ServerName TLS extension sent by the client. + * For a given ServerName there can be multiple matching certificates based on the + * type of key in the certificate. + * + * A match is determined using s2n_map lookup by DNS name. + * Wildcards that have a single * in the left most label are supported. + */ +int s2n_conn_find_name_matching_certs(struct s2n_connection *conn) +{ + if (!s2n_server_received_server_name(conn)) { + return S2N_SUCCESS; + } + const char *name = conn->server_name; + struct s2n_blob hostname_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) name, strlen(name))); + POSIX_ENSURE_LTE(hostname_blob.size, S2N_MAX_SERVER_NAME); + char normalized_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; + POSIX_CHECKED_MEMCPY(normalized_hostname, hostname_blob.data, hostname_blob.size); + struct s2n_blob normalized_name = { 0 }; + POSIX_GUARD(s2n_blob_init(&normalized_name, (uint8_t *) normalized_hostname, hostname_blob.size)); + + POSIX_GUARD(s2n_blob_char_to_lower(&normalized_name)); + struct s2n_stuffer normalized_hostname_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&normalized_hostname_stuffer, &normalized_name)); + POSIX_GUARD(s2n_stuffer_skip_write(&normalized_hostname_stuffer, normalized_name.size)); + + /* Find the exact matches for the ServerName */ + POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, + &normalized_name, + conn->handshake_params.exact_sni_matches, + &(conn->handshake_params.exact_sni_match_exists))); + + if (!conn->handshake_params.exact_sni_match_exists) { + /* We have not yet found an exact domain match. Try to find wildcard matches. */ + char wildcard_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; + struct s2n_blob wildcard_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&wildcard_blob, (uint8_t *) wildcard_hostname, sizeof(wildcard_hostname))); + struct s2n_stuffer wildcard_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&wildcard_stuffer, &wildcard_blob)); + POSIX_GUARD(s2n_create_wildcard_hostname(&normalized_hostname_stuffer, &wildcard_stuffer)); + const uint32_t wildcard_len = s2n_stuffer_data_available(&wildcard_stuffer); + + /* Couldn't create a valid wildcard from the input */ + if (wildcard_len == 0) { + return S2N_SUCCESS; + } + + /* The client's SNI is wildcardified, do an exact match against the set of server certs. */ + wildcard_blob.size = wildcard_len; + POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, + &wildcard_blob, + conn->handshake_params.wc_sni_matches, + &(conn->handshake_params.wc_sni_match_exists))); + } + + /* If we found a suitable cert, we should send back the ServerName extension. + * Note that this may have already been set by the client hello callback, so we won't override its value + */ + conn->server_name_used = conn->server_name_used + || conn->handshake_params.exact_sni_match_exists + || conn->handshake_params.wc_sni_match_exists; + + return S2N_SUCCESS; +} + +/* Find the optimal certificate of a specific type. + * The priority of set of certificates to choose from: + * 1. Certificates that match the client's ServerName extension. + * 2. Default certificates + */ +struct s2n_cert_chain_and_key *s2n_get_compatible_cert_chain_and_key(struct s2n_connection *conn, const s2n_pkey_type cert_type) +{ + if (conn->handshake_params.exact_sni_match_exists) { + /* This may return NULL if there was an SNI match, but not a match the cipher_suite's authentication type. */ + return conn->handshake_params.exact_sni_matches[cert_type]; + } + if (conn->handshake_params.wc_sni_match_exists) { + return conn->handshake_params.wc_sni_matches[cert_type]; + } else { + /* We don't have any name matches. Use the default certificate that works with the key type. */ + return conn->config->default_certs_by_type.certs[cert_type]; + } +} + +/* This method will work when testing S2N, and for the EndOfEarlyData message. + * + * However, it will NOT work for arbitrary message types when potentially receiving records + * that contain multiple messages, like when talking to a non-S2N TLS implementation. If the "end_message" + * is not the first message in a multi-message record, negotiation will not stop. + * (This is not an issue for EndOfEarlyData because encryption and message order requirements force + * EndOfEarlyData to always be the first and only handshake message in its handshake record) + */ +S2N_RESULT s2n_negotiate_until_message(struct s2n_connection *conn, s2n_blocked_status *blocked, message_type_t end_message) +{ + RESULT_ENSURE_REF(conn); + conn->handshake.end_of_messages = end_message; + int r = s2n_negotiate(conn, blocked); + conn->handshake.end_of_messages = APPLICATION_DATA; + RESULT_GUARD_POSIX(r); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_handshake_validate(const struct s2n_handshake *s2n_handshake) +{ + RESULT_ENSURE_REF(s2n_handshake); + RESULT_DEBUG_ENSURE(s2n_handshake->handshake_type < 256, S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(s2n_handshake->message_number >= 0 && s2n_handshake->message_number < 32, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_handshake_set_finished_len(struct s2n_connection *conn, uint8_t len) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_GT(len, 0); + RESULT_ENSURE_LTE(len, sizeof(conn->handshake.server_finished)); + RESULT_ENSURE_LTE(len, sizeof(conn->handshake.client_finished)); + + /* + * We maintain a version of the "finished" / "verify_data" field + * for both the client and server, so this method will be called + * once for the client version and once for the server version. + * + * The lengths of both versions must match, or something has + * gone wrong in our implementation. + */ + uint8_t *finished_length = &conn->handshake.finished_len; + if (*finished_length == 0) { + *finished_length = len; + } + RESULT_ENSURE_EQ(*finished_length, len); + + return S2N_RESULT_OK; +} + +bool s2n_handshake_is_renegotiation(struct s2n_connection *conn) +{ + return conn && conn->handshake.renegotiation; +} diff --git a/tls/s2n_handshake_io.c b/tls/s2n_handshake_io.c index 1fa89db0df1..024f9d72229 100644 --- a/tls/s2n_handshake_io.c +++ b/tls/s2n_handshake_io.c @@ -1,1751 +1,1752 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_async_pkey.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_kex.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls13_key_schedule.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_events.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" - -/* clang-format off */ -struct s2n_handshake_action { - uint8_t record_type; - uint8_t message_type; - char writer; /* 'S' or 'C' for server or client, 'B' for both */ - int (*handler[2]) (struct s2n_connection * conn); -}; - -static int s2n_always_fail_send(struct s2n_connection *conn) -{ - /* This state should never be sending a handshake message. */ - POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); -} - -static int s2n_always_fail_recv(struct s2n_connection *conn) -{ - /* This state should never have an incoming handshake message. */ - POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); -} - -/* Client and Server handlers for each message type we support. - * See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7 for the list of handshake message types - */ -static struct s2n_handshake_action state_machine[] = { - /* message_type_t = {Record type Message type Writer S2N_SERVER S2N_CLIENT } */ - [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, - [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, - [SERVER_NEW_SESSION_TICKET] = {TLS_HANDSHAKE, TLS_SERVER_NEW_SESSION_TICKET,'S', {s2n_server_nst_send, s2n_server_nst_recv}}, - [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, - [SERVER_CERT_STATUS] = {TLS_HANDSHAKE, TLS_SERVER_CERT_STATUS, 'S', {s2n_server_status_send, s2n_server_status_recv}}, - [SERVER_KEY] = {TLS_HANDSHAKE, TLS_SERVER_KEY, 'S', {s2n_server_key_send, s2n_server_key_recv}}, - [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_cert_req_send, s2n_cert_req_recv}}, - [SERVER_HELLO_DONE] = {TLS_HANDSHAKE, TLS_SERVER_HELLO_DONE, 'S', {s2n_server_done_send, s2n_server_done_recv}}, - [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, - [CLIENT_KEY] = {TLS_HANDSHAKE, TLS_CLIENT_KEY, 'C', {s2n_client_key_recv, s2n_client_key_send}}, - [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_client_cert_verify_recv, s2n_client_cert_verify_send}}, - [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_client_ccs_recv, s2n_ccs_send}}, - [CLIENT_NPN] = {TLS_HANDSHAKE, TLS_NPN, 'C', {s2n_next_protocol_recv, s2n_next_protocol_send}}, - [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_client_finished_recv, s2n_client_finished_send}}, - [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_server_ccs_recv}}, - [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_server_finished_send, s2n_server_finished_recv}}, - [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, -}; - -/* - * Client and Server handlers for TLS1.3. - */ -static struct s2n_handshake_action tls13_state_machine[] = { - /* message_type_t = {Record type, Message type, Writer, {Server handler, client handler} } */ - [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, - [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, - [HELLO_RETRY_MSG] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_retry_send, s2n_server_hello_retry_recv}}, - [ENCRYPTED_EXTENSIONS] = {TLS_HANDSHAKE, TLS_ENCRYPTED_EXTENSIONS, 'S', {s2n_encrypted_extensions_send, s2n_encrypted_extensions_recv}}, - [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_tls13_cert_req_send, s2n_tls13_cert_req_recv}}, - [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, - [SERVER_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'S', {s2n_tls13_cert_verify_send, s2n_tls13_cert_verify_recv}}, - [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_tls13_server_finished_send, s2n_tls13_server_finished_recv}}, - - [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, - [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_tls13_cert_verify_recv, s2n_tls13_cert_verify_send}}, - [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_tls13_client_finished_recv, s2n_tls13_client_finished_send}}, - [END_OF_EARLY_DATA] = {TLS_HANDSHAKE, TLS_END_OF_EARLY_DATA, 'C', {s2n_end_of_early_data_recv, s2n_end_of_early_data_send}}, - - /* Not used by TLS1.3, except to maintain middlebox compatibility */ - [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_basic_ccs_recv, s2n_ccs_send}}, - [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_basic_ccs_recv}}, - - [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, -}; - -#define MESSAGE_NAME_ENTRY(msg) [msg] = #msg - -static const char *message_names[] = { - MESSAGE_NAME_ENTRY(CLIENT_HELLO), - MESSAGE_NAME_ENTRY(SERVER_HELLO), - MESSAGE_NAME_ENTRY(ENCRYPTED_EXTENSIONS), - MESSAGE_NAME_ENTRY(SERVER_NEW_SESSION_TICKET), - MESSAGE_NAME_ENTRY(SERVER_CERT), - MESSAGE_NAME_ENTRY(SERVER_CERT_STATUS), - MESSAGE_NAME_ENTRY(SERVER_CERT_VERIFY), - MESSAGE_NAME_ENTRY(SERVER_KEY), - MESSAGE_NAME_ENTRY(SERVER_CERT_REQ), - MESSAGE_NAME_ENTRY(SERVER_HELLO_DONE), - MESSAGE_NAME_ENTRY(CLIENT_CERT), - MESSAGE_NAME_ENTRY(CLIENT_KEY), - MESSAGE_NAME_ENTRY(CLIENT_CERT_VERIFY), - MESSAGE_NAME_ENTRY(CLIENT_CHANGE_CIPHER_SPEC), - MESSAGE_NAME_ENTRY(CLIENT_FINISHED), - MESSAGE_NAME_ENTRY(SERVER_CHANGE_CIPHER_SPEC), - MESSAGE_NAME_ENTRY(SERVER_FINISHED), - MESSAGE_NAME_ENTRY(HELLO_RETRY_MSG), - MESSAGE_NAME_ENTRY(END_OF_EARLY_DATA), - MESSAGE_NAME_ENTRY(APPLICATION_DATA), - MESSAGE_NAME_ENTRY(CLIENT_NPN), -}; - -/* We support different ordering of TLS Handshake messages, depending on what is being negotiated. There's also a dummy "INITIAL" handshake - * that everything starts out as until we know better. - */ - -static message_type_t handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { - [INITIAL] = { - CLIENT_HELLO, - SERVER_HELLO - }, - - [NEGOTIATED] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA}, - - [NEGOTIATED | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - APPLICATION_DATA}, - - [NEGOTIATED | FULL_HANDSHAKE ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_NPN ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH| WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, -}; - -/* - * This selection of handshakes resembles the standard set, but with changes made to support tls1.3. - * - * The CHANGE_CIPHER_SPEC messages are included only for middlebox compatibility. - * See https://tools.ietf.org/html/rfc8446#appendix-D.4 - */ -static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { - [INITIAL] = { - CLIENT_HELLO, - SERVER_HELLO - }, - - [INITIAL | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO - }, - - [INITIAL | HELLO_RETRY_REQUEST] = { - CLIENT_HELLO, - HELLO_RETRY_MSG - }, - - [INITIAL | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG - }, - - [NEGOTIATED] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | WITH_EARLY_DATA] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - END_OF_EARLY_DATA, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT | WITH_EARLY_DATA] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, END_OF_EARLY_DATA, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS | WITH_EARLY_DATA] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - END_OF_EARLY_DATA, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | HELLO_RETRY_REQUEST] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, -}; -/* clang-format on */ - -#define MAX_HANDSHAKE_TYPE_LEN 142 -/* The handshake type labels used for TLS 1.0 - TLS 1.2, e.g. `NEGOTIATED|WITH_SESSION_TICKET` */ -static char handshake_type_str_tls12ish[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; - -/* The handshake type labels used for TLS 1.3, e.g. `NEGOTIATED|HELLO_RETRY_REQUEST` */ -static char handshake_type_str_tls13[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; - -static const char *tls12_handshake_type_names[] = { - "NEGOTIATED|", - "FULL_HANDSHAKE|", - "CLIENT_AUTH|", - "NO_CLIENT_CERT|", - "TLS12_PERFECT_FORWARD_SECRECY|", - "OCSP_STATUS|", - "WITH_SESSION_TICKET|", - "WITH_NPN|", -}; - -static const char *tls13_handshake_type_names[] = { - "NEGOTIATED|", - "FULL_HANDSHAKE|", - "CLIENT_AUTH|", - "NO_CLIENT_CERT|", - "HELLO_RETRY_REQUEST|", - "MIDDLEBOX_COMPAT|", - "WITH_EARLY_DATA|", - "EARLY_CLIENT_CCS|", -}; - -#define IS_TLS13_HANDSHAKE(conn) ((conn)->handshake.state_machine == S2N_STATE_MACHINE_TLS13) - -#define ACTIVE_STATE_MACHINE(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_state_machine : state_machine) -#define ACTIVE_HANDSHAKES(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_handshakes : handshakes) - -#define ACTIVE_MESSAGE(conn) ACTIVE_HANDSHAKES(conn)[(conn)->handshake.handshake_type][(conn)->handshake.message_number] - -#define ACTIVE_STATE(conn) ACTIVE_STATE_MACHINE(conn)[ACTIVE_MESSAGE((conn))] - -#define CCS_STATE(conn) (((conn)->mode == S2N_CLIENT) ? \ - ACTIVE_STATE_MACHINE(conn)[SERVER_CHANGE_CIPHER_SPEC] : \ - ACTIVE_STATE_MACHINE(conn)[CLIENT_CHANGE_CIPHER_SPEC]) - -#define EXPECTED_RECORD_TYPE(conn) ACTIVE_STATE(conn).record_type -#define EXPECTED_MESSAGE_TYPE(conn) ACTIVE_STATE(conn).message_type - -#define CONNECTION_WRITER(conn) (conn->mode == S2N_CLIENT ? 'C' : 'S') -#define CONNECTION_IS_WRITER(conn) (ACTIVE_STATE(conn).writer == CONNECTION_WRITER(conn)) - -/* Only used in our test cases. */ -message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn) -{ - return ACTIVE_MESSAGE(conn); -} - -static int s2n_advance_message(struct s2n_connection *conn) -{ - /* Get the mode: 'C'lient or 'S'erver */ - char previous_writer = ACTIVE_STATE(conn).writer; - char this_mode = CONNECTION_WRITER(conn); - - /* Actually advance the message number */ - conn->handshake.message_number++; - - /* When reading and using TLS1.3, skip optional change_cipher_spec states. */ - if (ACTIVE_STATE(conn).writer != this_mode && EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && IS_TLS13_HANDSHAKE(conn)) { - conn->handshake.message_number++; - } - - /* Set TCP_QUICKACK to avoid artificial delay during the handshake */ - POSIX_GUARD(s2n_socket_quickack(conn)); - - /* If optimized io hasn't been enabled or if the caller started out with a corked socket, - * we don't mess with it - */ - if (!conn->corked_io || s2n_socket_was_corked(conn)) { - return S2N_SUCCESS; - } - - /* Are we changing I/O directions */ - if (ACTIVE_STATE(conn).writer == previous_writer || ACTIVE_STATE(conn).writer == 'A') { - return S2N_SUCCESS; - } - - /* We're the new writer */ - if (ACTIVE_STATE(conn).writer == this_mode) { - if (s2n_connection_is_managed_corked(conn)) { - /* Set TCP_CORK/NOPUSH */ - POSIX_GUARD(s2n_socket_write_cork(conn)); - } - - return S2N_SUCCESS; - } - - /* We're the new reader, or we reached the "B" writer stage indicating that - we're at the application data stage - uncork the data */ - if (s2n_connection_is_managed_corked(conn)) { - POSIX_GUARD(s2n_socket_write_uncork(conn)); - } - - return S2N_SUCCESS; -} - -int s2n_generate_new_client_session_id(struct s2n_connection *conn) -{ - if (conn->mode == S2N_SERVER) { - struct s2n_blob session_id = { 0 }; - POSIX_GUARD(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); - - /* Generate a new session id */ - POSIX_GUARD_RESULT(s2n_get_public_random_data(&session_id)); - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - } - - return S2N_SUCCESS; -} - -/* Lets the server flag whether a HelloRetryRequest is needed while processing extensions */ -int s2n_set_hello_retry_required(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_INVALID_HELLO_RETRY); - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls13_flag(conn, HELLO_RETRY_REQUEST)); - - /* HelloRetryRequests also indicate rejection of early data. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# A server which receives an "early_data" extension MUST behave in one - *# of three ways: - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# - Request that the client send another ClientHello by responding - *# with a HelloRetryRequest. - **/ - if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - } - - return S2N_SUCCESS; -} - -bool s2n_is_hello_retry_message(struct s2n_connection *conn) -{ - return (conn != NULL && s2n_result_is_ok(s2n_handshake_validate(&(conn->handshake))) && ACTIVE_MESSAGE(conn) == HELLO_RETRY_MSG); -} - -bool s2n_is_hello_retry_handshake(struct s2n_connection *conn) -{ - return IS_HELLO_RETRY_HANDSHAKE(conn); -} - -static S2N_RESULT s2n_conn_set_tls13_handshake_type(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* Most handshake type flags should be reset before we calculate the handshake type, - * in order to handle changes during retries. - * However, flags that have already affected the message order must be kept to avoid - * rewriting the past. - */ - conn->handshake.handshake_type &= (HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS); - - /* A handshake type has been negotiated */ - RESULT_GUARD(s2n_handshake_type_set_flag(conn, NEGOTIATED)); - - if (conn->psk_params.chosen_psk == NULL) { - RESULT_GUARD(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); - } - - if (conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { - conn->handshake.handshake_type |= WITH_EARLY_DATA; - } - - s2n_cert_auth_type client_cert_auth_type; - RESULT_GUARD_POSIX(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - - if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED - && IS_FULL_HANDSHAKE(conn)) { - /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ - RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE - && IS_FULL_HANDSHAKE(conn)) { - /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ - RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } - - if (s2n_is_middlebox_compat_enabled(conn)) { - RESULT_GUARD(s2n_handshake_type_set_tls13_flag(conn, MIDDLEBOX_COMPAT)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_validate_ems_status(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - s2n_extension_type_id ems_ext_id = 0; - RESULT_GUARD_POSIX(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); - bool ems_extension_recv = S2N_CBIT_TEST(conn->extension_requests_received, ems_ext_id); - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *# If the original session used the "extended_master_secret" - *# extension but the new ClientHello does not contain it, the server - *# MUST abort the abbreviated handshake. - **/ - if (conn->ems_negotiated) { - RESULT_ENSURE(ems_extension_recv, S2N_ERR_MISSING_EXTENSION); - } - - /* Since we're discarding the resumption ticket, ignore EMS value from the ticket */ - conn->ems_negotiated = ems_extension_recv; - - return S2N_RESULT_OK; -} - -int s2n_conn_set_handshake_type(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - POSIX_GUARD_RESULT(s2n_conn_choose_state_machine(conn, conn->actual_protocol_version)); - - if (IS_TLS13_HANDSHAKE(conn)) { - POSIX_GUARD_RESULT(s2n_conn_set_tls13_handshake_type(conn)); - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_handshake_type_reset(conn)); - - /* A handshake type has been negotiated */ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NEGOTIATED)); - - s2n_cert_auth_type client_cert_auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - - if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED) { - /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE) { - /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } - - if (conn->npn_negotiated) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_NPN)); - } - - if (conn->config->use_tickets) { - if (conn->session_ticket_status == S2N_DECRYPT_TICKET) { - /* We reuse the session if a valid TLS12 ticket is provided. - * Otherwise, we will perform a full handshake and then generate - * a new session ticket. */ - if (s2n_result_is_ok(s2n_resume_decrypt_session(conn, &conn->client_ticket_to_decrypt))) { - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); - - /* Set up the handshake to send a session ticket since a valid ticket was not provided */ - if (s2n_result_is_ok(s2n_config_is_encrypt_key_available(conn->config))) { - conn->session_ticket_status = S2N_NEW_TICKET; - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); - } - - /* If a session ticket is presented by the client, then skip lookup in Session ID server cache */ - goto skip_cache_lookup; - } - - if (conn->session_ticket_status == S2N_NEW_TICKET) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); - } - } - - /* If a TLS session is resumed, the Server should respond in its ServerHello with the same SessionId the - * Client sent in the ClientHello. */ - if (conn->actual_protocol_version <= S2N_TLS12 && conn->mode == S2N_SERVER && s2n_allowed_to_cache_connection(conn)) { - int r = s2n_resume_from_cache(conn); - if (r == S2N_SUCCESS || (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno))) { - return r; - } - POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); - } - -skip_cache_lookup: - if (conn->mode == S2N_CLIENT && conn->client_session_resumed == 1) { - return S2N_SUCCESS; - } - - /* If we're doing full handshake, generate a new session id. */ - POSIX_GUARD(s2n_generate_new_client_session_id(conn)); - - /* If we get this far, it's a full handshake */ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); - - bool is_ephemeral = false; - POSIX_GUARD_RESULT(s2n_kex_is_ephemeral(conn->secure->cipher_suite->key_exchange_alg, &is_ephemeral)); - if (is_ephemeral) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, TLS12_PERFECT_FORWARD_SECRECY)); - } - - if (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, OCSP_STATUS)); - } - - return S2N_SUCCESS; -} - -int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn) -{ - s2n_cert_auth_type client_cert_auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_MISSING_CLIENT_CERT); - - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NO_CLIENT_CERT)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_conn_choose_state_machine(struct s2n_connection *conn, uint8_t protocol_version) -{ - RESULT_ENSURE_REF(conn); - - /* This should never be called before we know what version we're on */ - RESULT_ENSURE_NE(protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); - - if (protocol_version == S2N_TLS13) { - /* State machine should not change once set */ - RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS12); - conn->handshake.state_machine = S2N_STATE_MACHINE_TLS13; - } else { - /* State machine should not change once set */ - RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS13); - conn->handshake.state_machine = S2N_STATE_MACHINE_TLS12; - } - - return S2N_RESULT_OK; -} - -const char *s2n_connection_get_last_message_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_GUARD_RESULT(s2n_handshake_validate(&(conn->handshake))); - return message_names[ACTIVE_MESSAGE(conn)]; -} - -const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_PRECONDITION(s2n_handshake_validate(&(conn->handshake))); - - uint32_t handshake_type = conn->handshake.handshake_type; - - if (handshake_type == INITIAL) { - return "INITIAL"; - } - - const char **handshake_labels = tls13_handshake_type_names; - size_t handshake_labels_len = s2n_array_len(tls13_handshake_type_names); - char(*names)[MAX_HANDSHAKE_TYPE_LEN] = handshake_type_str_tls13; - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - handshake_labels = tls12_handshake_type_names; - handshake_labels_len = s2n_array_len(tls12_handshake_type_names); - names = handshake_type_str_tls12ish; - } - - /* Not all handshake strings will be created already. If the handshake string - * is not null, we can just return the handshake. Otherwise we have to compute - * it down below. */ - if (names[handshake_type][0] != '\0') { - return names[handshake_type]; - } - - /* Compute the cached string by concatenating each applicable handshake_type. - * - * Unit tests enforce that the elements of the cache are always - * long enough to contain the longest possible valid handshake_type, but - * for safety we still handle the case where we need to truncate. - */ - char *p = names[handshake_type]; - size_t remaining = MAX_HANDSHAKE_TYPE_LEN; - for (size_t i = 0; i < handshake_labels_len; i++) { - bool label_applies = handshake_type & (1 << i); - if (label_applies) { - size_t bytes_to_copy = S2N_MIN(remaining, strlen(handshake_labels[i])); - PTR_CHECKED_MEMCPY(p, handshake_labels[i], bytes_to_copy); - p[bytes_to_copy] = '\0'; - p += bytes_to_copy; - remaining -= bytes_to_copy; - } - } - - if (p != names[handshake_type] && '|' == *(p - 1)) { - *(p - 1) = '\0'; - } - - return names[handshake_type]; -} - -S2N_RESULT s2n_handshake_message_send(struct s2n_connection *conn, uint8_t content_type, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - struct s2n_stuffer *in = &conn->handshake.io; - - uint32_t size = s2n_stuffer_data_available(in); - if (size == 0) { - return S2N_RESULT_OK; - } - - if (s2n_connection_is_quic_enabled(conn)) { - RESULT_GUARD(s2n_quic_write_handshake_message(conn)); - RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); - return S2N_RESULT_OK; - } - - struct iovec iov = { 0 }; - iov.iov_len = size; - iov.iov_base = s2n_stuffer_raw_read(in, size); - RESULT_ENSURE_REF(iov.iov_base); - RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(in, size)); - - uint32_t total_bytes_written = 0; - while (total_bytes_written < size) { - int bytes_written = s2n_record_writev(conn, content_type, &iov, 1, - total_bytes_written, size - total_bytes_written); - RESULT_GUARD_POSIX(bytes_written); - total_bytes_written += bytes_written; - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(in, bytes_written)); - RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); - } - return S2N_RESULT_OK; -} - -/* Writing is relatively straight forward, simply write each message out as a record, - * we may fragment a message across multiple records, but we never coalesce multiple - * messages into single records. - * Precondition: secure outbound I/O has already been flushed - */ -static int s2n_handshake_write_io(struct s2n_connection *conn) -{ - uint8_t record_type = EXPECTED_RECORD_TYPE(conn); - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - /* Populate handshake.io with header/payload for the current state, once. - * Check wiped instead of s2n_stuffer_data_available to differentiate between the initial call - * to s2n_handshake_write_io and a repeated call after an EWOULDBLOCK. - */ - if (s2n_stuffer_is_wiped(&conn->handshake.io)) { - if (record_type == TLS_HANDSHAKE) { - POSIX_GUARD(s2n_handshake_write_header(&conn->handshake.io, ACTIVE_STATE(conn).message_type)); - } - POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn)); - if (record_type == TLS_HANDSHAKE) { - POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); - } - } - - POSIX_GUARD_RESULT(s2n_handshake_message_send(conn, record_type, &blocked)); - if (record_type == TLS_HANDSHAKE) { - POSIX_GUARD_RESULT(s2n_handshake_transcript_update(conn)); - } - - /* We're done sending the last record, reset everything */ - POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - - /* Update the secrets, if necessary */ - POSIX_GUARD_RESULT(s2n_tls13_secrets_update(conn)); - POSIX_GUARD_RESULT(s2n_tls13_key_schedule_update(conn)); - - /* Advance the state machine */ - POSIX_GUARD(s2n_advance_message(conn)); - - return S2N_SUCCESS; -} - -/* - * Returns: - * 1 - more data is needed to complete the handshake message. - * 0 - we read the whole handshake message. - * -1 - error processing the handshake message. - */ -static int s2n_read_full_handshake_message(struct s2n_connection *conn, uint8_t *message_type) -{ - uint32_t current_handshake_data = s2n_stuffer_data_available(&conn->handshake.io); - if (current_handshake_data < TLS_HANDSHAKE_HEADER_LENGTH) { - /* The message may be so badly fragmented that we don't even read the full header, take - * what we can and then continue to the next record read iteration. - */ - if (s2n_stuffer_data_available(&conn->in) < (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data)) { - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); - return 1; - } - - /* Get the remainder of the header */ - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data))); - } - - uint32_t handshake_message_length = 0; - POSIX_GUARD_RESULT(s2n_handshake_parse_header(&conn->handshake.io, message_type, &handshake_message_length)); - - S2N_ERROR_IF(handshake_message_length > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - - uint32_t bytes_to_take = handshake_message_length - s2n_stuffer_data_available(&conn->handshake.io); - bytes_to_take = S2N_MIN(bytes_to_take, s2n_stuffer_data_available(&conn->in)); - - /* If the record is handshake data, add it to the handshake buffer */ - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, bytes_to_take)); - - /* If we have the whole handshake message, then success */ - if (s2n_stuffer_data_available(&conn->handshake.io) == handshake_message_length) { - return 0; - } - - /* We don't have the whole message, so we'll need to go again */ - POSIX_GUARD(s2n_stuffer_reread(&conn->handshake.io)); - - return 1; -} - -static int s2n_handshake_handle_sslv2(struct s2n_connection *conn) -{ - S2N_ERROR_IF(ACTIVE_MESSAGE(conn) != CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); - - /* Add the message to our handshake hashes */ - struct s2n_blob hashed = { 0 }; - POSIX_GUARD(s2n_blob_init(&hashed, conn->header_in.blob.data + 2, 3)); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); - - hashed.data = conn->in.blob.data; - hashed.size = s2n_stuffer_data_available(&conn->in); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); - - /* Handle an SSLv2 client hello */ - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); - conn->client_hello.sslv2 = true; - /* Execute the state machine handler */ - int r = ACTIVE_STATE(conn).handler[conn->mode](conn); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); - - /* Advance the state machine */ - POSIX_GUARD(s2n_advance_message(conn)); - - return S2N_SUCCESS; -} - -static int s2n_try_delete_session_cache(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (s2n_allowed_to_cache_connection(conn) > 0) { - conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); - } - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_finish_read(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - RESULT_GUARD(s2n_handshake_transcript_update(conn)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->handshake.io)); - RESULT_GUARD(s2n_tls13_secrets_update(conn)); - RESULT_GUARD(s2n_tls13_key_schedule_update(conn)); - RESULT_GUARD_POSIX(s2n_advance_message(conn)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_handshake_app_data_recv(struct s2n_connection *conn) -{ - if (conn->early_data_expected) { - RESULT_GUARD(s2n_early_data_validate_recv(conn)); - RESULT_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); - } - - if (conn->handshake.renegotiation) { - RESULT_GUARD(s2n_renegotiate_validate(conn)); - /* During renegotiation, Application Data may only be received until - * the server acknowledges the new handshake with a ServerHello. - */ - RESULT_ENSURE(ACTIVE_MESSAGE(conn) == SERVER_HELLO, S2N_ERR_BAD_MESSAGE); - RESULT_BAIL(S2N_ERR_APP_DATA_BLOCKED); - } - - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); -} - -static int s2n_handshake_message_process(struct s2n_connection *conn, uint8_t record_type) -{ - POSIX_ENSURE_REF(conn); - - uint8_t message_type = 0; - while (s2n_stuffer_data_available(&conn->in)) { - /* We're done with negotiating but we have trailing data in this record. Bail on the handshake. */ - S2N_ERROR_IF(EXPECTED_RECORD_TYPE(conn) == TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE); - int r = 0; - POSIX_GUARD((r = s2n_read_full_handshake_message(conn, &message_type))); - - /* Do we need more data? This happens for message fragmentation */ - if (r == 1) { - /* Break out of this inner loop, but since we're not changing the state, the - * outer loop in s2n_handshake_io() will read another record. - */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - return S2N_SUCCESS; - } - - if (conn->mode == S2N_CLIENT) { - s2n_cert_auth_type client_cert_auth_type = { 0 }; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - /* If client auth is optional, we initially assume it will not be requested. - * If we received a request, switch to a client auth handshake. - */ - if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) { - POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST); - POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE); - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } - - /* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus" - * message even if it has sent a "status_request" extension in the ServerHello message. - */ - if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS - && message_type != TLS_SERVER_CERT_STATUS) { - POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS)); - } - } - - /* - *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4 - *# The one message that is not bound by these ordering rules - *# is the HelloRequest message, which can be sent at any time, but which - *# SHOULD be ignored by the client if it arrives in the middle of a handshake. - */ - if (message_type == TLS_HELLO_REQUEST) { - POSIX_GUARD_RESULT(s2n_client_hello_request_validate(conn)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - continue; - } - - /* Check for missing Certificate Requests to surface a more specific error */ - if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) { - POSIX_ENSURE(message_type == TLS_CERT_REQ, - S2N_ERR_MISSING_CERT_REQUEST); - } - - POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); - - /* Call the relevant handler */ - WITH_ERROR_BLINDING(conn, POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn))); - - /* Advance the state machine */ - POSIX_GUARD_RESULT(s2n_finish_read(conn)); - } - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - return S2N_SUCCESS; -} - -/* Reading is a little more complicated than writing as the TLS RFCs allow content - * types to be interleaved at the record layer. We may get an alert message - * during the handshake phase, or messages of types that we don't support (e.g. - * HEARTBEAT messages), or during renegotiations we may even get application - * data messages that need to be handled by the application. - */ -static int s2n_handshake_read_io(struct s2n_connection *conn) -{ - uint8_t record_type = 0; - int isSSLv2 = 0; - - /* Fill conn->in stuffer necessary for the handshake. - * If using TCP, read a record. If using QUIC, read a message. */ - if (s2n_connection_is_quic_enabled(conn)) { - record_type = TLS_HANDSHAKE; - uint8_t message_type = 0; - POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); - } else { - int r = s2n_read_full_record(conn, &record_type, &isSSLv2); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# If the client attempts a 0-RTT handshake but the server - *# rejects it, the server will generally not have the 0-RTT record - *# protection keys and must instead use trial decryption (either with - *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in - *# the case of a HelloRetryRequest) to find the first non-0-RTT message. - *# - *# If the server chooses to accept the "early_data" extension, then it - *# MUST comply with the same error-handling requirements specified for - *# all records when processing early data records. Specifically, if the - *# server fails to decrypt a 0-RTT record following an accepted - *# "early_data" extension, it MUST terminate the connection with a - *# "bad_record_mac" alert as per Section 5.2. - */ - if ((r < S2N_SUCCESS) && (s2n_errno == S2N_ERR_EARLY_DATA_TRIAL_DECRYPT)) { - POSIX_GUARD(s2n_stuffer_reread(&conn->in)); - POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, s2n_stuffer_data_available(&conn->in))); - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - return S2N_SUCCESS; - } - POSIX_GUARD(r); - } - - if (isSSLv2) { - S2N_ERROR_IF(record_type != SSLv2_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_handshake_handle_sslv2(conn)); - } - - /* Now we have a record, but it could be a partial fragment of a message, or it might - * contain several messages. - */ - - if (record_type == TLS_APPLICATION_DATA) { - POSIX_GUARD_RESULT(s2n_handshake_app_data_recv(conn)); - } else if (record_type == TLS_CHANGE_CIPHER_SPEC) { - /* TLS1.3 can receive unexpected CCS messages at any point in the handshake - * due to a peer operating in middlebox compatibility mode. - * However, when operating in QUIC mode, S2N should not accept ANY CCS messages, - * including these unexpected ones.*/ - if (!IS_TLS13_HANDSHAKE(conn) || s2n_connection_is_quic_enabled(conn)) { - POSIX_ENSURE(EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC, S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); - } - - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) != 1, S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); - POSIX_GUARD(CCS_STATE(conn).handler[conn->mode](conn)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - /* Advance the state machine if this was an expected message */ - if (EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && !CONNECTION_IS_WRITER(conn)) { - POSIX_GUARD(s2n_advance_message(conn)); - } - - return S2N_SUCCESS; - } else if (record_type != TLS_HANDSHAKE) { - if (record_type == TLS_ALERT) { - POSIX_GUARD(s2n_process_alert_fragment(conn)); - } - - /* Ignore record types that we don't support */ - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - return S2N_SUCCESS; - } - - /* Record is a handshake message */ - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_handshake_message_process(conn, record_type)); - - return S2N_SUCCESS; -} - -static int s2n_handle_retry_state(struct s2n_connection *conn) -{ - /* If we were blocked reading or writing a record, then the handler is waiting on - * external data. The handler will know how to continue, so we should call the - * handler right away. We aren't going to read more handshake data yet or proceed - * to the next handler because the current message has not finished processing. */ - s2n_errno = S2N_ERR_OK; - const int r = ACTIVE_STATE(conn).handler[conn->mode](conn); - - if (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno)) { - /* If the handler is still waiting for data, return control to the caller. */ - S2N_ERROR_PRESERVE_ERRNO(); - } - - /* Resume the handshake */ - conn->handshake.paused = false; - - if (CONNECTION_IS_WRITER(conn)) { - POSIX_GUARD(r); - - /* If we're the writer and handler just finished, update the record header if - * needed and let the s2n_handshake_write_io write the data to the socket */ - if (EXPECTED_RECORD_TYPE(conn) == TLS_HANDSHAKE) { - POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); - } - } else { - if (r < S2N_SUCCESS && conn->session_id_len) { - s2n_try_delete_session_cache(conn); - } - WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); - - /* The read handler processed the message successfully, we are done with this - * message. Advance the state machine. */ - POSIX_GUARD_RESULT(s2n_finish_read(conn)); - - /* We may need to handle remaining handshake messages in the record */ - POSIX_GUARD(s2n_handshake_message_process(conn, TLS_HANDSHAKE)); - } - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_set_blocked_error_from_errno(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(blocked); - - if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { - *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; - conn->handshake.paused = true; - } else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) { - *blocked = S2N_BLOCKED_ON_EARLY_DATA; - } - - return S2N_RESULT_OK; -} - -bool s2n_handshake_is_complete(struct s2n_connection *conn) -{ - /* A deserialized connection implies that the handshake is complete because - * connections cannot be serialized before completing the handshake. */ - return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); -} - -int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(blocked); - - while (!s2n_handshake_is_complete(conn) && ACTIVE_MESSAGE(conn) != conn->handshake.end_of_messages) { - errno = 0; - s2n_errno = S2N_ERR_OK; - - /* Flush any pending I/O or alert messages */ - POSIX_GUARD(s2n_flush(conn, blocked)); - - POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX), S2N_ERR_CLOSED); - - /* If the handshake was paused, retry the current message */ - if (conn->handshake.paused) { - *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; - const int retry_result = s2n_handle_retry_state(conn); - if (retry_result != S2N_SUCCESS) { - POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); - S2N_ERROR_PRESERVE_ERRNO(); - } - - continue; - } - - if (CONNECTION_IS_WRITER(conn)) { - *blocked = S2N_BLOCKED_ON_WRITE; - const int write_result = s2n_handshake_write_io(conn); - - if (write_result < S2N_SUCCESS) { - if (!S2N_ERROR_IS_BLOCKING(s2n_errno)) { - /* Non-retryable write error. The peer might have sent an alert. Try and read it. */ - const int write_errno = errno; - const int write_s2n_errno = s2n_errno; - struct s2n_debug_info write_s2n_debug_info = _s2n_debug_info; - - if (s2n_handshake_read_io(conn) < 0 && s2n_errno == S2N_ERR_ALERT) { - /* s2n_handshake_read_io has set s2n_errno */ - S2N_ERROR_PRESERVE_ERRNO(); - } else { - /* Let the write error take precedence if we didn't read an alert. */ - errno = write_errno; - s2n_errno = write_s2n_errno; - _s2n_debug_info = write_s2n_debug_info; - S2N_ERROR_PRESERVE_ERRNO(); - } - } - - POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); - - S2N_ERROR_PRESERVE_ERRNO(); - } - } else { - *blocked = S2N_BLOCKED_ON_READ; - const int read_result = s2n_handshake_read_io(conn); - - if (read_result < S2N_SUCCESS) { - /* One blocking condition is waiting on the session resumption cache. */ - /* So we don't want to delete anything if we are blocked. */ - if (!S2N_ERROR_IS_BLOCKING(s2n_errno) && conn->session_id_len) { - s2n_try_delete_session_cache(conn); - } - - POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); - - S2N_ERROR_PRESERVE_ERRNO(); - } - } - } - - if (ACTIVE_STATE(conn).writer == 'B') { - /* Clean up handshake secrets */ - POSIX_GUARD_RESULT(s2n_tls13_secrets_clean(conn)); - - /* Send any pending post-handshake messages */ - POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); - - /* If the handshake has just ended, free up memory */ - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); - } - - *blocked = S2N_NOT_BLOCKED; - - return S2N_SUCCESS; -} - -int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(!conn->negotiate_in_use, S2N_ERR_REENTRANCY); - conn->negotiate_in_use = true; - - /* We use the default monotonic clock so that we can avoid referencing any - * item on the config until after the client hello callback is invoked. */ - uint64_t negotiate_start = 0; - POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_start)); - if (conn->handshake_event.handshake_start_ns == 0) { - conn->handshake_event.handshake_start_ns = negotiate_start; - } - - int result = s2n_negotiate_impl(conn, blocked); - - /* finish up sending and receiving */ - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); - - uint64_t negotiate_end = 0; - POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_end)); - conn->handshake_event.handshake_time_ns += negotiate_end - negotiate_start; - - if (result == S2N_SUCCESS) { - conn->handshake_event.handshake_end_ns = negotiate_end; - POSIX_GUARD_RESULT(s2n_event_handshake_populate(conn, &conn->handshake_event)); - POSIX_GUARD_RESULT(s2n_event_handshake_send(conn, &conn->handshake_event)); - } - - conn->negotiate_in_use = false; - return result; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls13_key_schedule.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_events.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +/* clang-format off */ +struct s2n_handshake_action { + uint8_t record_type; + uint8_t message_type; + char writer; /* 'S' or 'C' for server or client, 'B' for both */ + int (*handler[2]) (struct s2n_connection * conn); +}; + +static int s2n_always_fail_send(struct s2n_connection *conn) +{ + /* This state should never be sending a handshake message. */ + POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); +} + +static int s2n_always_fail_recv(struct s2n_connection *conn) +{ + /* This state should never have an incoming handshake message. */ + POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); +} + +/* Client and Server handlers for each message type we support. + * See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7 for the list of handshake message types + */ +static struct s2n_handshake_action state_machine[] = { + /* message_type_t = {Record type Message type Writer S2N_SERVER S2N_CLIENT } */ + [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, + [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, + [SERVER_NEW_SESSION_TICKET] = {TLS_HANDSHAKE, TLS_SERVER_NEW_SESSION_TICKET,'S', {s2n_server_nst_send, s2n_server_nst_recv}}, + [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, + [SERVER_CERT_STATUS] = {TLS_HANDSHAKE, TLS_SERVER_CERT_STATUS, 'S', {s2n_server_status_send, s2n_server_status_recv}}, + [SERVER_KEY] = {TLS_HANDSHAKE, TLS_SERVER_KEY, 'S', {s2n_server_key_send, s2n_server_key_recv}}, + [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_cert_req_send, s2n_cert_req_recv}}, + [SERVER_HELLO_DONE] = {TLS_HANDSHAKE, TLS_SERVER_HELLO_DONE, 'S', {s2n_server_done_send, s2n_server_done_recv}}, + [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, + [CLIENT_KEY] = {TLS_HANDSHAKE, TLS_CLIENT_KEY, 'C', {s2n_client_key_recv, s2n_client_key_send}}, + [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_client_cert_verify_recv, s2n_client_cert_verify_send}}, + [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_client_ccs_recv, s2n_ccs_send}}, + [CLIENT_NPN] = {TLS_HANDSHAKE, TLS_NPN, 'C', {s2n_next_protocol_recv, s2n_next_protocol_send}}, + [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_client_finished_recv, s2n_client_finished_send}}, + [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_server_ccs_recv}}, + [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_server_finished_send, s2n_server_finished_recv}}, + [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, +}; + +/* + * Client and Server handlers for TLS1.3. + */ +static struct s2n_handshake_action tls13_state_machine[] = { + /* message_type_t = {Record type, Message type, Writer, {Server handler, client handler} } */ + [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, + [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, + [HELLO_RETRY_MSG] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_retry_send, s2n_server_hello_retry_recv}}, + [ENCRYPTED_EXTENSIONS] = {TLS_HANDSHAKE, TLS_ENCRYPTED_EXTENSIONS, 'S', {s2n_encrypted_extensions_send, s2n_encrypted_extensions_recv}}, + [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_tls13_cert_req_send, s2n_tls13_cert_req_recv}}, + [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, + [SERVER_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'S', {s2n_tls13_cert_verify_send, s2n_tls13_cert_verify_recv}}, + [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_tls13_server_finished_send, s2n_tls13_server_finished_recv}}, + + [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, + [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_tls13_cert_verify_recv, s2n_tls13_cert_verify_send}}, + [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_tls13_client_finished_recv, s2n_tls13_client_finished_send}}, + [END_OF_EARLY_DATA] = {TLS_HANDSHAKE, TLS_END_OF_EARLY_DATA, 'C', {s2n_end_of_early_data_recv, s2n_end_of_early_data_send}}, + + /* Not used by TLS1.3, except to maintain middlebox compatibility */ + [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_basic_ccs_recv, s2n_ccs_send}}, + [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_basic_ccs_recv}}, + + [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, +}; + +#define MESSAGE_NAME_ENTRY(msg) [msg] = #msg + +static const char *message_names[] = { + MESSAGE_NAME_ENTRY(CLIENT_HELLO), + MESSAGE_NAME_ENTRY(SERVER_HELLO), + MESSAGE_NAME_ENTRY(ENCRYPTED_EXTENSIONS), + MESSAGE_NAME_ENTRY(SERVER_NEW_SESSION_TICKET), + MESSAGE_NAME_ENTRY(SERVER_CERT), + MESSAGE_NAME_ENTRY(SERVER_CERT_STATUS), + MESSAGE_NAME_ENTRY(SERVER_CERT_VERIFY), + MESSAGE_NAME_ENTRY(SERVER_KEY), + MESSAGE_NAME_ENTRY(SERVER_CERT_REQ), + MESSAGE_NAME_ENTRY(SERVER_HELLO_DONE), + MESSAGE_NAME_ENTRY(CLIENT_CERT), + MESSAGE_NAME_ENTRY(CLIENT_KEY), + MESSAGE_NAME_ENTRY(CLIENT_CERT_VERIFY), + MESSAGE_NAME_ENTRY(CLIENT_CHANGE_CIPHER_SPEC), + MESSAGE_NAME_ENTRY(CLIENT_FINISHED), + MESSAGE_NAME_ENTRY(SERVER_CHANGE_CIPHER_SPEC), + MESSAGE_NAME_ENTRY(SERVER_FINISHED), + MESSAGE_NAME_ENTRY(HELLO_RETRY_MSG), + MESSAGE_NAME_ENTRY(END_OF_EARLY_DATA), + MESSAGE_NAME_ENTRY(APPLICATION_DATA), + MESSAGE_NAME_ENTRY(CLIENT_NPN), +}; + +/* We support different ordering of TLS Handshake messages, depending on what is being negotiated. There's also a dummy "INITIAL" handshake + * that everything starts out as until we know better. + */ + +static message_type_t handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { + [INITIAL] = { + CLIENT_HELLO, + SERVER_HELLO + }, + + [NEGOTIATED] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA}, + + [NEGOTIATED | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + APPLICATION_DATA}, + + [NEGOTIATED | FULL_HANDSHAKE ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_NPN ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH| WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, +}; + +/* + * This selection of handshakes resembles the standard set, but with changes made to support tls1.3. + * + * The CHANGE_CIPHER_SPEC messages are included only for middlebox compatibility. + * See https://tools.ietf.org/html/rfc8446#appendix-D.4 + */ +static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { + [INITIAL] = { + CLIENT_HELLO, + SERVER_HELLO + }, + + [INITIAL | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO + }, + + [INITIAL | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG + }, + + [INITIAL | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG + }, + + [NEGOTIATED] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | WITH_EARLY_DATA] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + END_OF_EARLY_DATA, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT | WITH_EARLY_DATA] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, END_OF_EARLY_DATA, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS | WITH_EARLY_DATA] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + END_OF_EARLY_DATA, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, +}; +/* clang-format on */ + +#define MAX_HANDSHAKE_TYPE_LEN 142 +/* The handshake type labels used for TLS 1.0 - TLS 1.2, e.g. `NEGOTIATED|WITH_SESSION_TICKET` */ +static char handshake_type_str_tls12ish[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; + +/* The handshake type labels used for TLS 1.3, e.g. `NEGOTIATED|HELLO_RETRY_REQUEST` */ +static char handshake_type_str_tls13[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; + +static const char *tls12_handshake_type_names[] = { + "NEGOTIATED|", + "FULL_HANDSHAKE|", + "CLIENT_AUTH|", + "NO_CLIENT_CERT|", + "TLS12_PERFECT_FORWARD_SECRECY|", + "OCSP_STATUS|", + "WITH_SESSION_TICKET|", + "WITH_NPN|", +}; + +static const char *tls13_handshake_type_names[] = { + "NEGOTIATED|", + "FULL_HANDSHAKE|", + "CLIENT_AUTH|", + "NO_CLIENT_CERT|", + "HELLO_RETRY_REQUEST|", + "MIDDLEBOX_COMPAT|", + "WITH_EARLY_DATA|", + "EARLY_CLIENT_CCS|", +}; + +#define IS_TLS13_HANDSHAKE(conn) ((conn)->handshake.state_machine == S2N_STATE_MACHINE_TLS13) + +#define ACTIVE_STATE_MACHINE(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_state_machine : state_machine) +#define ACTIVE_HANDSHAKES(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_handshakes : handshakes) + +#define ACTIVE_MESSAGE(conn) ACTIVE_HANDSHAKES(conn)[(conn)->handshake.handshake_type][(conn)->handshake.message_number] + +#define ACTIVE_STATE(conn) ACTIVE_STATE_MACHINE(conn)[ACTIVE_MESSAGE((conn))] + +#define CCS_STATE(conn) (((conn)->mode == S2N_CLIENT) ? \ + ACTIVE_STATE_MACHINE(conn)[SERVER_CHANGE_CIPHER_SPEC] : \ + ACTIVE_STATE_MACHINE(conn)[CLIENT_CHANGE_CIPHER_SPEC]) + +#define EXPECTED_RECORD_TYPE(conn) ACTIVE_STATE(conn).record_type +#define EXPECTED_MESSAGE_TYPE(conn) ACTIVE_STATE(conn).message_type + +#define CONNECTION_WRITER(conn) (conn->mode == S2N_CLIENT ? 'C' : 'S') +#define CONNECTION_IS_WRITER(conn) (ACTIVE_STATE(conn).writer == CONNECTION_WRITER(conn)) + +/* Only used in our test cases. */ +message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn) +{ + return ACTIVE_MESSAGE(conn); +} + +static int s2n_advance_message(struct s2n_connection *conn) +{ + /* Get the mode: 'C'lient or 'S'erver */ + char previous_writer = ACTIVE_STATE(conn).writer; + char this_mode = CONNECTION_WRITER(conn); + + /* Actually advance the message number */ + conn->handshake.message_number++; + + /* When reading and using TLS1.3, skip optional change_cipher_spec states. */ + if (ACTIVE_STATE(conn).writer != this_mode && EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && IS_TLS13_HANDSHAKE(conn)) { + conn->handshake.message_number++; + } + + /* Set TCP_QUICKACK to avoid artificial delay during the handshake */ + POSIX_GUARD(s2n_socket_quickack(conn)); + + /* If optimized io hasn't been enabled or if the caller started out with a corked socket, + * we don't mess with it + */ + if (!conn->corked_io || s2n_socket_was_corked(conn)) { + return S2N_SUCCESS; + } + + /* Are we changing I/O directions */ + if (ACTIVE_STATE(conn).writer == previous_writer || ACTIVE_STATE(conn).writer == 'A') { + return S2N_SUCCESS; + } + + /* We're the new writer */ + if (ACTIVE_STATE(conn).writer == this_mode) { + if (s2n_connection_is_managed_corked(conn)) { + /* Set TCP_CORK/NOPUSH */ + POSIX_GUARD(s2n_socket_write_cork(conn)); + } + + return S2N_SUCCESS; + } + + /* We're the new reader, or we reached the "B" writer stage indicating that + we're at the application data stage - uncork the data */ + if (s2n_connection_is_managed_corked(conn)) { + POSIX_GUARD(s2n_socket_write_uncork(conn)); + } + + return S2N_SUCCESS; +} + +int s2n_generate_new_client_session_id(struct s2n_connection *conn) +{ + if (conn->mode == S2N_SERVER) { + struct s2n_blob session_id = { 0 }; + POSIX_GUARD(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* Generate a new session id */ + POSIX_GUARD_RESULT(s2n_get_public_random_data(&session_id)); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + } + + return S2N_SUCCESS; +} + +/* Lets the server flag whether a HelloRetryRequest is needed while processing extensions */ +int s2n_set_hello_retry_required(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_INVALID_HELLO_RETRY); + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls13_flag(conn, HELLO_RETRY_REQUEST)); + + /* HelloRetryRequests also indicate rejection of early data. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# A server which receives an "early_data" extension MUST behave in one + *# of three ways: + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# - Request that the client send another ClientHello by responding + *# with a HelloRetryRequest. + **/ + if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + } + + return S2N_SUCCESS; +} + +bool s2n_is_hello_retry_message(struct s2n_connection *conn) +{ + return (conn != NULL && s2n_result_is_ok(s2n_handshake_validate(&(conn->handshake))) && ACTIVE_MESSAGE(conn) == HELLO_RETRY_MSG); +} + +bool s2n_is_hello_retry_handshake(struct s2n_connection *conn) +{ + return IS_HELLO_RETRY_HANDSHAKE(conn); +} + +static S2N_RESULT s2n_conn_set_tls13_handshake_type(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* Most handshake type flags should be reset before we calculate the handshake type, + * in order to handle changes during retries. + * However, flags that have already affected the message order must be kept to avoid + * rewriting the past. + */ + conn->handshake.handshake_type &= (HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS); + + /* A handshake type has been negotiated */ + RESULT_GUARD(s2n_handshake_type_set_flag(conn, NEGOTIATED)); + + if (conn->psk_params.chosen_psk == NULL) { + RESULT_GUARD(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + } + + if (conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { + conn->handshake.handshake_type |= WITH_EARLY_DATA; + } + + s2n_cert_auth_type client_cert_auth_type; + RESULT_GUARD_POSIX(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED + && IS_FULL_HANDSHAKE(conn)) { + /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ + RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE + && IS_FULL_HANDSHAKE(conn)) { + /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ + RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } + + if (s2n_is_middlebox_compat_enabled(conn)) { + RESULT_GUARD(s2n_handshake_type_set_tls13_flag(conn, MIDDLEBOX_COMPAT)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_validate_ems_status(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + s2n_extension_type_id ems_ext_id = 0; + RESULT_GUARD_POSIX(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + bool ems_extension_recv = S2N_CBIT_TEST(conn->extension_requests_received, ems_ext_id); + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *# If the original session used the "extended_master_secret" + *# extension but the new ClientHello does not contain it, the server + *# MUST abort the abbreviated handshake. + **/ + if (conn->ems_negotiated) { + RESULT_ENSURE(ems_extension_recv, S2N_ERR_MISSING_EXTENSION); + } + + /* Since we're discarding the resumption ticket, ignore EMS value from the ticket */ + conn->ems_negotiated = ems_extension_recv; + + return S2N_RESULT_OK; +} + +int s2n_conn_set_handshake_type(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + POSIX_GUARD_RESULT(s2n_conn_choose_state_machine(conn, conn->actual_protocol_version)); + + if (IS_TLS13_HANDSHAKE(conn)) { + POSIX_GUARD_RESULT(s2n_conn_set_tls13_handshake_type(conn)); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_handshake_type_reset(conn)); + + /* A handshake type has been negotiated */ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NEGOTIATED)); + + s2n_cert_auth_type client_cert_auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED) { + /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE) { + /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } + + if (conn->npn_negotiated) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_NPN)); + } + + if (conn->config->use_tickets) { + if (conn->session_ticket_status == S2N_DECRYPT_TICKET) { + /* We reuse the session if a valid TLS12 ticket is provided. + * Otherwise, we will perform a full handshake and then generate + * a new session ticket. */ + if (s2n_result_is_ok(s2n_resume_decrypt_session(conn, &conn->client_ticket_to_decrypt))) { + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); + + /* Set up the handshake to send a session ticket since a valid ticket was not provided */ + if (s2n_result_is_ok(s2n_config_is_encrypt_key_available(conn->config))) { + conn->session_ticket_status = S2N_NEW_TICKET; + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); + } + + /* If a session ticket is presented by the client, then skip lookup in Session ID server cache */ + goto skip_cache_lookup; + } + + if (conn->session_ticket_status == S2N_NEW_TICKET) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); + } + } + + /* If a TLS session is resumed, the Server should respond in its ServerHello with the same SessionId the + * Client sent in the ClientHello. */ + if (conn->actual_protocol_version <= S2N_TLS12 && conn->mode == S2N_SERVER && s2n_allowed_to_cache_connection(conn)) { + int r = s2n_resume_from_cache(conn); + if (r == S2N_SUCCESS || (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno))) { + return r; + } + POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); + } + +skip_cache_lookup: + if (conn->mode == S2N_CLIENT && conn->client_session_resumed == 1) { + return S2N_SUCCESS; + } + + /* If we're doing full handshake, generate a new session id. */ + POSIX_GUARD(s2n_generate_new_client_session_id(conn)); + + /* If we get this far, it's a full handshake */ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + + bool is_ephemeral = false; + POSIX_GUARD_RESULT(s2n_kex_is_ephemeral(conn->secure->cipher_suite->key_exchange_alg, &is_ephemeral)); + if (is_ephemeral) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, TLS12_PERFECT_FORWARD_SECRECY)); + } + + if (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, OCSP_STATUS)); + } + + return S2N_SUCCESS; +} + +int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn) +{ + s2n_cert_auth_type client_cert_auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_MISSING_CLIENT_CERT); + + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NO_CLIENT_CERT)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_conn_choose_state_machine(struct s2n_connection *conn, uint8_t protocol_version) +{ + RESULT_ENSURE_REF(conn); + + /* This should never be called before we know what version we're on */ + RESULT_ENSURE_NE(protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + if (protocol_version == S2N_TLS13) { + /* State machine should not change once set */ + RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS12); + conn->handshake.state_machine = S2N_STATE_MACHINE_TLS13; + } else { + /* State machine should not change once set */ + RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS13); + conn->handshake.state_machine = S2N_STATE_MACHINE_TLS12; + } + + return S2N_RESULT_OK; +} + +const char *s2n_connection_get_last_message_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_GUARD_RESULT(s2n_handshake_validate(&(conn->handshake))); + return message_names[ACTIVE_MESSAGE(conn)]; +} + +const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_PRECONDITION(s2n_handshake_validate(&(conn->handshake))); + + uint32_t handshake_type = conn->handshake.handshake_type; + + if (handshake_type == INITIAL) { + return "INITIAL"; + } + + const char **handshake_labels = tls13_handshake_type_names; + size_t handshake_labels_len = s2n_array_len(tls13_handshake_type_names); + char(*names)[MAX_HANDSHAKE_TYPE_LEN] = handshake_type_str_tls13; + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + handshake_labels = tls12_handshake_type_names; + handshake_labels_len = s2n_array_len(tls12_handshake_type_names); + names = handshake_type_str_tls12ish; + } + + /* Not all handshake strings will be created already. If the handshake string + * is not null, we can just return the handshake. Otherwise we have to compute + * it down below. */ + if (names[handshake_type][0] != '\0') { + return names[handshake_type]; + } + + /* Compute the cached string by concatenating each applicable handshake_type. + * + * Unit tests enforce that the elements of the cache are always + * long enough to contain the longest possible valid handshake_type, but + * for safety we still handle the case where we need to truncate. + */ + char *p = names[handshake_type]; + size_t remaining = MAX_HANDSHAKE_TYPE_LEN; + for (size_t i = 0; i < handshake_labels_len; i++) { + bool label_applies = handshake_type & (1 << i); + if (label_applies) { + size_t bytes_to_copy = S2N_MIN(remaining, strlen(handshake_labels[i])); + PTR_CHECKED_MEMCPY(p, handshake_labels[i], bytes_to_copy); + p[bytes_to_copy] = '\0'; + p += bytes_to_copy; + remaining -= bytes_to_copy; + } + } + + if (p != names[handshake_type] && '|' == *(p - 1)) { + *(p - 1) = '\0'; + } + + return names[handshake_type]; +} + +S2N_RESULT s2n_handshake_message_send(struct s2n_connection *conn, uint8_t content_type, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + struct s2n_stuffer *in = &conn->handshake.io; + + uint32_t size = s2n_stuffer_data_available(in); + if (size == 0) { + return S2N_RESULT_OK; + } + + if (s2n_connection_is_quic_enabled(conn)) { + RESULT_GUARD(s2n_quic_write_handshake_message(conn)); + RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); + return S2N_RESULT_OK; + } + + struct iovec iov = { 0 }; + iov.iov_len = size; + iov.iov_base = s2n_stuffer_raw_read(in, size); + RESULT_ENSURE_REF(iov.iov_base); + RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(in, size)); + + uint32_t total_bytes_written = 0; + while (total_bytes_written < size) { + int bytes_written = s2n_record_writev(conn, content_type, &iov, 1, + total_bytes_written, size - total_bytes_written); + RESULT_GUARD_POSIX(bytes_written); + total_bytes_written += bytes_written; + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(in, bytes_written)); + RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); + } + return S2N_RESULT_OK; +} + +/* Writing is relatively straight forward, simply write each message out as a record, + * we may fragment a message across multiple records, but we never coalesce multiple + * messages into single records. + * Precondition: secure outbound I/O has already been flushed + */ +static int s2n_handshake_write_io(struct s2n_connection *conn) +{ + uint8_t record_type = EXPECTED_RECORD_TYPE(conn); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Populate handshake.io with header/payload for the current state, once. + * Check wiped instead of s2n_stuffer_data_available to differentiate between the initial call + * to s2n_handshake_write_io and a repeated call after an EWOULDBLOCK. + */ + if (s2n_stuffer_is_wiped(&conn->handshake.io)) { + if (record_type == TLS_HANDSHAKE) { + POSIX_GUARD(s2n_handshake_write_header(&conn->handshake.io, ACTIVE_STATE(conn).message_type)); + } + POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn)); + if (record_type == TLS_HANDSHAKE) { + POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); + } + } + + POSIX_GUARD_RESULT(s2n_handshake_message_send(conn, record_type, &blocked)); + if (record_type == TLS_HANDSHAKE) { + POSIX_GUARD_RESULT(s2n_handshake_transcript_update(conn)); + } + + /* We're done sending the last record, reset everything */ + POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + + /* Update the secrets, if necessary */ + POSIX_GUARD_RESULT(s2n_tls13_secrets_update(conn)); + POSIX_GUARD_RESULT(s2n_tls13_key_schedule_update(conn)); + + /* Advance the state machine */ + POSIX_GUARD(s2n_advance_message(conn)); + + return S2N_SUCCESS; +} + +/* + * Returns: + * 1 - more data is needed to complete the handshake message. + * 0 - we read the whole handshake message. + * -1 - error processing the handshake message. + */ +static int s2n_read_full_handshake_message(struct s2n_connection *conn, uint8_t *message_type) +{ + uint32_t current_handshake_data = s2n_stuffer_data_available(&conn->handshake.io); + if (current_handshake_data < TLS_HANDSHAKE_HEADER_LENGTH) { + /* The message may be so badly fragmented that we don't even read the full header, take + * what we can and then continue to the next record read iteration. + */ + if (s2n_stuffer_data_available(&conn->in) < (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data)) { + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); + return 1; + } + + /* Get the remainder of the header */ + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data))); + } + + uint32_t handshake_message_length = 0; + POSIX_GUARD_RESULT(s2n_handshake_parse_header(&conn->handshake.io, message_type, &handshake_message_length)); + + S2N_ERROR_IF(handshake_message_length > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); + + uint32_t bytes_to_take = handshake_message_length - s2n_stuffer_data_available(&conn->handshake.io); + bytes_to_take = S2N_MIN(bytes_to_take, s2n_stuffer_data_available(&conn->in)); + + /* If the record is handshake data, add it to the handshake buffer */ + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, bytes_to_take)); + + /* If we have the whole handshake message, then success */ + if (s2n_stuffer_data_available(&conn->handshake.io) == handshake_message_length) { + return 0; + } + + /* We don't have the whole message, so we'll need to go again */ + POSIX_GUARD(s2n_stuffer_reread(&conn->handshake.io)); + + return 1; +} + +static int s2n_handshake_handle_sslv2(struct s2n_connection *conn) +{ + S2N_ERROR_IF(ACTIVE_MESSAGE(conn) != CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); + + /* Add the message to our handshake hashes */ + struct s2n_blob hashed = { 0 }; + POSIX_GUARD(s2n_blob_init(&hashed, conn->header_in.blob.data + 2, 3)); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); + + hashed.data = conn->in.blob.data; + hashed.size = s2n_stuffer_data_available(&conn->in); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); + + /* Handle an SSLv2 client hello */ + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); + conn->client_hello.sslv2 = true; + /* Execute the state machine handler */ + int r = ACTIVE_STATE(conn).handler[conn->mode](conn); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); + + /* Advance the state machine */ + POSIX_GUARD(s2n_advance_message(conn)); + + return S2N_SUCCESS; +} + +static int s2n_try_delete_session_cache(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (s2n_allowed_to_cache_connection(conn) > 0) { + conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_finish_read(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD(s2n_handshake_transcript_update(conn)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->handshake.io)); + RESULT_GUARD(s2n_tls13_secrets_update(conn)); + RESULT_GUARD(s2n_tls13_key_schedule_update(conn)); + RESULT_GUARD_POSIX(s2n_advance_message(conn)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_handshake_app_data_recv(struct s2n_connection *conn) +{ + if (conn->early_data_expected) { + RESULT_GUARD(s2n_early_data_validate_recv(conn)); + RESULT_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); + } + + if (conn->handshake.renegotiation) { + RESULT_GUARD(s2n_renegotiate_validate(conn)); + /* During renegotiation, Application Data may only be received until + * the server acknowledges the new handshake with a ServerHello. + */ + RESULT_ENSURE(ACTIVE_MESSAGE(conn) == SERVER_HELLO, S2N_ERR_BAD_MESSAGE); + RESULT_BAIL(S2N_ERR_APP_DATA_BLOCKED); + } + + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); +} + +static int s2n_handshake_message_process(struct s2n_connection *conn, uint8_t record_type) +{ + POSIX_ENSURE_REF(conn); + + uint8_t message_type = 0; + while (s2n_stuffer_data_available(&conn->in)) { + /* We're done with negotiating but we have trailing data in this record. Bail on the handshake. */ + S2N_ERROR_IF(EXPECTED_RECORD_TYPE(conn) == TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE); + int r = 0; + POSIX_GUARD((r = s2n_read_full_handshake_message(conn, &message_type))); + + /* Do we need more data? This happens for message fragmentation */ + if (r == 1) { + /* Break out of this inner loop, but since we're not changing the state, the + * outer loop in s2n_handshake_io() will read another record. + */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + return S2N_SUCCESS; + } + + if (conn->mode == S2N_CLIENT) { + s2n_cert_auth_type client_cert_auth_type = { 0 }; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + /* If client auth is optional, we initially assume it will not be requested. + * If we received a request, switch to a client auth handshake. + */ + if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) { + POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST); + POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE); + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } + + /* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus" + * message even if it has sent a "status_request" extension in the ServerHello message. + */ + if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS + && message_type != TLS_SERVER_CERT_STATUS) { + POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS)); + } + } + + /* + *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4 + *# The one message that is not bound by these ordering rules + *# is the HelloRequest message, which can be sent at any time, but which + *# SHOULD be ignored by the client if it arrives in the middle of a handshake. + */ + if (message_type == TLS_HELLO_REQUEST) { + POSIX_GUARD_RESULT(s2n_client_hello_request_validate(conn)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + continue; + } + + /* Check for missing Certificate Requests to surface a more specific error */ + if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) { + POSIX_ENSURE(message_type == TLS_CERT_REQ, + S2N_ERR_MISSING_CERT_REQUEST); + } + + POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); + + /* Call the relevant handler */ + WITH_ERROR_BLINDING(conn, POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn))); + + /* Advance the state machine */ + POSIX_GUARD_RESULT(s2n_finish_read(conn)); + } + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + return S2N_SUCCESS; +} + +/* Reading is a little more complicated than writing as the TLS RFCs allow content + * types to be interleaved at the record layer. We may get an alert message + * during the handshake phase, or messages of types that we don't support (e.g. + * HEARTBEAT messages), or during renegotiations we may even get application + * data messages that need to be handled by the application. + */ +static int s2n_handshake_read_io(struct s2n_connection *conn) +{ + uint8_t record_type = 0; + int isSSLv2 = 0; + + /* Fill conn->in stuffer necessary for the handshake. + * If using TCP, read a record. If using QUIC, read a message. */ + if (s2n_connection_is_quic_enabled(conn)) { + record_type = TLS_HANDSHAKE; + uint8_t message_type = 0; + POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); + } else { + int r = s2n_read_full_record(conn, &record_type, &isSSLv2); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# If the client attempts a 0-RTT handshake but the server + *# rejects it, the server will generally not have the 0-RTT record + *# protection keys and must instead use trial decryption (either with + *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in + *# the case of a HelloRetryRequest) to find the first non-0-RTT message. + *# + *# If the server chooses to accept the "early_data" extension, then it + *# MUST comply with the same error-handling requirements specified for + *# all records when processing early data records. Specifically, if the + *# server fails to decrypt a 0-RTT record following an accepted + *# "early_data" extension, it MUST terminate the connection with a + *# "bad_record_mac" alert as per Section 5.2. + */ + if ((r < S2N_SUCCESS) && (s2n_errno == S2N_ERR_EARLY_DATA_TRIAL_DECRYPT)) { + POSIX_GUARD(s2n_stuffer_reread(&conn->in)); + POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, s2n_stuffer_data_available(&conn->in))); + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + return S2N_SUCCESS; + } + POSIX_GUARD(r); + } + + if (isSSLv2) { + S2N_ERROR_IF(record_type != SSLv2_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_handshake_handle_sslv2(conn)); + } + + /* Now we have a record, but it could be a partial fragment of a message, or it might + * contain several messages. + */ + + if (record_type == TLS_APPLICATION_DATA) { + POSIX_GUARD_RESULT(s2n_handshake_app_data_recv(conn)); + } else if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* TLS1.3 can receive unexpected CCS messages at any point in the handshake + * due to a peer operating in middlebox compatibility mode. + * However, when operating in QUIC mode, S2N should not accept ANY CCS messages, + * including these unexpected ones.*/ + if (!IS_TLS13_HANDSHAKE(conn) || s2n_connection_is_quic_enabled(conn)) { + POSIX_ENSURE(EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); + } + + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) != 1, S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); + POSIX_GUARD(CCS_STATE(conn).handler[conn->mode](conn)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + /* Advance the state machine if this was an expected message */ + if (EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && !CONNECTION_IS_WRITER(conn)) { + POSIX_GUARD(s2n_advance_message(conn)); + } + + return S2N_SUCCESS; + } else if (record_type != TLS_HANDSHAKE) { + if (record_type == TLS_ALERT) { + POSIX_GUARD(s2n_process_alert_fragment(conn)); + } + + /* Ignore record types that we don't support */ + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + return S2N_SUCCESS; + } + + /* Record is a handshake message */ + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_handshake_message_process(conn, record_type)); + + return S2N_SUCCESS; +} + +static int s2n_handle_retry_state(struct s2n_connection *conn) +{ + /* If we were blocked reading or writing a record, then the handler is waiting on + * external data. The handler will know how to continue, so we should call the + * handler right away. We aren't going to read more handshake data yet or proceed + * to the next handler because the current message has not finished processing. */ + s2n_errno = S2N_ERR_OK; + const int r = ACTIVE_STATE(conn).handler[conn->mode](conn); + + if (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno)) { + /* If the handler is still waiting for data, return control to the caller. */ + S2N_ERROR_PRESERVE_ERRNO(); + } + + /* Resume the handshake */ + conn->handshake.paused = false; + + if (CONNECTION_IS_WRITER(conn)) { + POSIX_GUARD(r); + + /* If we're the writer and handler just finished, update the record header if + * needed and let the s2n_handshake_write_io write the data to the socket */ + if (EXPECTED_RECORD_TYPE(conn) == TLS_HANDSHAKE) { + POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); + } + } else { + if (r < S2N_SUCCESS && conn->session_id_len) { + s2n_try_delete_session_cache(conn); + } + WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); + + /* The read handler processed the message successfully, we are done with this + * message. Advance the state machine. */ + POSIX_GUARD_RESULT(s2n_finish_read(conn)); + + /* We may need to handle remaining handshake messages in the record */ + POSIX_GUARD(s2n_handshake_message_process(conn, TLS_HANDSHAKE)); + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_set_blocked_error_from_errno(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(blocked); + + if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { + *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; + conn->handshake.paused = true; + } else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) { + *blocked = S2N_BLOCKED_ON_EARLY_DATA; + } + + return S2N_RESULT_OK; +} + +bool s2n_handshake_is_complete(struct s2n_connection *conn) +{ + /* A deserialized connection implies that the handshake is complete because + * connections cannot be serialized before completing the handshake. */ + return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); +} + +int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(blocked); + + while (!s2n_handshake_is_complete(conn) && ACTIVE_MESSAGE(conn) != conn->handshake.end_of_messages) { + errno = 0; + s2n_errno = S2N_ERR_OK; + + /* Flush any pending I/O or alert messages */ + POSIX_GUARD(s2n_flush(conn, blocked)); + + POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX), S2N_ERR_CLOSED); + + /* If the handshake was paused, retry the current message */ + if (conn->handshake.paused) { + *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; + const int retry_result = s2n_handle_retry_state(conn); + if (retry_result != S2N_SUCCESS) { + POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); + S2N_ERROR_PRESERVE_ERRNO(); + } + + continue; + } + + if (CONNECTION_IS_WRITER(conn)) { + *blocked = S2N_BLOCKED_ON_WRITE; + const int write_result = s2n_handshake_write_io(conn); + + if (write_result < S2N_SUCCESS) { + if (!S2N_ERROR_IS_BLOCKING(s2n_errno)) { + /* Non-retryable write error. The peer might have sent an alert. Try and read it. */ + const int write_errno = errno; + const int write_s2n_errno = s2n_errno; + struct s2n_debug_info write_s2n_debug_info = _s2n_debug_info; + + if (s2n_handshake_read_io(conn) < 0 && s2n_errno == S2N_ERR_ALERT) { + /* s2n_handshake_read_io has set s2n_errno */ + S2N_ERROR_PRESERVE_ERRNO(); + } else { + /* Let the write error take precedence if we didn't read an alert. */ + errno = write_errno; + s2n_errno = write_s2n_errno; + _s2n_debug_info = write_s2n_debug_info; + S2N_ERROR_PRESERVE_ERRNO(); + } + } + + POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); + + S2N_ERROR_PRESERVE_ERRNO(); + } + } else { + *blocked = S2N_BLOCKED_ON_READ; + const int read_result = s2n_handshake_read_io(conn); + + if (read_result < S2N_SUCCESS) { + /* One blocking condition is waiting on the session resumption cache. */ + /* So we don't want to delete anything if we are blocked. */ + if (!S2N_ERROR_IS_BLOCKING(s2n_errno) && conn->session_id_len) { + s2n_try_delete_session_cache(conn); + } + + POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); + + S2N_ERROR_PRESERVE_ERRNO(); + } + } + } + + if (ACTIVE_STATE(conn).writer == 'B') { + /* Clean up handshake secrets */ + POSIX_GUARD_RESULT(s2n_tls13_secrets_clean(conn)); + + /* Send any pending post-handshake messages */ + POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); + + /* If the handshake has just ended, free up memory */ + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); + } + + *blocked = S2N_NOT_BLOCKED; + + return S2N_SUCCESS; +} + +int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(!conn->negotiate_in_use, S2N_ERR_REENTRANCY); + conn->negotiate_in_use = true; + + /* We use the default monotonic clock so that we can avoid referencing any + * item on the config until after the client hello callback is invoked. */ + uint64_t negotiate_start = 0; + POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_start)); + if (conn->handshake_event.handshake_start_ns == 0) { + conn->handshake_event.handshake_start_ns = negotiate_start; + } + + int result = s2n_negotiate_impl(conn, blocked); + + /* finish up sending and receiving */ + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); + + uint64_t negotiate_end = 0; + POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_end)); + conn->handshake_event.handshake_time_ns += negotiate_end - negotiate_start; + + if (result == S2N_SUCCESS) { + conn->handshake_event.handshake_end_ns = negotiate_end; + POSIX_GUARD_RESULT(s2n_event_handshake_populate(conn, &conn->handshake_event)); + POSIX_GUARD_RESULT(s2n_event_handshake_send(conn, &conn->handshake_event)); + } + + conn->negotiate_in_use = false; + return result; +} diff --git a/tls/s2n_handshake_transcript.c b/tls/s2n_handshake_transcript.c index 3a23ff85cef..2ebec134fdc 100644 --- a/tls/s2n_handshake_transcript.c +++ b/tls/s2n_handshake_transcript.c @@ -1,129 +1,130 @@ -/* - * 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 "stuffer/s2n_stuffer.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_blob.h" - -/* Length of the synthetic message header */ -#define MESSAGE_HASH_HEADER_LENGTH 4 - -S2N_RESULT s2n_handshake_transcript_update(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_stuffer message = conn->handshake.io; - RESULT_GUARD_POSIX(s2n_stuffer_reread(&message)); - - struct s2n_blob data = { 0 }; - uint32_t len = s2n_stuffer_data_available(&message); - uint8_t *bytes = s2n_stuffer_raw_read(&message, len); - RESULT_ENSURE_REF(bytes); - RESULT_GUARD_POSIX(s2n_blob_init(&data, bytes, len)); - - RESULT_GUARD_POSIX(s2n_conn_update_handshake_hashes(conn, &data)); - return S2N_RESULT_OK; -} - -int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(data); - struct s2n_handshake_hashes *hashes = conn->handshake.hashes; - POSIX_ENSURE_REF(hashes); - - /* MD5 and SHA1 are not permitted in FIPS mode, but an exception is made in - * order to continue to support TLS1.0 and TLS1.1. NIST SP 800-52r1 approves - * their continued use for the signature check in the CertificateVerify message - * and the PRF when negotiating TLS1.0 or TLS1.1 (see footnotes 15 and 20, - * and section 3.3.2) - */ - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5)) { - POSIX_GUARD(s2n_hash_update(&hashes->md5, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha1, data->data, data->size)); - } - - const uint8_t md5_sha1_required = - (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5) - && s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)); - - if (md5_sha1_required) { - POSIX_GUARD(s2n_hash_update(&hashes->md5_sha1, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA224)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha224, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA256)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha256, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA384)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha384, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA512)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha512, data->data, data->size)); - } - - return S2N_SUCCESS; -} - -/* When a HelloRetryRequest message is used, the hash transcript needs to be recreated. - * This is done with a synthetic message header, and the hash of ClientHello1. - * - * https://tools.ietf.org/html/rfc8446#section-4.4.1 - */ -int s2n_server_hello_retry_recreate_transcript(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - struct s2n_handshake_hashes *hashes = conn->handshake.hashes; - POSIX_ENSURE_REF(hashes); - - s2n_tls13_connection_keys(keys, conn); - uint8_t hash_digest_length = keys.size; - - /* Create the MessageHash (our synthetic message) */ - uint8_t msghdr[MESSAGE_HASH_HEADER_LENGTH] = { 0 }; - msghdr[0] = TLS_MESSAGE_HASH; - msghdr[MESSAGE_HASH_HEADER_LENGTH - 1] = hash_digest_length; - - /* Grab the current transcript hash to use as the ClientHello1 value. */ - struct s2n_hash_state *client_hello1_hash = &hashes->hash_workspace; - uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, client_hello1_hash)); - POSIX_GUARD(s2n_hash_digest(client_hello1_hash, client_hello1_digest_out, hash_digest_length)); - - /* Step 1: Reset the hash state */ - POSIX_GUARD_RESULT(s2n_handshake_reset_hash_state(conn, keys.hash_algorithm)); - - /* Step 2: Update the transcript with the synthetic message */ - struct s2n_blob msg_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&msg_blob, msghdr, MESSAGE_HASH_HEADER_LENGTH)); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); - - /* Step 3: Update the transcript with the ClientHello1 hash */ - POSIX_GUARD(s2n_blob_init(&msg_blob, client_hello1_digest_out, hash_digest_length)); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "stuffer/s2n_stuffer.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_blob.h" + +/* Length of the synthetic message header */ +#define MESSAGE_HASH_HEADER_LENGTH 4 + +S2N_RESULT s2n_handshake_transcript_update(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_stuffer message = conn->handshake.io; + RESULT_GUARD_POSIX(s2n_stuffer_reread(&message)); + + struct s2n_blob data = { 0 }; + uint32_t len = s2n_stuffer_data_available(&message); + uint8_t *bytes = s2n_stuffer_raw_read(&message, len); + RESULT_ENSURE_REF(bytes); + RESULT_GUARD_POSIX(s2n_blob_init(&data, bytes, len)); + + RESULT_GUARD_POSIX(s2n_conn_update_handshake_hashes(conn, &data)); + return S2N_RESULT_OK; +} + +int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(data); + struct s2n_handshake_hashes *hashes = conn->handshake.hashes; + POSIX_ENSURE_REF(hashes); + + /* MD5 and SHA1 are not permitted in FIPS mode, but an exception is made in + * order to continue to support TLS1.0 and TLS1.1. NIST SP 800-52r1 approves + * their continued use for the signature check in the CertificateVerify message + * and the PRF when negotiating TLS1.0 or TLS1.1 (see footnotes 15 and 20, + * and section 3.3.2) + */ + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5)) { + POSIX_GUARD(s2n_hash_update(&hashes->md5, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha1, data->data, data->size)); + } + + const uint8_t md5_sha1_required = + (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5) + && s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)); + + if (md5_sha1_required) { + POSIX_GUARD(s2n_hash_update(&hashes->md5_sha1, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA224)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha224, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA256)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha256, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA384)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha384, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA512)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha512, data->data, data->size)); + } + + return S2N_SUCCESS; +} + +/* When a HelloRetryRequest message is used, the hash transcript needs to be recreated. + * This is done with a synthetic message header, and the hash of ClientHello1. + * + * https://tools.ietf.org/html/rfc8446#section-4.4.1 + */ +int s2n_server_hello_retry_recreate_transcript(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + struct s2n_handshake_hashes *hashes = conn->handshake.hashes; + POSIX_ENSURE_REF(hashes); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + + /* Create the MessageHash (our synthetic message) */ + uint8_t msghdr[MESSAGE_HASH_HEADER_LENGTH] = { 0 }; + msghdr[0] = TLS_MESSAGE_HASH; + msghdr[MESSAGE_HASH_HEADER_LENGTH - 1] = hash_digest_length; + + /* Grab the current transcript hash to use as the ClientHello1 value. */ + struct s2n_hash_state *client_hello1_hash = &hashes->hash_workspace; + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, client_hello1_hash)); + POSIX_GUARD(s2n_hash_digest(client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + + /* Step 1: Reset the hash state */ + POSIX_GUARD_RESULT(s2n_handshake_reset_hash_state(conn, keys.hash_algorithm)); + + /* Step 2: Update the transcript with the synthetic message */ + struct s2n_blob msg_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&msg_blob, msghdr, MESSAGE_HASH_HEADER_LENGTH)); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); + + /* Step 3: Update the transcript with the ClientHello1 hash */ + POSIX_GUARD(s2n_blob_init(&msg_blob, client_hello1_digest_out, hash_digest_length)); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); + + return S2N_SUCCESS; +} diff --git a/tls/s2n_ktls.h b/tls/s2n_ktls.h index 2f4ef448747..846016afdfe 100644 --- a/tls/s2n_ktls.h +++ b/tls/s2n_ktls.h @@ -1,79 +1,81 @@ -/* - * 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 - -#include "api/unstable/ktls.h" -#include "tls/s2n_connection.h" -/* Define headers needed to enable and use kTLS. - * - * The inline header definitions are required to compile kTLS specific code. - * kTLS has been tested on linux. For all other platforms, kTLS is marked as - * unsupported, and will return an unsupported error. - */ -#include "tls/s2n_ktls_parameters.h" - -/* A set of kTLS configurations representing the combination of sending - * and receiving. - */ -typedef enum { - /* Enable kTLS for the send socket. */ - S2N_KTLS_MODE_SEND, - /* Enable kTLS for the receive socket. */ - S2N_KTLS_MODE_RECV, -} s2n_ktls_mode; - -bool s2n_ktls_is_supported_on_platform(); -S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd); - -int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len); -ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, - ssize_t count, ssize_t offs, s2n_blocked_status *blocked); -int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, - const struct iovec *in, int in_count, size_t offs, size_t to_write); -int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type); -S2N_RESULT s2n_ktls_key_update_send(struct s2n_connection *conn, size_t bytes_requested); -S2N_RESULT s2n_ktls_key_update_process(struct s2n_connection *conn); -S2N_RESULT s2n_ktls_set_estimated_sequence_number(struct s2n_connection *conn, size_t bytes_written); -S2N_RESULT s2n_ktls_check_estimated_record_limit(struct s2n_connection *conn, size_t bytes_requested); - -int s2n_connection_ktls_enable_send(struct s2n_connection *conn); -int s2n_connection_ktls_enable_recv(struct s2n_connection *conn); - -#ifndef _WIN32 - - #include - -/* These use POSIX socket types not available on Windows */ -S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, - size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written); -S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, - size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read); - -/* Testing */ -typedef int (*s2n_setsockopt_fn)(int socket, int level, int option_name, const void *option_value, - socklen_t option_len); -S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb); -typedef ssize_t (*s2n_ktls_sendmsg_fn)(void *io_context, const struct msghdr *msg); -typedef ssize_t (*s2n_ktls_recvmsg_fn)(void *io_context, struct msghdr *msg); -S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, - void *send_ctx); -S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, - void *recv_ctx); -void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode); - -int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, - size_t *bytes_written, s2n_blocked_status *blocked); -#endif /* _WIN32 */ +/* + * 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 + +#include "api/unstable/ktls.h" +#include "tls/s2n_connection.h" +/* Define headers needed to enable and use kTLS. + * + * The inline header definitions are required to compile kTLS specific code. + * kTLS has been tested on linux. For all other platforms, kTLS is marked as + * unsupported, and will return an unsupported error. + */ +#include "tls/s2n_ktls_parameters.h" + +/* A set of kTLS configurations representing the combination of sending + * and receiving. + */ +typedef enum { + /* Enable kTLS for the send socket. */ + S2N_KTLS_MODE_SEND, + /* Enable kTLS for the receive socket. */ + S2N_KTLS_MODE_RECV, +} s2n_ktls_mode; + +bool s2n_ktls_is_supported_on_platform(); +S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd); + +int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len); +ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, + ssize_t count, ssize_t offs, s2n_blocked_status *blocked); +int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, + const struct iovec *in, int in_count, size_t offs, size_t to_write); +int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type); +S2N_RESULT s2n_ktls_key_update_send(struct s2n_connection *conn, size_t bytes_requested); +S2N_RESULT s2n_ktls_key_update_process(struct s2n_connection *conn); +S2N_RESULT s2n_ktls_set_estimated_sequence_number(struct s2n_connection *conn, size_t bytes_written); +S2N_RESULT s2n_ktls_check_estimated_record_limit(struct s2n_connection *conn, size_t bytes_requested); + +int s2n_connection_ktls_enable_send(struct s2n_connection *conn); +int s2n_connection_ktls_enable_recv(struct s2n_connection *conn); + +#ifndef _WIN32 + +#if !defined(_MSC_VER) + #include +#endif + +/* These use POSIX socket types not available on Windows */ +S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, + size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written); +S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, + size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read); + +/* Testing */ +typedef int (*s2n_setsockopt_fn)(int socket, int level, int option_name, const void *option_value, + socklen_t option_len); +S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb); +typedef ssize_t (*s2n_ktls_sendmsg_fn)(void *io_context, const struct msghdr *msg); +typedef ssize_t (*s2n_ktls_recvmsg_fn)(void *io_context, struct msghdr *msg); +S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, + void *send_ctx); +S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, + void *recv_ctx); +void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode); + +int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, + size_t *bytes_written, s2n_blocked_status *blocked); +#endif /* _WIN32 */ diff --git a/tls/s2n_ktls_io.c b/tls/s2n_ktls_io.c index 2e2703c8eae..30e088da355 100644 --- a/tls/s2n_ktls_io.c +++ b/tls/s2n_ktls_io.c @@ -1,496 +1,499 @@ -/* - * 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. - */ - -/* kTLS I/O is not supported on Windows. */ -#ifndef _WIN32 - - #if defined(__FreeBSD__) || defined(__APPLE__) - /* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html - * The POSIX standard does not define the CMSG_LEN and CMSG_SPACE macros. FreeBSD - * and APPLE check and disable these macros if the _POSIX_C_SOURCE flag is set. - * - * Since s2n-tls already unsets the _POSIX_C_SOURCE in other files and is not - * POSIX compliant, we continue the pattern here. - */ - #undef _POSIX_C_SOURCE - #endif - #include - - #ifdef S2N_LINUX_SENDFILE - #include - #endif - - #include "error/s2n_errno.h" - #include "tls/s2n_ktls.h" - #include "tls/s2n_tls.h" - #include "utils/s2n_io.h" - #include "utils/s2n_result.h" - #include "utils/s2n_safety.h" - #include "utils/s2n_socket.h" - - /* record_type is of type uint8_t */ - #define S2N_KTLS_RECORD_TYPE_SIZE (sizeof(uint8_t)) - #define S2N_KTLS_CONTROL_BUFFER_SIZE (CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)) - - #define S2N_MAX_STACK_IOVECS 16 - #define S2N_MAX_STACK_IOVECS_MEM (S2N_MAX_STACK_IOVECS * sizeof(struct iovec)) - -/* Used to override sendmsg and recvmsg for testing. */ -static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg); -static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg); -s2n_ktls_sendmsg_fn s2n_sendmsg_fn = s2n_ktls_default_sendmsg; -s2n_ktls_recvmsg_fn s2n_recvmsg_fn = s2n_ktls_default_recvmsg; - -S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, - void *send_ctx) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(send_ctx); - RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); - conn->send_io_context = send_ctx; - s2n_sendmsg_fn = send_cb; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, - void *recv_ctx) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(recv_ctx); - RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); - conn->recv_io_context = recv_ctx; - s2n_recvmsg_fn = recv_cb; - return S2N_RESULT_OK; -} - -static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(msg); - - const struct s2n_socket_read_io_context *peer_socket_ctx = io_context; - POSIX_ENSURE_REF(peer_socket_ctx); - int fd = peer_socket_ctx->fd; - - return recvmsg(fd, msg, 0); -} - -static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(msg); - - const struct s2n_socket_write_io_context *peer_socket_ctx = io_context; - POSIX_ENSURE_REF(peer_socket_ctx); - int fd = peer_socket_ctx->fd; - - return sendmsg(fd, msg, 0); -} - -S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, - int cmsg_type, uint8_t record_type) -{ - RESULT_ENSURE_REF(msg); - RESULT_ENSURE_REF(buf); - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * To create ancillary data, first initialize the msg_controllen - * member of the msghdr with the length of the control message - * buffer. - */ - msg->msg_control = buf; - msg->msg_controllen = buf_size; - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * Use CMSG_FIRSTHDR() on the msghdr to get the first - * control message and CMSG_NXTHDR() to get all subsequent ones. - */ - struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); - RESULT_ENSURE_REF(hdr); - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * In each control message, initialize cmsg_len (with CMSG_LEN()), the - * other cmsghdr header fields, and the data portion using - * CMSG_DATA(). - */ - hdr->cmsg_len = CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE); - hdr->cmsg_level = S2N_SOL_TLS; - hdr->cmsg_type = cmsg_type; - *CMSG_DATA(hdr) = record_type; - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * Finally, the msg_controllen field of the msghdr - * should be set to the sum of the CMSG_SPACE() of the length of all - * control messages in the buffer - */ - RESULT_ENSURE_GTE(msg->msg_controllen, CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)); - msg->msg_controllen = CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE); - - return S2N_RESULT_OK; -} - -/* Expect to receive a single cmsghdr containing the TLS record_type. - * - * s2n-tls allocates enough space to receive a single cmsghdr. Since this is - * used to get the record_type when receiving over kTLS (enabled via - * `s2n_connection_ktls_enable_recv`), the application should not configure - * the socket to receive additional control messages. In the event s2n-tls - * can not retrieve the record_type, it is safer to drop the record. - */ -S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type) -{ - RESULT_ENSURE_REF(msg); - RESULT_ENSURE_REF(record_type); - - /* https://man7.org/linux/man-pages/man3/recvmsg.3p.html - * MSG_CTRUNC Control data was truncated. - */ - if (msg->msg_flags & MSG_CTRUNC) { - RESULT_BAIL(S2N_ERR_KTLS_BAD_CMSG); - } - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * To create ancillary data, first initialize the msg_controllen - * member of the msghdr with the length of the control message - * buffer. - */ - RESULT_ENSURE(msg->msg_control, S2N_ERR_SAFETY); - RESULT_ENSURE(msg->msg_controllen >= CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_SAFETY); - - /* https://man7.org/linux/man-pages/man3/cmsg.3.html - * Use CMSG_FIRSTHDR() on the msghdr to get the first - * control message and CMSG_NXTHDR() to get all subsequent ones. - */ - struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); - RESULT_ENSURE(hdr, S2N_ERR_KTLS_BAD_CMSG); - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * In each control message, initialize cmsg_len (with CMSG_LEN()), the - * other cmsghdr header fields, and the data portion using - * CMSG_DATA(). - */ - RESULT_ENSURE(hdr->cmsg_level == S2N_SOL_TLS, S2N_ERR_KTLS_BAD_CMSG); - RESULT_ENSURE(hdr->cmsg_type == cmsg_type, S2N_ERR_KTLS_BAD_CMSG); - RESULT_ENSURE(hdr->cmsg_len == CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_KTLS_BAD_CMSG); - *record_type = *CMSG_DATA(hdr); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, - size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written) -{ - RESULT_ENSURE_REF(bytes_written); - RESULT_ENSURE_REF(blocked); - RESULT_ENSURE(msg_iov != NULL || msg_iovlen == 0, S2N_ERR_NULL); - - *blocked = S2N_BLOCKED_ON_WRITE; - *bytes_written = 0; - - struct msghdr msg = { - /* msghdr requires a non-const iovec. This is safe because s2n-tls does - * not modify msg_iov after this point. - */ - .msg_iov = (struct iovec *) (uintptr_t) msg_iov, - .msg_iovlen = msg_iovlen, - }; - - char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; - RESULT_GUARD(s2n_ktls_set_control_data(&msg, control_data, sizeof(control_data), - S2N_TLS_SET_RECORD_TYPE, record_type)); - - ssize_t result = 0; - S2N_IO_RETRY_EINTR(result, s2n_sendmsg_fn(io_context, &msg)); - RESULT_GUARD(s2n_io_check_write_result(result)); - - *blocked = S2N_NOT_BLOCKED; - *bytes_written = result; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, - size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read) -{ - RESULT_ENSURE_REF(record_type); - RESULT_ENSURE_REF(bytes_read); - RESULT_ENSURE_REF(blocked); - RESULT_ENSURE_REF(buf); - /* Ensure that buf_len is > 0 since trying to receive 0 bytes does not - * make sense and a return value of `0` from recvmsg is treated as EOF. - */ - RESULT_ENSURE_GT(buf_len, 0); - - *blocked = S2N_BLOCKED_ON_READ; - *record_type = 0; - *bytes_read = 0; - struct iovec msg_iov = { - .iov_base = buf, - .iov_len = buf_len - }; - struct msghdr msg = { - .msg_iov = &msg_iov, - .msg_iovlen = 1, - }; - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * To create ancillary data, first initialize the msg_controllen - * member of the msghdr with the length of the control message - * buffer. - */ - char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; - msg.msg_controllen = sizeof(control_data); - msg.msg_control = control_data; - - ssize_t result = 0; - S2N_IO_RETRY_EINTR(result, s2n_recvmsg_fn(io_context, &msg)); - RESULT_GUARD(s2n_io_check_read_result(result)); - - RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type)); - - *blocked = S2N_NOT_BLOCKED; - *bytes_read = result; - return S2N_RESULT_OK; -} - -/* The iovec array `bufs` is constant and owned by the application. - * - * However, we need to apply the given offset to `bufs`. That may involve - * updating the iov_base and iov_len of entries in `bufs` to reflect the bytes - * already sent. Because `bufs` is constant, we need to instead copy `bufs` and - * modify the copy. - * - * Since one of the primary benefits of kTLS is that we avoid buffering application - * data and can pass application data as-is to the kernel, we try to limit the - * situations where we need to copy `bufs` and use stack memory where possible. - * - * Note: We are copying an array of iovecs here, NOT the scattered application - * data the iovecs reference. On Linux, the maximum data copied would be - * 1024 (IOV_MAX on Linux) * 16 (sizeof(struct iovec)) = ~16KB. - * - * To avoid any copies when using a large number of iovecs, applications should - * call s2n_sendv instead of s2n_sendv_with_offset. - */ -static S2N_RESULT s2n_ktls_update_bufs_with_offset(const struct iovec **bufs, size_t *count, - size_t offs, struct s2n_blob *mem) -{ - RESULT_ENSURE_REF(bufs); - RESULT_ENSURE_REF(count); - RESULT_ENSURE(*bufs != NULL || *count == 0, S2N_ERR_NULL); - RESULT_ENSURE_REF(mem); - - size_t skipped = 0; - while (offs > 0) { - /* If we need to skip more iovecs than actually exist, - * then the offset is too large and therefore invalid. - */ - RESULT_ENSURE(skipped < *count, S2N_ERR_INVALID_ARGUMENT); - - size_t iov_len = (*bufs)[skipped].iov_len; - - /* This is the last iovec affected by the offset. */ - if (offs < iov_len) { - break; - } - - offs -= iov_len; - skipped++; - } - - *count = (*count) - skipped; - if (*count == 0) { - return S2N_RESULT_OK; - } - - *bufs = &(*bufs)[skipped]; - if (offs == 0) { - return S2N_RESULT_OK; - } - - size_t size = (*count) * (sizeof(struct iovec)); - /* If possible, use the existing stack memory in `mem` for the copy. - * Otherwise, we need to allocate sufficient new heap memory. */ - if (size > mem->size) { - RESULT_GUARD_POSIX(s2n_alloc(mem, size)); - } - - struct iovec *new_bufs = (struct iovec *) (void *) mem->data; - RESULT_CHECKED_MEMCPY(new_bufs, *bufs, size); - new_bufs[0].iov_base = (uint8_t *) new_bufs[0].iov_base + offs; - new_bufs[0].iov_len = new_bufs[0].iov_len - offs; - *bufs = new_bufs; - - return S2N_RESULT_OK; -} - -ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, - ssize_t count_in, ssize_t offs_in, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(count_in >= 0, S2N_ERR_INVALID_ARGUMENT); - size_t count = count_in; - POSIX_ENSURE(offs_in >= 0, S2N_ERR_INVALID_ARGUMENT); - size_t offs = offs_in; - - ssize_t total_bytes = 0; - POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count_in, offs_in, &total_bytes)); - POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, total_bytes)); - - /* The order of new_bufs and new_bufs_mem matters. See https://github.com/aws/s2n-tls/issues/4354 */ - uint8_t new_bufs_mem[S2N_MAX_STACK_IOVECS_MEM] = { 0 }; - DEFER_CLEANUP(struct s2n_blob new_bufs = { 0 }, s2n_free_or_wipe); - POSIX_GUARD(s2n_blob_init(&new_bufs, new_bufs_mem, sizeof(new_bufs_mem))); - if (offs > 0) { - POSIX_GUARD_RESULT(s2n_ktls_update_bufs_with_offset(&bufs, &count, offs, &new_bufs)); - } - - size_t bytes_written = 0; - POSIX_GUARD_RESULT(s2n_ktls_sendmsg(conn->send_io_context, TLS_APPLICATION_DATA, - bufs, count, blocked, &bytes_written)); - - POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, bytes_written)); - return bytes_written; -} - -int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(buf); - - /* For now, all control records are assumed to be alerts. - * We can set the record_type on the io_context in the future. - */ - const uint8_t record_type = TLS_ALERT; - - const struct iovec iov = { - .iov_base = (void *) (uintptr_t) buf, - .iov_len = len, - }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - size_t bytes_written = 0; - - POSIX_GUARD_RESULT(s2n_ktls_sendmsg(io_context, record_type, &iov, 1, - &blocked, &bytes_written)); - - POSIX_ENSURE_LTE(bytes_written, len); - return bytes_written; -} - -int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, - const struct iovec *in, int in_count, size_t offs, size_t to_write) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(in_count > 0, S2N_ERR_INVALID_ARGUMENT); - size_t count = in_count; - POSIX_ENSURE_REF(in); - - /* Currently, ktls only supports sending alerts. - * To also support handshake messages, we would need a way to track record_type. - * We could add a field to the send io context. - */ - POSIX_ENSURE(content_type == TLS_ALERT, S2N_ERR_UNIMPLEMENTED); - - /* When stuffers automatically resize, they allocate a potentially large - * chunk of memory to avoid repeated resizes. - * Since ktls only uses conn->out for control messages (alerts and eventually - * handshake messages), we expect infrequent small writes with conn->out - * freed in between. Since we're therefore more concerned with the size of - * the allocation than the frequency, use a more accurate size for each write. - */ - POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->out, to_write)); - - POSIX_GUARD(s2n_stuffer_writev_bytes(&conn->out, in, count, offs, to_write)); - return to_write; -} - -int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, - size_t *bytes_written, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(blocked); - *blocked = S2N_BLOCKED_ON_WRITE; - POSIX_ENSURE_REF(bytes_written); - *bytes_written = 0; - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(conn->ktls_send_enabled, S2N_ERR_KTLS_UNSUPPORTED_CONN); - POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, count)); - - int out_fd = 0; - POSIX_GUARD_RESULT(s2n_ktls_get_file_descriptor(conn, S2N_KTLS_MODE_SEND, &out_fd)); - - #ifdef S2N_LINUX_SENDFILE - /* https://man7.org/linux/man-pages/man2/sendfile.2.html */ - ssize_t result = 0; - S2N_IO_RETRY_EINTR(result, sendfile(out_fd, in_fd, &offset, count)); - POSIX_GUARD_RESULT(s2n_io_check_write_result(result)); - *bytes_written = result; - #else - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); - #endif - - POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, *bytes_written)); - *blocked = S2N_NOT_BLOCKED; - return S2N_SUCCESS; -} - -int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(record_type); - - /* If any unread data remains in conn->in, it must be application data that - * couldn't be returned due to the size of the application's provided buffer. - */ - if (s2n_stuffer_data_available(&conn->in)) { - *record_type = TLS_APPLICATION_DATA; - return S2N_SUCCESS; - } - - POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_DEFAULT_FRAGMENT_LENGTH)); - - struct s2n_stuffer record_stuffer = conn->buffer_in; - size_t len = s2n_stuffer_space_remaining(&record_stuffer); - uint8_t *buf = s2n_stuffer_raw_write(&record_stuffer, len); - POSIX_ENSURE_REF(buf); - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - size_t bytes_read = 0; - - /* Since recvmsg is responsible for decrypting the record in ktls, - * we apply blinding to the recvmsg call. - */ - s2n_result result = s2n_ktls_recvmsg(conn->recv_io_context, record_type, - buf, len, &blocked, &bytes_read); - WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); - - POSIX_GUARD(s2n_stuffer_skip_write(&conn->buffer_in, bytes_read)); - - /* We don't care about returning a full fragment because we don't need to decrypt. - * kTLS handled decryption already. - * So we can always set conn->in equal to the full buffer_in. - */ - POSIX_GUARD_RESULT(s2n_recv_in_init(conn, bytes_read, bytes_read)); - return S2N_SUCCESS; -} - -#endif - -/* Suppress empty translation unit warning when compiled on Windows. */ -#pragma clang diagnostic ignored "-Wempty-translation-unit" +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* kTLS I/O is not supported on Windows. */ +#ifndef _WIN32 + + #if defined(__FreeBSD__) || defined(__APPLE__) + /* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html + * The POSIX standard does not define the CMSG_LEN and CMSG_SPACE macros. FreeBSD + * and APPLE check and disable these macros if the _POSIX_C_SOURCE flag is set. + * + * Since s2n-tls already unsets the _POSIX_C_SOURCE in other files and is not + * POSIX compliant, we continue the pattern here. + */ + #undef _POSIX_C_SOURCE + #endif +#if !defined(_MSC_VER) + #include +#endif + + #ifdef S2N_LINUX_SENDFILE + #include + #endif + + #include "error/s2n_errno.h" + #include "tls/s2n_ktls.h" + #include "tls/s2n_tls.h" + #include "utils/s2n_io.h" + #include "utils/s2n_result.h" + #include "utils/s2n_safety.h" + #include "utils/s2n_socket.h" + + /* record_type is of type uint8_t */ + #define S2N_KTLS_RECORD_TYPE_SIZE (sizeof(uint8_t)) + #define S2N_KTLS_CONTROL_BUFFER_SIZE (CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)) + + #define S2N_MAX_STACK_IOVECS 16 + #define S2N_MAX_STACK_IOVECS_MEM (S2N_MAX_STACK_IOVECS * sizeof(struct iovec)) + +/* Used to override sendmsg and recvmsg for testing. */ +static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg); +static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg); +s2n_ktls_sendmsg_fn s2n_sendmsg_fn = s2n_ktls_default_sendmsg; +s2n_ktls_recvmsg_fn s2n_recvmsg_fn = s2n_ktls_default_recvmsg; + +S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, + void *send_ctx) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(send_ctx); + RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); + conn->send_io_context = send_ctx; + s2n_sendmsg_fn = send_cb; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, + void *recv_ctx) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(recv_ctx); + RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); + conn->recv_io_context = recv_ctx; + s2n_recvmsg_fn = recv_cb; + return S2N_RESULT_OK; +} + +static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(msg); + + const struct s2n_socket_read_io_context *peer_socket_ctx = io_context; + POSIX_ENSURE_REF(peer_socket_ctx); + int fd = peer_socket_ctx->fd; + + return recvmsg(fd, msg, 0); +} + +static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(msg); + + const struct s2n_socket_write_io_context *peer_socket_ctx = io_context; + POSIX_ENSURE_REF(peer_socket_ctx); + int fd = peer_socket_ctx->fd; + + return sendmsg(fd, msg, 0); +} + +S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, + int cmsg_type, uint8_t record_type) +{ + RESULT_ENSURE_REF(msg); + RESULT_ENSURE_REF(buf); + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * To create ancillary data, first initialize the msg_controllen + * member of the msghdr with the length of the control message + * buffer. + */ + msg->msg_control = buf; + msg->msg_controllen = buf_size; + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * Use CMSG_FIRSTHDR() on the msghdr to get the first + * control message and CMSG_NXTHDR() to get all subsequent ones. + */ + struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); + RESULT_ENSURE_REF(hdr); + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * In each control message, initialize cmsg_len (with CMSG_LEN()), the + * other cmsghdr header fields, and the data portion using + * CMSG_DATA(). + */ + hdr->cmsg_len = CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE); + hdr->cmsg_level = S2N_SOL_TLS; + hdr->cmsg_type = cmsg_type; + *CMSG_DATA(hdr) = record_type; + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * Finally, the msg_controllen field of the msghdr + * should be set to the sum of the CMSG_SPACE() of the length of all + * control messages in the buffer + */ + RESULT_ENSURE_GTE(msg->msg_controllen, CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)); + msg->msg_controllen = CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE); + + return S2N_RESULT_OK; +} + +/* Expect to receive a single cmsghdr containing the TLS record_type. + * + * s2n-tls allocates enough space to receive a single cmsghdr. Since this is + * used to get the record_type when receiving over kTLS (enabled via + * `s2n_connection_ktls_enable_recv`), the application should not configure + * the socket to receive additional control messages. In the event s2n-tls + * can not retrieve the record_type, it is safer to drop the record. + */ +S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type) +{ + RESULT_ENSURE_REF(msg); + RESULT_ENSURE_REF(record_type); + + /* https://man7.org/linux/man-pages/man3/recvmsg.3p.html + * MSG_CTRUNC Control data was truncated. + */ + if (msg->msg_flags & MSG_CTRUNC) { + RESULT_BAIL(S2N_ERR_KTLS_BAD_CMSG); + } + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * To create ancillary data, first initialize the msg_controllen + * member of the msghdr with the length of the control message + * buffer. + */ + RESULT_ENSURE(msg->msg_control, S2N_ERR_SAFETY); + RESULT_ENSURE(msg->msg_controllen >= CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_SAFETY); + + /* https://man7.org/linux/man-pages/man3/cmsg.3.html + * Use CMSG_FIRSTHDR() on the msghdr to get the first + * control message and CMSG_NXTHDR() to get all subsequent ones. + */ + struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); + RESULT_ENSURE(hdr, S2N_ERR_KTLS_BAD_CMSG); + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * In each control message, initialize cmsg_len (with CMSG_LEN()), the + * other cmsghdr header fields, and the data portion using + * CMSG_DATA(). + */ + RESULT_ENSURE(hdr->cmsg_level == S2N_SOL_TLS, S2N_ERR_KTLS_BAD_CMSG); + RESULT_ENSURE(hdr->cmsg_type == cmsg_type, S2N_ERR_KTLS_BAD_CMSG); + RESULT_ENSURE(hdr->cmsg_len == CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_KTLS_BAD_CMSG); + *record_type = *CMSG_DATA(hdr); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, + size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written) +{ + RESULT_ENSURE_REF(bytes_written); + RESULT_ENSURE_REF(blocked); + RESULT_ENSURE(msg_iov != NULL || msg_iovlen == 0, S2N_ERR_NULL); + + *blocked = S2N_BLOCKED_ON_WRITE; + *bytes_written = 0; + + struct msghdr msg = { + /* msghdr requires a non-const iovec. This is safe because s2n-tls does + * not modify msg_iov after this point. + */ + .msg_iov = (struct iovec *) (uintptr_t) msg_iov, + .msg_iovlen = msg_iovlen, + }; + + char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; + RESULT_GUARD(s2n_ktls_set_control_data(&msg, control_data, sizeof(control_data), + S2N_TLS_SET_RECORD_TYPE, record_type)); + + ssize_t result = 0; + S2N_IO_RETRY_EINTR(result, s2n_sendmsg_fn(io_context, &msg)); + RESULT_GUARD(s2n_io_check_write_result(result)); + + *blocked = S2N_NOT_BLOCKED; + *bytes_written = result; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, + size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read) +{ + RESULT_ENSURE_REF(record_type); + RESULT_ENSURE_REF(bytes_read); + RESULT_ENSURE_REF(blocked); + RESULT_ENSURE_REF(buf); + /* Ensure that buf_len is > 0 since trying to receive 0 bytes does not + * make sense and a return value of `0` from recvmsg is treated as EOF. + */ + RESULT_ENSURE_GT(buf_len, 0); + + *blocked = S2N_BLOCKED_ON_READ; + *record_type = 0; + *bytes_read = 0; + struct iovec msg_iov = { + .iov_base = buf, + .iov_len = buf_len + }; + struct msghdr msg = { + .msg_iov = &msg_iov, + .msg_iovlen = 1, + }; + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * To create ancillary data, first initialize the msg_controllen + * member of the msghdr with the length of the control message + * buffer. + */ + char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; + msg.msg_controllen = sizeof(control_data); + msg.msg_control = control_data; + + ssize_t result = 0; + S2N_IO_RETRY_EINTR(result, s2n_recvmsg_fn(io_context, &msg)); + RESULT_GUARD(s2n_io_check_read_result(result)); + + RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type)); + + *blocked = S2N_NOT_BLOCKED; + *bytes_read = result; + return S2N_RESULT_OK; +} + +/* The iovec array `bufs` is constant and owned by the application. + * + * However, we need to apply the given offset to `bufs`. That may involve + * updating the iov_base and iov_len of entries in `bufs` to reflect the bytes + * already sent. Because `bufs` is constant, we need to instead copy `bufs` and + * modify the copy. + * + * Since one of the primary benefits of kTLS is that we avoid buffering application + * data and can pass application data as-is to the kernel, we try to limit the + * situations where we need to copy `bufs` and use stack memory where possible. + * + * Note: We are copying an array of iovecs here, NOT the scattered application + * data the iovecs reference. On Linux, the maximum data copied would be + * 1024 (IOV_MAX on Linux) * 16 (sizeof(struct iovec)) = ~16KB. + * + * To avoid any copies when using a large number of iovecs, applications should + * call s2n_sendv instead of s2n_sendv_with_offset. + */ +static S2N_RESULT s2n_ktls_update_bufs_with_offset(const struct iovec **bufs, size_t *count, + size_t offs, struct s2n_blob *mem) +{ + RESULT_ENSURE_REF(bufs); + RESULT_ENSURE_REF(count); + RESULT_ENSURE(*bufs != NULL || *count == 0, S2N_ERR_NULL); + RESULT_ENSURE_REF(mem); + + size_t skipped = 0; + while (offs > 0) { + /* If we need to skip more iovecs than actually exist, + * then the offset is too large and therefore invalid. + */ + RESULT_ENSURE(skipped < *count, S2N_ERR_INVALID_ARGUMENT); + + size_t iov_len = (*bufs)[skipped].iov_len; + + /* This is the last iovec affected by the offset. */ + if (offs < iov_len) { + break; + } + + offs -= iov_len; + skipped++; + } + + *count = (*count) - skipped; + if (*count == 0) { + return S2N_RESULT_OK; + } + + *bufs = &(*bufs)[skipped]; + if (offs == 0) { + return S2N_RESULT_OK; + } + + size_t size = (*count) * (sizeof(struct iovec)); + /* If possible, use the existing stack memory in `mem` for the copy. + * Otherwise, we need to allocate sufficient new heap memory. */ + if (size > mem->size) { + RESULT_GUARD_POSIX(s2n_alloc(mem, size)); + } + + struct iovec *new_bufs = (struct iovec *) (void *) mem->data; + RESULT_CHECKED_MEMCPY(new_bufs, *bufs, size); + new_bufs[0].iov_base = (uint8_t *) new_bufs[0].iov_base + offs; + new_bufs[0].iov_len = new_bufs[0].iov_len - offs; + *bufs = new_bufs; + + return S2N_RESULT_OK; +} + +ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, + ssize_t count_in, ssize_t offs_in, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(count_in >= 0, S2N_ERR_INVALID_ARGUMENT); + size_t count = count_in; + POSIX_ENSURE(offs_in >= 0, S2N_ERR_INVALID_ARGUMENT); + size_t offs = offs_in; + + ssize_t total_bytes = 0; + POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count_in, offs_in, &total_bytes)); + POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, total_bytes)); + + /* The order of new_bufs and new_bufs_mem matters. See https://github.com/aws/s2n-tls/issues/4354 */ + uint8_t new_bufs_mem[S2N_MAX_STACK_IOVECS_MEM] = { 0 }; + DEFER_CLEANUP(struct s2n_blob new_bufs = { 0 }, s2n_free_or_wipe); + POSIX_GUARD(s2n_blob_init(&new_bufs, new_bufs_mem, sizeof(new_bufs_mem))); + if (offs > 0) { + POSIX_GUARD_RESULT(s2n_ktls_update_bufs_with_offset(&bufs, &count, offs, &new_bufs)); + } + + size_t bytes_written = 0; + POSIX_GUARD_RESULT(s2n_ktls_sendmsg(conn->send_io_context, TLS_APPLICATION_DATA, + bufs, count, blocked, &bytes_written)); + + POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, bytes_written)); + return bytes_written; +} + +int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(buf); + + /* For now, all control records are assumed to be alerts. + * We can set the record_type on the io_context in the future. + */ + const uint8_t record_type = TLS_ALERT; + + const struct iovec iov = { + .iov_base = (void *) (uintptr_t) buf, + .iov_len = len, + }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + + POSIX_GUARD_RESULT(s2n_ktls_sendmsg(io_context, record_type, &iov, 1, + &blocked, &bytes_written)); + + POSIX_ENSURE_LTE(bytes_written, len); + return bytes_written; +} + +int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, + const struct iovec *in, int in_count, size_t offs, size_t to_write) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(in_count > 0, S2N_ERR_INVALID_ARGUMENT); + size_t count = in_count; + POSIX_ENSURE_REF(in); + + /* Currently, ktls only supports sending alerts. + * To also support handshake messages, we would need a way to track record_type. + * We could add a field to the send io context. + */ + POSIX_ENSURE(content_type == TLS_ALERT, S2N_ERR_UNIMPLEMENTED); + + /* When stuffers automatically resize, they allocate a potentially large + * chunk of memory to avoid repeated resizes. + * Since ktls only uses conn->out for control messages (alerts and eventually + * handshake messages), we expect infrequent small writes with conn->out + * freed in between. Since we're therefore more concerned with the size of + * the allocation than the frequency, use a more accurate size for each write. + */ + POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->out, to_write)); + + POSIX_GUARD(s2n_stuffer_writev_bytes(&conn->out, in, count, offs, to_write)); + return to_write; +} + +int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, + size_t *bytes_written, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(blocked); + *blocked = S2N_BLOCKED_ON_WRITE; + POSIX_ENSURE_REF(bytes_written); + *bytes_written = 0; + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(conn->ktls_send_enabled, S2N_ERR_KTLS_UNSUPPORTED_CONN); + POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, count)); + + int out_fd = 0; + POSIX_GUARD_RESULT(s2n_ktls_get_file_descriptor(conn, S2N_KTLS_MODE_SEND, &out_fd)); + + #ifdef S2N_LINUX_SENDFILE + /* https://man7.org/linux/man-pages/man2/sendfile.2.html */ + ssize_t result = 0; + S2N_IO_RETRY_EINTR(result, sendfile(out_fd, in_fd, &offset, count)); + POSIX_GUARD_RESULT(s2n_io_check_write_result(result)); + *bytes_written = result; + #else + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); + #endif + + POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, *bytes_written)); + *blocked = S2N_NOT_BLOCKED; + return S2N_SUCCESS; +} + +int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(record_type); + + /* If any unread data remains in conn->in, it must be application data that + * couldn't be returned due to the size of the application's provided buffer. + */ + if (s2n_stuffer_data_available(&conn->in)) { + *record_type = TLS_APPLICATION_DATA; + return S2N_SUCCESS; + } + + POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_DEFAULT_FRAGMENT_LENGTH)); + + struct s2n_stuffer record_stuffer = conn->buffer_in; + size_t len = s2n_stuffer_space_remaining(&record_stuffer); + uint8_t *buf = s2n_stuffer_raw_write(&record_stuffer, len); + POSIX_ENSURE_REF(buf); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_read = 0; + + /* Since recvmsg is responsible for decrypting the record in ktls, + * we apply blinding to the recvmsg call. + */ + s2n_result result = s2n_ktls_recvmsg(conn->recv_io_context, record_type, + buf, len, &blocked, &bytes_read); + WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); + + POSIX_GUARD(s2n_stuffer_skip_write(&conn->buffer_in, bytes_read)); + + /* We don't care about returning a full fragment because we don't need to decrypt. + * kTLS handled decryption already. + * So we can always set conn->in equal to the full buffer_in. + */ + POSIX_GUARD_RESULT(s2n_recv_in_init(conn, bytes_read, bytes_read)); + return S2N_SUCCESS; +} + +#endif + +/* Suppress empty translation unit warning when compiled on Windows. */ +#pragma clang diagnostic ignored "-Wempty-translation-unit" diff --git a/tls/s2n_ocsp_stapling.c b/tls/s2n_ocsp_stapling.c index e9059f5c047..dcf3478fa6d 100644 --- a/tls/s2n_ocsp_stapling.c +++ b/tls/s2n_ocsp_stapling.c @@ -1,39 +1,41 @@ -/* - * 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 "error/s2n_errno.h" -#include "tls/extensions/s2n_cert_status.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_x509_validator.h" -#include "utils/s2n_safety.h" - -int s2n_server_status_send(struct s2n_connection *conn) -{ - if (s2n_server_can_send_ocsp(conn)) { - POSIX_GUARD(s2n_cert_status_send(conn, &conn->handshake.io)); - } - - return 0; -} - -int s2n_server_status_recv(struct s2n_connection *conn) -{ - return s2n_cert_status_recv(conn, &conn->handshake.io); -} +/* + * 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. + */ + +#if !defined(_MSC_VER) +#include +#endif + +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_cert_status.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_x509_validator.h" +#include "utils/s2n_safety.h" + +int s2n_server_status_send(struct s2n_connection *conn) +{ + if (s2n_server_can_send_ocsp(conn)) { + POSIX_GUARD(s2n_cert_status_send(conn, &conn->handshake.io)); + } + + return 0; +} + +int s2n_server_status_recv(struct s2n_connection *conn) +{ + return s2n_cert_status_recv(conn, &conn->handshake.io); +} diff --git a/tls/s2n_post_handshake.c b/tls/s2n_post_handshake.c index 50ed44054dd..6d4a58baa59 100644 --- a/tls/s2n_post_handshake.c +++ b/tls/s2n_post_handshake.c @@ -1,200 +1,201 @@ -/* - * 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 "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_key_update.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_post_handshake_process(struct s2n_connection *conn, struct s2n_stuffer *in, uint8_t message_type) -{ - RESULT_ENSURE_REF(conn); - - switch (message_type) { - case TLS_KEY_UPDATE: - RESULT_GUARD_POSIX(s2n_key_update_recv(conn, in)); - break; - case TLS_SERVER_NEW_SESSION_TICKET: - RESULT_GUARD(s2n_tls13_server_nst_recv(conn, in)); - break; - case TLS_HELLO_REQUEST: - RESULT_GUARD(s2n_client_hello_request_recv(conn)); - break; - case TLS_CERT_REQ: - /* - * s2n-tls does not support post-handshake authentication. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.2 - *# A client that receives a CertificateRequest message without having - *# sent the "post_handshake_auth" extension MUST send an - *# "unexpected_message" fatal alert. - */ - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); - default: - /* All other messages are unexpected */ - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); - } - - return S2N_RESULT_OK; -} - -/* - * Read a handshake message from conn->in. - * - * Handshake messages can be fragmented, meaning that a single message - * may be split between multiple records. conn->in only holds a single - * record at a time, so we may need to call this method multiple - * times to construct the complete message. We store the partial message - * in conn->post_handshake.in between calls. - */ -S2N_RESULT s2n_post_handshake_message_recv(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_stuffer *in = &conn->in; - struct s2n_stuffer *message = &conn->post_handshake.in; - uint8_t message_type = 0; - uint32_t message_len = 0; - - /* We always start reading from the beginning of the message. - * Reset the read progress, but keep the write progress since - * there may already be a partial message stored in `message`. - */ - RESULT_GUARD_POSIX(s2n_stuffer_reread(message)); - - /* At minimum, the message stuffer needs to have enough space to read the header. - * For small messages like KeyUpdate and HelloRequest, this is all the space we will need. - */ - if (s2n_stuffer_is_freed(message)) { - struct s2n_blob b = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&b, conn->post_handshake.header_in, - sizeof(conn->post_handshake.header_in))); - RESULT_GUARD_POSIX(s2n_stuffer_init(message, &b)); - } - - /* Try to copy the header into the message stuffer. - * The message stuffer may already contain some or all of the header if - * we have read fragments of this message from previous records. - */ - if (s2n_stuffer_data_available(message) < TLS_HANDSHAKE_HEADER_LENGTH) { - uint32_t remaining = TLS_HANDSHAKE_HEADER_LENGTH - s2n_stuffer_data_available(message); - uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); - RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); - } - RESULT_ENSURE(s2n_stuffer_data_available(message) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_IO_BLOCKED); - - /* Parse the header */ - RESULT_GUARD(s2n_handshake_parse_header(message, &message_type, &message_len)); - RESULT_ENSURE(message_len == 0 || s2n_stuffer_data_available(in), S2N_ERR_IO_BLOCKED); - RESULT_ENSURE(message_len <= S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - - /* If the message body is not fragmented, just process it directly from conn->in. - * This will be the most common case, and does not require us to allocate any new memory. - */ - if (s2n_stuffer_data_available(message) == 0 && s2n_stuffer_data_available(in) >= message_len) { - struct s2n_stuffer full_message = { 0 }; - struct s2n_blob full_message_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&full_message_blob, s2n_stuffer_raw_read(in, message_len), message_len)); - RESULT_GUARD_POSIX(s2n_stuffer_init(&full_message, &full_message_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&full_message, message_len)); - RESULT_GUARD(s2n_post_handshake_process(conn, &full_message, message_type)); - return S2N_RESULT_OK; - } - - /* If the message body is fragmented, then the current fragment will be wiped from conn->in - * in order to read the next record. So the message stuffer needs enough space to store - * the full message as we reconstruct it from multiple records. - * For large messages like NewSessionTicket, this will require allocating new memory. - */ - if (s2n_stuffer_space_remaining(message) < message_len) { - /* We want to avoid servers allocating memory in response to post-handshake messages - * to avoid a potential DDOS / resource exhaustion attack. - * - * Currently, s2n-tls servers only support the KeyUpdate message, - * which should never require additional memory to parse. - */ - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); - - uint32_t total_size = message_len + TLS_HANDSHAKE_HEADER_LENGTH; - if (message->alloced) { - RESULT_GUARD_POSIX(s2n_stuffer_resize(message, total_size)); - } else { - /* Manually convert our static stuffer to a growable stuffer */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(message, total_size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(message, conn->post_handshake.header_in, TLS_HANDSHAKE_HEADER_LENGTH)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(message, TLS_HANDSHAKE_HEADER_LENGTH)); - } - } - - /* Try to copy the message body into the message stuffer. - * The message stuffer may already contain some of the message body if - * we have already read fragments from previous records. - */ - if (s2n_stuffer_data_available(message) < message_len) { - uint32_t remaining = message_len - s2n_stuffer_data_available(message); - uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); - RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); - } - RESULT_ENSURE(s2n_stuffer_data_available(message) == message_len, S2N_ERR_IO_BLOCKED); - - /* Now that the full message body is available, process it. */ - RESULT_GUARD(s2n_post_handshake_process(conn, message, message_type)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_post_handshake_recv(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - while (s2n_stuffer_data_available(&conn->in)) { - RESULT_GUARD(s2n_post_handshake_message_recv(conn)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->post_handshake.in)); - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_post_handshake_write_records(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - struct s2n_stuffer *message = &conn->handshake.io; - - /* Flush any existing records before we write a new handshake record. - * We do not support buffering multiple handshake records. - */ - if (s2n_stuffer_data_available(message)) { - RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); - } - - RESULT_GUARD(s2n_handshake_message_send(conn, TLS_HANDSHAKE, blocked)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(message)); - return S2N_RESULT_OK; -} - -int s2n_post_handshake_send(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - - /* Currently, we only support TLS1.3 post-handshake messages. */ - if (conn->actual_protocol_version < S2N_TLS13) { - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_post_handshake_write_records(conn, blocked)); - - POSIX_GUARD(s2n_key_update_send(conn, blocked)); - POSIX_GUARD_RESULT(s2n_tls13_server_nst_send(conn, blocked)); - - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_key_update.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_post_handshake_process(struct s2n_connection *conn, struct s2n_stuffer *in, uint8_t message_type) +{ + RESULT_ENSURE_REF(conn); + + switch (message_type) { + case TLS_KEY_UPDATE: + RESULT_GUARD_POSIX(s2n_key_update_recv(conn, in)); + break; + case TLS_SERVER_NEW_SESSION_TICKET: + RESULT_GUARD(s2n_tls13_server_nst_recv(conn, in)); + break; + case TLS_HELLO_REQUEST: + RESULT_GUARD(s2n_client_hello_request_recv(conn)); + break; + case TLS_CERT_REQ: + /* + * s2n-tls does not support post-handshake authentication. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.2 + *# A client that receives a CertificateRequest message without having + *# sent the "post_handshake_auth" extension MUST send an + *# "unexpected_message" fatal alert. + */ + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); + default: + /* All other messages are unexpected */ + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); + } + + return S2N_RESULT_OK; +} + +/* + * Read a handshake message from conn->in. + * + * Handshake messages can be fragmented, meaning that a single message + * may be split between multiple records. conn->in only holds a single + * record at a time, so we may need to call this method multiple + * times to construct the complete message. We store the partial message + * in conn->post_handshake.in between calls. + */ +S2N_RESULT s2n_post_handshake_message_recv(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_stuffer *in = &conn->in; + struct s2n_stuffer *message = &conn->post_handshake.in; + uint8_t message_type = 0; + uint32_t message_len = 0; + + /* We always start reading from the beginning of the message. + * Reset the read progress, but keep the write progress since + * there may already be a partial message stored in `message`. + */ + RESULT_GUARD_POSIX(s2n_stuffer_reread(message)); + + /* At minimum, the message stuffer needs to have enough space to read the header. + * For small messages like KeyUpdate and HelloRequest, this is all the space we will need. + */ + if (s2n_stuffer_is_freed(message)) { + struct s2n_blob b = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&b, conn->post_handshake.header_in, + sizeof(conn->post_handshake.header_in))); + RESULT_GUARD_POSIX(s2n_stuffer_init(message, &b)); + } + + /* Try to copy the header into the message stuffer. + * The message stuffer may already contain some or all of the header if + * we have read fragments of this message from previous records. + */ + if (s2n_stuffer_data_available(message) < TLS_HANDSHAKE_HEADER_LENGTH) { + uint32_t remaining = TLS_HANDSHAKE_HEADER_LENGTH - s2n_stuffer_data_available(message); + uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); + } + RESULT_ENSURE(s2n_stuffer_data_available(message) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_IO_BLOCKED); + + /* Parse the header */ + RESULT_GUARD(s2n_handshake_parse_header(message, &message_type, &message_len)); + RESULT_ENSURE(message_len == 0 || s2n_stuffer_data_available(in), S2N_ERR_IO_BLOCKED); + RESULT_ENSURE(message_len <= S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); + + /* If the message body is not fragmented, just process it directly from conn->in. + * This will be the most common case, and does not require us to allocate any new memory. + */ + if (s2n_stuffer_data_available(message) == 0 && s2n_stuffer_data_available(in) >= message_len) { + struct s2n_stuffer full_message = { 0 }; + struct s2n_blob full_message_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&full_message_blob, s2n_stuffer_raw_read(in, message_len), message_len)); + RESULT_GUARD_POSIX(s2n_stuffer_init(&full_message, &full_message_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&full_message, message_len)); + RESULT_GUARD(s2n_post_handshake_process(conn, &full_message, message_type)); + return S2N_RESULT_OK; + } + + /* If the message body is fragmented, then the current fragment will be wiped from conn->in + * in order to read the next record. So the message stuffer needs enough space to store + * the full message as we reconstruct it from multiple records. + * For large messages like NewSessionTicket, this will require allocating new memory. + */ + if (s2n_stuffer_space_remaining(message) < message_len) { + /* We want to avoid servers allocating memory in response to post-handshake messages + * to avoid a potential DDOS / resource exhaustion attack. + * + * Currently, s2n-tls servers only support the KeyUpdate message, + * which should never require additional memory to parse. + */ + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); + + uint32_t total_size = message_len + TLS_HANDSHAKE_HEADER_LENGTH; + if (message->alloced) { + RESULT_GUARD_POSIX(s2n_stuffer_resize(message, total_size)); + } else { + /* Manually convert our static stuffer to a growable stuffer */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(message, total_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(message, conn->post_handshake.header_in, TLS_HANDSHAKE_HEADER_LENGTH)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(message, TLS_HANDSHAKE_HEADER_LENGTH)); + } + } + + /* Try to copy the message body into the message stuffer. + * The message stuffer may already contain some of the message body if + * we have already read fragments from previous records. + */ + if (s2n_stuffer_data_available(message) < message_len) { + uint32_t remaining = message_len - s2n_stuffer_data_available(message); + uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); + } + RESULT_ENSURE(s2n_stuffer_data_available(message) == message_len, S2N_ERR_IO_BLOCKED); + + /* Now that the full message body is available, process it. */ + RESULT_GUARD(s2n_post_handshake_process(conn, message, message_type)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_post_handshake_recv(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + while (s2n_stuffer_data_available(&conn->in)) { + RESULT_GUARD(s2n_post_handshake_message_recv(conn)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->post_handshake.in)); + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_post_handshake_write_records(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + struct s2n_stuffer *message = &conn->handshake.io; + + /* Flush any existing records before we write a new handshake record. + * We do not support buffering multiple handshake records. + */ + if (s2n_stuffer_data_available(message)) { + RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); + } + + RESULT_GUARD(s2n_handshake_message_send(conn, TLS_HANDSHAKE, blocked)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(message)); + return S2N_RESULT_OK; +} + +int s2n_post_handshake_send(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + + /* Currently, we only support TLS1.3 post-handshake messages. */ + if (conn->actual_protocol_version < S2N_TLS13) { + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_post_handshake_write_records(conn, blocked)); + + POSIX_GUARD(s2n_key_update_send(conn, blocked)); + POSIX_GUARD_RESULT(s2n_tls13_server_nst_send(conn, blocked)); + + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); + return S2N_SUCCESS; +} diff --git a/tls/s2n_prf.c b/tls/s2n_prf.c index 931ea39dbf7..a352d10fb16 100644 --- a/tls/s2n_prf.c +++ b/tls/s2n_prf.c @@ -1,851 +1,852 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_prf.h" - -#include -#include -#include -#include - -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hash.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_prf_libcrypto.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto_constants.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* The s2n p_hash implementation is abstracted to allow for separate implementations. - * Currently the only implementation uses s2n-tls's custom HMAC implementation. - */ -struct s2n_p_hash_hmac { - int (*alloc)(struct s2n_prf_working_space *ws); - int (*init)(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret); - int (*update)(struct s2n_prf_working_space *ws, const void *data, uint32_t size); - int (*final)(struct s2n_prf_working_space *ws, void *digest, uint32_t size); - int (*reset)(struct s2n_prf_working_space *ws); - int (*cleanup)(struct s2n_prf_working_space *ws); - int (*free)(struct s2n_prf_working_space *ws); -}; - -S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, - s2n_hash_algorithm hash_alg, struct s2n_blob *output); -S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, - struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); - -S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(key_material); - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - RESULT_ENSURE_REF(cipher); - - uint8_t mac_size = 0; - uint32_t key_size = 0; - uint32_t iv_size = 0; - - /* MAC size */ - if (cipher->type == S2N_COMPOSITE) { - mac_size = cipher->io.comp.mac_key_size; - } else { - RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->record_alg->hmac_alg, &mac_size)); - } - - /* KEY size */ - key_size = cipher->key_material_size; - - /* Only AEAD ciphers have implicit IVs for TLS >= 1.1 */ - if (conn->actual_protocol_version <= S2N_TLS10 || cipher->type == S2N_AEAD) { - /* IV size */ - switch (cipher->type) { - case S2N_AEAD: - iv_size = cipher->io.aead.fixed_iv_size; - break; - case S2N_CBC: - iv_size = cipher->io.cbc.block_size; - break; - case S2N_COMPOSITE: - iv_size = cipher->io.comp.block_size; - break; - /* No-op for stream ciphers */ - default: - break; - } - } - - struct s2n_stuffer key_material_stuffer = { 0 }; - struct s2n_blob key_material_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&key_material_blob, key_material->key_block, sizeof(key_material->key_block))); - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&key_material_stuffer, &key_material_blob)); - - /* initialize key_material blobs; incrementing ptr to point to the next slice of memory */ - uint8_t *ptr = NULL; - /* MAC */ - ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_mac, ptr, mac_size)); - - ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_mac, ptr, mac_size)); - - /* KEY */ - ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_key, ptr, key_size)); - - ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_key, ptr, key_size)); - - /* IV */ - ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_iv, ptr, iv_size)); - - ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_iv, ptr, iv_size)); - - return S2N_RESULT_OK; -} - -/* SSLv3 PRF uses MD5 and SHA-1 in a custom hash-based construction (not - * HMAC). The use of weak hash algorithms is inherent to the SSLv3 protocol - * specification. SSLv3 is disabled by default and not recommended. - */ -static int s2n_prf_sslv3(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *seed_a, - struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - struct s2n_hash_state *workspace = &conn->handshake.hashes->hash_workspace; - - uint32_t outputlen = out->size; - uint8_t *output = out->data; - uint8_t iteration = 1; - - uint8_t md5_digest[MD5_DIGEST_LENGTH] = { 0 }, sha_digest[SHA_DIGEST_LENGTH] = { 0 }; - - uint8_t A = 'A'; - while (outputlen) { - struct s2n_hash_state *sha1 = workspace; - POSIX_GUARD(s2n_hash_reset(sha1)); - POSIX_GUARD(s2n_hash_init(sha1, S2N_HASH_SHA1)); - - for (int i = 0; i < iteration; i++) { - POSIX_GUARD(s2n_hash_update(sha1, &A, 1)); - } - - POSIX_GUARD(s2n_hash_update(sha1, secret->data, secret->size)); - POSIX_GUARD(s2n_hash_update(sha1, seed_a->data, seed_a->size)); - - if (seed_b) { - POSIX_GUARD(s2n_hash_update(sha1, seed_b->data, seed_b->size)); - if (seed_c) { - POSIX_GUARD(s2n_hash_update(sha1, seed_c->data, seed_c->size)); - } - } - - POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, sizeof(sha_digest))); - - struct s2n_hash_state *md5 = workspace; - POSIX_GUARD(s2n_hash_reset(md5)); - POSIX_GUARD(s2n_hash_init(md5, S2N_HASH_MD5)); - POSIX_GUARD(s2n_hash_update(md5, secret->data, secret->size)); - POSIX_GUARD(s2n_hash_update(md5, sha_digest, sizeof(sha_digest))); - POSIX_GUARD(s2n_hash_digest(md5, md5_digest, sizeof(md5_digest))); - - uint32_t bytes_to_copy = S2N_MIN(outputlen, sizeof(md5_digest)); - - POSIX_CHECKED_MEMCPY(output, md5_digest, bytes_to_copy); - - outputlen -= bytes_to_copy; - output += bytes_to_copy; - - /* Increment the letter */ - A++; - iteration++; - } - - return 0; -} - -static int s2n_hmac_p_hash_new(struct s2n_prf_working_space *ws) -{ - POSIX_GUARD(s2n_hmac_new(&ws->p_hash.s2n_hmac)); - return s2n_hmac_init(&ws->p_hash.s2n_hmac, S2N_HMAC_NONE, NULL, 0); -} - -static int s2n_hmac_p_hash_init(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret) -{ - return s2n_hmac_init(&ws->p_hash.s2n_hmac, alg, secret->data, secret->size); -} - -static int s2n_hmac_p_hash_update(struct s2n_prf_working_space *ws, const void *data, uint32_t size) -{ - return s2n_hmac_update(&ws->p_hash.s2n_hmac, data, size); -} - -static int s2n_hmac_p_hash_digest(struct s2n_prf_working_space *ws, void *digest, uint32_t size) -{ - return s2n_hmac_digest(&ws->p_hash.s2n_hmac, digest, size); -} - -static int s2n_hmac_p_hash_reset(struct s2n_prf_working_space *ws) -{ - /* If we actually initialized s2n_hmac, wipe it. - * A valid, initialized s2n_hmac_state will have a valid block size. - */ - if (ws->p_hash.s2n_hmac.hash_block_size != 0) { - return s2n_hmac_reset(&ws->p_hash.s2n_hmac); - } - return S2N_SUCCESS; -} - -static int s2n_hmac_p_hash_cleanup(struct s2n_prf_working_space *ws) -{ - return s2n_hmac_p_hash_reset(ws); -} - -static int s2n_hmac_p_hash_free(struct s2n_prf_working_space *ws) -{ - return s2n_hmac_free(&ws->p_hash.s2n_hmac); -} - -static const struct s2n_p_hash_hmac s2n_internal_p_hash_hmac = { - .alloc = &s2n_hmac_p_hash_new, - .init = &s2n_hmac_p_hash_init, - .update = &s2n_hmac_p_hash_update, - .final = &s2n_hmac_p_hash_digest, - .reset = &s2n_hmac_p_hash_reset, - .cleanup = &s2n_hmac_p_hash_cleanup, - .free = &s2n_hmac_p_hash_free, -}; - -/* - * For now, use the internal s2n-tls hmac abstraction. - * However, that is a custom implementation of hmac built on hashes. - * Ideally we should stop using our custom implementation here and switch - * to using a libcrypto implementation. Unfortunately, what each libcrypto - * can support varies a lot for HMACs. - * - * For historical reference, there used to be two other hmac implementations: - * https://github.com/aws/s2n-tls/blob/711ee0df658cd7c44088cf7a1b20a9f3cf5296d6/tls/s2n_prf.c#L174-L337 - * Both implementations have compatibility issues with one or more libcryptos. - */ -const struct s2n_p_hash_hmac *s2n_get_hmac_implementation() -{ - return &s2n_internal_p_hash_hmac; -} - -static int s2n_p_hash(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret, struct s2n_blob *label, - struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - uint8_t digest_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &digest_size)); - - const struct s2n_p_hash_hmac *hmac = s2n_get_hmac_implementation(); - POSIX_ENSURE_REF(hmac); - - /* First compute hmac(secret + A(0)) */ - POSIX_GUARD(hmac->init(ws, alg, secret)); - POSIX_GUARD(hmac->update(ws, label->data, label->size)); - POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); - - if (seed_b) { - POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); - if (seed_c) { - POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); - } - } - POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); - - uint32_t outputlen = out->size; - uint8_t *output = out->data; - - while (outputlen) { - /* Now compute hmac(secret + A(N - 1) + seed) */ - POSIX_GUARD(hmac->reset(ws)); - POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); - - /* Add the label + seed and compute this round's A */ - POSIX_GUARD(hmac->update(ws, label->data, label->size)); - POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); - if (seed_b) { - POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); - if (seed_c) { - POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); - } - } - - POSIX_GUARD(hmac->final(ws, ws->digest1, digest_size)); - - uint32_t bytes_to_xor = S2N_MIN(outputlen, digest_size); - - for (size_t i = 0; i < bytes_to_xor; i++) { - *output ^= ws->digest1[i]; - output++; - outputlen--; - } - - /* Stash a digest of A(N), in A(N), for the next round */ - POSIX_GUARD(hmac->reset(ws)); - POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); - POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); - } - - POSIX_GUARD(hmac->cleanup(ws)); - - return 0; -} - -S2N_RESULT s2n_prf_new(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_EQ(conn->prf_space, NULL); - - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_realloc(&mem, sizeof(struct s2n_prf_working_space))); - RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); - conn->prf_space = (struct s2n_prf_working_space *) (void *) mem.data; - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - /* Allocate the hmac state */ - const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); - RESULT_ENSURE_REF(hmac_impl); - RESULT_GUARD_POSIX(hmac_impl->alloc(conn->prf_space)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_wipe(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->prf_space); - - const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); - RESULT_ENSURE_REF(hmac_impl); - RESULT_GUARD_POSIX(hmac_impl->reset(conn->prf_space)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_free(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (conn->prf_space == NULL) { - return S2N_RESULT_OK; - } - - const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); - RESULT_ENSURE_REF(hmac_impl); - RESULT_GUARD_POSIX(hmac_impl->free(conn->prf_space)); - - RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &conn->prf_space, sizeof(struct s2n_prf_working_space))); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_custom(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, - struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - /* We zero the out blob because p_hash works by XOR'ing with the existing - * buffer. This is a little convoluted but means we can avoid dynamic memory - * allocation. When we call p_hash once (in the TLS1.2 case) it will produce - * the right values. When we call it twice in the regular case, the two - * outputs will be XORd just ass the TLS 1.0 and 1.1 RFCs require. - */ - RESULT_GUARD_POSIX(s2n_blob_zero(out)); - - if (conn->actual_protocol_version == S2N_TLS12) { - RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, conn->secure->cipher_suite->prf_alg, secret, label, seed_a, - seed_b, seed_c, out)); - return S2N_RESULT_OK; - } - - struct s2n_blob half_secret = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&half_secret, secret->data, (secret->size + 1) / 2)); - - RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_MD5, &half_secret, label, seed_a, seed_b, seed_c, out)); - half_secret.data += secret->size - half_secret.size; - RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_SHA1, &half_secret, label, seed_a, seed_b, seed_c, out)); - - return S2N_RESULT_OK; -} - -int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, struct s2n_blob *seed_a, - struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_REF(conn->prf_space); - POSIX_ENSURE_REF(secret); - POSIX_ENSURE_REF(label); - POSIX_ENSURE_REF(out); - - /* seed_a is always required, seed_b is optional, if seed_c is provided seed_b must also be provided */ - POSIX_ENSURE(seed_a != NULL, S2N_ERR_PRF_INVALID_SEED); - POSIX_ENSURE(S2N_IMPLIES(seed_c != NULL, seed_b != NULL), S2N_ERR_PRF_INVALID_SEED); - - if (conn->actual_protocol_version == S2N_SSLv3) { - POSIX_GUARD(s2n_prf_sslv3(conn, secret, seed_a, seed_b, seed_c, out)); - return S2N_SUCCESS; - } - - /* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the - * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. - */ - if (s2n_is_in_fips_mode()) { - POSIX_GUARD_RESULT(s2n_prf_libcrypto(conn, secret, label, seed_a, seed_b, seed_c, out)); - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_prf_custom(conn, secret, label, seed_a, seed_b, seed_c, out)); - - return S2N_SUCCESS; -} - -int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) -{ - POSIX_ENSURE_REF(conn); - - struct s2n_blob client_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); - struct s2n_blob server_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); - struct s2n_blob master_secret = { 0 }; - POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - uint8_t master_secret_label[] = "master secret"; - struct s2n_blob label = { 0 }; - POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); - - return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, NULL, &master_secret); -} - -int s2n_prf_hybrid_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) -{ - POSIX_ENSURE_REF(conn); - - struct s2n_blob client_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); - struct s2n_blob server_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); - struct s2n_blob master_secret = { 0 }; - POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - uint8_t master_secret_label[] = "hybrid master secret"; - struct s2n_blob label = { 0 }; - POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); - - return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, &conn->kex_params.client_key_exchange_message, &master_secret); -} - -int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_KEY); - - if (!conn->ems_negotiated) { - POSIX_GUARD(s2n_prf_tls_master_secret(conn, premaster_secret)); - return S2N_SUCCESS; - } - - /* Only the client writes the Client Key Exchange message */ - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); - } - struct s2n_stuffer client_key_message = conn->handshake.io; - POSIX_GUARD(s2n_stuffer_reread(&client_key_message)); - uint32_t client_key_message_size = s2n_stuffer_data_available(&client_key_message); - struct s2n_blob client_key_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_key_blob, client_key_message.blob.data, client_key_message_size)); - - uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob digest = { 0 }; - POSIX_GUARD(s2n_blob_init(&digest, data, sizeof(data))); - if (conn->actual_protocol_version < S2N_TLS12) { - uint8_t sha1_data[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob sha1_digest = { 0 }; - POSIX_GUARD(s2n_blob_init(&sha1_digest, sha1_data, sizeof(sha1_data))); - POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_MD5, &digest)); - POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_SHA1, &sha1_digest)); - POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, &sha1_digest)); - } else { - s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; - s2n_hash_algorithm hash_alg = 0; - POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); - POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, hash_alg, &digest)); - POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, NULL)); - } - return S2N_SUCCESS; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc7627#section-4 - *# When the extended master secret extension is negotiated in a full - *# handshake, the "master_secret" is computed as - *# - *# master_secret = PRF(pre_master_secret, "extended master secret", - *# session_hash) - *# [0..47]; - */ -S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_blob extended_master_secret = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&extended_master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - uint8_t extended_master_secret_label[] = "extended master secret"; - /* Subtract one from the label size to remove the "\0" */ - struct s2n_blob label = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&label, extended_master_secret_label, sizeof(extended_master_secret_label) - 1)); - - RESULT_GUARD_POSIX(s2n_prf(conn, premaster_secret, &label, session_hash, sha1_hash, NULL, &extended_master_secret)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->handshake.hashes); - RESULT_ENSURE_REF(message); - RESULT_ENSURE_REF(output); - - struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; - RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); - RESULT_GUARD_POSIX(s2n_hash_update(hash_state, message->data, message->size)); - - uint8_t digest_size = 0; - RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_alg, &digest_size)); - RESULT_ENSURE_GTE(output->size, digest_size); - RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, output->data, digest_size)); - output->size = digest_size; - - return S2N_RESULT_OK; -} - -static int s2n_prf_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], struct s2n_hash_state *hash_workspace, uint8_t *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - uint8_t xorpad1[48] = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; - uint8_t xorpad2[48] = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c }; - uint8_t *md5_digest = out; - uint8_t *sha_digest = out + MD5_DIGEST_LENGTH; - - POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH)); - - struct s2n_hash_state *md5 = hash_workspace; - POSIX_GUARD(s2n_hash_copy(md5, &conn->handshake.hashes->md5)); - POSIX_GUARD(s2n_hash_update(md5, prefix, 4)); - POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(md5, xorpad1, 48)); - POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(md5)); - POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(md5, xorpad2, 48)); - POSIX_GUARD(s2n_hash_update(md5, md5_digest, MD5_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(md5)); - - struct s2n_hash_state *sha1 = hash_workspace; - POSIX_GUARD(s2n_hash_copy(sha1, &conn->handshake.hashes->sha1)); - POSIX_GUARD(s2n_hash_update(sha1, prefix, 4)); - POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(sha1, xorpad1, 40)); - POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(sha1)); - POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(sha1, xorpad2, 40)); - POSIX_GUARD(s2n_hash_update(sha1, sha_digest, SHA_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(sha1)); - - return 0; -} - -static int s2n_prf_sslv3_client_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - uint8_t prefix[4] = { 0x43, 0x4c, 0x4e, 0x54 }; - - return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.client_finished); -} - -static int s2n_prf_sslv3_server_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - uint8_t prefix[4] = { 0x53, 0x52, 0x56, 0x52 }; - - return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.server_finished); -} - -int s2n_prf_client_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->handshake.hashes); - - struct s2n_blob master_secret, md5, sha; - uint8_t md5_digest[MD5_DIGEST_LENGTH]; - uint8_t sha_digest[SHA384_DIGEST_LENGTH]; - uint8_t client_finished_label[] = "client finished"; - struct s2n_blob client_finished = { 0 }; - struct s2n_blob label = { 0 }; - - if (conn->actual_protocol_version == S2N_SSLv3) { - return s2n_prf_sslv3_client_finished(conn); - } - - client_finished.data = conn->handshake.client_finished; - client_finished.size = S2N_TLS_FINISHED_LEN; - POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, client_finished.size)); - label.data = client_finished_label; - label.size = sizeof(client_finished_label) - 1; - - master_secret.data = conn->secrets.version.tls12.master_secret; - master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); - if (conn->actual_protocol_version == S2N_TLS12) { - switch (conn->secure->cipher_suite->prf_alg) { - case S2N_HMAC_SHA256: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); - sha.size = SHA256_DIGEST_LENGTH; - break; - case S2N_HMAC_SHA384: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); - sha.size = SHA384_DIGEST_LENGTH; - break; - default: - POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); - } - - sha.data = sha_digest; - return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &client_finished); - } - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); - md5.data = md5_digest; - md5.size = MD5_DIGEST_LENGTH; - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); - sha.data = sha_digest; - sha.size = SHA_DIGEST_LENGTH; - - return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &client_finished); -} - -int s2n_prf_server_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->handshake.hashes); - - struct s2n_blob master_secret, md5, sha; - uint8_t md5_digest[MD5_DIGEST_LENGTH]; - uint8_t sha_digest[SHA384_DIGEST_LENGTH]; - uint8_t server_finished_label[] = "server finished"; - struct s2n_blob server_finished = { 0 }; - struct s2n_blob label = { 0 }; - - if (conn->actual_protocol_version == S2N_SSLv3) { - return s2n_prf_sslv3_server_finished(conn); - } - - server_finished.data = conn->handshake.server_finished; - server_finished.size = S2N_TLS_FINISHED_LEN; - POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, server_finished.size)); - label.data = server_finished_label; - label.size = sizeof(server_finished_label) - 1; - - master_secret.data = conn->secrets.version.tls12.master_secret; - master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); - if (conn->actual_protocol_version == S2N_TLS12) { - switch (conn->secure->cipher_suite->prf_alg) { - case S2N_HMAC_SHA256: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); - sha.size = SHA256_DIGEST_LENGTH; - break; - case S2N_HMAC_SHA384: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); - sha.size = SHA384_DIGEST_LENGTH; - break; - default: - POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); - } - - sha.data = sha_digest; - return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &server_finished); - } - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); - md5.data = md5_digest; - md5.size = MD5_DIGEST_LENGTH; - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); - sha.data = sha_digest; - sha.size = SHA_DIGEST_LENGTH; - - return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &server_finished); -} - -static int s2n_prf_make_client_key(struct s2n_connection *conn, struct s2n_key_material *key_material) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - POSIX_ENSURE_REF(cipher); - POSIX_ENSURE_REF(cipher->set_encryption_key); - POSIX_ENSURE_REF(cipher->set_decryption_key); - - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->client_key, &key_material->client_key)); - } else { - POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->client_key, &key_material->client_key)); - } - - return 0; -} - -static int s2n_prf_make_server_key(struct s2n_connection *conn, struct s2n_key_material *key_material) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - POSIX_ENSURE_REF(cipher); - POSIX_ENSURE_REF(cipher->set_encryption_key); - POSIX_ENSURE_REF(cipher->set_decryption_key); - - if (conn->mode == S2N_SERVER) { - POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->server_key, &key_material->server_key)); - } else { - POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->server_key, &key_material->server_key)); - } - - return 0; -} - -S2N_RESULT s2n_prf_generate_key_material(struct s2n_connection *conn, struct s2n_key_material *key_material) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(key_material); - - struct s2n_blob client_random = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); - struct s2n_blob server_random = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); - struct s2n_blob master_secret = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - struct s2n_blob label = { 0 }; - uint8_t key_expansion_label[] = "key expansion"; - RESULT_GUARD_POSIX(s2n_blob_init(&label, key_expansion_label, sizeof(key_expansion_label) - 1)); - - RESULT_GUARD(s2n_key_material_init(key_material, conn)); - struct s2n_blob prf_out = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&prf_out, key_material->key_block, sizeof(key_material->key_block))); - RESULT_GUARD_POSIX(s2n_prf(conn, &master_secret, &label, &server_random, &client_random, NULL, &prf_out)); - - return S2N_RESULT_OK; -} - -int s2n_prf_key_expansion(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite; - POSIX_ENSURE_REF(cipher_suite); - POSIX_ENSURE_REF(cipher_suite->record_alg); - const struct s2n_cipher *cipher = cipher_suite->record_alg->cipher; - POSIX_ENSURE_REF(cipher); - - struct s2n_key_material key_material = { 0 }; - POSIX_GUARD_RESULT(s2n_prf_generate_key_material(conn, &key_material)); - - POSIX_ENSURE(cipher_suite->available, S2N_ERR_PRF_INVALID_ALGORITHM); - POSIX_GUARD_RESULT(cipher->init(&conn->secure->client_key)); - POSIX_GUARD_RESULT(cipher->init(&conn->secure->server_key)); - - /* Seed the client MAC */ - POSIX_GUARD(s2n_hmac_reset(&conn->secure->client_record_mac)); - POSIX_GUARD(s2n_hmac_init( - &conn->secure->client_record_mac, - cipher_suite->record_alg->hmac_alg, - key_material.client_mac.data, - key_material.client_mac.size)); - - /* Seed the server MAC */ - POSIX_GUARD(s2n_hmac_reset(&conn->secure->server_record_mac)); - POSIX_GUARD(s2n_hmac_init( - &conn->secure->server_record_mac, - conn->secure->cipher_suite->record_alg->hmac_alg, - key_material.server_mac.data, - key_material.server_mac.size)); - - /* Make the client key */ - POSIX_GUARD(s2n_prf_make_client_key(conn, &key_material)); - - /* Make the server key */ - POSIX_GUARD(s2n_prf_make_server_key(conn, &key_material)); - - /* Composite CBC does MAC inside the cipher, pass it the MAC key. - * Must happen after setting encryption/decryption keys. - */ - if (cipher->type == S2N_COMPOSITE) { - POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->client_key, key_material.client_mac.data, key_material.client_mac.size)); - POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->server_key, key_material.server_mac.data, key_material.server_mac.size)); - } - - /* set IV */ - POSIX_ENSURE_EQ(key_material.client_iv.size, key_material.server_iv.size); - POSIX_ENSURE_LTE(key_material.client_iv.size, S2N_TLS_MAX_IV_LEN); - POSIX_CHECKED_MEMCPY(conn->secure->client_implicit_iv, key_material.client_iv.data, key_material.client_iv.size); - POSIX_CHECKED_MEMCPY(conn->secure->server_implicit_iv, key_material.server_iv.data, key_material.server_iv.size); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_prf.h" + +#include +#include +#include +#include + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_prf_libcrypto.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto_constants.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* The s2n p_hash implementation is abstracted to allow for separate implementations. + * Currently the only implementation uses s2n-tls's custom HMAC implementation. + */ +struct s2n_p_hash_hmac { + int (*alloc)(struct s2n_prf_working_space *ws); + int (*init)(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret); + int (*update)(struct s2n_prf_working_space *ws, const void *data, uint32_t size); + int (*final)(struct s2n_prf_working_space *ws, void *digest, uint32_t size); + int (*reset)(struct s2n_prf_working_space *ws); + int (*cleanup)(struct s2n_prf_working_space *ws); + int (*free)(struct s2n_prf_working_space *ws); +}; + +S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, + s2n_hash_algorithm hash_alg, struct s2n_blob *output); +S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, + struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); + +S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(key_material); + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + RESULT_ENSURE_REF(cipher); + + uint8_t mac_size = 0; + uint32_t key_size = 0; + uint32_t iv_size = 0; + + /* MAC size */ + if (cipher->type == S2N_COMPOSITE) { + mac_size = cipher->io.comp.mac_key_size; + } else { + RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->record_alg->hmac_alg, &mac_size)); + } + + /* KEY size */ + key_size = cipher->key_material_size; + + /* Only AEAD ciphers have implicit IVs for TLS >= 1.1 */ + if (conn->actual_protocol_version <= S2N_TLS10 || cipher->type == S2N_AEAD) { + /* IV size */ + switch (cipher->type) { + case S2N_AEAD: + iv_size = cipher->io.aead.fixed_iv_size; + break; + case S2N_CBC: + iv_size = cipher->io.cbc.block_size; + break; + case S2N_COMPOSITE: + iv_size = cipher->io.comp.block_size; + break; + /* No-op for stream ciphers */ + default: + break; + } + } + + struct s2n_stuffer key_material_stuffer = { 0 }; + struct s2n_blob key_material_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&key_material_blob, key_material->key_block, sizeof(key_material->key_block))); + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&key_material_stuffer, &key_material_blob)); + + /* initialize key_material blobs; incrementing ptr to point to the next slice of memory */ + uint8_t *ptr = NULL; + /* MAC */ + ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_mac, ptr, mac_size)); + + ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_mac, ptr, mac_size)); + + /* KEY */ + ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_key, ptr, key_size)); + + ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_key, ptr, key_size)); + + /* IV */ + ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_iv, ptr, iv_size)); + + ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_iv, ptr, iv_size)); + + return S2N_RESULT_OK; +} + +/* SSLv3 PRF uses MD5 and SHA-1 in a custom hash-based construction (not + * HMAC). The use of weak hash algorithms is inherent to the SSLv3 protocol + * specification. SSLv3 is disabled by default and not recommended. + */ +static int s2n_prf_sslv3(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *seed_a, + struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + struct s2n_hash_state *workspace = &conn->handshake.hashes->hash_workspace; + + uint32_t outputlen = out->size; + uint8_t *output = out->data; + uint8_t iteration = 1; + + uint8_t md5_digest[MD5_DIGEST_LENGTH] = { 0 }, sha_digest[SHA_DIGEST_LENGTH] = { 0 }; + + uint8_t A = 'A'; + while (outputlen) { + struct s2n_hash_state *sha1 = workspace; + POSIX_GUARD(s2n_hash_reset(sha1)); + POSIX_GUARD(s2n_hash_init(sha1, S2N_HASH_SHA1)); + + for (int i = 0; i < iteration; i++) { + POSIX_GUARD(s2n_hash_update(sha1, &A, 1)); + } + + POSIX_GUARD(s2n_hash_update(sha1, secret->data, secret->size)); + POSIX_GUARD(s2n_hash_update(sha1, seed_a->data, seed_a->size)); + + if (seed_b) { + POSIX_GUARD(s2n_hash_update(sha1, seed_b->data, seed_b->size)); + if (seed_c) { + POSIX_GUARD(s2n_hash_update(sha1, seed_c->data, seed_c->size)); + } + } + + POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, sizeof(sha_digest))); + + struct s2n_hash_state *md5 = workspace; + POSIX_GUARD(s2n_hash_reset(md5)); + POSIX_GUARD(s2n_hash_init(md5, S2N_HASH_MD5)); + POSIX_GUARD(s2n_hash_update(md5, secret->data, secret->size)); + POSIX_GUARD(s2n_hash_update(md5, sha_digest, sizeof(sha_digest))); + POSIX_GUARD(s2n_hash_digest(md5, md5_digest, sizeof(md5_digest))); + + uint32_t bytes_to_copy = S2N_MIN(outputlen, sizeof(md5_digest)); + + POSIX_CHECKED_MEMCPY(output, md5_digest, bytes_to_copy); + + outputlen -= bytes_to_copy; + output += bytes_to_copy; + + /* Increment the letter */ + A++; + iteration++; + } + + return 0; +} + +static int s2n_hmac_p_hash_new(struct s2n_prf_working_space *ws) +{ + POSIX_GUARD(s2n_hmac_new(&ws->p_hash.s2n_hmac)); + return s2n_hmac_init(&ws->p_hash.s2n_hmac, S2N_HMAC_NONE, NULL, 0); +} + +static int s2n_hmac_p_hash_init(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret) +{ + return s2n_hmac_init(&ws->p_hash.s2n_hmac, alg, secret->data, secret->size); +} + +static int s2n_hmac_p_hash_update(struct s2n_prf_working_space *ws, const void *data, uint32_t size) +{ + return s2n_hmac_update(&ws->p_hash.s2n_hmac, data, size); +} + +static int s2n_hmac_p_hash_digest(struct s2n_prf_working_space *ws, void *digest, uint32_t size) +{ + return s2n_hmac_digest(&ws->p_hash.s2n_hmac, digest, size); +} + +static int s2n_hmac_p_hash_reset(struct s2n_prf_working_space *ws) +{ + /* If we actually initialized s2n_hmac, wipe it. + * A valid, initialized s2n_hmac_state will have a valid block size. + */ + if (ws->p_hash.s2n_hmac.hash_block_size != 0) { + return s2n_hmac_reset(&ws->p_hash.s2n_hmac); + } + return S2N_SUCCESS; +} + +static int s2n_hmac_p_hash_cleanup(struct s2n_prf_working_space *ws) +{ + return s2n_hmac_p_hash_reset(ws); +} + +static int s2n_hmac_p_hash_free(struct s2n_prf_working_space *ws) +{ + return s2n_hmac_free(&ws->p_hash.s2n_hmac); +} + +static const struct s2n_p_hash_hmac s2n_internal_p_hash_hmac = { + .alloc = &s2n_hmac_p_hash_new, + .init = &s2n_hmac_p_hash_init, + .update = &s2n_hmac_p_hash_update, + .final = &s2n_hmac_p_hash_digest, + .reset = &s2n_hmac_p_hash_reset, + .cleanup = &s2n_hmac_p_hash_cleanup, + .free = &s2n_hmac_p_hash_free, +}; + +/* + * For now, use the internal s2n-tls hmac abstraction. + * However, that is a custom implementation of hmac built on hashes. + * Ideally we should stop using our custom implementation here and switch + * to using a libcrypto implementation. Unfortunately, what each libcrypto + * can support varies a lot for HMACs. + * + * For historical reference, there used to be two other hmac implementations: + * https://github.com/aws/s2n-tls/blob/711ee0df658cd7c44088cf7a1b20a9f3cf5296d6/tls/s2n_prf.c#L174-L337 + * Both implementations have compatibility issues with one or more libcryptos. + */ +const struct s2n_p_hash_hmac *s2n_get_hmac_implementation() +{ + return &s2n_internal_p_hash_hmac; +} + +static int s2n_p_hash(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret, struct s2n_blob *label, + struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + uint8_t digest_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &digest_size)); + + const struct s2n_p_hash_hmac *hmac = s2n_get_hmac_implementation(); + POSIX_ENSURE_REF(hmac); + + /* First compute hmac(secret + A(0)) */ + POSIX_GUARD(hmac->init(ws, alg, secret)); + POSIX_GUARD(hmac->update(ws, label->data, label->size)); + POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); + + if (seed_b) { + POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); + if (seed_c) { + POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); + } + } + POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); + + uint32_t outputlen = out->size; + uint8_t *output = out->data; + + while (outputlen) { + /* Now compute hmac(secret + A(N - 1) + seed) */ + POSIX_GUARD(hmac->reset(ws)); + POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); + + /* Add the label + seed and compute this round's A */ + POSIX_GUARD(hmac->update(ws, label->data, label->size)); + POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); + if (seed_b) { + POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); + if (seed_c) { + POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); + } + } + + POSIX_GUARD(hmac->final(ws, ws->digest1, digest_size)); + + uint32_t bytes_to_xor = S2N_MIN(outputlen, digest_size); + + for (size_t i = 0; i < bytes_to_xor; i++) { + *output ^= ws->digest1[i]; + output++; + outputlen--; + } + + /* Stash a digest of A(N), in A(N), for the next round */ + POSIX_GUARD(hmac->reset(ws)); + POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); + POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); + } + + POSIX_GUARD(hmac->cleanup(ws)); + + return 0; +} + +S2N_RESULT s2n_prf_new(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_EQ(conn->prf_space, NULL); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_realloc(&mem, sizeof(struct s2n_prf_working_space))); + RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); + conn->prf_space = (struct s2n_prf_working_space *) (void *) mem.data; + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + /* Allocate the hmac state */ + const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); + RESULT_ENSURE_REF(hmac_impl); + RESULT_GUARD_POSIX(hmac_impl->alloc(conn->prf_space)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_wipe(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->prf_space); + + const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); + RESULT_ENSURE_REF(hmac_impl); + RESULT_GUARD_POSIX(hmac_impl->reset(conn->prf_space)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_free(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (conn->prf_space == NULL) { + return S2N_RESULT_OK; + } + + const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); + RESULT_ENSURE_REF(hmac_impl); + RESULT_GUARD_POSIX(hmac_impl->free(conn->prf_space)); + + RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &conn->prf_space, sizeof(struct s2n_prf_working_space))); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_custom(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, + struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + /* We zero the out blob because p_hash works by XOR'ing with the existing + * buffer. This is a little convoluted but means we can avoid dynamic memory + * allocation. When we call p_hash once (in the TLS1.2 case) it will produce + * the right values. When we call it twice in the regular case, the two + * outputs will be XORd just ass the TLS 1.0 and 1.1 RFCs require. + */ + RESULT_GUARD_POSIX(s2n_blob_zero(out)); + + if (conn->actual_protocol_version == S2N_TLS12) { + RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, conn->secure->cipher_suite->prf_alg, secret, label, seed_a, + seed_b, seed_c, out)); + return S2N_RESULT_OK; + } + + struct s2n_blob half_secret = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&half_secret, secret->data, (secret->size + 1) / 2)); + + RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_MD5, &half_secret, label, seed_a, seed_b, seed_c, out)); + half_secret.data += secret->size - half_secret.size; + RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_SHA1, &half_secret, label, seed_a, seed_b, seed_c, out)); + + return S2N_RESULT_OK; +} + +int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, struct s2n_blob *seed_a, + struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->prf_space); + POSIX_ENSURE_REF(secret); + POSIX_ENSURE_REF(label); + POSIX_ENSURE_REF(out); + + /* seed_a is always required, seed_b is optional, if seed_c is provided seed_b must also be provided */ + POSIX_ENSURE(seed_a != NULL, S2N_ERR_PRF_INVALID_SEED); + POSIX_ENSURE(S2N_IMPLIES(seed_c != NULL, seed_b != NULL), S2N_ERR_PRF_INVALID_SEED); + + if (conn->actual_protocol_version == S2N_SSLv3) { + POSIX_GUARD(s2n_prf_sslv3(conn, secret, seed_a, seed_b, seed_c, out)); + return S2N_SUCCESS; + } + + /* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the + * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. + */ + if (s2n_is_in_fips_mode()) { + POSIX_GUARD_RESULT(s2n_prf_libcrypto(conn, secret, label, seed_a, seed_b, seed_c, out)); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_prf_custom(conn, secret, label, seed_a, seed_b, seed_c, out)); + + return S2N_SUCCESS; +} + +int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) +{ + POSIX_ENSURE_REF(conn); + + struct s2n_blob client_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); + struct s2n_blob server_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); + struct s2n_blob master_secret = { 0 }; + POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + uint8_t master_secret_label[] = "master secret"; + struct s2n_blob label = { 0 }; + POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); + + return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, NULL, &master_secret); +} + +int s2n_prf_hybrid_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) +{ + POSIX_ENSURE_REF(conn); + + struct s2n_blob client_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); + struct s2n_blob server_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); + struct s2n_blob master_secret = { 0 }; + POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + uint8_t master_secret_label[] = "hybrid master secret"; + struct s2n_blob label = { 0 }; + POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); + + return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, &conn->kex_params.client_key_exchange_message, &master_secret); +} + +int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_KEY); + + if (!conn->ems_negotiated) { + POSIX_GUARD(s2n_prf_tls_master_secret(conn, premaster_secret)); + return S2N_SUCCESS; + } + + /* Only the client writes the Client Key Exchange message */ + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); + } + struct s2n_stuffer client_key_message = conn->handshake.io; + POSIX_GUARD(s2n_stuffer_reread(&client_key_message)); + uint32_t client_key_message_size = s2n_stuffer_data_available(&client_key_message); + struct s2n_blob client_key_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_key_blob, client_key_message.blob.data, client_key_message_size)); + + uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest = { 0 }; + POSIX_GUARD(s2n_blob_init(&digest, data, sizeof(data))); + if (conn->actual_protocol_version < S2N_TLS12) { + uint8_t sha1_data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob sha1_digest = { 0 }; + POSIX_GUARD(s2n_blob_init(&sha1_digest, sha1_data, sizeof(sha1_data))); + POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_MD5, &digest)); + POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_SHA1, &sha1_digest)); + POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, &sha1_digest)); + } else { + s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg = 0; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, hash_alg, &digest)); + POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, NULL)); + } + return S2N_SUCCESS; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc7627#section-4 + *# When the extended master secret extension is negotiated in a full + *# handshake, the "master_secret" is computed as + *# + *# master_secret = PRF(pre_master_secret, "extended master secret", + *# session_hash) + *# [0..47]; + */ +S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_blob extended_master_secret = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&extended_master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + uint8_t extended_master_secret_label[] = "extended master secret"; + /* Subtract one from the label size to remove the "\0" */ + struct s2n_blob label = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&label, extended_master_secret_label, sizeof(extended_master_secret_label) - 1)); + + RESULT_GUARD_POSIX(s2n_prf(conn, premaster_secret, &label, session_hash, sha1_hash, NULL, &extended_master_secret)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->handshake.hashes); + RESULT_ENSURE_REF(message); + RESULT_ENSURE_REF(output); + + struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; + RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); + RESULT_GUARD_POSIX(s2n_hash_update(hash_state, message->data, message->size)); + + uint8_t digest_size = 0; + RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_alg, &digest_size)); + RESULT_ENSURE_GTE(output->size, digest_size); + RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, output->data, digest_size)); + output->size = digest_size; + + return S2N_RESULT_OK; +} + +static int s2n_prf_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], struct s2n_hash_state *hash_workspace, uint8_t *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + uint8_t xorpad1[48] = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; + uint8_t xorpad2[48] = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c }; + uint8_t *md5_digest = out; + uint8_t *sha_digest = out + MD5_DIGEST_LENGTH; + + POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH)); + + struct s2n_hash_state *md5 = hash_workspace; + POSIX_GUARD(s2n_hash_copy(md5, &conn->handshake.hashes->md5)); + POSIX_GUARD(s2n_hash_update(md5, prefix, 4)); + POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(md5, xorpad1, 48)); + POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(md5)); + POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(md5, xorpad2, 48)); + POSIX_GUARD(s2n_hash_update(md5, md5_digest, MD5_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(md5)); + + struct s2n_hash_state *sha1 = hash_workspace; + POSIX_GUARD(s2n_hash_copy(sha1, &conn->handshake.hashes->sha1)); + POSIX_GUARD(s2n_hash_update(sha1, prefix, 4)); + POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(sha1, xorpad1, 40)); + POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(sha1)); + POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(sha1, xorpad2, 40)); + POSIX_GUARD(s2n_hash_update(sha1, sha_digest, SHA_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(sha1)); + + return 0; +} + +static int s2n_prf_sslv3_client_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + uint8_t prefix[4] = { 0x43, 0x4c, 0x4e, 0x54 }; + + return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.client_finished); +} + +static int s2n_prf_sslv3_server_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + uint8_t prefix[4] = { 0x53, 0x52, 0x56, 0x52 }; + + return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.server_finished); +} + +int s2n_prf_client_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->handshake.hashes); + + struct s2n_blob master_secret, md5, sha; + uint8_t md5_digest[MD5_DIGEST_LENGTH]; + uint8_t sha_digest[SHA384_DIGEST_LENGTH]; + uint8_t client_finished_label[] = "client finished"; + struct s2n_blob client_finished = { 0 }; + struct s2n_blob label = { 0 }; + + if (conn->actual_protocol_version == S2N_SSLv3) { + return s2n_prf_sslv3_client_finished(conn); + } + + client_finished.data = conn->handshake.client_finished; + client_finished.size = S2N_TLS_FINISHED_LEN; + POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, client_finished.size)); + label.data = client_finished_label; + label.size = sizeof(client_finished_label) - 1; + + master_secret.data = conn->secrets.version.tls12.master_secret; + master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); + if (conn->actual_protocol_version == S2N_TLS12) { + switch (conn->secure->cipher_suite->prf_alg) { + case S2N_HMAC_SHA256: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); + sha.size = SHA256_DIGEST_LENGTH; + break; + case S2N_HMAC_SHA384: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); + sha.size = SHA384_DIGEST_LENGTH; + break; + default: + POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); + } + + sha.data = sha_digest; + return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &client_finished); + } + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); + md5.data = md5_digest; + md5.size = MD5_DIGEST_LENGTH; + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); + sha.data = sha_digest; + sha.size = SHA_DIGEST_LENGTH; + + return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &client_finished); +} + +int s2n_prf_server_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->handshake.hashes); + + struct s2n_blob master_secret, md5, sha; + uint8_t md5_digest[MD5_DIGEST_LENGTH]; + uint8_t sha_digest[SHA384_DIGEST_LENGTH]; + uint8_t server_finished_label[] = "server finished"; + struct s2n_blob server_finished = { 0 }; + struct s2n_blob label = { 0 }; + + if (conn->actual_protocol_version == S2N_SSLv3) { + return s2n_prf_sslv3_server_finished(conn); + } + + server_finished.data = conn->handshake.server_finished; + server_finished.size = S2N_TLS_FINISHED_LEN; + POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, server_finished.size)); + label.data = server_finished_label; + label.size = sizeof(server_finished_label) - 1; + + master_secret.data = conn->secrets.version.tls12.master_secret; + master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); + if (conn->actual_protocol_version == S2N_TLS12) { + switch (conn->secure->cipher_suite->prf_alg) { + case S2N_HMAC_SHA256: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); + sha.size = SHA256_DIGEST_LENGTH; + break; + case S2N_HMAC_SHA384: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); + sha.size = SHA384_DIGEST_LENGTH; + break; + default: + POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); + } + + sha.data = sha_digest; + return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &server_finished); + } + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); + md5.data = md5_digest; + md5.size = MD5_DIGEST_LENGTH; + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); + sha.data = sha_digest; + sha.size = SHA_DIGEST_LENGTH; + + return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &server_finished); +} + +static int s2n_prf_make_client_key(struct s2n_connection *conn, struct s2n_key_material *key_material) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + POSIX_ENSURE_REF(cipher); + POSIX_ENSURE_REF(cipher->set_encryption_key); + POSIX_ENSURE_REF(cipher->set_decryption_key); + + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->client_key, &key_material->client_key)); + } else { + POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->client_key, &key_material->client_key)); + } + + return 0; +} + +static int s2n_prf_make_server_key(struct s2n_connection *conn, struct s2n_key_material *key_material) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + POSIX_ENSURE_REF(cipher); + POSIX_ENSURE_REF(cipher->set_encryption_key); + POSIX_ENSURE_REF(cipher->set_decryption_key); + + if (conn->mode == S2N_SERVER) { + POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->server_key, &key_material->server_key)); + } else { + POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->server_key, &key_material->server_key)); + } + + return 0; +} + +S2N_RESULT s2n_prf_generate_key_material(struct s2n_connection *conn, struct s2n_key_material *key_material) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(key_material); + + struct s2n_blob client_random = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); + struct s2n_blob server_random = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); + struct s2n_blob master_secret = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + struct s2n_blob label = { 0 }; + uint8_t key_expansion_label[] = "key expansion"; + RESULT_GUARD_POSIX(s2n_blob_init(&label, key_expansion_label, sizeof(key_expansion_label) - 1)); + + RESULT_GUARD(s2n_key_material_init(key_material, conn)); + struct s2n_blob prf_out = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&prf_out, key_material->key_block, sizeof(key_material->key_block))); + RESULT_GUARD_POSIX(s2n_prf(conn, &master_secret, &label, &server_random, &client_random, NULL, &prf_out)); + + return S2N_RESULT_OK; +} + +int s2n_prf_key_expansion(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite; + POSIX_ENSURE_REF(cipher_suite); + POSIX_ENSURE_REF(cipher_suite->record_alg); + const struct s2n_cipher *cipher = cipher_suite->record_alg->cipher; + POSIX_ENSURE_REF(cipher); + + struct s2n_key_material key_material = { 0 }; + POSIX_GUARD_RESULT(s2n_prf_generate_key_material(conn, &key_material)); + + POSIX_ENSURE(cipher_suite->available, S2N_ERR_PRF_INVALID_ALGORITHM); + POSIX_GUARD_RESULT(cipher->init(&conn->secure->client_key)); + POSIX_GUARD_RESULT(cipher->init(&conn->secure->server_key)); + + /* Seed the client MAC */ + POSIX_GUARD(s2n_hmac_reset(&conn->secure->client_record_mac)); + POSIX_GUARD(s2n_hmac_init( + &conn->secure->client_record_mac, + cipher_suite->record_alg->hmac_alg, + key_material.client_mac.data, + key_material.client_mac.size)); + + /* Seed the server MAC */ + POSIX_GUARD(s2n_hmac_reset(&conn->secure->server_record_mac)); + POSIX_GUARD(s2n_hmac_init( + &conn->secure->server_record_mac, + conn->secure->cipher_suite->record_alg->hmac_alg, + key_material.server_mac.data, + key_material.server_mac.size)); + + /* Make the client key */ + POSIX_GUARD(s2n_prf_make_client_key(conn, &key_material)); + + /* Make the server key */ + POSIX_GUARD(s2n_prf_make_server_key(conn, &key_material)); + + /* Composite CBC does MAC inside the cipher, pass it the MAC key. + * Must happen after setting encryption/decryption keys. + */ + if (cipher->type == S2N_COMPOSITE) { + POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->client_key, key_material.client_mac.data, key_material.client_mac.size)); + POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->server_key, key_material.server_mac.data, key_material.server_mac.size)); + } + + /* set IV */ + POSIX_ENSURE_EQ(key_material.client_iv.size, key_material.server_iv.size); + POSIX_ENSURE_LTE(key_material.client_iv.size, S2N_TLS_MAX_IV_LEN); + POSIX_CHECKED_MEMCPY(conn->secure->client_implicit_iv, key_material.client_iv.data, key_material.client_iv.size); + POSIX_CHECKED_MEMCPY(conn->secure->server_implicit_iv, key_material.server_iv.data, key_material.server_iv.size); + + return 0; +} diff --git a/tls/s2n_psk.c b/tls/s2n_psk.c index 25b66a2715f..f25d89d0e85 100644 --- a/tls/s2n_psk.c +++ b/tls/s2n_psk.c @@ -1,711 +1,712 @@ -/* - * 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 "crypto/s2n_tls13_keys.h" -#include "tls/extensions/s2n_extension_type.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls13_secrets.h" -#include "utils/s2n_array.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_psk_init(struct s2n_psk *psk, s2n_psk_type type) -{ - RESULT_ENSURE_MUT(psk); - - RESULT_CHECKED_MEMSET(psk, 0, sizeof(struct s2n_psk)); - psk->hmac_alg = S2N_HMAC_SHA256; - psk->type = type; - - return S2N_RESULT_OK; -} - -struct s2n_psk *s2n_external_psk_new() -{ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_psk))); - - struct s2n_psk *psk = (struct s2n_psk *) (void *) mem.data; - PTR_GUARD_RESULT(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); - - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - return psk; -} - -int s2n_psk_set_identity(struct s2n_psk *psk, const uint8_t *identity, uint16_t identity_size) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(identity); - POSIX_ENSURE(identity_size != 0, S2N_ERR_INVALID_ARGUMENT); - - POSIX_GUARD(s2n_realloc(&psk->identity, identity_size)); - POSIX_CHECKED_MEMCPY(psk->identity.data, identity, identity_size); - - return S2N_SUCCESS; -} - -int s2n_psk_set_secret(struct s2n_psk *psk, const uint8_t *secret, uint16_t secret_size) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(secret); - POSIX_ENSURE(secret_size != 0, S2N_ERR_INVALID_ARGUMENT); - - /* There are a number of application level errors that might result in an - * all-zero secret accidentally getting used. Error if that happens. - */ - bool secret_is_all_zero = true; - for (uint16_t i = 0; i < secret_size; i++) { - secret_is_all_zero = secret_is_all_zero && secret[i] == 0; - } - POSIX_ENSURE(!secret_is_all_zero, S2N_ERR_INVALID_ARGUMENT); - - POSIX_GUARD(s2n_realloc(&psk->secret, secret_size)); - POSIX_CHECKED_MEMCPY(psk->secret.data, secret, secret_size); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_psk_clone(struct s2n_psk *new_psk, struct s2n_psk *original_psk) -{ - if (original_psk == NULL) { - return S2N_RESULT_OK; - } - RESULT_ENSURE_REF(new_psk); - - struct s2n_psk psk_copy = *new_psk; - - /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ - *new_psk = *original_psk; - new_psk->identity = psk_copy.identity; - new_psk->secret = psk_copy.secret; - new_psk->early_secret = psk_copy.early_secret; - new_psk->early_data_config = psk_copy.early_data_config; - - /* Clone / realloc blobs */ - RESULT_GUARD_POSIX(s2n_psk_set_identity(new_psk, original_psk->identity.data, original_psk->identity.size)); - RESULT_GUARD_POSIX(s2n_psk_set_secret(new_psk, original_psk->secret.data, original_psk->secret.size)); - RESULT_GUARD_POSIX(s2n_realloc(&new_psk->early_secret, original_psk->early_secret.size)); - RESULT_CHECKED_MEMCPY(new_psk->early_secret.data, original_psk->early_secret.data, original_psk->early_secret.size); - RESULT_GUARD(s2n_early_data_config_clone(new_psk, &original_psk->early_data_config)); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_psk_wipe(struct s2n_psk *psk) -{ - if (psk == NULL) { - return S2N_RESULT_OK; - } - - RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); - RESULT_GUARD_POSIX(s2n_free(&psk->identity)); - RESULT_GUARD_POSIX(s2n_free(&psk->secret)); - RESULT_GUARD(s2n_early_data_config_free(&psk->early_data_config)); - - return S2N_RESULT_OK; -} - -int s2n_psk_free(struct s2n_psk **psk) -{ - if (psk == NULL) { - return S2N_SUCCESS; - } - POSIX_GUARD_RESULT(s2n_psk_wipe(*psk)); - return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_psk)); -} - -S2N_RESULT s2n_psk_parameters_init(struct s2n_psk_parameters *params) -{ - RESULT_ENSURE_REF(params); - RESULT_CHECKED_MEMSET(params, 0, sizeof(struct s2n_psk_parameters)); - RESULT_GUARD(s2n_array_init(¶ms->psk_list, sizeof(struct s2n_psk))); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_psk_offered_psk_size(struct s2n_psk *psk, uint32_t *size) -{ - *size = sizeof(uint16_t) /* identity size */ - + sizeof(uint32_t) /* obfuscated ticket age */ - + sizeof(uint8_t); /* binder size */ - - RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk->identity.size, size)); - - uint8_t binder_size = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &binder_size)); - RESULT_GUARD_POSIX(s2n_add_overflow(*size, binder_size, size)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_psk_parameters_offered_psks_size(struct s2n_psk_parameters *params, uint32_t *size) -{ - RESULT_ENSURE_REF(params); - RESULT_ENSURE_REF(size); - - *size = sizeof(uint16_t) /* identity list size */ - + sizeof(uint16_t) /* binder list size */; - - for (uint32_t i = 0; i < params->psk_list.len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - - uint32_t psk_size = 0; - RESULT_GUARD(s2n_psk_offered_psk_size(psk, &psk_size)); - RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk_size, size)); - } - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_psk_parameters_wipe(struct s2n_psk_parameters *params) -{ - RESULT_ENSURE_REF(params); - - for (size_t i = 0; i < params->psk_list.len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); - RESULT_GUARD(s2n_psk_wipe(psk)); - } - RESULT_GUARD_POSIX(s2n_free(¶ms->psk_list.mem)); - RESULT_GUARD(s2n_psk_parameters_init(params)); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_psk_parameters_wipe_secrets(struct s2n_psk_parameters *params) -{ - RESULT_ENSURE_REF(params); - - for (size_t i = 0; i < params->psk_list.len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); - RESULT_GUARD_POSIX(s2n_free(&psk->secret)); - } - - return S2N_RESULT_OK; -} - -bool s2n_offered_psk_list_has_next(struct s2n_offered_psk_list *psk_list) -{ - return psk_list != NULL && s2n_stuffer_data_available(&psk_list->wire_data) > 0; -} - -S2N_RESULT s2n_offered_psk_list_read_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) -{ - RESULT_ENSURE_REF(psk_list); - RESULT_ENSURE_REF(psk_list->conn); - RESULT_ENSURE_MUT(psk); - - uint16_t identity_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&psk_list->wire_data, &identity_size)); - RESULT_ENSURE_GT(identity_size, 0); - - uint8_t *identity_data = NULL; - identity_data = s2n_stuffer_raw_read(&psk_list->wire_data, identity_size); - RESULT_ENSURE_REF(identity_data); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# For identities established externally, an obfuscated_ticket_age of 0 SHOULD be - *# used, and servers MUST ignore the value. - */ - if (psk_list->conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&psk_list->wire_data, sizeof(uint32_t))); - } else { - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&psk_list->wire_data, &psk->obfuscated_ticket_age)); - } - - RESULT_GUARD_POSIX(s2n_blob_init(&psk->identity, identity_data, identity_size)); - psk->wire_index = psk_list->wire_index; - - RESULT_ENSURE(psk_list->wire_index < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); - psk_list->wire_index++; - return S2N_RESULT_OK; -} - -int s2n_offered_psk_list_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) -{ - POSIX_ENSURE_REF(psk_list); - POSIX_ENSURE_REF(psk); - *psk = (struct s2n_offered_psk){ 0 }; - POSIX_ENSURE(s2n_offered_psk_list_has_next(psk_list), S2N_ERR_STUFFER_OUT_OF_DATA); - POSIX_ENSURE(s2n_result_is_ok(s2n_offered_psk_list_read_next(psk_list, psk)), S2N_ERR_BAD_MESSAGE); - return S2N_SUCCESS; -} - -int s2n_offered_psk_list_reread(struct s2n_offered_psk_list *psk_list) -{ - POSIX_ENSURE_REF(psk_list); - psk_list->wire_index = 0; - return s2n_stuffer_reread(&psk_list->wire_data); -} - -/* Match a PSK identity received from the client against the server's known PSK identities. - * This method compares a single client identity to all server identities. - * - * While both the client's offered identities and whether a match was found are public, we should make an attempt - * to keep the server's known identities a secret. We will make comparisons to the server's identities constant - * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). - * - * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, - * and an attacker could probably guess the server's known identities just by observing the public identities - * sent by clients. - */ -static S2N_RESULT s2n_match_psk_identity(struct s2n_array *known_psks, const struct s2n_blob *wire_identity, - struct s2n_psk **match) -{ - RESULT_ENSURE_REF(match); - RESULT_ENSURE_REF(wire_identity); - RESULT_ENSURE_REF(known_psks); - *match = NULL; - for (size_t i = 0; i < known_psks->len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(known_psks, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - RESULT_ENSURE_REF(psk->identity.data); - RESULT_ENSURE_REF(wire_identity->data); - uint32_t compare_size = S2N_MIN(wire_identity->size, psk->identity.size); - if (s2n_constant_time_equals(psk->identity.data, wire_identity->data, compare_size) - & (psk->identity.size == wire_identity->size) & (!*match)) { - *match = psk; - } - } - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# For PSKs provisioned via NewSessionTicket, a server MUST validate - *# that the ticket age for the selected PSK identity (computed by - *# subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age - *# modulo 2^32) is within a small tolerance of the time since the ticket - *# was issued (see Section 8). - **/ -static S2N_RESULT s2n_validate_ticket_lifetime(struct s2n_connection *conn, uint32_t obfuscated_ticket_age, uint32_t ticket_age_add) -{ - RESULT_ENSURE_REF(conn); - - if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { - return S2N_RESULT_OK; - } - - /* Subtract the ticket_age_add value from the ticket age in milliseconds. The resulting uint32_t value - * may wrap, resulting in the modulo 2^32 operation. */ - uint32_t ticket_age_in_millis = obfuscated_ticket_age - ticket_age_add; - uint32_t session_lifetime_in_millis = conn->config->session_state_lifetime_in_nanos / ONE_MILLISEC_IN_NANOS; - RESULT_ENSURE(ticket_age_in_millis < session_lifetime_in_millis, S2N_ERR_INVALID_SESSION_TICKET); - - return S2N_RESULT_OK; -} - -int s2n_offered_psk_list_choose_psk(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) -{ - POSIX_ENSURE_REF(psk_list); - POSIX_ENSURE_REF(psk_list->conn); - - struct s2n_psk_parameters *psk_params = &psk_list->conn->psk_params; - struct s2n_stuffer ticket_stuffer = { 0 }; - - if (!psk) { - psk_params->chosen_psk = NULL; - return S2N_SUCCESS; - } - - if (psk_params->type == S2N_PSK_TYPE_RESUMPTION && psk_list->conn->config->use_tickets) { - POSIX_GUARD(s2n_stuffer_init(&ticket_stuffer, &psk->identity)); - POSIX_GUARD(s2n_stuffer_skip_write(&ticket_stuffer, psk->identity.size)); - - /* s2n_resume_decrypt_session appends a new PSK with the decrypted values. */ - POSIX_GUARD_RESULT(s2n_resume_decrypt_session(psk_list->conn, &ticket_stuffer)); - } - - struct s2n_psk *chosen_psk = NULL; - POSIX_GUARD_RESULT(s2n_match_psk_identity(&psk_params->psk_list, &psk->identity, &chosen_psk)); - POSIX_ENSURE_REF(chosen_psk); - POSIX_GUARD_RESULT(s2n_validate_ticket_lifetime(psk_list->conn, psk->obfuscated_ticket_age, chosen_psk->ticket_age_add)); - psk_params->chosen_psk = chosen_psk; - psk_params->chosen_psk_wire_index = psk->wire_index; - - return S2N_SUCCESS; -} - -struct s2n_offered_psk *s2n_offered_psk_new() -{ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_offered_psk))); - PTR_GUARD_POSIX(s2n_blob_zero(&mem)); - - struct s2n_offered_psk *psk = (struct s2n_offered_psk *) (void *) mem.data; - - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - return psk; -} - -int s2n_offered_psk_free(struct s2n_offered_psk **psk) -{ - if (psk == NULL) { - return S2N_SUCCESS; - } - return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_offered_psk)); -} - -int s2n_offered_psk_get_identity(struct s2n_offered_psk *psk, uint8_t **identity, uint16_t *size) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(psk->identity.data); - POSIX_ENSURE_REF(identity); - POSIX_ENSURE_REF(size); - *identity = psk->identity.data; - *size = psk->identity.size; - return S2N_SUCCESS; -} - -/* The binder hash is computed by hashing the concatenation of the current transcript - * and a partial ClientHello that does not include the binders themselves. - */ -int s2n_psk_calculate_binder_hash(struct s2n_connection *conn, s2n_hmac_algorithm hmac_alg, - const struct s2n_blob *partial_client_hello, struct s2n_blob *output_binder_hash) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(partial_client_hello); - POSIX_ENSURE_REF(output_binder_hash); - struct s2n_handshake_hashes *hashes = conn->handshake.hashes; - POSIX_ENSURE_REF(hashes); - - /* Retrieve the current transcript. - * The current transcript will be empty unless this handshake included a HelloRetryRequest. */ - s2n_hash_algorithm hash_alg = S2N_HASH_NONE; - struct s2n_hash_state *hash_state = &hashes->hash_workspace; - POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); - - /* Add the partial client hello to the transcript. */ - POSIX_GUARD(s2n_hash_update(hash_state, partial_client_hello->data, partial_client_hello->size)); - - /* Get the transcript digest */ - POSIX_GUARD(s2n_hash_digest(hash_state, output_binder_hash->data, output_binder_hash->size)); - - return S2N_SUCCESS; -} - -/* The binder is computed in the same way as the Finished message - * (https://tools.ietf.org/html/rfc8446#section-4.4.4) but with the BaseKey being the binder_key - * derived via the key schedule from the corresponding PSK which is being offered - * (https://tools.ietf.org/html/rfc8446#section-7.1) - */ -int s2n_psk_calculate_binder(struct s2n_psk *psk, const struct s2n_blob *binder_hash, - struct s2n_blob *output_binder) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(binder_hash); - POSIX_ENSURE_REF(output_binder); - - DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); - POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); - POSIX_ENSURE_EQ(binder_hash->size, psk_keys.size); - POSIX_ENSURE_EQ(output_binder->size, psk_keys.size); - - /* Derive the binder key */ - POSIX_GUARD_RESULT(s2n_derive_binder_key(psk, &psk_keys.derive_secret)); - POSIX_GUARD(s2n_blob_init(&psk_keys.extract_secret, psk->early_secret.data, psk_keys.size)); - struct s2n_blob *binder_key = &psk_keys.derive_secret; - - /* Expand the binder key into the finished key */ - s2n_tls13_key_blob(finished_key, psk_keys.size); - POSIX_GUARD(s2n_tls13_derive_finished_key(&psk_keys, binder_key, &finished_key)); - - /* HMAC the binder hash with the binder finished key */ - POSIX_GUARD(s2n_hkdf_extract(&psk_keys.hmac, psk_keys.hmac_algorithm, &finished_key, binder_hash, output_binder)); - - return S2N_SUCCESS; -} - -int s2n_psk_verify_binder(struct s2n_connection *conn, struct s2n_psk *psk, - const struct s2n_blob *partial_client_hello, struct s2n_blob *binder_to_verify) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(binder_to_verify); - - DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); - POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); - POSIX_ENSURE_EQ(binder_to_verify->size, psk_keys.size); - - /* Calculate the binder hash from the transcript */ - s2n_tls13_key_blob(binder_hash, psk_keys.size); - POSIX_GUARD(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, &binder_hash)); - - /* Calculate the expected binder from the binder hash */ - s2n_tls13_key_blob(expected_binder, psk_keys.size); - POSIX_GUARD(s2n_psk_calculate_binder(psk, &binder_hash, &expected_binder)); - - /* Verify the expected binder matches the given binder. - * This operation must be constant time. */ - POSIX_GUARD(s2n_tls13_mac_verify(&psk_keys, &expected_binder, binder_to_verify)); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_psk_write_binder(struct s2n_connection *conn, struct s2n_psk *psk, - const struct s2n_blob *binder_hash, struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(binder_hash); - - struct s2n_blob binder = { 0 }; - uint8_t binder_data[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&binder, binder_data, binder_hash->size)); - - RESULT_GUARD_POSIX(s2n_psk_calculate_binder(psk, binder_hash, &binder)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, binder.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write(out, &binder)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_psk_write_binder_list(struct s2n_connection *conn, const struct s2n_blob *partial_client_hello, - struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(partial_client_hello); - RESULT_ENSURE_REF(conn->secure); - - struct s2n_psk_parameters *psk_params = &conn->psk_params; - struct s2n_array *psk_list = &psk_params->psk_list; - - /* Setup memory to hold the binder hashes. We potentially need one for - * every hash algorithm. */ - uint8_t binder_hashes_data[S2N_HASH_ALGS_COUNT][S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - struct s2n_blob binder_hashes[S2N_HASH_ALGS_COUNT] = { 0 }; - - struct s2n_stuffer_reservation binder_list_size = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(out, &binder_list_size)); - - /* Write binder for every psk */ - for (size_t i = 0; i < psk_list->len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(psk_list, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 - *# In addition, in its updated ClientHello, the client SHOULD NOT offer - *# any pre-shared keys associated with a hash other than that of the - *# selected cipher suite. This allows the client to avoid having to - *# compute partial hash transcripts for multiple hashes in the second - *# ClientHello. - */ - if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { - continue; - } - - /* Retrieve or calculate the binder hash. */ - struct s2n_blob *binder_hash = &binder_hashes[psk->hmac_alg]; - if (binder_hash->size == 0) { - uint8_t hash_size = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); - RESULT_GUARD_POSIX(s2n_blob_init(binder_hash, binder_hashes_data[psk->hmac_alg], hash_size)); - RESULT_GUARD_POSIX(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, binder_hash)); - } - - RESULT_GUARD(s2n_psk_write_binder(conn, psk, binder_hash, out)); - } - RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&binder_list_size)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_finish_psk_extension(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - if (!conn->psk_params.binder_list_size) { - return S2N_RESULT_OK; - } - - struct s2n_stuffer *client_hello = &conn->handshake.io; - struct s2n_psk_parameters *psk_params = &conn->psk_params; - - /* Fill in the correct message size. */ - RESULT_GUARD_POSIX(s2n_handshake_finish_header(client_hello)); - - /* Remove the empty space allocated for the binder list. - * It was originally added to ensure the extension / extension list / message sizes - * were properly calculated. */ - RESULT_GUARD_POSIX(s2n_stuffer_wipe_n(client_hello, psk_params->binder_list_size)); - - /* Store the partial client hello for use in calculating the binder hash. */ - struct s2n_blob partial_client_hello = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&partial_client_hello, client_hello->blob.data, - s2n_stuffer_data_available(client_hello))); - - RESULT_GUARD(s2n_psk_write_binder_list(conn, &partial_client_hello, client_hello)); - - /* Reset binder list size. - * This is important because the psk extension can be removed during a retry. - */ - conn->psk_params.binder_list_size = 0; - - return S2N_RESULT_OK; -} - -int s2n_psk_set_hmac(struct s2n_psk *psk, s2n_psk_hmac hmac) -{ - POSIX_ENSURE_REF(psk); - switch (hmac) { - case S2N_PSK_HMAC_SHA256: - psk->hmac_alg = S2N_HMAC_SHA256; - break; - case S2N_PSK_HMAC_SHA384: - psk->hmac_alg = S2N_HMAC_SHA384; - break; - default: - POSIX_BAIL(S2N_ERR_HMAC_INVALID_ALGORITHM); - } - return S2N_SUCCESS; -} - -S2N_RESULT s2n_connection_set_psk_type(struct s2n_connection *conn, s2n_psk_type type) -{ - RESULT_ENSURE_REF(conn); - if (conn->psk_params.psk_list.len != 0) { - RESULT_ENSURE(conn->psk_params.type == type, S2N_ERR_PSK_MODE); - } - conn->psk_params.type = type; - return S2N_RESULT_OK; -} - -int s2n_connection_append_psk(struct s2n_connection *conn, struct s2n_psk *input_psk) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(input_psk); - POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, input_psk->type)); - - struct s2n_array *psk_list = &conn->psk_params.psk_list; - - /* Check for duplicate identities */ - for (uint32_t j = 0; j < psk_list->len; j++) { - struct s2n_psk *existing_psk = NULL; - POSIX_GUARD_RESULT(s2n_array_get(psk_list, j, (void **) &existing_psk)); - POSIX_ENSURE_REF(existing_psk); - - bool duplicate = existing_psk->identity.size == input_psk->identity.size - && memcmp(existing_psk->identity.data, input_psk->identity.data, existing_psk->identity.size) == 0; - POSIX_ENSURE(!duplicate, S2N_ERR_DUPLICATE_PSK_IDENTITIES); - } - - /* Verify the PSK list will fit in the ClientHello pre_shared_key extension */ - if (conn->mode == S2N_CLIENT) { - uint32_t list_size = 0; - POSIX_GUARD_RESULT(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &list_size)); - - uint32_t psk_size = 0; - POSIX_GUARD_RESULT(s2n_psk_offered_psk_size(input_psk, &psk_size)); - - POSIX_ENSURE(list_size + psk_size + S2N_EXTENSION_HEADER_LENGTH <= UINT16_MAX, S2N_ERR_OFFERED_PSKS_TOO_LONG); - } - - DEFER_CLEANUP(struct s2n_psk new_psk = { 0 }, s2n_psk_wipe); - POSIX_ENSURE(s2n_result_is_ok(s2n_psk_clone(&new_psk, input_psk)), S2N_ERR_INVALID_ARGUMENT); - POSIX_GUARD_RESULT(s2n_array_insert_and_copy(psk_list, psk_list->len, &new_psk)); - - ZERO_TO_DISABLE_DEFER_CLEANUP(new_psk); - return S2N_SUCCESS; -} - -int s2n_config_set_psk_mode(struct s2n_config *config, s2n_psk_mode mode) -{ - POSIX_ENSURE_REF(config); - config->psk_mode = mode; - return S2N_SUCCESS; -} - -int s2n_connection_set_psk_mode(struct s2n_connection *conn, s2n_psk_mode mode) -{ - POSIX_ENSURE_REF(conn); - s2n_psk_type type = 0; - switch (mode) { - case S2N_PSK_MODE_RESUMPTION: - type = S2N_PSK_TYPE_RESUMPTION; - break; - case S2N_PSK_MODE_EXTERNAL: - type = S2N_PSK_TYPE_EXTERNAL; - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); - break; - } - POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, type)); - conn->psk_mode_overridden = true; - return S2N_SUCCESS; -} - -int s2n_connection_get_negotiated_psk_identity_length(struct s2n_connection *conn, uint16_t *identity_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(identity_length); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - - if (chosen_psk == NULL) { - *identity_length = 0; - } else { - *identity_length = chosen_psk->identity.size; - } - - return S2N_SUCCESS; -} - -int s2n_connection_get_negotiated_psk_identity(struct s2n_connection *conn, uint8_t *identity, - uint16_t max_identity_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(identity); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - - if (chosen_psk == NULL) { - return S2N_SUCCESS; - } - - POSIX_ENSURE(chosen_psk->identity.size <= max_identity_length, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(identity, chosen_psk->identity.data, chosen_psk->identity.size); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_psk_validate_keying_material(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - if (!chosen_psk || chosen_psk->type != S2N_PSK_TYPE_RESUMPTION) { - return S2N_RESULT_OK; - } - - /* - * The minimum ticket lifetime is 1s, because ticket_lifetime is given - * in seconds and 0 indicates that the ticket should be immediately discarded. - */ - uint32_t min_lifetime = ONE_SEC_IN_NANOS; - - uint64_t current_time = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); - RESULT_ENSURE(chosen_psk->keying_material_expiration > current_time + min_lifetime, S2N_ERR_KEYING_MATERIAL_EXPIRED); - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_tls13_keys.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls13_secrets.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_psk_init(struct s2n_psk *psk, s2n_psk_type type) +{ + RESULT_ENSURE_MUT(psk); + + RESULT_CHECKED_MEMSET(psk, 0, sizeof(struct s2n_psk)); + psk->hmac_alg = S2N_HMAC_SHA256; + psk->type = type; + + return S2N_RESULT_OK; +} + +struct s2n_psk *s2n_external_psk_new() +{ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_psk))); + + struct s2n_psk *psk = (struct s2n_psk *) (void *) mem.data; + PTR_GUARD_RESULT(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + return psk; +} + +int s2n_psk_set_identity(struct s2n_psk *psk, const uint8_t *identity, uint16_t identity_size) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(identity); + POSIX_ENSURE(identity_size != 0, S2N_ERR_INVALID_ARGUMENT); + + POSIX_GUARD(s2n_realloc(&psk->identity, identity_size)); + POSIX_CHECKED_MEMCPY(psk->identity.data, identity, identity_size); + + return S2N_SUCCESS; +} + +int s2n_psk_set_secret(struct s2n_psk *psk, const uint8_t *secret, uint16_t secret_size) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(secret); + POSIX_ENSURE(secret_size != 0, S2N_ERR_INVALID_ARGUMENT); + + /* There are a number of application level errors that might result in an + * all-zero secret accidentally getting used. Error if that happens. + */ + bool secret_is_all_zero = true; + for (uint16_t i = 0; i < secret_size; i++) { + secret_is_all_zero = secret_is_all_zero && secret[i] == 0; + } + POSIX_ENSURE(!secret_is_all_zero, S2N_ERR_INVALID_ARGUMENT); + + POSIX_GUARD(s2n_realloc(&psk->secret, secret_size)); + POSIX_CHECKED_MEMCPY(psk->secret.data, secret, secret_size); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_psk_clone(struct s2n_psk *new_psk, struct s2n_psk *original_psk) +{ + if (original_psk == NULL) { + return S2N_RESULT_OK; + } + RESULT_ENSURE_REF(new_psk); + + struct s2n_psk psk_copy = *new_psk; + + /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ + *new_psk = *original_psk; + new_psk->identity = psk_copy.identity; + new_psk->secret = psk_copy.secret; + new_psk->early_secret = psk_copy.early_secret; + new_psk->early_data_config = psk_copy.early_data_config; + + /* Clone / realloc blobs */ + RESULT_GUARD_POSIX(s2n_psk_set_identity(new_psk, original_psk->identity.data, original_psk->identity.size)); + RESULT_GUARD_POSIX(s2n_psk_set_secret(new_psk, original_psk->secret.data, original_psk->secret.size)); + RESULT_GUARD_POSIX(s2n_realloc(&new_psk->early_secret, original_psk->early_secret.size)); + RESULT_CHECKED_MEMCPY(new_psk->early_secret.data, original_psk->early_secret.data, original_psk->early_secret.size); + RESULT_GUARD(s2n_early_data_config_clone(new_psk, &original_psk->early_data_config)); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_psk_wipe(struct s2n_psk *psk) +{ + if (psk == NULL) { + return S2N_RESULT_OK; + } + + RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); + RESULT_GUARD_POSIX(s2n_free(&psk->identity)); + RESULT_GUARD_POSIX(s2n_free(&psk->secret)); + RESULT_GUARD(s2n_early_data_config_free(&psk->early_data_config)); + + return S2N_RESULT_OK; +} + +int s2n_psk_free(struct s2n_psk **psk) +{ + if (psk == NULL) { + return S2N_SUCCESS; + } + POSIX_GUARD_RESULT(s2n_psk_wipe(*psk)); + return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_psk)); +} + +S2N_RESULT s2n_psk_parameters_init(struct s2n_psk_parameters *params) +{ + RESULT_ENSURE_REF(params); + RESULT_CHECKED_MEMSET(params, 0, sizeof(struct s2n_psk_parameters)); + RESULT_GUARD(s2n_array_init(¶ms->psk_list, sizeof(struct s2n_psk))); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_psk_offered_psk_size(struct s2n_psk *psk, uint32_t *size) +{ + *size = sizeof(uint16_t) /* identity size */ + + sizeof(uint32_t) /* obfuscated ticket age */ + + sizeof(uint8_t); /* binder size */ + + RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk->identity.size, size)); + + uint8_t binder_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &binder_size)); + RESULT_GUARD_POSIX(s2n_add_overflow(*size, binder_size, size)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_psk_parameters_offered_psks_size(struct s2n_psk_parameters *params, uint32_t *size) +{ + RESULT_ENSURE_REF(params); + RESULT_ENSURE_REF(size); + + *size = sizeof(uint16_t) /* identity list size */ + + sizeof(uint16_t) /* binder list size */; + + for (uint32_t i = 0; i < params->psk_list.len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + + uint32_t psk_size = 0; + RESULT_GUARD(s2n_psk_offered_psk_size(psk, &psk_size)); + RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk_size, size)); + } + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_psk_parameters_wipe(struct s2n_psk_parameters *params) +{ + RESULT_ENSURE_REF(params); + + for (size_t i = 0; i < params->psk_list.len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); + RESULT_GUARD(s2n_psk_wipe(psk)); + } + RESULT_GUARD_POSIX(s2n_free(¶ms->psk_list.mem)); + RESULT_GUARD(s2n_psk_parameters_init(params)); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_psk_parameters_wipe_secrets(struct s2n_psk_parameters *params) +{ + RESULT_ENSURE_REF(params); + + for (size_t i = 0; i < params->psk_list.len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); + RESULT_GUARD_POSIX(s2n_free(&psk->secret)); + } + + return S2N_RESULT_OK; +} + +bool s2n_offered_psk_list_has_next(struct s2n_offered_psk_list *psk_list) +{ + return psk_list != NULL && s2n_stuffer_data_available(&psk_list->wire_data) > 0; +} + +S2N_RESULT s2n_offered_psk_list_read_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) +{ + RESULT_ENSURE_REF(psk_list); + RESULT_ENSURE_REF(psk_list->conn); + RESULT_ENSURE_MUT(psk); + + uint16_t identity_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&psk_list->wire_data, &identity_size)); + RESULT_ENSURE_GT(identity_size, 0); + + uint8_t *identity_data = NULL; + identity_data = s2n_stuffer_raw_read(&psk_list->wire_data, identity_size); + RESULT_ENSURE_REF(identity_data); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# For identities established externally, an obfuscated_ticket_age of 0 SHOULD be + *# used, and servers MUST ignore the value. + */ + if (psk_list->conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&psk_list->wire_data, sizeof(uint32_t))); + } else { + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&psk_list->wire_data, &psk->obfuscated_ticket_age)); + } + + RESULT_GUARD_POSIX(s2n_blob_init(&psk->identity, identity_data, identity_size)); + psk->wire_index = psk_list->wire_index; + + RESULT_ENSURE(psk_list->wire_index < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); + psk_list->wire_index++; + return S2N_RESULT_OK; +} + +int s2n_offered_psk_list_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) +{ + POSIX_ENSURE_REF(psk_list); + POSIX_ENSURE_REF(psk); + *psk = (struct s2n_offered_psk){ 0 }; + POSIX_ENSURE(s2n_offered_psk_list_has_next(psk_list), S2N_ERR_STUFFER_OUT_OF_DATA); + POSIX_ENSURE(s2n_result_is_ok(s2n_offered_psk_list_read_next(psk_list, psk)), S2N_ERR_BAD_MESSAGE); + return S2N_SUCCESS; +} + +int s2n_offered_psk_list_reread(struct s2n_offered_psk_list *psk_list) +{ + POSIX_ENSURE_REF(psk_list); + psk_list->wire_index = 0; + return s2n_stuffer_reread(&psk_list->wire_data); +} + +/* Match a PSK identity received from the client against the server's known PSK identities. + * This method compares a single client identity to all server identities. + * + * While both the client's offered identities and whether a match was found are public, we should make an attempt + * to keep the server's known identities a secret. We will make comparisons to the server's identities constant + * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). + * + * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, + * and an attacker could probably guess the server's known identities just by observing the public identities + * sent by clients. + */ +static S2N_RESULT s2n_match_psk_identity(struct s2n_array *known_psks, const struct s2n_blob *wire_identity, + struct s2n_psk **match) +{ + RESULT_ENSURE_REF(match); + RESULT_ENSURE_REF(wire_identity); + RESULT_ENSURE_REF(known_psks); + *match = NULL; + for (size_t i = 0; i < known_psks->len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(known_psks, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + RESULT_ENSURE_REF(psk->identity.data); + RESULT_ENSURE_REF(wire_identity->data); + uint32_t compare_size = S2N_MIN(wire_identity->size, psk->identity.size); + if (s2n_constant_time_equals(psk->identity.data, wire_identity->data, compare_size) + & (psk->identity.size == wire_identity->size) & (!*match)) { + *match = psk; + } + } + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# For PSKs provisioned via NewSessionTicket, a server MUST validate + *# that the ticket age for the selected PSK identity (computed by + *# subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age + *# modulo 2^32) is within a small tolerance of the time since the ticket + *# was issued (see Section 8). + **/ +static S2N_RESULT s2n_validate_ticket_lifetime(struct s2n_connection *conn, uint32_t obfuscated_ticket_age, uint32_t ticket_age_add) +{ + RESULT_ENSURE_REF(conn); + + if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { + return S2N_RESULT_OK; + } + + /* Subtract the ticket_age_add value from the ticket age in milliseconds. The resulting uint32_t value + * may wrap, resulting in the modulo 2^32 operation. */ + uint32_t ticket_age_in_millis = obfuscated_ticket_age - ticket_age_add; + uint32_t session_lifetime_in_millis = conn->config->session_state_lifetime_in_nanos / ONE_MILLISEC_IN_NANOS; + RESULT_ENSURE(ticket_age_in_millis < session_lifetime_in_millis, S2N_ERR_INVALID_SESSION_TICKET); + + return S2N_RESULT_OK; +} + +int s2n_offered_psk_list_choose_psk(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) +{ + POSIX_ENSURE_REF(psk_list); + POSIX_ENSURE_REF(psk_list->conn); + + struct s2n_psk_parameters *psk_params = &psk_list->conn->psk_params; + struct s2n_stuffer ticket_stuffer = { 0 }; + + if (!psk) { + psk_params->chosen_psk = NULL; + return S2N_SUCCESS; + } + + if (psk_params->type == S2N_PSK_TYPE_RESUMPTION && psk_list->conn->config->use_tickets) { + POSIX_GUARD(s2n_stuffer_init(&ticket_stuffer, &psk->identity)); + POSIX_GUARD(s2n_stuffer_skip_write(&ticket_stuffer, psk->identity.size)); + + /* s2n_resume_decrypt_session appends a new PSK with the decrypted values. */ + POSIX_GUARD_RESULT(s2n_resume_decrypt_session(psk_list->conn, &ticket_stuffer)); + } + + struct s2n_psk *chosen_psk = NULL; + POSIX_GUARD_RESULT(s2n_match_psk_identity(&psk_params->psk_list, &psk->identity, &chosen_psk)); + POSIX_ENSURE_REF(chosen_psk); + POSIX_GUARD_RESULT(s2n_validate_ticket_lifetime(psk_list->conn, psk->obfuscated_ticket_age, chosen_psk->ticket_age_add)); + psk_params->chosen_psk = chosen_psk; + psk_params->chosen_psk_wire_index = psk->wire_index; + + return S2N_SUCCESS; +} + +struct s2n_offered_psk *s2n_offered_psk_new() +{ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_offered_psk))); + PTR_GUARD_POSIX(s2n_blob_zero(&mem)); + + struct s2n_offered_psk *psk = (struct s2n_offered_psk *) (void *) mem.data; + + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + return psk; +} + +int s2n_offered_psk_free(struct s2n_offered_psk **psk) +{ + if (psk == NULL) { + return S2N_SUCCESS; + } + return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_offered_psk)); +} + +int s2n_offered_psk_get_identity(struct s2n_offered_psk *psk, uint8_t **identity, uint16_t *size) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(psk->identity.data); + POSIX_ENSURE_REF(identity); + POSIX_ENSURE_REF(size); + *identity = psk->identity.data; + *size = psk->identity.size; + return S2N_SUCCESS; +} + +/* The binder hash is computed by hashing the concatenation of the current transcript + * and a partial ClientHello that does not include the binders themselves. + */ +int s2n_psk_calculate_binder_hash(struct s2n_connection *conn, s2n_hmac_algorithm hmac_alg, + const struct s2n_blob *partial_client_hello, struct s2n_blob *output_binder_hash) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(partial_client_hello); + POSIX_ENSURE_REF(output_binder_hash); + struct s2n_handshake_hashes *hashes = conn->handshake.hashes; + POSIX_ENSURE_REF(hashes); + + /* Retrieve the current transcript. + * The current transcript will be empty unless this handshake included a HelloRetryRequest. */ + s2n_hash_algorithm hash_alg = S2N_HASH_NONE; + struct s2n_hash_state *hash_state = &hashes->hash_workspace; + POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); + + /* Add the partial client hello to the transcript. */ + POSIX_GUARD(s2n_hash_update(hash_state, partial_client_hello->data, partial_client_hello->size)); + + /* Get the transcript digest */ + POSIX_GUARD(s2n_hash_digest(hash_state, output_binder_hash->data, output_binder_hash->size)); + + return S2N_SUCCESS; +} + +/* The binder is computed in the same way as the Finished message + * (https://tools.ietf.org/html/rfc8446#section-4.4.4) but with the BaseKey being the binder_key + * derived via the key schedule from the corresponding PSK which is being offered + * (https://tools.ietf.org/html/rfc8446#section-7.1) + */ +int s2n_psk_calculate_binder(struct s2n_psk *psk, const struct s2n_blob *binder_hash, + struct s2n_blob *output_binder) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(binder_hash); + POSIX_ENSURE_REF(output_binder); + + DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); + POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); + POSIX_ENSURE_EQ(binder_hash->size, psk_keys.size); + POSIX_ENSURE_EQ(output_binder->size, psk_keys.size); + + /* Derive the binder key */ + POSIX_GUARD_RESULT(s2n_derive_binder_key(psk, &psk_keys.derive_secret)); + POSIX_GUARD(s2n_blob_init(&psk_keys.extract_secret, psk->early_secret.data, psk_keys.size)); + struct s2n_blob *binder_key = &psk_keys.derive_secret; + + /* Expand the binder key into the finished key */ + s2n_tls13_key_blob(finished_key, psk_keys.size); + POSIX_GUARD(s2n_tls13_derive_finished_key(&psk_keys, binder_key, &finished_key)); + + /* HMAC the binder hash with the binder finished key */ + POSIX_GUARD(s2n_hkdf_extract(&psk_keys.hmac, psk_keys.hmac_algorithm, &finished_key, binder_hash, output_binder)); + + return S2N_SUCCESS; +} + +int s2n_psk_verify_binder(struct s2n_connection *conn, struct s2n_psk *psk, + const struct s2n_blob *partial_client_hello, struct s2n_blob *binder_to_verify) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(binder_to_verify); + + DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); + POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); + POSIX_ENSURE_EQ(binder_to_verify->size, psk_keys.size); + + /* Calculate the binder hash from the transcript */ + s2n_tls13_key_blob(binder_hash, psk_keys.size); + POSIX_GUARD(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, &binder_hash)); + + /* Calculate the expected binder from the binder hash */ + s2n_tls13_key_blob(expected_binder, psk_keys.size); + POSIX_GUARD(s2n_psk_calculate_binder(psk, &binder_hash, &expected_binder)); + + /* Verify the expected binder matches the given binder. + * This operation must be constant time. */ + POSIX_GUARD(s2n_tls13_mac_verify(&psk_keys, &expected_binder, binder_to_verify)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_psk_write_binder(struct s2n_connection *conn, struct s2n_psk *psk, + const struct s2n_blob *binder_hash, struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(binder_hash); + + struct s2n_blob binder = { 0 }; + uint8_t binder_data[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&binder, binder_data, binder_hash->size)); + + RESULT_GUARD_POSIX(s2n_psk_calculate_binder(psk, binder_hash, &binder)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, binder.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write(out, &binder)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_psk_write_binder_list(struct s2n_connection *conn, const struct s2n_blob *partial_client_hello, + struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(partial_client_hello); + RESULT_ENSURE_REF(conn->secure); + + struct s2n_psk_parameters *psk_params = &conn->psk_params; + struct s2n_array *psk_list = &psk_params->psk_list; + + /* Setup memory to hold the binder hashes. We potentially need one for + * every hash algorithm. */ + uint8_t binder_hashes_data[S2N_HASH_ALGS_COUNT][S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + struct s2n_blob binder_hashes[S2N_HASH_ALGS_COUNT] = { 0 }; + + struct s2n_stuffer_reservation binder_list_size = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(out, &binder_list_size)); + + /* Write binder for every psk */ + for (size_t i = 0; i < psk_list->len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(psk_list, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. This allows the client to avoid having to + *# compute partial hash transcripts for multiple hashes in the second + *# ClientHello. + */ + if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { + continue; + } + + /* Retrieve or calculate the binder hash. */ + struct s2n_blob *binder_hash = &binder_hashes[psk->hmac_alg]; + if (binder_hash->size == 0) { + uint8_t hash_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); + RESULT_GUARD_POSIX(s2n_blob_init(binder_hash, binder_hashes_data[psk->hmac_alg], hash_size)); + RESULT_GUARD_POSIX(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, binder_hash)); + } + + RESULT_GUARD(s2n_psk_write_binder(conn, psk, binder_hash, out)); + } + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&binder_list_size)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_finish_psk_extension(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + if (!conn->psk_params.binder_list_size) { + return S2N_RESULT_OK; + } + + struct s2n_stuffer *client_hello = &conn->handshake.io; + struct s2n_psk_parameters *psk_params = &conn->psk_params; + + /* Fill in the correct message size. */ + RESULT_GUARD_POSIX(s2n_handshake_finish_header(client_hello)); + + /* Remove the empty space allocated for the binder list. + * It was originally added to ensure the extension / extension list / message sizes + * were properly calculated. */ + RESULT_GUARD_POSIX(s2n_stuffer_wipe_n(client_hello, psk_params->binder_list_size)); + + /* Store the partial client hello for use in calculating the binder hash. */ + struct s2n_blob partial_client_hello = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&partial_client_hello, client_hello->blob.data, + s2n_stuffer_data_available(client_hello))); + + RESULT_GUARD(s2n_psk_write_binder_list(conn, &partial_client_hello, client_hello)); + + /* Reset binder list size. + * This is important because the psk extension can be removed during a retry. + */ + conn->psk_params.binder_list_size = 0; + + return S2N_RESULT_OK; +} + +int s2n_psk_set_hmac(struct s2n_psk *psk, s2n_psk_hmac hmac) +{ + POSIX_ENSURE_REF(psk); + switch (hmac) { + case S2N_PSK_HMAC_SHA256: + psk->hmac_alg = S2N_HMAC_SHA256; + break; + case S2N_PSK_HMAC_SHA384: + psk->hmac_alg = S2N_HMAC_SHA384; + break; + default: + POSIX_BAIL(S2N_ERR_HMAC_INVALID_ALGORITHM); + } + return S2N_SUCCESS; +} + +S2N_RESULT s2n_connection_set_psk_type(struct s2n_connection *conn, s2n_psk_type type) +{ + RESULT_ENSURE_REF(conn); + if (conn->psk_params.psk_list.len != 0) { + RESULT_ENSURE(conn->psk_params.type == type, S2N_ERR_PSK_MODE); + } + conn->psk_params.type = type; + return S2N_RESULT_OK; +} + +int s2n_connection_append_psk(struct s2n_connection *conn, struct s2n_psk *input_psk) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(input_psk); + POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, input_psk->type)); + + struct s2n_array *psk_list = &conn->psk_params.psk_list; + + /* Check for duplicate identities */ + for (uint32_t j = 0; j < psk_list->len; j++) { + struct s2n_psk *existing_psk = NULL; + POSIX_GUARD_RESULT(s2n_array_get(psk_list, j, (void **) &existing_psk)); + POSIX_ENSURE_REF(existing_psk); + + bool duplicate = existing_psk->identity.size == input_psk->identity.size + && memcmp(existing_psk->identity.data, input_psk->identity.data, existing_psk->identity.size) == 0; + POSIX_ENSURE(!duplicate, S2N_ERR_DUPLICATE_PSK_IDENTITIES); + } + + /* Verify the PSK list will fit in the ClientHello pre_shared_key extension */ + if (conn->mode == S2N_CLIENT) { + uint32_t list_size = 0; + POSIX_GUARD_RESULT(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &list_size)); + + uint32_t psk_size = 0; + POSIX_GUARD_RESULT(s2n_psk_offered_psk_size(input_psk, &psk_size)); + + POSIX_ENSURE(list_size + psk_size + S2N_EXTENSION_HEADER_LENGTH <= UINT16_MAX, S2N_ERR_OFFERED_PSKS_TOO_LONG); + } + + DEFER_CLEANUP(struct s2n_psk new_psk = { 0 }, s2n_psk_wipe); + POSIX_ENSURE(s2n_result_is_ok(s2n_psk_clone(&new_psk, input_psk)), S2N_ERR_INVALID_ARGUMENT); + POSIX_GUARD_RESULT(s2n_array_insert_and_copy(psk_list, psk_list->len, &new_psk)); + + ZERO_TO_DISABLE_DEFER_CLEANUP(new_psk); + return S2N_SUCCESS; +} + +int s2n_config_set_psk_mode(struct s2n_config *config, s2n_psk_mode mode) +{ + POSIX_ENSURE_REF(config); + config->psk_mode = mode; + return S2N_SUCCESS; +} + +int s2n_connection_set_psk_mode(struct s2n_connection *conn, s2n_psk_mode mode) +{ + POSIX_ENSURE_REF(conn); + s2n_psk_type type = 0; + switch (mode) { + case S2N_PSK_MODE_RESUMPTION: + type = S2N_PSK_TYPE_RESUMPTION; + break; + case S2N_PSK_MODE_EXTERNAL: + type = S2N_PSK_TYPE_EXTERNAL; + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); + break; + } + POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, type)); + conn->psk_mode_overridden = true; + return S2N_SUCCESS; +} + +int s2n_connection_get_negotiated_psk_identity_length(struct s2n_connection *conn, uint16_t *identity_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(identity_length); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + + if (chosen_psk == NULL) { + *identity_length = 0; + } else { + *identity_length = chosen_psk->identity.size; + } + + return S2N_SUCCESS; +} + +int s2n_connection_get_negotiated_psk_identity(struct s2n_connection *conn, uint8_t *identity, + uint16_t max_identity_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(identity); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + + if (chosen_psk == NULL) { + return S2N_SUCCESS; + } + + POSIX_ENSURE(chosen_psk->identity.size <= max_identity_length, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(identity, chosen_psk->identity.data, chosen_psk->identity.size); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_psk_validate_keying_material(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + if (!chosen_psk || chosen_psk->type != S2N_PSK_TYPE_RESUMPTION) { + return S2N_RESULT_OK; + } + + /* + * The minimum ticket lifetime is 1s, because ticket_lifetime is given + * in seconds and 0 indicates that the ticket should be immediately discarded. + */ + uint32_t min_lifetime = ONE_SEC_IN_NANOS; + + uint64_t current_time = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); + RESULT_ENSURE(chosen_psk->keying_material_expiration > current_time + min_lifetime, S2N_ERR_KEYING_MATERIAL_EXPIRED); + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_quic_support.c b/tls/s2n_quic_support.c index 5a34423b939..6db154b11ae 100644 --- a/tls/s2n_quic_support.c +++ b/tls/s2n_quic_support.c @@ -1,182 +1,183 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_quic_support.h" - -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* When reading and writing records with TCP, S2N sets its input and output buffers - * to the maximum record fragment size to prevent resizing those buffers later. - * - * However, because S2N with QUIC reads and writes messages instead of records, - * the "maximum size" for the input and output buffers would be the maximum message size: 64k. - * Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful. - * - * Instead, we intentionally choose a smaller size and accept that an abnormally large message - * could cause the buffer to resize. */ -#define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH - -S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length); - -int s2n_config_enable_quic(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - config->quic_enabled = true; - return S2N_SUCCESS; -} - -int s2n_connection_enable_quic(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn)); - /* QUIC support is not currently compatible with recv_buffering */ - POSIX_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); - conn->quic_enabled = true; - return S2N_SUCCESS; -} - -bool s2n_connection_is_quic_enabled(struct s2n_connection *conn) -{ - return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled); -} - -bool s2n_connection_are_session_tickets_enabled(struct s2n_connection *conn) -{ - return conn && conn->config && conn->config->use_tickets; -} - -int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn, - const uint8_t *data_buffer, uint16_t data_len) -{ - POSIX_ENSURE_REF(conn); - - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len)); - POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len); - - return S2N_SUCCESS; -} - -int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn, - const uint8_t **data_buffer, uint16_t *data_len) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(data_buffer); - POSIX_ENSURE_REF(data_len); - - *data_buffer = conn->peer_quic_transport_parameters.data; - *data_len = conn->peer_quic_transport_parameters.size; - - return S2N_SUCCESS; -} - -int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(cb_func); - - conn->secret_cb = cb_func; - conn->secret_cb_context = ctx; - - return S2N_SUCCESS; -} - -/* Currently we need an API that quic can call to process post-handshake messages. Ideally - * we could re-use the s2n_recv API but that function needs to be refactored to support quic. - * For now we just call this API. - */ -int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - - *blocked = S2N_BLOCKED_ON_READ; - - uint8_t message_type = 0; - /* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used - * for sending post-handshake messages. This could cause a concurrency issue if we start both sending - * and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake - * message that is both sent and received in quic (servers only send session tickets - * and clients only receive session tickets.) Therefore it is safe for us - * to use the stuffer here. - */ - POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); - - /* The only post-handshake messages we support from QUIC currently are session tickets */ - POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC); - POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type)); - - /* Successfully read the message, wipe the header and message buffer */ - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - *blocked = S2N_NOT_BLOCKED; - - return S2N_SUCCESS; -} - -/* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records. - * This method sets up the S2N input buffers to match the results of using s2n_read_full_record. - */ -S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) -{ - RESULT_ENSURE_REF(conn); - /* The use of handshake.io here would complicate recv_buffering, and there's - * no real use case for recv_buffering when QUIC is handling the IO. - */ - RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); - - /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); - - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); - - uint32_t message_len = 0; - RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len)); - /* Reset the read cursor rather than wiping the stuffer. During the - * handshake, s2n_read_full_handshake_message expects the header to still - * be present in handshake.io so it can accumulate the full message. - */ - RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); - - RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); - - /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. - * Therefore buffer_in will always contain exactly message_len bytes of data. - * So we don't need to handle the possibility of extra data in buffer_in. - */ - RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); - RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); - return S2N_RESULT_OK; -} - -/* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records. - * This method sets up the S2N output buffer to match the result of using s2n_record_write. - */ -S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); - - RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out, - s2n_stuffer_data_available(&conn->handshake.io))); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_quic_support.h" + +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* When reading and writing records with TCP, S2N sets its input and output buffers + * to the maximum record fragment size to prevent resizing those buffers later. + * + * However, because S2N with QUIC reads and writes messages instead of records, + * the "maximum size" for the input and output buffers would be the maximum message size: 64k. + * Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful. + * + * Instead, we intentionally choose a smaller size and accept that an abnormally large message + * could cause the buffer to resize. */ +#define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH + +S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length); + +int s2n_config_enable_quic(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + config->quic_enabled = true; + return S2N_SUCCESS; +} + +int s2n_connection_enable_quic(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn)); + /* QUIC support is not currently compatible with recv_buffering */ + POSIX_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); + conn->quic_enabled = true; + return S2N_SUCCESS; +} + +bool s2n_connection_is_quic_enabled(struct s2n_connection *conn) +{ + return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled); +} + +bool s2n_connection_are_session_tickets_enabled(struct s2n_connection *conn) +{ + return conn && conn->config && conn->config->use_tickets; +} + +int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn, + const uint8_t *data_buffer, uint16_t data_len) +{ + POSIX_ENSURE_REF(conn); + + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len)); + POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len); + + return S2N_SUCCESS; +} + +int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn, + const uint8_t **data_buffer, uint16_t *data_len) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(data_buffer); + POSIX_ENSURE_REF(data_len); + + *data_buffer = conn->peer_quic_transport_parameters.data; + *data_len = conn->peer_quic_transport_parameters.size; + + return S2N_SUCCESS; +} + +int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(cb_func); + + conn->secret_cb = cb_func; + conn->secret_cb_context = ctx; + + return S2N_SUCCESS; +} + +/* Currently we need an API that quic can call to process post-handshake messages. Ideally + * we could re-use the s2n_recv API but that function needs to be refactored to support quic. + * For now we just call this API. + */ +int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + + *blocked = S2N_BLOCKED_ON_READ; + + uint8_t message_type = 0; + /* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used + * for sending post-handshake messages. This could cause a concurrency issue if we start both sending + * and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake + * message that is both sent and received in quic (servers only send session tickets + * and clients only receive session tickets.) Therefore it is safe for us + * to use the stuffer here. + */ + POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); + + /* The only post-handshake messages we support from QUIC currently are session tickets */ + POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC); + POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type)); + + /* Successfully read the message, wipe the header and message buffer */ + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + *blocked = S2N_NOT_BLOCKED; + + return S2N_SUCCESS; +} + +/* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records. + * This method sets up the S2N input buffers to match the results of using s2n_read_full_record. + */ +S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) +{ + RESULT_ENSURE_REF(conn); + /* The use of handshake.io here would complicate recv_buffering, and there's + * no real use case for recv_buffering when QUIC is handling the IO. + */ + RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); + + /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); + + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); + + uint32_t message_len = 0; + RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len)); + /* Reset the read cursor rather than wiping the stuffer. During the + * handshake, s2n_read_full_handshake_message expects the header to still + * be present in handshake.io so it can accumulate the full message. + */ + RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); + + RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); + + /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. + * Therefore buffer_in will always contain exactly message_len bytes of data. + * So we don't need to handle the possibility of extra data in buffer_in. + */ + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); + RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); + return S2N_RESULT_OK; +} + +/* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records. + * This method sets up the S2N output buffer to match the result of using s2n_record_write. + */ +S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); + + RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out, + s2n_stuffer_data_available(&conn->handshake.io))); + return S2N_RESULT_OK; +} diff --git a/tls/s2n_record_read.c b/tls/s2n_record_read.c index 489ef57d64d..527a950e195 100644 --- a/tls/s2n_record_read.c +++ b/tls/s2n_record_read.c @@ -1,287 +1,288 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_record_read.h" - -#include "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_sequence.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -int s2n_sslv2_record_header_parse( - struct s2n_connection *conn, - uint8_t *record_type, - uint8_t *client_protocol_version, - uint16_t *fragment_length) -{ - struct s2n_stuffer *header_in = &conn->header_in; - - POSIX_ENSURE(s2n_stuffer_data_available(header_in) >= S2N_TLS_RECORD_HEADER_LENGTH, - S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_stuffer_read_uint16(header_in, fragment_length)); - - /* The first bit of the SSLv2 message would usually indicate whether the - * length is 2 bytes long or 3 bytes long. - * See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt - * - * However, s2n-tls only supports SSLv2 for ClientHellos as defined in the - * TLS1.2 RFC. In that case, the first bit must always be set to distinguish - * SSLv2 from non-SSLv2 headers. The length is always 2 bytes. - * See https://datatracker.ietf.org/doc/html/rfc5246#appendix-E.2 - * - * Since the first bit is not actually used to indicate length, we need to - * remove it from the length. - * - *= https://www.rfc-editor.org/rfc/rfc5246#appendix-E.2 - *# msg_length - *# The highest bit MUST be 1; the remaining bits contain the length - *# of the following data in bytes. - */ - POSIX_ENSURE(*fragment_length & S2N_TLS_SSLV2_HEADER_FLAG_UINT16, S2N_ERR_BAD_MESSAGE); - *fragment_length ^= S2N_TLS_SSLV2_HEADER_FLAG_UINT16; - - /* We read 5 bytes into header_in because we expected a standard, non-SSLv2 record header - * instead of an SSLv2 message. We have therefore already read 3 bytes of the payload. - * We need to adjust "fragment_length" to account for the bytes we have already - * read so that we will only attempt to read the remainder of the payload on - * our next call to conn->recv. - */ - POSIX_ENSURE(*fragment_length >= s2n_stuffer_data_available(header_in), S2N_ERR_BAD_MESSAGE); - *fragment_length -= s2n_stuffer_data_available(header_in); - - /* By reading 5 bytes for a standard header we have also read the first - * 3 bytes of the SSLv2 ClientHello message. - * So we now need to parse those three bytes. - * - * The first field of an SSLv2 ClientHello is the msg_type. - * This is always '1', matching the ClientHello msg_type used by later - * handshake messages. - */ - POSIX_GUARD(s2n_stuffer_read_uint8(header_in, record_type)); - - /* - * The second field of an SSLv2 ClientHello is the version. - * - * The protocol version read here will likely not be SSLv2, since we only - * accept SSLv2 ClientHellos offering higher protocol versions. - * See s2n_sslv2_client_hello_parse. - */ - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD(s2n_stuffer_read_bytes(header_in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - *client_protocol_version = (protocol_version[0] * 10) + protocol_version[1]; - - POSIX_GUARD(s2n_stuffer_reread(header_in)); - return 0; -} - -int s2n_record_header_parse( - struct s2n_connection *conn, - uint8_t *content_type, - uint16_t *fragment_length) -{ - struct s2n_stuffer *in = &conn->header_in; - - S2N_ERROR_IF(s2n_stuffer_data_available(in) < S2N_TLS_RECORD_HEADER_LENGTH, S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_stuffer_read_uint8(in, content_type)); - - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - const uint8_t version = (protocol_version[0] * 10) + protocol_version[1]; - /* We record the protocol version in the first record seen by the server for fingerprinting usecases */ - if (!conn->client_hello.record_version_recorded) { - conn->client_hello.legacy_record_version = version; - conn->client_hello.record_version_recorded = 1; - } - - /* https://tools.ietf.org/html/rfc5246#appendix-E.1 states that servers must accept any value {03,XX} as the record - * layer version number for the first TLS record. There is some ambiguity here because the client does not know - * what version to use in the record header prior to receiving the ServerHello. Some client implementations may use - * a garbage value(not {03,XX}) in the ClientHello. - * Choose to be lenient to these clients. After protocol negotiation, we will enforce that all record versions - * match the negotiated version. - */ - - S2N_ERROR_IF(conn->actual_protocol_version_established && S2N_MIN(conn->actual_protocol_version, S2N_TLS12) /* check against legacy record version (1.2) in tls 1.3 */ - != version, - S2N_ERR_BAD_MESSAGE); - - /* Some servers send fragments that are above the maximum length (e.g. - * Openssl 1.0.1), so we don't check if the fragment length is > - * S2N_TLS_MAXIMUM_FRAGMENT_LENGTH. We allow up to 2^16. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *= type=exception - *= reason=Incorrect implementations exist in the wild. Ignoring instead. - *# The length MUST NOT exceed 2^14 bytes. An - *# endpoint that receives a record that exceeds this length MUST - *# terminate the connection with a "record_overflow" alert. - */ - POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length)); - POSIX_GUARD(s2n_stuffer_reread(in)); - - return 0; -} - -/* In TLS 1.3, handle CCS message as unprotected records all the time. - * https://tools.ietf.org/html/rfc8446#section-5 - * - * In TLS 1.2 and TLS 1.3 Alert messages are plaintext or encrypted - * depending on the context of the connection. If we receive an encrypted - * alert, the record type is TLS_APPLICATION_DATA at this point. It will - * be decrypted and processed in s2n_handshake_io. We may receive a - * plaintext alert if we hit an error before the handshake completed - * (like a certificate failed to validate). - * https://tools.ietf.org/html/rfc8446#section-6 - * - * This function is specific to TLS 1.3 to avoid changing the behavior - * of existing interpretation of TLS 1.2 alerts. */ -static bool s2n_is_tls13_plaintext_content(struct s2n_connection *conn, uint8_t content_type) -{ - return conn->actual_protocol_version == S2N_TLS13 && (content_type == TLS_ALERT || content_type == TLS_CHANGE_CIPHER_SPEC); -} - -int s2n_record_parse(struct s2n_connection *conn) -{ - uint8_t content_type = 0; - uint16_t encrypted_length = 0; - POSIX_GUARD(s2n_record_header_parse(conn, &content_type, &encrypted_length)); - - struct s2n_crypto_parameters *current_client_crypto = conn->client; - struct s2n_crypto_parameters *current_server_crypto = conn->server; - if (s2n_is_tls13_plaintext_content(conn, content_type)) { - POSIX_ENSURE_REF(conn->initial); - conn->client = conn->initial; - conn->server = conn->initial; - } - - const struct s2n_cipher_suite *cipher_suite = conn->client->cipher_suite; - uint8_t *implicit_iv = conn->client->client_implicit_iv; - struct s2n_hmac_state *mac = &conn->client->client_record_mac; - uint8_t *sequence_number = conn->client->client_sequence_number; - struct s2n_session_key *session_key = &conn->client->client_key; - - if (conn->mode == S2N_CLIENT) { - cipher_suite = conn->server->cipher_suite; - implicit_iv = conn->server->server_implicit_iv; - mac = &conn->server->server_record_mac; - sequence_number = conn->server->server_sequence_number; - session_key = &conn->server->server_key; - } - - if (s2n_is_tls13_plaintext_content(conn, content_type)) { - conn->client = current_client_crypto; - conn->server = current_server_crypto; - } - - /* The NULL stream cipher MUST NEVER be used for ApplicationData. - * If ApplicationData is unencrypted, we can't trust it. */ - if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { - POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_DECRYPT); - } - - switch (cipher_suite->record_alg->cipher->type) { - case S2N_AEAD: - POSIX_GUARD(s2n_record_parse_aead(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - case S2N_CBC: - POSIX_GUARD(s2n_record_parse_cbc(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - case S2N_COMPOSITE: - POSIX_GUARD(s2n_record_parse_composite(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - case S2N_STREAM: - POSIX_GUARD(s2n_record_parse_stream(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - default: - POSIX_BAIL(S2N_ERR_CIPHER_TYPE); - break; - } - - return 0; -} - -int s2n_tls13_parse_record_type(struct s2n_stuffer *stuffer, uint8_t *record_type) -{ - uint32_t bytes_left = s2n_stuffer_data_available(stuffer); - - /* From rfc8446 Section 5.4 - * The presence of padding does not change the overall record size - * limitations: the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 - * + 1 octets - * - * Certain versions of Java can generate inner plaintexts with lengths up to - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) - * However, after the padding is stripped, the result will always be no more than - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 - */ - S2N_ERROR_IF(bytes_left > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - - /* set cursor to the end of the stuffer */ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, bytes_left)); - - /* Record type should have values greater than zero. - * If zero, treat as padding, keep reading and wiping from the back - * until a non-zero value is found - */ - *record_type = 0; - while (*record_type == 0) { - /* back the cursor by one to read off the last byte */ - POSIX_GUARD(s2n_stuffer_rewind_read(stuffer, 1)); - - /* set the record type */ - POSIX_GUARD(s2n_stuffer_read_uint8(stuffer, record_type)); - - /* wipe the last byte at the end of the stuffer */ - POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1)); - } - - /* only the original plaintext should remain */ - /* now reset the read cursor at where it should be */ - POSIX_GUARD(s2n_stuffer_reread(stuffer)); - - /* Even in the incorrect case above with up to 16 extra bytes, we should never see too much data after unpadding */ - S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - - return 0; -} - -S2N_RESULT s2n_record_wipe(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->header_in)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->in)); - conn->in_status = ENCRYPTED; - - /* Release the memory in conn->in, which un-taints buffer_in */ - RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); - conn->buffer_in.tainted = false; - - /* Reclaim any memory in buffer_in if possible. - * We want to avoid an expensive shift / copy later if possible. - */ - if (s2n_stuffer_is_consumed(&conn->buffer_in)) { - RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&conn->buffer_in)); - } - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_record_read.h" + +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int s2n_sslv2_record_header_parse( + struct s2n_connection *conn, + uint8_t *record_type, + uint8_t *client_protocol_version, + uint16_t *fragment_length) +{ + struct s2n_stuffer *header_in = &conn->header_in; + + POSIX_ENSURE(s2n_stuffer_data_available(header_in) >= S2N_TLS_RECORD_HEADER_LENGTH, + S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_stuffer_read_uint16(header_in, fragment_length)); + + /* The first bit of the SSLv2 message would usually indicate whether the + * length is 2 bytes long or 3 bytes long. + * See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt + * + * However, s2n-tls only supports SSLv2 for ClientHellos as defined in the + * TLS1.2 RFC. In that case, the first bit must always be set to distinguish + * SSLv2 from non-SSLv2 headers. The length is always 2 bytes. + * See https://datatracker.ietf.org/doc/html/rfc5246#appendix-E.2 + * + * Since the first bit is not actually used to indicate length, we need to + * remove it from the length. + * + *= https://www.rfc-editor.org/rfc/rfc5246#appendix-E.2 + *# msg_length + *# The highest bit MUST be 1; the remaining bits contain the length + *# of the following data in bytes. + */ + POSIX_ENSURE(*fragment_length & S2N_TLS_SSLV2_HEADER_FLAG_UINT16, S2N_ERR_BAD_MESSAGE); + *fragment_length ^= S2N_TLS_SSLV2_HEADER_FLAG_UINT16; + + /* We read 5 bytes into header_in because we expected a standard, non-SSLv2 record header + * instead of an SSLv2 message. We have therefore already read 3 bytes of the payload. + * We need to adjust "fragment_length" to account for the bytes we have already + * read so that we will only attempt to read the remainder of the payload on + * our next call to conn->recv. + */ + POSIX_ENSURE(*fragment_length >= s2n_stuffer_data_available(header_in), S2N_ERR_BAD_MESSAGE); + *fragment_length -= s2n_stuffer_data_available(header_in); + + /* By reading 5 bytes for a standard header we have also read the first + * 3 bytes of the SSLv2 ClientHello message. + * So we now need to parse those three bytes. + * + * The first field of an SSLv2 ClientHello is the msg_type. + * This is always '1', matching the ClientHello msg_type used by later + * handshake messages. + */ + POSIX_GUARD(s2n_stuffer_read_uint8(header_in, record_type)); + + /* + * The second field of an SSLv2 ClientHello is the version. + * + * The protocol version read here will likely not be SSLv2, since we only + * accept SSLv2 ClientHellos offering higher protocol versions. + * See s2n_sslv2_client_hello_parse. + */ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_read_bytes(header_in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + *client_protocol_version = (protocol_version[0] * 10) + protocol_version[1]; + + POSIX_GUARD(s2n_stuffer_reread(header_in)); + return 0; +} + +int s2n_record_header_parse( + struct s2n_connection *conn, + uint8_t *content_type, + uint16_t *fragment_length) +{ + struct s2n_stuffer *in = &conn->header_in; + + S2N_ERROR_IF(s2n_stuffer_data_available(in) < S2N_TLS_RECORD_HEADER_LENGTH, S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_stuffer_read_uint8(in, content_type)); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + const uint8_t version = (protocol_version[0] * 10) + protocol_version[1]; + /* We record the protocol version in the first record seen by the server for fingerprinting usecases */ + if (!conn->client_hello.record_version_recorded) { + conn->client_hello.legacy_record_version = version; + conn->client_hello.record_version_recorded = 1; + } + + /* https://tools.ietf.org/html/rfc5246#appendix-E.1 states that servers must accept any value {03,XX} as the record + * layer version number for the first TLS record. There is some ambiguity here because the client does not know + * what version to use in the record header prior to receiving the ServerHello. Some client implementations may use + * a garbage value(not {03,XX}) in the ClientHello. + * Choose to be lenient to these clients. After protocol negotiation, we will enforce that all record versions + * match the negotiated version. + */ + + S2N_ERROR_IF(conn->actual_protocol_version_established && S2N_MIN(conn->actual_protocol_version, S2N_TLS12) /* check against legacy record version (1.2) in tls 1.3 */ + != version, + S2N_ERR_BAD_MESSAGE); + + /* Some servers send fragments that are above the maximum length (e.g. + * Openssl 1.0.1), so we don't check if the fragment length is > + * S2N_TLS_MAXIMUM_FRAGMENT_LENGTH. We allow up to 2^16. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *= type=exception + *= reason=Incorrect implementations exist in the wild. Ignoring instead. + *# The length MUST NOT exceed 2^14 bytes. An + *# endpoint that receives a record that exceeds this length MUST + *# terminate the connection with a "record_overflow" alert. + */ + POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length)); + POSIX_GUARD(s2n_stuffer_reread(in)); + + return 0; +} + +/* In TLS 1.3, handle CCS message as unprotected records all the time. + * https://tools.ietf.org/html/rfc8446#section-5 + * + * In TLS 1.2 and TLS 1.3 Alert messages are plaintext or encrypted + * depending on the context of the connection. If we receive an encrypted + * alert, the record type is TLS_APPLICATION_DATA at this point. It will + * be decrypted and processed in s2n_handshake_io. We may receive a + * plaintext alert if we hit an error before the handshake completed + * (like a certificate failed to validate). + * https://tools.ietf.org/html/rfc8446#section-6 + * + * This function is specific to TLS 1.3 to avoid changing the behavior + * of existing interpretation of TLS 1.2 alerts. */ +static bool s2n_is_tls13_plaintext_content(struct s2n_connection *conn, uint8_t content_type) +{ + return conn->actual_protocol_version == S2N_TLS13 && (content_type == TLS_ALERT || content_type == TLS_CHANGE_CIPHER_SPEC); +} + +int s2n_record_parse(struct s2n_connection *conn) +{ + uint8_t content_type = 0; + uint16_t encrypted_length = 0; + POSIX_GUARD(s2n_record_header_parse(conn, &content_type, &encrypted_length)); + + struct s2n_crypto_parameters *current_client_crypto = conn->client; + struct s2n_crypto_parameters *current_server_crypto = conn->server; + if (s2n_is_tls13_plaintext_content(conn, content_type)) { + POSIX_ENSURE_REF(conn->initial); + conn->client = conn->initial; + conn->server = conn->initial; + } + + const struct s2n_cipher_suite *cipher_suite = conn->client->cipher_suite; + uint8_t *implicit_iv = conn->client->client_implicit_iv; + struct s2n_hmac_state *mac = &conn->client->client_record_mac; + uint8_t *sequence_number = conn->client->client_sequence_number; + struct s2n_session_key *session_key = &conn->client->client_key; + + if (conn->mode == S2N_CLIENT) { + cipher_suite = conn->server->cipher_suite; + implicit_iv = conn->server->server_implicit_iv; + mac = &conn->server->server_record_mac; + sequence_number = conn->server->server_sequence_number; + session_key = &conn->server->server_key; + } + + if (s2n_is_tls13_plaintext_content(conn, content_type)) { + conn->client = current_client_crypto; + conn->server = current_server_crypto; + } + + /* The NULL stream cipher MUST NEVER be used for ApplicationData. + * If ApplicationData is unencrypted, we can't trust it. */ + if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { + POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_DECRYPT); + } + + switch (cipher_suite->record_alg->cipher->type) { + case S2N_AEAD: + POSIX_GUARD(s2n_record_parse_aead(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + case S2N_CBC: + POSIX_GUARD(s2n_record_parse_cbc(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + case S2N_COMPOSITE: + POSIX_GUARD(s2n_record_parse_composite(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + case S2N_STREAM: + POSIX_GUARD(s2n_record_parse_stream(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + default: + POSIX_BAIL(S2N_ERR_CIPHER_TYPE); + break; + } + + return 0; +} + +int s2n_tls13_parse_record_type(struct s2n_stuffer *stuffer, uint8_t *record_type) +{ + uint32_t bytes_left = s2n_stuffer_data_available(stuffer); + + /* From rfc8446 Section 5.4 + * The presence of padding does not change the overall record size + * limitations: the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 + * + 1 octets + * + * Certain versions of Java can generate inner plaintexts with lengths up to + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) + * However, after the padding is stripped, the result will always be no more than + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 + */ + S2N_ERROR_IF(bytes_left > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + /* set cursor to the end of the stuffer */ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, bytes_left)); + + /* Record type should have values greater than zero. + * If zero, treat as padding, keep reading and wiping from the back + * until a non-zero value is found + */ + *record_type = 0; + while (*record_type == 0) { + /* back the cursor by one to read off the last byte */ + POSIX_GUARD(s2n_stuffer_rewind_read(stuffer, 1)); + + /* set the record type */ + POSIX_GUARD(s2n_stuffer_read_uint8(stuffer, record_type)); + + /* wipe the last byte at the end of the stuffer */ + POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1)); + } + + /* only the original plaintext should remain */ + /* now reset the read cursor at where it should be */ + POSIX_GUARD(s2n_stuffer_reread(stuffer)); + + /* Even in the incorrect case above with up to 16 extra bytes, we should never see too much data after unpadding */ + S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + return 0; +} + +S2N_RESULT s2n_record_wipe(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->header_in)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->in)); + conn->in_status = ENCRYPTED; + + /* Release the memory in conn->in, which un-taints buffer_in */ + RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); + conn->buffer_in.tainted = false; + + /* Reclaim any memory in buffer_in if possible. + * We want to avoid an expensive shift / copy later if possible. + */ + if (s2n_stuffer_is_consumed(&conn->buffer_in)) { + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&conn->buffer_in)); + } + return S2N_RESULT_OK; +} diff --git a/tls/s2n_record_read_stream.c b/tls/s2n_record_read_stream.c index 927ab00fe7a..6117a2b22d5 100644 --- a/tls/s2n_record_read_stream.c +++ b/tls/s2n_record_read_stream.c @@ -1,93 +1,94 @@ -/* - * 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 "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_sequence.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_record_read.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -int s2n_record_parse_stream( - const struct s2n_cipher_suite *cipher_suite, - struct s2n_connection *conn, - uint8_t content_type, - uint16_t encrypted_length, - uint8_t *implicit_iv, - struct s2n_hmac_state *mac, - uint8_t *sequence_number, - struct s2n_session_key *session_key) -{ - /* Add the header to the HMAC */ - uint8_t *header = s2n_stuffer_raw_read(&conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH); - POSIX_ENSURE_REF(header); - - struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_read(&conn->in, encrypted_length) }; - POSIX_ENSURE_REF(en.data); - - uint16_t payload_length = encrypted_length; - uint8_t mac_digest_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); - - POSIX_ENSURE_GTE(payload_length, mac_digest_size); - payload_length -= mac_digest_size; - - /* Decrypt stuff! */ - POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.decrypt(session_key, &en, &en)); - - /* Update the MAC */ - header[3] = (payload_length >> 8); - header[4] = payload_length & 0xff; - POSIX_GUARD(s2n_hmac_reset(mac)); - POSIX_GUARD(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - - if (conn->actual_protocol_version == S2N_SSLv3) { - POSIX_GUARD(s2n_hmac_update(mac, header, 1)); - POSIX_GUARD(s2n_hmac_update(mac, header + 3, 2)); - } else { - POSIX_GUARD(s2n_hmac_update(mac, header, S2N_TLS_RECORD_HEADER_LENGTH)); - } - - struct s2n_blob seq = { .data = sequence_number, .size = S2N_TLS_SEQUENCE_NUM_LEN }; - POSIX_GUARD(s2n_increment_sequence_number(&seq)); - - /* MAC check for streaming ciphers - no padding */ - POSIX_GUARD(s2n_hmac_update(mac, en.data, payload_length)); - - uint8_t check_digest[S2N_MAX_DIGEST_LEN]; - POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); - POSIX_GUARD(s2n_hmac_digest(mac, check_digest, mac_digest_size)); - - if (s2n_hmac_digest_verify(en.data + payload_length, check_digest, mac_digest_size) < 0) { - POSIX_BAIL(S2N_ERR_BAD_MESSAGE); - } - - /* O.k., we've successfully read and decrypted the record, now we need to align the stuffer - * for reading the plaintext data. - */ - POSIX_GUARD(s2n_stuffer_reread(&conn->in)); - POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); - - /* Truncate and wipe the MAC and any padding */ - POSIX_GUARD(s2n_stuffer_wipe_n(&conn->in, s2n_stuffer_data_available(&conn->in) - payload_length)); - conn->in_status = PLAINTEXT; - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_record_read.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int s2n_record_parse_stream( + const struct s2n_cipher_suite *cipher_suite, + struct s2n_connection *conn, + uint8_t content_type, + uint16_t encrypted_length, + uint8_t *implicit_iv, + struct s2n_hmac_state *mac, + uint8_t *sequence_number, + struct s2n_session_key *session_key) +{ + /* Add the header to the HMAC */ + uint8_t *header = s2n_stuffer_raw_read(&conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH); + POSIX_ENSURE_REF(header); + + struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_read(&conn->in, encrypted_length) }; + POSIX_ENSURE_REF(en.data); + + uint16_t payload_length = encrypted_length; + uint8_t mac_digest_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); + + POSIX_ENSURE_GTE(payload_length, mac_digest_size); + payload_length -= mac_digest_size; + + /* Decrypt stuff! */ + POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.decrypt(session_key, &en, &en)); + + /* Update the MAC */ + header[3] = (payload_length >> 8); + header[4] = payload_length & 0xff; + POSIX_GUARD(s2n_hmac_reset(mac)); + POSIX_GUARD(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + if (conn->actual_protocol_version == S2N_SSLv3) { + POSIX_GUARD(s2n_hmac_update(mac, header, 1)); + POSIX_GUARD(s2n_hmac_update(mac, header + 3, 2)); + } else { + POSIX_GUARD(s2n_hmac_update(mac, header, S2N_TLS_RECORD_HEADER_LENGTH)); + } + + struct s2n_blob seq = { .data = sequence_number, .size = S2N_TLS_SEQUENCE_NUM_LEN }; + POSIX_GUARD(s2n_increment_sequence_number(&seq)); + + /* MAC check for streaming ciphers - no padding */ + POSIX_GUARD(s2n_hmac_update(mac, en.data, payload_length)); + + uint8_t check_digest[S2N_MAX_DIGEST_LEN]; + POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); + POSIX_GUARD(s2n_hmac_digest(mac, check_digest, mac_digest_size)); + + if (s2n_hmac_digest_verify(en.data + payload_length, check_digest, mac_digest_size) < 0) { + POSIX_BAIL(S2N_ERR_BAD_MESSAGE); + } + + /* O.k., we've successfully read and decrypted the record, now we need to align the stuffer + * for reading the plaintext data. + */ + POSIX_GUARD(s2n_stuffer_reread(&conn->in)); + POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); + + /* Truncate and wipe the MAC and any padding */ + POSIX_GUARD(s2n_stuffer_wipe_n(&conn->in, s2n_stuffer_data_available(&conn->in) - payload_length)); + conn->in_status = PLAINTEXT; + + return 0; +} diff --git a/tls/s2n_record_write.c b/tls/s2n_record_write.c index b073929c2c4..1916fb1b955 100644 --- a/tls/s2n_record_write.c +++ b/tls/s2n_record_write.c @@ -1,642 +1,643 @@ -/* - * 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 "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_sequence.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_record.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -extern uint8_t s2n_unknown_protocol_version; - -/* In TLS1.3 the record type is obfuscated as APPLICATION_DATA once the handshake begins to be encrypted. - * The real record type is encrypted and written in the final byte of the record. - * In TLS1.2 the record type is always cleartext. */ -#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type) - -/* How much overhead does the IV, MAC, TAG and padding bytes introduce ? */ -static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_MUT(out); - struct s2n_crypto_parameters *active = conn->server; - - if (conn->mode == S2N_CLIENT) { - active = conn->client; - } - - uint8_t extra = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(active->cipher_suite->record_alg->hmac_alg, &extra)); - - if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { - /* Subtract one for the padding length byte */ - extra += 1; - - if (conn->actual_protocol_version > S2N_TLS10) { - extra += active->cipher_suite->record_alg->cipher->io.cbc.record_iv_size; - } - } else if (active->cipher_suite->record_alg->cipher->type == S2N_AEAD) { - extra += active->cipher_suite->record_alg->cipher->io.aead.tag_size; - extra += active->cipher_suite->record_alg->cipher->io.aead.record_iv_size; - } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE && conn->actual_protocol_version > S2N_TLS10) { - extra += active->cipher_suite->record_alg->cipher->io.comp.record_iv_size; - } - - *out = extra; - - return S2N_RESULT_OK; -} - -/* This function returns maximum size of plaintext data to write for the payload. - * Record overheads are not included here. - */ -S2N_RESULT s2n_record_max_write_payload_size(struct s2n_connection *conn, uint16_t *max_fragment_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_MUT(max_fragment_size); - RESULT_ENSURE(conn->max_outgoing_fragment_length > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - - *max_fragment_size = S2N_MIN(conn->max_outgoing_fragment_length, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* If a custom send buffer is configured, ensure it will be large enough for the payload. - * That may mean we need a smaller fragment size. - */ - uint32_t send_buffer_override = conn->config->send_buffer_size_override; - if (send_buffer_override) { - uint16_t max_record_size = 0; - RESULT_GUARD(s2n_record_max_write_size(conn, *max_fragment_size, &max_record_size)); - if (send_buffer_override < max_record_size) { - size_t overhead = (max_record_size - *max_fragment_size); - RESULT_ENSURE_GT(send_buffer_override, overhead); - *max_fragment_size = send_buffer_override - overhead; - } - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_record_max_write_size(struct s2n_connection *conn, uint16_t max_fragment_size, uint16_t *max_record_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_MUT(max_record_size); - - if (!IS_NEGOTIATED(conn)) { - *max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(max_fragment_size); - } else if (conn->actual_protocol_version < S2N_TLS13) { - *max_record_size = S2N_TLS12_MAX_RECORD_LEN_FOR(max_fragment_size); - } else { - *max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(max_fragment_size); - } - return S2N_RESULT_OK; -} - -/* Find the largest size that will fit within an ethernet frame for a "small" payload */ -S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16_t *payload_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_MUT(payload_size); - - /* remove ethernet, TCP/IP and TLS header overheads */ - const uint16_t min_outgoing_fragment_length = ETH_MTU - (conn->ipv6 ? IP_V6_HEADER_LENGTH : IP_V4_HEADER_LENGTH) - - TCP_HEADER_LENGTH - TCP_OPTIONS_LENGTH - S2N_TLS_RECORD_HEADER_LENGTH; - - RESULT_ENSURE(min_outgoing_fragment_length <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - uint16_t size = min_outgoing_fragment_length; - - const struct s2n_crypto_parameters *active = conn->mode == S2N_CLIENT ? conn->client : conn->server; - - /* Round the fragment size down to be block aligned */ - if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { - size -= size % active->cipher_suite->record_alg->cipher->io.cbc.block_size; - } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - size -= size % active->cipher_suite->record_alg->cipher->io.comp.block_size; - /* Composite digest length */ - size -= active->cipher_suite->record_alg->cipher->io.comp.mac_key_size; - /* Padding length byte */ - size -= 1; - } - - /* If TLS1.3, remove content type */ - if (conn->actual_protocol_version >= S2N_TLS13) { - RESULT_ENSURE(size > S2N_TLS_CONTENT_TYPE_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - size -= S2N_TLS_CONTENT_TYPE_LENGTH; - } - - /* subtract overheads of a TLS record */ - uint16_t overhead = 0; - RESULT_GUARD(s2n_tls_record_overhead(conn, &overhead)); - RESULT_ENSURE(size > overhead, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - size -= overhead; - - RESULT_ENSURE(size > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - RESULT_ENSURE(size <= ETH_MTU, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - - *payload_size = size; - - return S2N_RESULT_OK; -} - -int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out) -{ - uint8_t record_protocol_version = conn->actual_protocol_version; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *# This version value is historical, deriving from the use of 0x0301 for - *# TLS 1.0 and 0x0300 for SSL 3.0. In order to maximize backward - *# compatibility, a record containing an initial ClientHello SHOULD have - *# version 0x0301 (reflecting TLS 1.0) - * - * We set actual_protocol_version early for clients, but we do not - * use that assumed value here in case we are talking to a legacy - * server that expects TLS1.0. - * - * Both TLS 1.3 early data and a deserialized connection will - * send data without the server_protocol_version being known. However, - * the record type would be set to APPLICATION_DATA in their cases - * so this check is avoided. - **/ - if (conn->server_protocol_version == s2n_unknown_protocol_version - && record_type == TLS_HANDSHAKE) { - record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS10); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *# legacy_record_version: MUST be set to 0x0303 for all records - *# generated by a TLS 1.3 implementation other than an initial - *# ClientHello (i.e., one not generated after a HelloRetryRequest), - *# where it MAY also be 0x0301 for compatibility purposes. - **/ - record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS12); - - /* Never send an empty protocol version. - * If the protocol version is unknown, default to TLS1.0 like we do for initial ClientHellos. - */ - if (record_protocol_version == s2n_unknown_protocol_version) { - record_protocol_version = S2N_TLS10; - } - - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - protocol_version[0] = record_protocol_version / 10; - protocol_version[1] = record_protocol_version % 10; - - POSIX_GUARD(s2n_stuffer_write_bytes(out, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - return 0; -} - -static inline int s2n_record_encrypt( - struct s2n_connection *conn, - const struct s2n_cipher_suite *cipher_suite, - struct s2n_session_key *session_key, - struct s2n_blob *iv, - struct s2n_blob *aad, - struct s2n_blob *en, - uint8_t *implicit_iv, uint16_t block_size) -{ - POSIX_ENSURE_REF(en->data); - - switch (cipher_suite->record_alg->cipher->type) { - case S2N_STREAM: - POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.encrypt(session_key, en, en)); - break; - case S2N_CBC: - POSIX_GUARD(cipher_suite->record_alg->cipher->io.cbc.encrypt(session_key, iv, en, en)); - - /* Copy the last encrypted block to be the next IV */ - if (conn->actual_protocol_version < S2N_TLS11) { - POSIX_ENSURE_GTE(en->size, block_size); - POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); - } - break; - case S2N_AEAD: - POSIX_GUARD(cipher_suite->record_alg->cipher->io.aead.encrypt(session_key, iv, aad, en, en)); - break; - case S2N_COMPOSITE: - /* This will: compute mac, append padding, append padding length, and encrypt */ - POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.encrypt(session_key, iv, en, en)); - - /* Copy the last encrypted block to be the next IV */ - POSIX_ENSURE_GTE(en->size, block_size); - POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); - break; - default: - POSIX_BAIL(S2N_ERR_CIPHER_TYPE); - break; - } - - return 0; -} - -static S2N_RESULT s2n_record_write_mac(struct s2n_connection *conn, struct s2n_blob *record_header, - struct s2n_blob *plaintext, struct s2n_stuffer *out, uint32_t *bytes_written) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->server); - RESULT_ENSURE_REF(conn->client); - RESULT_ENSURE_REF(record_header); - RESULT_ENSURE_REF(plaintext); - RESULT_ENSURE_REF(out); - RESULT_ENSURE_REF(bytes_written); - *bytes_written = 0; - - struct s2n_hmac_state *mac = &conn->server->server_record_mac; - const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; - uint8_t *sequence_number = conn->server->server_sequence_number; - - if (conn->mode == S2N_CLIENT) { - mac = &conn->client->client_record_mac; - cipher_suite = conn->client->cipher_suite; - sequence_number = conn->client->client_sequence_number; - } - - RESULT_ENSURE_REF(cipher_suite); - RESULT_ENSURE_REF(cipher_suite->record_alg); - - if (cipher_suite->record_alg->hmac_alg == S2N_HMAC_NONE) { - /* If the S2N_HMAC_NONE algorithm is specified, a MAC should not be explicitly written. - * This is the case for AEAD and Composite cipher types, where the MAC is written as part - * of encryption. This is also the case for plaintext handshake records, where the null - * stream cipher is used. - */ - return S2N_RESULT_OK; - } - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# The MAC is generated as: - *# - *# MAC(MAC_write_key, seq_num + - */ - RESULT_GUARD_POSIX(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - - struct s2n_stuffer header_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&header_stuffer, record_header)); - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.type + - */ - void *record_type_byte = s2n_stuffer_raw_read(&header_stuffer, sizeof(uint8_t)); - RESULT_ENSURE_REF(record_type_byte); - RESULT_GUARD_POSIX(s2n_hmac_update(mac, record_type_byte, sizeof(uint8_t))); - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.version + - */ - void *protocol_version_bytes = s2n_stuffer_raw_read(&header_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN); - RESULT_ENSURE_REF(protocol_version_bytes); - if (conn->actual_protocol_version > S2N_SSLv3) { - /* SSLv3 doesn't include the protocol version in the MAC. */ - RESULT_GUARD_POSIX(s2n_hmac_update(mac, protocol_version_bytes, S2N_TLS_PROTOCOL_VERSION_LEN)); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.length + - * - * Note that the length field refers to the length of the plaintext content, not the length of - * TLSCiphertext fragment written to the record header, which accounts for additional fields - * such as the padding and MAC. - */ - uint8_t content_length_bytes[sizeof(uint16_t)] = { 0 }; - struct s2n_blob content_length_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&content_length_blob, content_length_bytes, sizeof(content_length_bytes))); - struct s2n_stuffer content_length_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&content_length_stuffer, &content_length_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&content_length_stuffer, plaintext->size)); - RESULT_GUARD_POSIX(s2n_hmac_update(mac, content_length_bytes, sizeof(content_length_bytes))); - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.fragment); - *# - *# where "+" denotes concatenation. - */ - RESULT_GUARD_POSIX(s2n_hmac_update(mac, plaintext->data, plaintext->size)); - - uint8_t mac_digest_size = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); - uint8_t *digest = s2n_stuffer_raw_write(out, mac_digest_size); - RESULT_ENSURE_REF(digest); - RESULT_GUARD_POSIX(s2n_hmac_digest(mac, digest, mac_digest_size)); - *bytes_written = mac_digest_size; - - RESULT_GUARD_POSIX(s2n_hmac_reset(mac)); - - return S2N_RESULT_OK; -} - -int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const struct iovec *in, int in_count, size_t offs, size_t to_write) -{ - if (conn->ktls_send_enabled) { - return s2n_ktls_record_writev(conn, content_type, in, in_count, offs, to_write); - } - - struct s2n_blob iv = { 0 }; - uint8_t padding = 0; - uint16_t block_size = 0; - uint8_t aad_iv[S2N_TLS_MAX_IV_LEN] = { 0 }; - - /* In TLS 1.3, handle CCS message as unprotected records */ - struct s2n_crypto_parameters *current_client_crypto = conn->client; - struct s2n_crypto_parameters *current_server_crypto = conn->server; - if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { - POSIX_ENSURE_REF(conn->initial); - conn->client = conn->initial; - conn->server = conn->initial; - } - - uint8_t *sequence_number = conn->server->server_sequence_number; - struct s2n_session_key *session_key = &conn->server->server_key; - const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; - uint8_t *implicit_iv = conn->server->server_implicit_iv; - - if (conn->mode == S2N_CLIENT) { - sequence_number = conn->client->client_sequence_number; - session_key = &conn->client->client_key; - cipher_suite = conn->client->cipher_suite; - implicit_iv = conn->client->client_implicit_iv; - } - - /* The NULL stream cipher MUST NEVER be used for ApplicationData. - * Writing ApplicationData unencrypted defeats the purpose of TLS. */ - if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { - POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_ENCRYPT); - } - - const int is_tls13_record = cipher_suite->record_alg->flags & S2N_TLS13_RECORD_AEAD_NONCE; - s2n_stack_blob(aad, is_tls13_record ? S2N_TLS13_AAD_LEN : S2N_TLS_MAX_AAD_LEN, S2N_TLS_MAX_AAD_LEN); - - /* If we aren't buffering multiple records, then the output stuffer should be empty. */ - if (!conn->multirecord_send) { - POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - } - - /* Before we do anything, we need to figure out what the length of the - * fragment is going to be. - */ - uint16_t max_write_payload_size = 0; - POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_write_payload_size)); - const uint16_t data_bytes_to_take = S2N_MIN(to_write, max_write_payload_size); - - uint16_t extra = 0; - POSIX_GUARD_RESULT(s2n_tls_record_overhead(conn, &extra)); - - /* If we have padding to worry about, figure that out too */ - if (cipher_suite->record_alg->cipher->type == S2N_CBC) { - block_size = cipher_suite->record_alg->cipher->io.cbc.block_size; - if (((data_bytes_to_take + extra) % block_size)) { - padding = block_size - ((data_bytes_to_take + extra) % block_size); - } - } else if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - block_size = cipher_suite->record_alg->cipher->io.comp.block_size; - } - - if (s2n_stuffer_is_freed(&conn->out)) { - /* If the output buffer has not been allocated yet, allocate - * at least enough memory to hold a record with the local maximum fragment length. - * - * The local maximum fragment length is: - * 1) The local default configured for new connections - * 2) The local value set by the user via s2n_connection_prefer_throughput() - * or s2n_connection_prefer_low_latency() - * 3) On the server, the minimum of the local value and the value negotiated with the - * client via the max_fragment_length extension - * - * Because this only occurs if the output buffer has not been allocated, - * it does NOT resize existing buffers. - */ - uint16_t max_wire_record_size = 0; - POSIX_GUARD_RESULT(s2n_record_max_write_size(conn, max_write_payload_size, &max_wire_record_size)); - - uint32_t buffer_size = S2N_MAX(conn->config->send_buffer_size_override, max_wire_record_size); - POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); - } - - /* A record only local stuffer used to avoid tainting the conn->out stuffer or overwriting - * previous records. It should be used to add an individual record to the out stuffer. - */ - struct s2n_blob record_blob = { 0 }; - struct s2n_stuffer record_stuffer = { 0 }; - POSIX_GUARD(s2n_blob_init(&record_blob, - conn->out.blob.data + conn->out.write_cursor, - s2n_stuffer_space_remaining(&conn->out))); - POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob)); - - /* Now that we know the length, start writing the record */ - uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type); - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type)); - POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer)); - - /* Compute non-payload parts of the MAC(seq num, type, proto vers, fragment length) for composite ciphers. - * Composite "encrypt" will MAC the payload data and fill in padding. - */ - if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - /* Only fragment length is needed for MAC, but the EVP ctrl function needs fragment length + eiv len. */ - uint16_t payload_and_eiv_len = data_bytes_to_take; - if (conn->actual_protocol_version > S2N_TLS10) { - payload_and_eiv_len += block_size; - } - - /* Outputs number of extra bytes required for MAC and padding */ - int pad_and_mac_len = 0; - POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.initial_hmac(session_key, sequence_number, content_type, conn->actual_protocol_version, - payload_and_eiv_len, &pad_and_mac_len)); - extra += pad_and_mac_len; - } - - /* TLS 1.3 protected record occupies one extra byte for content type */ - if (is_tls13_record) { - extra += S2N_TLS_CONTENT_TYPE_LENGTH; - } - - /* Rewrite the length to be the actual fragment length */ - const uint16_t actual_fragment_length = data_bytes_to_take + padding + extra; - /* ensure actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH <= max record length */ - const uint16_t max_record_length = is_tls13_record ? S2N_TLS13_MAXIMUM_RECORD_LENGTH : S2N_TLS_MAXIMUM_RECORD_LENGTH; - S2N_ERROR_IF(actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH > max_record_length, S2N_ERR_RECORD_LENGTH_TOO_LARGE); - POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, actual_fragment_length)); - - /* If we're AEAD, write the sequence number as an IV, and generate the AAD */ - if (cipher_suite->record_alg->cipher->type == S2N_AEAD) { - struct s2n_stuffer iv_stuffer = { 0 }; - POSIX_GUARD(s2n_blob_init(&iv, aad_iv, sizeof(aad_iv))); - POSIX_GUARD(s2n_stuffer_init(&iv_stuffer, &iv)); - - if (cipher_suite->record_alg->flags & S2N_TLS12_AES_GCM_AEAD_NONCE) { - /* Partially explicit nonce. See RFC 5288 Section 3 */ - POSIX_GUARD(s2n_stuffer_write_bytes(&record_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, implicit_iv, cipher_suite->record_alg->cipher->io.aead.fixed_iv_size)); - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - } else if (cipher_suite->record_alg->flags & S2N_TLS12_CHACHA_POLY_AEAD_NONCE || is_tls13_record) { - /* Fully implicit nonce. See RFC7905 Section 2 */ - uint8_t four_zeroes[4] = { 0 }; - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, four_zeroes, 4)); - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - for (int i = 0; i < cipher_suite->record_alg->cipher->io.aead.fixed_iv_size; i++) { - aad_iv[i] = aad_iv[i] ^ implicit_iv[i]; - } - } else { - POSIX_BAIL(S2N_ERR_INVALID_NONCE_TYPE); - } - - /* Set the IV size to the amount of data written */ - iv.size = s2n_stuffer_data_available(&iv_stuffer); - if (is_tls13_record) { - POSIX_GUARD_RESULT(s2n_tls13_aead_aad_init(data_bytes_to_take + S2N_TLS_CONTENT_TYPE_LENGTH, cipher_suite->record_alg->cipher->io.aead.tag_size, &aad)); - } else { - POSIX_GUARD_RESULT(s2n_aead_aad_init(conn, sequence_number, content_type, data_bytes_to_take, &aad)); - } - } else if (cipher_suite->record_alg->cipher->type == S2N_CBC || cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - POSIX_GUARD(s2n_blob_init(&iv, implicit_iv, block_size)); - - /* For TLS1.1/1.2; write the IV with random data */ - if (conn->actual_protocol_version > S2N_TLS10) { - POSIX_GUARD_RESULT(s2n_get_public_random_data(&iv)); - if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - /* Write a separate random block to the record. This will be used along with the previously generated - * iv blob to generate the final explicit_iv for this record. - * - * How? Openssl's AES-CBC stitched encrypt populates the first block of application data with: - * AES(Key, XOR(iv, initial_block)) - * - * If we make initial_block a random block unrelated to random_iv, explicit IV for this record - * is random value based on the two random blobs we just generated: - * AES(Key, XOR(random_iv, explicit_iv_placeholder) == AES(Key, XOR(random_iv, random_iv2)) - * - * NOTE: We can't use the same random IV blob as both the initial block and IV since it will result in: - * AES(Key, XOR(random_iv, random_iv)) == AES(Key, 0), which will be shared by all records in this session. - */ - struct s2n_blob explicit_iv_placeholder = { 0 }; - uint8_t zero_block[S2N_TLS_MAX_IV_LEN] = { 0 }; - POSIX_GUARD(s2n_blob_init(&explicit_iv_placeholder, zero_block, block_size)); - POSIX_GUARD_RESULT(s2n_get_public_random_data(&explicit_iv_placeholder)); - POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &explicit_iv_placeholder)); - } else { - /* We can write the explicit IV directly to the record for non composite CBC because - * s2n starts AES *after* the explicit IV. - */ - POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &iv)); - } - } - } - - /* Write the plaintext data */ - POSIX_GUARD(s2n_stuffer_writev_bytes(&record_stuffer, in, in_count, offs, data_bytes_to_take)); - void *orig_write_ptr = record_stuffer.blob.data + record_stuffer.write_cursor - data_bytes_to_take; - - /* Write the MAC */ - struct s2n_blob header_blob = { 0 }; - POSIX_GUARD(s2n_blob_slice(&record_blob, &header_blob, 0, S2N_TLS_RECORD_HEADER_LENGTH)); - struct s2n_blob plaintext_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&plaintext_blob, orig_write_ptr, data_bytes_to_take)); - uint32_t mac_digest_size = 0; - POSIX_GUARD_RESULT(s2n_record_write_mac(conn, &header_blob, &plaintext_blob, &record_stuffer, &mac_digest_size)); - - /* We are done with this sequence number, so we can increment it */ - struct s2n_blob seq = { 0 }; - POSIX_GUARD(s2n_blob_init(&seq, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - POSIX_GUARD(s2n_increment_sequence_number(&seq)); - - /* Write content type for TLS 1.3 record (RFC 8446 Section 5.2) */ - if (is_tls13_record) { - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, content_type)); - } - - if (cipher_suite->record_alg->cipher->type == S2N_CBC) { - /* Include padding bytes, each with the value 'p', and - * include an extra padding length byte, also with the value 'p'. - */ - for (int i = 0; i <= padding; i++) { - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, padding)); - } - } - - /* Rewind to rewrite/encrypt the packet */ - POSIX_GUARD(s2n_stuffer_rewrite(&record_stuffer)); - - /* Skip the header */ - POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, S2N_TLS_RECORD_HEADER_LENGTH)); - - uint16_t encrypted_length = data_bytes_to_take + mac_digest_size; - switch (cipher_suite->record_alg->cipher->type) { - case S2N_AEAD: - POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, cipher_suite->record_alg->cipher->io.aead.record_iv_size)); - encrypted_length += cipher_suite->record_alg->cipher->io.aead.tag_size; - if (is_tls13_record) { - /* one extra byte for content type */ - encrypted_length += S2N_TLS_CONTENT_TYPE_LENGTH; - } - break; - case S2N_CBC: - if (conn->actual_protocol_version > S2N_TLS10) { - /* Leave the IV alone and unencrypted */ - POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, iv.size)); - } - /* Encrypt the padding and the padding length byte too */ - encrypted_length += padding + 1; - break; - case S2N_COMPOSITE: - /* Composite CBC expects a pointer starting at explicit IV: [Explicit IV | fragment | MAC | padding | padding len ] - * extra will account for the explicit IV len(if applicable), MAC digest len, padding len + padding byte. - */ - encrypted_length += extra; - break; - default: - break; - } - - /* Check that stuffer have enough space to write encrypted record, because raw_write cannot expand tainted stuffer */ - S2N_ERROR_IF(s2n_stuffer_space_remaining(&record_stuffer) < encrypted_length, S2N_ERR_RECORD_STUFFER_SIZE); - - /* Do the encryption */ - struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_write(&record_stuffer, encrypted_length) }; - POSIX_GUARD(s2n_record_encrypt(conn, cipher_suite, session_key, &iv, &aad, &en, implicit_iv, block_size)); - - /* Sync the out stuffer write cursor with the record stuffer. */ - POSIX_GUARD(s2n_stuffer_skip_write(&conn->out, s2n_stuffer_data_available(&record_stuffer))); - - if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { - conn->client = current_client_crypto; - conn->server = current_server_crypto; - } - - return data_bytes_to_take; -} - -S2N_RESULT s2n_record_write(struct s2n_connection *conn, uint8_t content_type, struct s2n_blob *in) -{ - struct iovec iov; - iov.iov_base = in->data; - iov.iov_len = in->size; - int written = s2n_record_writev(conn, content_type, &iov, 1, 0, in->size); - RESULT_GUARD_POSIX(written); - RESULT_ENSURE((uint32_t) written == in->size, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_record.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +extern uint8_t s2n_unknown_protocol_version; + +/* In TLS1.3 the record type is obfuscated as APPLICATION_DATA once the handshake begins to be encrypted. + * The real record type is encrypted and written in the final byte of the record. + * In TLS1.2 the record type is always cleartext. */ +#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type) + +/* How much overhead does the IV, MAC, TAG and padding bytes introduce ? */ +static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_MUT(out); + struct s2n_crypto_parameters *active = conn->server; + + if (conn->mode == S2N_CLIENT) { + active = conn->client; + } + + uint8_t extra = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(active->cipher_suite->record_alg->hmac_alg, &extra)); + + if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { + /* Subtract one for the padding length byte */ + extra += 1; + + if (conn->actual_protocol_version > S2N_TLS10) { + extra += active->cipher_suite->record_alg->cipher->io.cbc.record_iv_size; + } + } else if (active->cipher_suite->record_alg->cipher->type == S2N_AEAD) { + extra += active->cipher_suite->record_alg->cipher->io.aead.tag_size; + extra += active->cipher_suite->record_alg->cipher->io.aead.record_iv_size; + } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE && conn->actual_protocol_version > S2N_TLS10) { + extra += active->cipher_suite->record_alg->cipher->io.comp.record_iv_size; + } + + *out = extra; + + return S2N_RESULT_OK; +} + +/* This function returns maximum size of plaintext data to write for the payload. + * Record overheads are not included here. + */ +S2N_RESULT s2n_record_max_write_payload_size(struct s2n_connection *conn, uint16_t *max_fragment_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_MUT(max_fragment_size); + RESULT_ENSURE(conn->max_outgoing_fragment_length > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + + *max_fragment_size = S2N_MIN(conn->max_outgoing_fragment_length, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* If a custom send buffer is configured, ensure it will be large enough for the payload. + * That may mean we need a smaller fragment size. + */ + uint32_t send_buffer_override = conn->config->send_buffer_size_override; + if (send_buffer_override) { + uint16_t max_record_size = 0; + RESULT_GUARD(s2n_record_max_write_size(conn, *max_fragment_size, &max_record_size)); + if (send_buffer_override < max_record_size) { + size_t overhead = (max_record_size - *max_fragment_size); + RESULT_ENSURE_GT(send_buffer_override, overhead); + *max_fragment_size = send_buffer_override - overhead; + } + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_record_max_write_size(struct s2n_connection *conn, uint16_t max_fragment_size, uint16_t *max_record_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_MUT(max_record_size); + + if (!IS_NEGOTIATED(conn)) { + *max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(max_fragment_size); + } else if (conn->actual_protocol_version < S2N_TLS13) { + *max_record_size = S2N_TLS12_MAX_RECORD_LEN_FOR(max_fragment_size); + } else { + *max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(max_fragment_size); + } + return S2N_RESULT_OK; +} + +/* Find the largest size that will fit within an ethernet frame for a "small" payload */ +S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16_t *payload_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_MUT(payload_size); + + /* remove ethernet, TCP/IP and TLS header overheads */ + const uint16_t min_outgoing_fragment_length = ETH_MTU - (conn->ipv6 ? IP_V6_HEADER_LENGTH : IP_V4_HEADER_LENGTH) + - TCP_HEADER_LENGTH - TCP_OPTIONS_LENGTH - S2N_TLS_RECORD_HEADER_LENGTH; + + RESULT_ENSURE(min_outgoing_fragment_length <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + uint16_t size = min_outgoing_fragment_length; + + const struct s2n_crypto_parameters *active = conn->mode == S2N_CLIENT ? conn->client : conn->server; + + /* Round the fragment size down to be block aligned */ + if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { + size -= size % active->cipher_suite->record_alg->cipher->io.cbc.block_size; + } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + size -= size % active->cipher_suite->record_alg->cipher->io.comp.block_size; + /* Composite digest length */ + size -= active->cipher_suite->record_alg->cipher->io.comp.mac_key_size; + /* Padding length byte */ + size -= 1; + } + + /* If TLS1.3, remove content type */ + if (conn->actual_protocol_version >= S2N_TLS13) { + RESULT_ENSURE(size > S2N_TLS_CONTENT_TYPE_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + size -= S2N_TLS_CONTENT_TYPE_LENGTH; + } + + /* subtract overheads of a TLS record */ + uint16_t overhead = 0; + RESULT_GUARD(s2n_tls_record_overhead(conn, &overhead)); + RESULT_ENSURE(size > overhead, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + size -= overhead; + + RESULT_ENSURE(size > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + RESULT_ENSURE(size <= ETH_MTU, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + + *payload_size = size; + + return S2N_RESULT_OK; +} + +int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out) +{ + uint8_t record_protocol_version = conn->actual_protocol_version; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *# This version value is historical, deriving from the use of 0x0301 for + *# TLS 1.0 and 0x0300 for SSL 3.0. In order to maximize backward + *# compatibility, a record containing an initial ClientHello SHOULD have + *# version 0x0301 (reflecting TLS 1.0) + * + * We set actual_protocol_version early for clients, but we do not + * use that assumed value here in case we are talking to a legacy + * server that expects TLS1.0. + * + * Both TLS 1.3 early data and a deserialized connection will + * send data without the server_protocol_version being known. However, + * the record type would be set to APPLICATION_DATA in their cases + * so this check is avoided. + **/ + if (conn->server_protocol_version == s2n_unknown_protocol_version + && record_type == TLS_HANDSHAKE) { + record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS10); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *# legacy_record_version: MUST be set to 0x0303 for all records + *# generated by a TLS 1.3 implementation other than an initial + *# ClientHello (i.e., one not generated after a HelloRetryRequest), + *# where it MAY also be 0x0301 for compatibility purposes. + **/ + record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS12); + + /* Never send an empty protocol version. + * If the protocol version is unknown, default to TLS1.0 like we do for initial ClientHellos. + */ + if (record_protocol_version == s2n_unknown_protocol_version) { + record_protocol_version = S2N_TLS10; + } + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + protocol_version[0] = record_protocol_version / 10; + protocol_version[1] = record_protocol_version % 10; + + POSIX_GUARD(s2n_stuffer_write_bytes(out, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + return 0; +} + +static inline int s2n_record_encrypt( + struct s2n_connection *conn, + const struct s2n_cipher_suite *cipher_suite, + struct s2n_session_key *session_key, + struct s2n_blob *iv, + struct s2n_blob *aad, + struct s2n_blob *en, + uint8_t *implicit_iv, uint16_t block_size) +{ + POSIX_ENSURE_REF(en->data); + + switch (cipher_suite->record_alg->cipher->type) { + case S2N_STREAM: + POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.encrypt(session_key, en, en)); + break; + case S2N_CBC: + POSIX_GUARD(cipher_suite->record_alg->cipher->io.cbc.encrypt(session_key, iv, en, en)); + + /* Copy the last encrypted block to be the next IV */ + if (conn->actual_protocol_version < S2N_TLS11) { + POSIX_ENSURE_GTE(en->size, block_size); + POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); + } + break; + case S2N_AEAD: + POSIX_GUARD(cipher_suite->record_alg->cipher->io.aead.encrypt(session_key, iv, aad, en, en)); + break; + case S2N_COMPOSITE: + /* This will: compute mac, append padding, append padding length, and encrypt */ + POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.encrypt(session_key, iv, en, en)); + + /* Copy the last encrypted block to be the next IV */ + POSIX_ENSURE_GTE(en->size, block_size); + POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); + break; + default: + POSIX_BAIL(S2N_ERR_CIPHER_TYPE); + break; + } + + return 0; +} + +static S2N_RESULT s2n_record_write_mac(struct s2n_connection *conn, struct s2n_blob *record_header, + struct s2n_blob *plaintext, struct s2n_stuffer *out, uint32_t *bytes_written) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->server); + RESULT_ENSURE_REF(conn->client); + RESULT_ENSURE_REF(record_header); + RESULT_ENSURE_REF(plaintext); + RESULT_ENSURE_REF(out); + RESULT_ENSURE_REF(bytes_written); + *bytes_written = 0; + + struct s2n_hmac_state *mac = &conn->server->server_record_mac; + const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; + uint8_t *sequence_number = conn->server->server_sequence_number; + + if (conn->mode == S2N_CLIENT) { + mac = &conn->client->client_record_mac; + cipher_suite = conn->client->cipher_suite; + sequence_number = conn->client->client_sequence_number; + } + + RESULT_ENSURE_REF(cipher_suite); + RESULT_ENSURE_REF(cipher_suite->record_alg); + + if (cipher_suite->record_alg->hmac_alg == S2N_HMAC_NONE) { + /* If the S2N_HMAC_NONE algorithm is specified, a MAC should not be explicitly written. + * This is the case for AEAD and Composite cipher types, where the MAC is written as part + * of encryption. This is also the case for plaintext handshake records, where the null + * stream cipher is used. + */ + return S2N_RESULT_OK; + } + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# The MAC is generated as: + *# + *# MAC(MAC_write_key, seq_num + + */ + RESULT_GUARD_POSIX(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + struct s2n_stuffer header_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&header_stuffer, record_header)); + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.type + + */ + void *record_type_byte = s2n_stuffer_raw_read(&header_stuffer, sizeof(uint8_t)); + RESULT_ENSURE_REF(record_type_byte); + RESULT_GUARD_POSIX(s2n_hmac_update(mac, record_type_byte, sizeof(uint8_t))); + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.version + + */ + void *protocol_version_bytes = s2n_stuffer_raw_read(&header_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN); + RESULT_ENSURE_REF(protocol_version_bytes); + if (conn->actual_protocol_version > S2N_SSLv3) { + /* SSLv3 doesn't include the protocol version in the MAC. */ + RESULT_GUARD_POSIX(s2n_hmac_update(mac, protocol_version_bytes, S2N_TLS_PROTOCOL_VERSION_LEN)); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.length + + * + * Note that the length field refers to the length of the plaintext content, not the length of + * TLSCiphertext fragment written to the record header, which accounts for additional fields + * such as the padding and MAC. + */ + uint8_t content_length_bytes[sizeof(uint16_t)] = { 0 }; + struct s2n_blob content_length_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&content_length_blob, content_length_bytes, sizeof(content_length_bytes))); + struct s2n_stuffer content_length_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&content_length_stuffer, &content_length_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&content_length_stuffer, plaintext->size)); + RESULT_GUARD_POSIX(s2n_hmac_update(mac, content_length_bytes, sizeof(content_length_bytes))); + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.fragment); + *# + *# where "+" denotes concatenation. + */ + RESULT_GUARD_POSIX(s2n_hmac_update(mac, plaintext->data, plaintext->size)); + + uint8_t mac_digest_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); + uint8_t *digest = s2n_stuffer_raw_write(out, mac_digest_size); + RESULT_ENSURE_REF(digest); + RESULT_GUARD_POSIX(s2n_hmac_digest(mac, digest, mac_digest_size)); + *bytes_written = mac_digest_size; + + RESULT_GUARD_POSIX(s2n_hmac_reset(mac)); + + return S2N_RESULT_OK; +} + +int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const struct iovec *in, int in_count, size_t offs, size_t to_write) +{ + if (conn->ktls_send_enabled) { + return s2n_ktls_record_writev(conn, content_type, in, in_count, offs, to_write); + } + + struct s2n_blob iv = { 0 }; + uint8_t padding = 0; + uint16_t block_size = 0; + uint8_t aad_iv[S2N_TLS_MAX_IV_LEN] = { 0 }; + + /* In TLS 1.3, handle CCS message as unprotected records */ + struct s2n_crypto_parameters *current_client_crypto = conn->client; + struct s2n_crypto_parameters *current_server_crypto = conn->server; + if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { + POSIX_ENSURE_REF(conn->initial); + conn->client = conn->initial; + conn->server = conn->initial; + } + + uint8_t *sequence_number = conn->server->server_sequence_number; + struct s2n_session_key *session_key = &conn->server->server_key; + const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; + uint8_t *implicit_iv = conn->server->server_implicit_iv; + + if (conn->mode == S2N_CLIENT) { + sequence_number = conn->client->client_sequence_number; + session_key = &conn->client->client_key; + cipher_suite = conn->client->cipher_suite; + implicit_iv = conn->client->client_implicit_iv; + } + + /* The NULL stream cipher MUST NEVER be used for ApplicationData. + * Writing ApplicationData unencrypted defeats the purpose of TLS. */ + if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { + POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_ENCRYPT); + } + + const int is_tls13_record = cipher_suite->record_alg->flags & S2N_TLS13_RECORD_AEAD_NONCE; + s2n_stack_blob(aad, is_tls13_record ? S2N_TLS13_AAD_LEN : S2N_TLS_MAX_AAD_LEN, S2N_TLS_MAX_AAD_LEN); + + /* If we aren't buffering multiple records, then the output stuffer should be empty. */ + if (!conn->multirecord_send) { + POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + } + + /* Before we do anything, we need to figure out what the length of the + * fragment is going to be. + */ + uint16_t max_write_payload_size = 0; + POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_write_payload_size)); + const uint16_t data_bytes_to_take = S2N_MIN(to_write, max_write_payload_size); + + uint16_t extra = 0; + POSIX_GUARD_RESULT(s2n_tls_record_overhead(conn, &extra)); + + /* If we have padding to worry about, figure that out too */ + if (cipher_suite->record_alg->cipher->type == S2N_CBC) { + block_size = cipher_suite->record_alg->cipher->io.cbc.block_size; + if (((data_bytes_to_take + extra) % block_size)) { + padding = block_size - ((data_bytes_to_take + extra) % block_size); + } + } else if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + block_size = cipher_suite->record_alg->cipher->io.comp.block_size; + } + + if (s2n_stuffer_is_freed(&conn->out)) { + /* If the output buffer has not been allocated yet, allocate + * at least enough memory to hold a record with the local maximum fragment length. + * + * The local maximum fragment length is: + * 1) The local default configured for new connections + * 2) The local value set by the user via s2n_connection_prefer_throughput() + * or s2n_connection_prefer_low_latency() + * 3) On the server, the minimum of the local value and the value negotiated with the + * client via the max_fragment_length extension + * + * Because this only occurs if the output buffer has not been allocated, + * it does NOT resize existing buffers. + */ + uint16_t max_wire_record_size = 0; + POSIX_GUARD_RESULT(s2n_record_max_write_size(conn, max_write_payload_size, &max_wire_record_size)); + + uint32_t buffer_size = S2N_MAX(conn->config->send_buffer_size_override, max_wire_record_size); + POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); + } + + /* A record only local stuffer used to avoid tainting the conn->out stuffer or overwriting + * previous records. It should be used to add an individual record to the out stuffer. + */ + struct s2n_blob record_blob = { 0 }; + struct s2n_stuffer record_stuffer = { 0 }; + POSIX_GUARD(s2n_blob_init(&record_blob, + conn->out.blob.data + conn->out.write_cursor, + s2n_stuffer_space_remaining(&conn->out))); + POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob)); + + /* Now that we know the length, start writing the record */ + uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type); + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type)); + POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer)); + + /* Compute non-payload parts of the MAC(seq num, type, proto vers, fragment length) for composite ciphers. + * Composite "encrypt" will MAC the payload data and fill in padding. + */ + if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + /* Only fragment length is needed for MAC, but the EVP ctrl function needs fragment length + eiv len. */ + uint16_t payload_and_eiv_len = data_bytes_to_take; + if (conn->actual_protocol_version > S2N_TLS10) { + payload_and_eiv_len += block_size; + } + + /* Outputs number of extra bytes required for MAC and padding */ + int pad_and_mac_len = 0; + POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.initial_hmac(session_key, sequence_number, content_type, conn->actual_protocol_version, + payload_and_eiv_len, &pad_and_mac_len)); + extra += pad_and_mac_len; + } + + /* TLS 1.3 protected record occupies one extra byte for content type */ + if (is_tls13_record) { + extra += S2N_TLS_CONTENT_TYPE_LENGTH; + } + + /* Rewrite the length to be the actual fragment length */ + const uint16_t actual_fragment_length = data_bytes_to_take + padding + extra; + /* ensure actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH <= max record length */ + const uint16_t max_record_length = is_tls13_record ? S2N_TLS13_MAXIMUM_RECORD_LENGTH : S2N_TLS_MAXIMUM_RECORD_LENGTH; + S2N_ERROR_IF(actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH > max_record_length, S2N_ERR_RECORD_LENGTH_TOO_LARGE); + POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, actual_fragment_length)); + + /* If we're AEAD, write the sequence number as an IV, and generate the AAD */ + if (cipher_suite->record_alg->cipher->type == S2N_AEAD) { + struct s2n_stuffer iv_stuffer = { 0 }; + POSIX_GUARD(s2n_blob_init(&iv, aad_iv, sizeof(aad_iv))); + POSIX_GUARD(s2n_stuffer_init(&iv_stuffer, &iv)); + + if (cipher_suite->record_alg->flags & S2N_TLS12_AES_GCM_AEAD_NONCE) { + /* Partially explicit nonce. See RFC 5288 Section 3 */ + POSIX_GUARD(s2n_stuffer_write_bytes(&record_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, implicit_iv, cipher_suite->record_alg->cipher->io.aead.fixed_iv_size)); + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + } else if (cipher_suite->record_alg->flags & S2N_TLS12_CHACHA_POLY_AEAD_NONCE || is_tls13_record) { + /* Fully implicit nonce. See RFC7905 Section 2 */ + uint8_t four_zeroes[4] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, four_zeroes, 4)); + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + for (int i = 0; i < cipher_suite->record_alg->cipher->io.aead.fixed_iv_size; i++) { + aad_iv[i] = aad_iv[i] ^ implicit_iv[i]; + } + } else { + POSIX_BAIL(S2N_ERR_INVALID_NONCE_TYPE); + } + + /* Set the IV size to the amount of data written */ + iv.size = s2n_stuffer_data_available(&iv_stuffer); + if (is_tls13_record) { + POSIX_GUARD_RESULT(s2n_tls13_aead_aad_init(data_bytes_to_take + S2N_TLS_CONTENT_TYPE_LENGTH, cipher_suite->record_alg->cipher->io.aead.tag_size, &aad)); + } else { + POSIX_GUARD_RESULT(s2n_aead_aad_init(conn, sequence_number, content_type, data_bytes_to_take, &aad)); + } + } else if (cipher_suite->record_alg->cipher->type == S2N_CBC || cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + POSIX_GUARD(s2n_blob_init(&iv, implicit_iv, block_size)); + + /* For TLS1.1/1.2; write the IV with random data */ + if (conn->actual_protocol_version > S2N_TLS10) { + POSIX_GUARD_RESULT(s2n_get_public_random_data(&iv)); + if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + /* Write a separate random block to the record. This will be used along with the previously generated + * iv blob to generate the final explicit_iv for this record. + * + * How? Openssl's AES-CBC stitched encrypt populates the first block of application data with: + * AES(Key, XOR(iv, initial_block)) + * + * If we make initial_block a random block unrelated to random_iv, explicit IV for this record + * is random value based on the two random blobs we just generated: + * AES(Key, XOR(random_iv, explicit_iv_placeholder) == AES(Key, XOR(random_iv, random_iv2)) + * + * NOTE: We can't use the same random IV blob as both the initial block and IV since it will result in: + * AES(Key, XOR(random_iv, random_iv)) == AES(Key, 0), which will be shared by all records in this session. + */ + struct s2n_blob explicit_iv_placeholder = { 0 }; + uint8_t zero_block[S2N_TLS_MAX_IV_LEN] = { 0 }; + POSIX_GUARD(s2n_blob_init(&explicit_iv_placeholder, zero_block, block_size)); + POSIX_GUARD_RESULT(s2n_get_public_random_data(&explicit_iv_placeholder)); + POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &explicit_iv_placeholder)); + } else { + /* We can write the explicit IV directly to the record for non composite CBC because + * s2n starts AES *after* the explicit IV. + */ + POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &iv)); + } + } + } + + /* Write the plaintext data */ + POSIX_GUARD(s2n_stuffer_writev_bytes(&record_stuffer, in, in_count, offs, data_bytes_to_take)); + void *orig_write_ptr = record_stuffer.blob.data + record_stuffer.write_cursor - data_bytes_to_take; + + /* Write the MAC */ + struct s2n_blob header_blob = { 0 }; + POSIX_GUARD(s2n_blob_slice(&record_blob, &header_blob, 0, S2N_TLS_RECORD_HEADER_LENGTH)); + struct s2n_blob plaintext_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&plaintext_blob, orig_write_ptr, data_bytes_to_take)); + uint32_t mac_digest_size = 0; + POSIX_GUARD_RESULT(s2n_record_write_mac(conn, &header_blob, &plaintext_blob, &record_stuffer, &mac_digest_size)); + + /* We are done with this sequence number, so we can increment it */ + struct s2n_blob seq = { 0 }; + POSIX_GUARD(s2n_blob_init(&seq, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + POSIX_GUARD(s2n_increment_sequence_number(&seq)); + + /* Write content type for TLS 1.3 record (RFC 8446 Section 5.2) */ + if (is_tls13_record) { + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, content_type)); + } + + if (cipher_suite->record_alg->cipher->type == S2N_CBC) { + /* Include padding bytes, each with the value 'p', and + * include an extra padding length byte, also with the value 'p'. + */ + for (int i = 0; i <= padding; i++) { + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, padding)); + } + } + + /* Rewind to rewrite/encrypt the packet */ + POSIX_GUARD(s2n_stuffer_rewrite(&record_stuffer)); + + /* Skip the header */ + POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, S2N_TLS_RECORD_HEADER_LENGTH)); + + uint16_t encrypted_length = data_bytes_to_take + mac_digest_size; + switch (cipher_suite->record_alg->cipher->type) { + case S2N_AEAD: + POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, cipher_suite->record_alg->cipher->io.aead.record_iv_size)); + encrypted_length += cipher_suite->record_alg->cipher->io.aead.tag_size; + if (is_tls13_record) { + /* one extra byte for content type */ + encrypted_length += S2N_TLS_CONTENT_TYPE_LENGTH; + } + break; + case S2N_CBC: + if (conn->actual_protocol_version > S2N_TLS10) { + /* Leave the IV alone and unencrypted */ + POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, iv.size)); + } + /* Encrypt the padding and the padding length byte too */ + encrypted_length += padding + 1; + break; + case S2N_COMPOSITE: + /* Composite CBC expects a pointer starting at explicit IV: [Explicit IV | fragment | MAC | padding | padding len ] + * extra will account for the explicit IV len(if applicable), MAC digest len, padding len + padding byte. + */ + encrypted_length += extra; + break; + default: + break; + } + + /* Check that stuffer have enough space to write encrypted record, because raw_write cannot expand tainted stuffer */ + S2N_ERROR_IF(s2n_stuffer_space_remaining(&record_stuffer) < encrypted_length, S2N_ERR_RECORD_STUFFER_SIZE); + + /* Do the encryption */ + struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_write(&record_stuffer, encrypted_length) }; + POSIX_GUARD(s2n_record_encrypt(conn, cipher_suite, session_key, &iv, &aad, &en, implicit_iv, block_size)); + + /* Sync the out stuffer write cursor with the record stuffer. */ + POSIX_GUARD(s2n_stuffer_skip_write(&conn->out, s2n_stuffer_data_available(&record_stuffer))); + + if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { + conn->client = current_client_crypto; + conn->server = current_server_crypto; + } + + return data_bytes_to_take; +} + +S2N_RESULT s2n_record_write(struct s2n_connection *conn, uint8_t content_type, struct s2n_blob *in) +{ + struct iovec iov; + iov.iov_base = in->data; + iov.iov_len = in->size; + int written = s2n_record_writev(conn, content_type, &iov, 1, 0, in->size); + RESULT_GUARD_POSIX(written); + RESULT_ENSURE((uint32_t) written == in->size, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + return S2N_RESULT_OK; +} diff --git a/tls/s2n_recv.c b/tls/s2n_recv.c index 55988ebf3ba..096dcd870f5 100644 --- a/tls/s2n_recv.c +++ b/tls/s2n_recv.c @@ -1,329 +1,332 @@ -/* - * 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. - */ - -/* Use usleep */ -#define _XOPEN_SOURCE 500 -#include -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_io.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" - -S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t total) -{ - RESULT_ENSURE_REF(conn); - - /* If we're going to initialize conn->in to point to more memory than - * is actually readable, make sure that the additional memory exists. - */ - RESULT_ENSURE_LTE(written, total); - uint32_t remaining = total - written; - RESULT_ENSURE_LTE(remaining, s2n_stuffer_space_remaining(&conn->buffer_in)); - - uint8_t *data = s2n_stuffer_raw_read(&conn->buffer_in, written); - RESULT_ENSURE_REF(data); - RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); - RESULT_GUARD_POSIX(s2n_blob_init(&conn->in.blob, data, total)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&conn->in, written)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length) -{ - while (s2n_stuffer_data_available(output) < length) { - uint32_t remaining = length - s2n_stuffer_data_available(output); - if (conn->recv_buffering) { - remaining = S2N_MAX(remaining, s2n_stuffer_space_remaining(output)); - } - errno = 0; - int r = s2n_connection_recv_stuffer(output, conn, remaining); - if (r == 0) { - s2n_atomic_flag_set(&conn->read_closed); - } - RESULT_GUARD(s2n_io_check_read_result(r)); - conn->wire_bytes_in += r; - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_recv_buffer_in(struct s2n_connection *conn, size_t min_size) -{ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_LARGE_FRAGMENT_LENGTH)); - uint32_t buffer_in_available = s2n_stuffer_data_available(&conn->buffer_in); - if (buffer_in_available < min_size) { - uint32_t remaining = min_size - buffer_in_available; - if (s2n_stuffer_space_remaining(&conn->buffer_in) < remaining) { - RESULT_GUARD_POSIX(s2n_stuffer_shift(&conn->buffer_in)); - } - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, min_size)); - } - return S2N_RESULT_OK; -} - -int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2) -{ - *isSSLv2 = 0; - - if (conn->ktls_recv_enabled) { - return s2n_ktls_read_full_record(conn, record_type); - } - - /* If the record has already been decrypted, then leave it alone */ - if (conn->in_status == PLAINTEXT) { - /* Only application data packets count as plaintext */ - *record_type = TLS_APPLICATION_DATA; - return S2N_SUCCESS; - } - - /* Read the record until we at least have a header */ - POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); - uint32_t header_available = s2n_stuffer_data_available(&conn->header_in); - if (header_available < S2N_TLS_RECORD_HEADER_LENGTH) { - uint32_t header_remaining = S2N_TLS_RECORD_HEADER_LENGTH - header_available; - s2n_result ret = s2n_recv_buffer_in(conn, header_remaining); - uint32_t header_read = S2N_MIN(header_remaining, s2n_stuffer_data_available(&conn->buffer_in)); - POSIX_GUARD(s2n_stuffer_copy(&conn->buffer_in, &conn->header_in, header_read)); - POSIX_GUARD_RESULT(ret); - } - - uint16_t fragment_length = 0; - - /* If the first bit is set then this is an SSLv2 record */ - if (conn->header_in.blob.data[0] & S2N_TLS_SSLV2_HEADER_FLAG) { - *isSSLv2 = 1; - WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_sslv2_record_header_parse(conn, record_type, &conn->client_hello.legacy_version, &fragment_length))); - } else { - WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_header_parse(conn, record_type, &fragment_length))); - } - - /* Read enough to have the whole record */ - uint32_t fragment_available = s2n_stuffer_data_available(&conn->in); - if (fragment_available < fragment_length || fragment_length == 0) { - POSIX_GUARD(s2n_stuffer_rewind_read(&conn->buffer_in, fragment_available)); - s2n_result ret = s2n_recv_buffer_in(conn, fragment_length); - uint32_t fragment_read = S2N_MIN(fragment_length, s2n_stuffer_data_available(&conn->buffer_in)); - POSIX_GUARD_RESULT(s2n_recv_in_init(conn, fragment_read, fragment_length)); - POSIX_GUARD_RESULT(ret); - } - - if (*isSSLv2) { - return 0; - } - - /* Decrypt and parse the record */ - if (s2n_early_data_is_trial_decryption_allowed(conn, *record_type)) { - POSIX_ENSURE(s2n_record_parse(conn) >= S2N_SUCCESS, S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); - } else { - WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_parse(conn))); - } - - /* In TLS 1.3, encrypted handshake records would appear to be of record type - * TLS_APPLICATION_DATA. The actual record content type is found after the encrypted - * is decrypted. - */ - if (conn->actual_protocol_version == S2N_TLS13 && *record_type == TLS_APPLICATION_DATA) { - POSIX_GUARD(s2n_tls13_parse_record_type(&conn->in, record_type)); - } - - return 0; -} - -ssize_t s2n_recv_impl(struct s2n_connection *conn, void *buf, ssize_t size_signed, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_GTE(size_signed, 0); - size_t size = size_signed; - ssize_t bytes_read = 0; - struct s2n_blob out = { 0 }; - POSIX_GUARD(s2n_blob_init(&out, (uint8_t *) buf, 0)); - - /* - * Set the `blocked` status to BLOCKED_ON_READ by default - * - * The only case in which it should be updated is on a successful read into the provided buffer. - * - * Unfortunately, the current `blocked` behavior has become ossified by buggy applications that ignore - * error types and only read `blocked`. As such, it's very important to avoid changing how this value is updated - * as it could break applications. - */ - *blocked = S2N_BLOCKED_ON_READ; - - if (!s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#6.1 - *# If a transport-level close - *# is received prior to a "close_notify", the receiver cannot know that - *# all the data that was sent has been received. - * - *= https://www.rfc-editor.org/rfc/rfc8446#6.1 - *# If the application protocol using TLS provides that any data may be - *# carried over the underlying transport after the TLS connection is - *# closed, the TLS implementation MUST receive a "close_notify" alert - *# before indicating end-of-data to the application layer. - */ - POSIX_ENSURE(s2n_atomic_flag_test(&conn->close_notify_received), S2N_ERR_CLOSED); - *blocked = S2N_NOT_BLOCKED; - return 0; - } - - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); - POSIX_GUARD_RESULT(s2n_early_data_validate_recv(conn)); - - while (size && s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { - int isSSLv2 = 0; - uint8_t record_type = 0; - int r = s2n_read_full_record(conn, &record_type, &isSSLv2); - if (r < 0) { - /* Don't propagate the error if we already read some bytes. */ - if (bytes_read && (s2n_errno == S2N_ERR_CLOSED || s2n_errno == S2N_ERR_IO_BLOCKED)) { - break; - } - - /* If we get here, it's an error condition. - * For stateful resumption, invalidate the session on error to prevent resumption with - * potentially corrupted session state. This ensures that a bad session state does not - * lead to repeated failures during resumption attempts. - */ - if (s2n_errno != S2N_ERR_IO_BLOCKED && s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { - conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); - } - - S2N_ERROR_PRESERVE_ERRNO(); - } - - S2N_ERROR_IF(isSSLv2, S2N_ERR_BAD_MESSAGE); - - if (record_type != TLS_HANDSHAKE) { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *# - Handshake messages MUST NOT be interleaved with other record - *# types. That is, if a handshake message is split over two or more - *# records, there MUST NOT be any other records between them. - */ - POSIX_ENSURE(s2n_stuffer_is_wiped(&conn->post_handshake.in), S2N_ERR_BAD_MESSAGE); - - /* If not handling a handshake message, free the post-handshake memory. - * Post-handshake messages are infrequent enough that we don't want to - * keep a potentially large buffer around unnecessarily. - */ - if (!s2n_stuffer_is_freed(&conn->post_handshake.in)) { - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - } - } - - if (record_type != TLS_APPLICATION_DATA) { - switch (record_type) { - case TLS_ALERT: - POSIX_GUARD(s2n_process_alert_fragment(conn)); - break; - case TLS_HANDSHAKE: { - s2n_result result = s2n_post_handshake_recv(conn); - /* Ignore any errors due to insufficient input data from io. - * The next iteration of this loop will attempt to read more input data. - */ - if (s2n_result_is_error(result) && s2n_errno != S2N_ERR_IO_BLOCKED) { - WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); - } - break; - } - } - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - continue; - } - - out.size = S2N_MIN(size, s2n_stuffer_data_available(&conn->in)); - - POSIX_GUARD(s2n_stuffer_erase_and_read(&conn->in, &out)); - bytes_read += out.size; - - out.data += out.size; - size -= out.size; - - /* Are we ready for more encrypted data? */ - if (s2n_stuffer_data_available(&conn->in) == 0) { - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - } - - /* If we've read some data, return it in legacy mode */ - if (bytes_read && !conn->config->recv_multi_record) { - break; - } - } - - /* Due to the history of this API, some applications depend on the blocked status to know if - * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. - * - * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing - * without conflating being blocked on reading from the OS socket vs blocked on the application's - * buffer size. - */ - if (s2n_stuffer_data_available(&conn->in) == 0) { - *blocked = S2N_NOT_BLOCKED; - } - - return bytes_read; -} - -ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked) -{ - POSIX_ENSURE(!conn->recv_in_use, S2N_ERR_REENTRANCY); - conn->recv_in_use = true; - - ssize_t result = s2n_recv_impl(conn, buf, size, blocked); - POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); - - /* finish the recv call */ - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); - - conn->recv_in_use = false; - return result; -} - -uint32_t s2n_peek(struct s2n_connection *conn) -{ - if (conn == NULL) { - return 0; - } - - /* If we have partially buffered an encrypted record, - * we should not report those bytes as available to read. - */ - if (conn->in_status != PLAINTEXT) { - return 0; - } - - return s2n_stuffer_data_available(&conn->in); -} - -uint32_t s2n_peek_buffered(struct s2n_connection *conn) -{ - if (conn == NULL) { - return 0; - } - return s2n_stuffer_data_available(&conn->buffer_in); -} +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Use usleep */ +#define _XOPEN_SOURCE 500 +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_io.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t total) +{ + RESULT_ENSURE_REF(conn); + + /* If we're going to initialize conn->in to point to more memory than + * is actually readable, make sure that the additional memory exists. + */ + RESULT_ENSURE_LTE(written, total); + uint32_t remaining = total - written; + RESULT_ENSURE_LTE(remaining, s2n_stuffer_space_remaining(&conn->buffer_in)); + + uint8_t *data = s2n_stuffer_raw_read(&conn->buffer_in, written); + RESULT_ENSURE_REF(data); + RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); + RESULT_GUARD_POSIX(s2n_blob_init(&conn->in.blob, data, total)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&conn->in, written)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length) +{ + while (s2n_stuffer_data_available(output) < length) { + uint32_t remaining = length - s2n_stuffer_data_available(output); + if (conn->recv_buffering) { + remaining = S2N_MAX(remaining, s2n_stuffer_space_remaining(output)); + } + errno = 0; + int r = s2n_connection_recv_stuffer(output, conn, remaining); + if (r == 0) { + s2n_atomic_flag_set(&conn->read_closed); + } + RESULT_GUARD(s2n_io_check_read_result(r)); + conn->wire_bytes_in += r; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_recv_buffer_in(struct s2n_connection *conn, size_t min_size) +{ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_LARGE_FRAGMENT_LENGTH)); + uint32_t buffer_in_available = s2n_stuffer_data_available(&conn->buffer_in); + if (buffer_in_available < min_size) { + uint32_t remaining = min_size - buffer_in_available; + if (s2n_stuffer_space_remaining(&conn->buffer_in) < remaining) { + RESULT_GUARD_POSIX(s2n_stuffer_shift(&conn->buffer_in)); + } + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, min_size)); + } + return S2N_RESULT_OK; +} + +int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2) +{ + *isSSLv2 = 0; + + if (conn->ktls_recv_enabled) { + return s2n_ktls_read_full_record(conn, record_type); + } + + /* If the record has already been decrypted, then leave it alone */ + if (conn->in_status == PLAINTEXT) { + /* Only application data packets count as plaintext */ + *record_type = TLS_APPLICATION_DATA; + return S2N_SUCCESS; + } + + /* Read the record until we at least have a header */ + POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); + uint32_t header_available = s2n_stuffer_data_available(&conn->header_in); + if (header_available < S2N_TLS_RECORD_HEADER_LENGTH) { + uint32_t header_remaining = S2N_TLS_RECORD_HEADER_LENGTH - header_available; + s2n_result ret = s2n_recv_buffer_in(conn, header_remaining); + uint32_t header_read = S2N_MIN(header_remaining, s2n_stuffer_data_available(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_copy(&conn->buffer_in, &conn->header_in, header_read)); + POSIX_GUARD_RESULT(ret); + } + + uint16_t fragment_length = 0; + + /* If the first bit is set then this is an SSLv2 record */ + if (conn->header_in.blob.data[0] & S2N_TLS_SSLV2_HEADER_FLAG) { + *isSSLv2 = 1; + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_sslv2_record_header_parse(conn, record_type, &conn->client_hello.legacy_version, &fragment_length))); + } else { + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_header_parse(conn, record_type, &fragment_length))); + } + + /* Read enough to have the whole record */ + uint32_t fragment_available = s2n_stuffer_data_available(&conn->in); + if (fragment_available < fragment_length || fragment_length == 0) { + POSIX_GUARD(s2n_stuffer_rewind_read(&conn->buffer_in, fragment_available)); + s2n_result ret = s2n_recv_buffer_in(conn, fragment_length); + uint32_t fragment_read = S2N_MIN(fragment_length, s2n_stuffer_data_available(&conn->buffer_in)); + POSIX_GUARD_RESULT(s2n_recv_in_init(conn, fragment_read, fragment_length)); + POSIX_GUARD_RESULT(ret); + } + + if (*isSSLv2) { + return 0; + } + + /* Decrypt and parse the record */ + if (s2n_early_data_is_trial_decryption_allowed(conn, *record_type)) { + POSIX_ENSURE(s2n_record_parse(conn) >= S2N_SUCCESS, S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); + } else { + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_parse(conn))); + } + + /* In TLS 1.3, encrypted handshake records would appear to be of record type + * TLS_APPLICATION_DATA. The actual record content type is found after the encrypted + * is decrypted. + */ + if (conn->actual_protocol_version == S2N_TLS13 && *record_type == TLS_APPLICATION_DATA) { + POSIX_GUARD(s2n_tls13_parse_record_type(&conn->in, record_type)); + } + + return 0; +} + +ssize_t s2n_recv_impl(struct s2n_connection *conn, void *buf, ssize_t size_signed, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_GTE(size_signed, 0); + size_t size = size_signed; + ssize_t bytes_read = 0; + struct s2n_blob out = { 0 }; + POSIX_GUARD(s2n_blob_init(&out, (uint8_t *) buf, 0)); + + /* + * Set the `blocked` status to BLOCKED_ON_READ by default + * + * The only case in which it should be updated is on a successful read into the provided buffer. + * + * Unfortunately, the current `blocked` behavior has become ossified by buggy applications that ignore + * error types and only read `blocked`. As such, it's very important to avoid changing how this value is updated + * as it could break applications. + */ + *blocked = S2N_BLOCKED_ON_READ; + + if (!s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#6.1 + *# If a transport-level close + *# is received prior to a "close_notify", the receiver cannot know that + *# all the data that was sent has been received. + * + *= https://www.rfc-editor.org/rfc/rfc8446#6.1 + *# If the application protocol using TLS provides that any data may be + *# carried over the underlying transport after the TLS connection is + *# closed, the TLS implementation MUST receive a "close_notify" alert + *# before indicating end-of-data to the application layer. + */ + POSIX_ENSURE(s2n_atomic_flag_test(&conn->close_notify_received), S2N_ERR_CLOSED); + *blocked = S2N_NOT_BLOCKED; + return 0; + } + + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); + POSIX_GUARD_RESULT(s2n_early_data_validate_recv(conn)); + + while (size && s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { + int isSSLv2 = 0; + uint8_t record_type = 0; + int r = s2n_read_full_record(conn, &record_type, &isSSLv2); + if (r < 0) { + /* Don't propagate the error if we already read some bytes. */ + if (bytes_read && (s2n_errno == S2N_ERR_CLOSED || s2n_errno == S2N_ERR_IO_BLOCKED)) { + break; + } + + /* If we get here, it's an error condition. + * For stateful resumption, invalidate the session on error to prevent resumption with + * potentially corrupted session state. This ensures that a bad session state does not + * lead to repeated failures during resumption attempts. + */ + if (s2n_errno != S2N_ERR_IO_BLOCKED && s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { + conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); + } + + S2N_ERROR_PRESERVE_ERRNO(); + } + + S2N_ERROR_IF(isSSLv2, S2N_ERR_BAD_MESSAGE); + + if (record_type != TLS_HANDSHAKE) { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *# - Handshake messages MUST NOT be interleaved with other record + *# types. That is, if a handshake message is split over two or more + *# records, there MUST NOT be any other records between them. + */ + POSIX_ENSURE(s2n_stuffer_is_wiped(&conn->post_handshake.in), S2N_ERR_BAD_MESSAGE); + + /* If not handling a handshake message, free the post-handshake memory. + * Post-handshake messages are infrequent enough that we don't want to + * keep a potentially large buffer around unnecessarily. + */ + if (!s2n_stuffer_is_freed(&conn->post_handshake.in)) { + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + } + } + + if (record_type != TLS_APPLICATION_DATA) { + switch (record_type) { + case TLS_ALERT: + POSIX_GUARD(s2n_process_alert_fragment(conn)); + break; + case TLS_HANDSHAKE: { + s2n_result result = s2n_post_handshake_recv(conn); + /* Ignore any errors due to insufficient input data from io. + * The next iteration of this loop will attempt to read more input data. + */ + if (s2n_result_is_error(result) && s2n_errno != S2N_ERR_IO_BLOCKED) { + WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); + } + break; + } + } + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + continue; + } + + out.size = S2N_MIN(size, s2n_stuffer_data_available(&conn->in)); + + POSIX_GUARD(s2n_stuffer_erase_and_read(&conn->in, &out)); + bytes_read += out.size; + + out.data += out.size; + size -= out.size; + + /* Are we ready for more encrypted data? */ + if (s2n_stuffer_data_available(&conn->in) == 0) { + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + } + + /* If we've read some data, return it in legacy mode */ + if (bytes_read && !conn->config->recv_multi_record) { + break; + } + } + + /* Due to the history of this API, some applications depend on the blocked status to know if + * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. + * + * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing + * without conflating being blocked on reading from the OS socket vs blocked on the application's + * buffer size. + */ + if (s2n_stuffer_data_available(&conn->in) == 0) { + *blocked = S2N_NOT_BLOCKED; + } + + return bytes_read; +} + +ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked) +{ + POSIX_ENSURE(!conn->recv_in_use, S2N_ERR_REENTRANCY); + conn->recv_in_use = true; + + ssize_t result = s2n_recv_impl(conn, buf, size, blocked); + POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); + + /* finish the recv call */ + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); + + conn->recv_in_use = false; + return result; +} + +uint32_t s2n_peek(struct s2n_connection *conn) +{ + if (conn == NULL) { + return 0; + } + + /* If we have partially buffered an encrypted record, + * we should not report those bytes as available to read. + */ + if (conn->in_status != PLAINTEXT) { + return 0; + } + + return s2n_stuffer_data_available(&conn->in); +} + +uint32_t s2n_peek_buffered(struct s2n_connection *conn) +{ + if (conn == NULL) { + return 0; + } + return s2n_stuffer_data_available(&conn->buffer_in); +} diff --git a/tls/s2n_resume.c b/tls/s2n_resume.c index f59527fd28d..c3f54af0810 100644 --- a/tls/s2n_resume.c +++ b/tls/s2n_resume.c @@ -1,1093 +1,1094 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -#include "tls/s2n_resume.h" - -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -int s2n_allowed_to_cache_connection(struct s2n_connection *conn) -{ - /* We're unable to cache connections with a Client Cert since we currently don't serialize the Client Cert, - * which means that callers won't have access to the Client's Cert if the connection is resumed. */ - if (s2n_connection_is_client_auth_enabled(conn)) { - return 0; - } - - struct s2n_config *config = conn->config; - - POSIX_ENSURE_REF(config); - return config->use_session_cache; -} - -/* If a protocol version is required before the actual_protocol_version - * is negotiated, we should fall back to resume_protocol_version if available. - * - * This covers the case where the application requests a ticket / session state - * before a NewSessionTicket message has been sent or received. Historically, - * in that case we return the ticket / session state already set for the connection. - * resume_protocol_version represents the protocol version of that existing ticket / state. - */ -static uint8_t s2n_resume_protocol_version(struct s2n_connection *conn) -{ - if (!IS_NEGOTIATED(conn) && conn->resume_protocol_version) { - return conn->resume_protocol_version; - } else { - return conn->actual_protocol_version; - } -} - -static int s2n_tls12_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) -{ - POSIX_ENSURE_REF(to); - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - uint64_t now = 0; - - S2N_ERROR_IF(s2n_stuffer_space_remaining(to) < S2N_TLS12_STATE_SIZE_IN_BYTES, S2N_ERR_STUFFER_IS_FULL); - - /* Get the time */ - POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); - - /* Write the entry */ - POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_SERIALIZED_FORMAT_TLS12_V3)); - POSIX_GUARD(s2n_stuffer_write_uint8(to, s2n_resume_protocol_version(conn))); - POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint64(to, now)); - POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->ems_negotiated)); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_tls13_serialize_keying_material_expiration(struct s2n_connection *conn, - uint64_t now, struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(out); - - if (conn->mode != S2N_SERVER) { - return S2N_RESULT_OK; - } - - uint64_t expiration_timestamp = now + (conn->server_keying_material_lifetime * (uint64_t) ONE_SEC_IN_NANOS); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - if (chosen_psk && chosen_psk->type == S2N_PSK_TYPE_RESUMPTION) { - expiration_timestamp = S2N_MIN(chosen_psk->keying_material_expiration, expiration_timestamp); - } - - RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, expiration_timestamp)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_tls13_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(out); - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - - uint64_t current_time = 0; - struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; - - /* Get the time */ - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); - - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, S2N_SERIALIZED_FORMAT_TLS13_V1)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, conn->actual_protocol_version)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, current_time)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, ticket_fields->ticket_age_add)); - RESULT_ENSURE_INCLUSIVE_RANGE(1, ticket_fields->session_secret.size, UINT8_MAX); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, ticket_fields->session_secret.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, ticket_fields->session_secret.data, ticket_fields->session_secret.size)); - RESULT_GUARD(s2n_tls13_serialize_keying_material_expiration(conn, current_time, out)); - - uint32_t server_max_early_data = 0; - RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, server_max_early_data)); - if (server_max_early_data > 0) { - uint8_t application_protocol_len = strlen(conn->application_protocol); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, application_protocol_len)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, (uint8_t *) conn->application_protocol, application_protocol_len)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, conn->server_early_data_context.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write(out, &conn->server_early_data_context)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - if (s2n_resume_protocol_version(conn) < S2N_TLS13) { - RESULT_GUARD_POSIX(s2n_tls12_serialize_resumption_state(conn, out)); - } else { - RESULT_GUARD(s2n_tls13_serialize_resumption_state(conn, out)); - } - return S2N_RESULT_OK; -} - -static int s2n_tls12_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - uint8_t protocol_version = 0; - uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - - S2N_ERROR_IF(s2n_stuffer_data_available(from) < S2N_TLS12_STATE_SIZE_IN_BYTES - sizeof(uint8_t), S2N_ERR_STUFFER_OUT_OF_DATA); - - POSIX_GUARD(s2n_stuffer_read_uint8(from, &protocol_version)); - S2N_ERROR_IF(protocol_version != conn->actual_protocol_version, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - - POSIX_GUARD(s2n_stuffer_read_bytes(from, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN), S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - - uint64_t now = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); - - uint64_t then = 0; - POSIX_GUARD(s2n_stuffer_read_uint64(from, &then)); - S2N_ERROR_IF(then > now, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - S2N_ERROR_IF(now - then > conn->config->session_state_lifetime_in_nanos, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - - POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); - - if (s2n_stuffer_data_available(from)) { - uint8_t ems_negotiated = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(from, &ems_negotiated)); - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *# o If the original session did not use the "extended_master_secret" - *# extension but the new ClientHello contains the extension, then the - *# server MUST NOT perform the abbreviated handshake. Instead, it - *# SHOULD continue with a full handshake (as described in - *# Section 5.2) to negotiate a new session. - *# - *# o If the original session used the "extended_master_secret" - *# extension but the new ClientHello does not contain it, the server - *# MUST abort the abbreviated handshake. - **/ - if (conn->ems_negotiated != ems_negotiated) { - /* The session ticket needs to have the same EMS state as the current session. If it doesn't - * have the same state, the current session takes the state of the session ticket and errors. - * If the deserialization process errors, we will use this state in a few extra checks - * to determine if we can fallback to a full handshake. - */ - conn->ems_negotiated = ems_negotiated; - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - } - - return S2N_SUCCESS; -} - -static int s2n_client_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) -{ - /* Serialize session ticket */ - if (conn->config->use_tickets && conn->client_ticket.size > 0) { - POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_TICKET)); - POSIX_GUARD(s2n_stuffer_write_uint16(to, conn->client_ticket.size)); - POSIX_GUARD(s2n_stuffer_write(to, &conn->client_ticket)); - } else { - /* Serialize session id */ - POSIX_ENSURE_LT(conn->actual_protocol_version, S2N_TLS13); - POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_ID)); - POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->session_id_len)); - POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->session_id, conn->session_id_len)); - } - - /* Serialize session state */ - POSIX_GUARD_RESULT(s2n_serialize_resumption_state(conn, to)); - - return 0; -} - -static S2N_RESULT s2n_tls12_client_deserialize_session_state(struct s2n_connection *conn, - struct s2n_blob *ticket, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(from); - - /* Operate on a copy of the connection to avoid mutating the connection on - * failure. We have tests in s2n_resume_test.c that prove this level of copy - * is sufficient. - */ - struct s2n_crypto_parameters *secure = conn->secure; - RESULT_ENSURE_REF(secure); - struct s2n_connection temp_conn = *conn; - struct s2n_crypto_parameters temp_secure = *secure; - temp_conn.secure = &temp_secure; - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &temp_conn.resume_protocol_version)); - - uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(from, S2N_TLS_CIPHER_SUITE_LEN); - RESULT_ENSURE_REF(cipher_suite_wire); - RESULT_GUARD_POSIX(s2n_set_cipher_as_client(&temp_conn, cipher_suite_wire)); - - uint64_t then = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &then)); - - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, temp_conn.secrets.version.tls12.master_secret, - S2N_TLS_SECRET_LEN)); - - if (s2n_stuffer_data_available(from)) { - uint8_t ems_negotiated = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &ems_negotiated)); - temp_conn.ems_negotiated = ems_negotiated; - } - - DEFER_CLEANUP(struct s2n_blob client_ticket = { 0 }, s2n_free); - if (ticket) { - RESULT_GUARD_POSIX(s2n_dup(ticket, &client_ticket)); - } - - /* Finally, actually update the connection */ - RESULT_GUARD_POSIX(s2n_free(&conn->client_ticket)); - *secure = temp_secure; - *conn = temp_conn; - conn->secure = secure; - conn->client_ticket = client_ticket; - ZERO_TO_DISABLE_DEFER_CLEANUP(client_ticket); - - return S2N_RESULT_OK; -} - -/* `s2n_validate_ticket_age` is a best effort check that the session ticket is - * less than one week old. - * - * Clock skew between hosts or the possibility of a clock jump prevent this from - * being a precise check. - */ -static S2N_RESULT s2n_validate_ticket_age(uint64_t current_time, uint64_t ticket_issue_time) -{ - /* If the `ticket_issue_time` is in the future, then we are observing clock skew. - * We shouldn't fully reject the ticket, but we assert that the clock skew is - * less than some MAX_ALLOWED_CLOCK_SKEW_SEC - */ - if (current_time < ticket_issue_time) { - uint64_t clock_skew_in_nanos = ticket_issue_time - current_time; - uint64_t clock_skew_in_seconds = clock_skew_in_nanos / ONE_SEC_IN_NANOS; - RESULT_ENSURE(clock_skew_in_seconds <= MAX_ALLOWED_CLOCK_SKEW_SEC, S2N_ERR_INVALID_SESSION_TICKET); - } else { - uint64_t ticket_age_in_nanos = current_time - ticket_issue_time; - uint64_t ticket_age_in_sec = ticket_age_in_nanos / ONE_SEC_IN_NANOS; - RESULT_ENSURE(ticket_age_in_sec <= ONE_WEEK_IN_SEC, S2N_ERR_INVALID_SESSION_TICKET); - } - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_tls13_deserialize_session_state(struct s2n_connection *conn, struct s2n_blob *psk_identity, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(psk_identity); - RESULT_ENSURE_REF(from); - - DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe); - RESULT_GUARD(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION)); - RESULT_GUARD_POSIX(s2n_psk_set_identity(&psk, psk_identity->data, psk_identity->size)); - - uint8_t protocol_version = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &protocol_version)); - RESULT_ENSURE_GTE(protocol_version, S2N_TLS13); - - uint8_t iana_id[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, iana_id, S2N_TLS_CIPHER_SUITE_LEN)); - struct s2n_cipher_suite *cipher_suite = NULL; - RESULT_GUARD(s2n_cipher_suite_from_iana(iana_id, sizeof(iana_id), &cipher_suite)); - RESULT_ENSURE_REF(cipher_suite); - psk.hmac_alg = cipher_suite->prf_alg; - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.ticket_issue_time)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Clients MUST NOT cache - *# tickets for longer than 7 days, regardless of the ticket_lifetime, - *# and MAY delete tickets earlier based on local policy. - */ - uint64_t current_time = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); - RESULT_GUARD(s2n_validate_ticket_age(current_time, psk.ticket_issue_time)); - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &psk.ticket_age_add)); - - uint8_t secret_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &secret_len)); - RESULT_ENSURE_LTE(secret_len, S2N_TLS_SECRET_LEN); - uint8_t *secret_data = s2n_stuffer_raw_read(from, secret_len); - RESULT_ENSURE_REF(secret_data); - RESULT_GUARD_POSIX(s2n_psk_set_secret(&psk, secret_data, secret_len)); - - if (conn->mode == S2N_SERVER) { - RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.keying_material_expiration)); - RESULT_ENSURE(psk.keying_material_expiration > current_time, S2N_ERR_KEYING_MATERIAL_EXPIRED); - } - - uint32_t max_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &max_early_data_size)); - if (max_early_data_size > 0) { - RESULT_GUARD_POSIX(s2n_psk_configure_early_data(&psk, max_early_data_size, - iana_id[0], iana_id[1])); - - uint8_t app_proto_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &app_proto_size)); - uint8_t *app_proto_data = s2n_stuffer_raw_read(from, app_proto_size); - RESULT_ENSURE_REF(app_proto_data); - RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(&psk, app_proto_data, app_proto_size)); - - uint16_t early_data_context_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(from, &early_data_context_size)); - uint8_t *early_data_context_data = s2n_stuffer_raw_read(from, early_data_context_size); - RESULT_ENSURE_REF(early_data_context_data); - RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(&psk, early_data_context_data, early_data_context_size)); - } - - /* Make sure that this connection is configured for resumption PSKs, not external PSKs */ - RESULT_GUARD(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_RESUMPTION)); - /* Remove all previously-set PSKs. To keep the session ticket API behavior consistent - * across protocol versions, we currently only support setting a single resumption PSK. */ - RESULT_GUARD(s2n_psk_parameters_wipe(&conn->psk_params)); - RESULT_GUARD_POSIX(s2n_connection_append_psk(conn, &psk)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_deserialize_resumption_state(struct s2n_connection *conn, - struct s2n_blob *ticket, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(from); - - uint8_t format = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &format)); - - if (format == S2N_SERIALIZED_FORMAT_TLS12_V3) { - if (conn->mode == S2N_SERVER) { - RESULT_GUARD_POSIX(s2n_tls12_deserialize_resumption_state(conn, from)); - } else { - RESULT_GUARD(s2n_tls12_client_deserialize_session_state(conn, ticket, from)); - } - } else if (format == S2N_SERIALIZED_FORMAT_TLS13_V1) { - RESULT_GUARD(s2n_tls13_deserialize_session_state(conn, ticket, from)); - } else { - RESULT_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - conn->set_session = true; - return S2N_RESULT_OK; -} - -static int s2n_client_deserialize_with_session_id(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - uint8_t session_id_len = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(from, &session_id_len)); - - if (session_id_len == 0 || session_id_len > S2N_TLS_SESSION_ID_MAX_LEN - || session_id_len > s2n_stuffer_data_available(from)) { - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - - conn->session_id_len = session_id_len; - POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->session_id, session_id_len)); - - POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, NULL, from)); - - return 0; -} - -static int s2n_client_deserialize_with_session_ticket(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - uint16_t session_ticket_len = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(from, &session_ticket_len)); - - if (session_ticket_len == 0 || session_ticket_len > s2n_stuffer_data_available(from)) { - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - - struct s2n_blob session_ticket = { 0 }; - uint8_t *session_ticket_bytes = s2n_stuffer_raw_read(from, session_ticket_len); - POSIX_ENSURE_REF(session_ticket_bytes); - POSIX_GUARD(s2n_blob_init(&session_ticket, session_ticket_bytes, session_ticket_len)); - - POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, &session_ticket, from)); - return 0; -} - -static int s2n_client_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - uint8_t format = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(from, &format)); - - switch (format) { - case S2N_STATE_WITH_SESSION_ID: - POSIX_GUARD(s2n_client_deserialize_with_session_id(conn, from)); - break; - case S2N_STATE_WITH_SESSION_TICKET: - POSIX_GUARD(s2n_client_deserialize_with_session_ticket(conn, from)); - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - - return 0; -} - -int s2n_resume_from_cache(struct s2n_connection *conn) -{ - S2N_ERROR_IF(conn->session_id_len == 0, S2N_ERR_SESSION_ID_TOO_SHORT); - S2N_ERROR_IF(conn->session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); - - uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; - struct s2n_blob entry = { 0 }; - POSIX_GUARD(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); - uint64_t size = entry.size; - int result = conn->config->cache_retrieve(conn, conn->config->cache_retrieve_data, conn->session_id, conn->session_id_len, entry.data, &size); - if (result == S2N_CALLBACK_BLOCKED) { - POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - POSIX_ENSURE(result >= S2N_SUCCESS, S2N_ERR_CANCELLED); - - S2N_ERROR_IF(size != entry.size, S2N_ERR_SIZE_MISMATCH); - - struct s2n_stuffer from = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&from, &entry)); - POSIX_GUARD(s2n_stuffer_write(&from, &entry)); - POSIX_GUARD_RESULT(s2n_resume_decrypt_session(conn, &from)); - - return 0; -} - -S2N_RESULT s2n_store_to_cache(struct s2n_connection *conn) -{ - uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; - struct s2n_blob entry = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); - struct s2n_stuffer to = { 0 }; - - /* session_id_len should always be >0 since either the Client provided a SessionId or the Server generated a new - * one for the Client */ - RESULT_ENSURE(conn->session_id_len > 0, S2N_ERR_SESSION_ID_TOO_SHORT); - RESULT_ENSURE(conn->session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); - - RESULT_GUARD_POSIX(s2n_stuffer_init(&to, &entry)); - - struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); - RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &to)); - - /* Store to the cache */ - conn->config->cache_store(conn, conn->config->cache_store_data, S2N_TLS_SESSION_CACHE_TTL, conn->session_id, conn->session_id_len, entry.data, entry.size); - - return S2N_RESULT_OK; -} - -int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(session); - - DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free); - /* size_t is 64-bit integer on 64-bit system, while s2n_alloc's length parameter is a 32-bit integer */ - POSIX_ENSURE(length <= UINT32_MAX, S2N_ERR_INVALID_ARGUMENT); - POSIX_GUARD(s2n_alloc(&session_data, length)); - POSIX_CHECKED_MEMCPY(session_data.data, session, length); - - struct s2n_stuffer from = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&from, &session_data)); - POSIX_GUARD(s2n_stuffer_write(&from, &session_data)); - POSIX_GUARD(s2n_client_deserialize_resumption_state(conn, &from)); - return 0; -} - -int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(session); - - const int len = s2n_connection_get_session_length(conn); - POSIX_GUARD(len); - - if (len == 0) { - return 0; - } - - POSIX_ENSURE((size_t) len <= max_length, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); - - struct s2n_blob serialized_data = { 0 }; - POSIX_GUARD(s2n_blob_init(&serialized_data, session, len)); - POSIX_GUARD(s2n_blob_zero(&serialized_data)); - - struct s2n_stuffer to = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&to, &serialized_data)); - POSIX_GUARD(s2n_client_serialize_resumption_state(conn, &to)); - - return len; -} - -int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - S2N_ERROR_IF(!(conn->config->use_tickets && conn->client_ticket.size > 0), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED); - - /* Session resumption using session ticket */ - return conn->ticket_lifetime_hint; -} - -S2N_RESULT s2n_connection_get_session_state_size(struct s2n_connection *conn, size_t *state_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(state_size); - - if (s2n_resume_protocol_version(conn) < S2N_TLS13) { - *state_size = S2N_TLS12_STATE_SIZE_IN_BYTES; - return S2N_RESULT_OK; - } - - *state_size = S2N_TLS13_FIXED_STATE_SIZE; - - uint8_t secret_size = 0; - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); - *state_size += secret_size; - - uint32_t server_max_early_data = 0; - RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); - if (server_max_early_data > 0) { - *state_size += S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE - + strlen(conn->application_protocol) - + conn->server_early_data_context.size; - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_connection_get_session_length_impl(struct s2n_connection *conn, size_t *length) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_REF(length); - *length = 0; - - if (conn->config->use_tickets && conn->client_ticket.size > 0) { - size_t session_state_size = 0; - RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); - *length = S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + session_state_size; - } else if (conn->session_id_len > 0 && conn->actual_protocol_version < S2N_TLS13) { - *length = S2N_STATE_FORMAT_LEN + sizeof(conn->session_id_len) + conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES; - } - return S2N_RESULT_OK; -} - -int s2n_connection_get_session_length(struct s2n_connection *conn) -{ - size_t length = 0; - if (s2n_result_is_ok(s2n_connection_get_session_length_impl(conn, &length))) { - return length; - } - return 0; -} - -int s2n_connection_is_session_resumed(struct s2n_connection *conn) -{ - return conn && IS_RESUMPTION_HANDSHAKE(conn) - && (conn->actual_protocol_version < S2N_TLS13 || conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION); -} - -int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->actual_protocol_version >= S2N_TLS13) { - return (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)); - } else { - return IS_OCSP_STAPLED(conn); - } -} - -S2N_RESULT s2n_config_is_encrypt_key_available(struct s2n_config *config) -{ - RESULT_ENSURE_REF(config); - - uint64_t now = 0; - struct s2n_ticket_key *ticket_key = NULL; - RESULT_GUARD(s2n_config_wall_clock(config, &now)); - RESULT_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - RESULT_GUARD(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - for (uint32_t i = ticket_keys_len; i > 0; i--) { - uint32_t idx = i - 1; - RESULT_GUARD(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); - uint64_t key_intro_time = ticket_key->intro_timestamp; - - if (key_intro_time <= now - && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { - return S2N_RESULT_OK; - } - } - - RESULT_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); -} - -/* This function is used in s2n_get_ticket_encrypt_decrypt_key to compute the weight - * of the keys and to choose a single key from all of the encrypt-decrypt keys. - * Higher the weight of the key, higher the probability of being picked. - */ -int s2n_compute_weight_of_encrypt_decrypt_keys(struct s2n_config *config, - uint8_t *encrypt_decrypt_keys_index, - uint8_t num_encrypt_decrypt_keys, - uint64_t now) -{ - double total_weight = 0; - struct s2n_ticket_key_weight ticket_keys_weight[S2N_MAX_TICKET_KEYS]; - struct s2n_ticket_key *ticket_key = NULL; - - /* Compute weight of encrypt-decrypt keys */ - for (int i = 0; i < num_encrypt_decrypt_keys; i++) { - POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[i], (void **) &ticket_key)); - - uint64_t key_intro_time = ticket_key->intro_timestamp; - uint64_t key_encryption_peak_time = key_intro_time + (config->encrypt_decrypt_key_lifetime_in_nanos / 2); - - /* The % of encryption using this key is linearly increasing */ - if (now < key_encryption_peak_time) { - ticket_keys_weight[i].key_weight = now - key_intro_time; - } else { - /* The % of encryption using this key is linearly decreasing */ - ticket_keys_weight[i].key_weight = (config->encrypt_decrypt_key_lifetime_in_nanos / 2) - (now - key_encryption_peak_time); - } - - ticket_keys_weight[i].key_index = encrypt_decrypt_keys_index[i]; - total_weight += ticket_keys_weight[i].key_weight; - } - - /* Pick a random number in [0, 1). Using 53 bits (IEEE 754 double-precision floats). */ - uint64_t random_int = 0; - POSIX_GUARD_RESULT(s2n_public_random(pow(2, 53), &random_int)); - double random = (double) random_int / (double) pow(2, 53); - - /* Compute cumulative weight of encrypt-decrypt keys */ - for (int i = 0; i < num_encrypt_decrypt_keys; i++) { - ticket_keys_weight[i].key_weight = ticket_keys_weight[i].key_weight / total_weight; - - if (i > 0) { - ticket_keys_weight[i].key_weight += ticket_keys_weight[i - 1].key_weight; - } - - if (ticket_keys_weight[i].key_weight > random) { - return ticket_keys_weight[i].key_index; - } - } - - POSIX_BAIL(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED); -} - -/* This function is used in s2n_resume_encrypt_session_ticket in order for s2n to - * choose a key in encrypt-decrypt state from all of the keys added to config - */ -struct s2n_ticket_key *s2n_get_ticket_encrypt_decrypt_key(struct s2n_config *config) -{ - uint8_t num_encrypt_decrypt_keys = 0; - uint8_t encrypt_decrypt_keys_index[S2N_MAX_TICKET_KEYS] = { 0 }; - struct s2n_ticket_key *ticket_key = NULL; - - uint64_t now = 0; - PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - PTR_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - for (uint32_t i = ticket_keys_len; i > 0; i--) { - uint32_t idx = i - 1; - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); - uint64_t key_intro_time = ticket_key->intro_timestamp; - - /* A key can be used at its intro time (<=) and it can be used up to (<) - * its expiration time. - */ - if (key_intro_time <= now - && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { - encrypt_decrypt_keys_index[num_encrypt_decrypt_keys] = idx; - num_encrypt_decrypt_keys++; - } - } - - if (num_encrypt_decrypt_keys == 0) { - PTR_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); - } - - if (num_encrypt_decrypt_keys == 1) { - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[0], (void **) &ticket_key)); - return ticket_key; - } - - int8_t idx = 0; - PTR_GUARD_POSIX(idx = s2n_compute_weight_of_encrypt_decrypt_keys(config, encrypt_decrypt_keys_index, num_encrypt_decrypt_keys, now)); - - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); - return ticket_key; -} - -/* This function is used in s2n_resume_decrypt_session in order for s2n to - * find the matching key that was used for encryption. - */ -struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint8_t name[S2N_TICKET_KEY_NAME_LEN]) -{ - uint64_t now = 0; - struct s2n_ticket_key *ticket_key = NULL; - PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - PTR_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - for (uint32_t i = 0; i < ticket_keys_len; i++) { - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); - - if (s2n_constant_time_equals(ticket_key->key_name, name, S2N_TICKET_KEY_NAME_LEN)) { - /* Check to see if the key has expired */ - if (now >= ticket_key->intro_timestamp - + config->encrypt_decrypt_key_lifetime_in_nanos - + config->decrypt_key_lifetime_in_nanos) { - return NULL; - } - - return ticket_key; - } - } - - 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_ticket_key *key, struct s2n_stuffer *to) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(to); - - 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, 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)); - RESULT_GUARD(s2n_aes256_gcm.set_encryption_key(&aes_ticket_key, &aes_key_blob)); - - /* Ensure we never encrypt with a zero-filled key */ - uint8_t zero_block[S2N_AES256_KEY_LEN] = { 0 }; - RESULT_ENSURE(!s2n_constant_time_equals(key->aes_key, zero_block, S2N_AES256_KEY_LEN), - S2N_ERR_KEY_CHECK); - - /* Initialize Additional Authenticated Data */ - uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; - struct s2n_blob aad_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); - struct s2n_stuffer aad = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); - 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 }; - RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); - RESULT_GUARD(s2n_get_public_random_data(&iv)); - RESULT_GUARD_POSIX(s2n_stuffer_write(to, &iv)); - - /* Write serialized session state */ - uint32_t plaintext_state_size = s2n_stuffer_data_available(to); - RESULT_GUARD(s2n_serialize_resumption_state(conn, to)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(to, S2N_TLS_GCM_TAG_LEN)); - - /* Initialize blob to be encrypted */ - struct s2n_blob state_blob = { 0 }; - struct s2n_stuffer copy_for_encryption = *to; - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(©_for_encryption, plaintext_state_size)); - uint32_t state_blob_size = s2n_stuffer_data_available(©_for_encryption); - uint8_t *state_blob_data = s2n_stuffer_raw_read(©_for_encryption, state_blob_size); - RESULT_ENSURE_REF(state_blob_data); - RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, state_blob_data, state_blob_size)); - - RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.encrypt(&aes_ticket_key, &iv, &aad_blob, &state_blob, &state_blob)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(from); - RESULT_ENSURE_REF(conn->config); - - /* 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))); - - struct s2n_ticket_key *key = s2n_find_ticket_key(conn->config, key_name); - /* 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 }; - RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); - RESULT_GUARD_POSIX(s2n_stuffer_read(from, &iv)); - - /* Initialize AES key */ - struct s2n_blob aes_key_blob = { 0 }; - 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)); - RESULT_GUARD(s2n_aes256_gcm.set_decryption_key(&aes_ticket_key, &aes_key_blob)); - - /* Initialize Additional Authenticated Data */ - uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; - struct s2n_blob aad_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); - struct s2n_stuffer aad = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); - 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))); - - /* Initialize blob to be decrypted */ - struct s2n_blob en_blob = { 0 }; - uint32_t en_blob_size = s2n_stuffer_data_available(from); - uint8_t *en_blob_data = s2n_stuffer_raw_read(from, en_blob_size); - RESULT_ENSURE_REF(en_blob_data); - RESULT_GUARD_POSIX(s2n_blob_init(&en_blob, en_blob_data, en_blob_size)); - - RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.decrypt(&aes_ticket_key, &iv, &aad_blob, &en_blob, &en_blob)); - - /* Parse decrypted state */ - struct s2n_blob state_blob = { 0 }; - uint32_t state_blob_size = en_blob_size - S2N_TLS_GCM_TAG_LEN; - RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, en_blob.data, state_blob_size)); - struct s2n_stuffer state_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&state_stuffer, &state_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&state_stuffer, state_blob_size)); - RESULT_GUARD(s2n_deserialize_resumption_state(conn, &from->blob, &state_stuffer)); - - return S2N_RESULT_OK; -} - -/* This function is used to remove all or just one expired key from server config */ -int s2n_config_wipe_expired_ticket_crypto_keys(struct s2n_config *config, int8_t expired_key_index) -{ - int num_of_expired_keys = 0; - int expired_keys_index[S2N_MAX_TICKET_KEYS]; - struct s2n_ticket_key *ticket_key = NULL; - - if (expired_key_index != -1) { - expired_keys_index[num_of_expired_keys] = expired_key_index; - num_of_expired_keys++; - - goto end; - } - - uint64_t now = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - POSIX_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - for (uint32_t i = 0; i < ticket_keys_len; i++) { - POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); - if (now >= ticket_key->intro_timestamp - + config->encrypt_decrypt_key_lifetime_in_nanos - + config->decrypt_key_lifetime_in_nanos) { - expired_keys_index[num_of_expired_keys] = i; - num_of_expired_keys++; - } - } - -end: - for (int j = 0; j < num_of_expired_keys; j++) { - POSIX_GUARD_RESULT(s2n_array_remove(config->ticket_keys, expired_keys_index[j] - j)); - } - - return 0; -} - -int s2n_config_store_ticket_key(struct s2n_config *config, struct s2n_ticket_key *key) -{ - uint32_t ticket_keys_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - /* The ticket key name and secret must both be unique. */ - for (uint32_t i = 0; i < ticket_keys_len; i++) { - struct s2n_ticket_key *other_key = NULL; - POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &other_key)); - POSIX_ENSURE(!s2n_constant_time_equals(key->key_name, other_key->key_name, s2n_array_len(key->key_name)), - S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - POSIX_ENSURE(!s2n_constant_time_equals(key->aes_key, other_key->aes_key, s2n_array_len(key->aes_key)), - S2N_ERR_TICKET_KEY_NOT_UNIQUE); - } - - POSIX_GUARD_RESULT(s2n_array_insert_and_copy(config->ticket_keys, ticket_keys_len, key)); - return S2N_SUCCESS; -} - -int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num) -{ - POSIX_ENSURE_REF(config); - - config->initial_tickets_to_send = num; - POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, true)); - - return S2N_SUCCESS; -} - -int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_psk_validate_keying_material(conn)); - - uint32_t out = conn->tickets_to_send + num; - POSIX_ENSURE(out <= UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); - conn->tickets_to_send = out; - - return S2N_SUCCESS; -} - -int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(num); - POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); - *num = conn->tickets_sent; - return S2N_SUCCESS; -} - -int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(conn); - conn->server_keying_material_lifetime = lifetime_in_secs; - return S2N_SUCCESS; -} - -int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx) -{ - POSIX_ENSURE_MUT(config); - - config->session_ticket_cb = callback; - config->session_ticket_ctx = ctx; - return S2N_SUCCESS; -} - -int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len) -{ - POSIX_ENSURE_REF(ticket); - POSIX_ENSURE_MUT(data_len); - - *data_len = ticket->ticket_data.size; - return S2N_SUCCESS; -} - -int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data) -{ - POSIX_ENSURE_REF(ticket); - POSIX_ENSURE_MUT(data); - - POSIX_ENSURE(ticket->ticket_data.size <= max_data_len, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); - POSIX_CHECKED_MEMCPY(data, ticket->ticket_data.data, ticket->ticket_data.size); - - return S2N_SUCCESS; -} - -int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime) -{ - POSIX_ENSURE_REF(ticket); - POSIX_ENSURE_REF(session_lifetime); - - *session_lifetime = ticket->session_lifetime; - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include "tls/s2n_resume.h" + +#include + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +int s2n_allowed_to_cache_connection(struct s2n_connection *conn) +{ + /* We're unable to cache connections with a Client Cert since we currently don't serialize the Client Cert, + * which means that callers won't have access to the Client's Cert if the connection is resumed. */ + if (s2n_connection_is_client_auth_enabled(conn)) { + return 0; + } + + struct s2n_config *config = conn->config; + + POSIX_ENSURE_REF(config); + return config->use_session_cache; +} + +/* If a protocol version is required before the actual_protocol_version + * is negotiated, we should fall back to resume_protocol_version if available. + * + * This covers the case where the application requests a ticket / session state + * before a NewSessionTicket message has been sent or received. Historically, + * in that case we return the ticket / session state already set for the connection. + * resume_protocol_version represents the protocol version of that existing ticket / state. + */ +static uint8_t s2n_resume_protocol_version(struct s2n_connection *conn) +{ + if (!IS_NEGOTIATED(conn) && conn->resume_protocol_version) { + return conn->resume_protocol_version; + } else { + return conn->actual_protocol_version; + } +} + +static int s2n_tls12_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) +{ + POSIX_ENSURE_REF(to); + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + uint64_t now = 0; + + S2N_ERROR_IF(s2n_stuffer_space_remaining(to) < S2N_TLS12_STATE_SIZE_IN_BYTES, S2N_ERR_STUFFER_IS_FULL); + + /* Get the time */ + POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); + + /* Write the entry */ + POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_SERIALIZED_FORMAT_TLS12_V3)); + POSIX_GUARD(s2n_stuffer_write_uint8(to, s2n_resume_protocol_version(conn))); + POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint64(to, now)); + POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->ems_negotiated)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_tls13_serialize_keying_material_expiration(struct s2n_connection *conn, + uint64_t now, struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(out); + + if (conn->mode != S2N_SERVER) { + return S2N_RESULT_OK; + } + + uint64_t expiration_timestamp = now + (conn->server_keying_material_lifetime * (uint64_t) ONE_SEC_IN_NANOS); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + if (chosen_psk && chosen_psk->type == S2N_PSK_TYPE_RESUMPTION) { + expiration_timestamp = S2N_MIN(chosen_psk->keying_material_expiration, expiration_timestamp); + } + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, expiration_timestamp)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_tls13_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(out); + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + + uint64_t current_time = 0; + struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; + + /* Get the time */ + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, S2N_SERIALIZED_FORMAT_TLS13_V1)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, conn->actual_protocol_version)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, current_time)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, ticket_fields->ticket_age_add)); + RESULT_ENSURE_INCLUSIVE_RANGE(1, ticket_fields->session_secret.size, UINT8_MAX); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, ticket_fields->session_secret.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, ticket_fields->session_secret.data, ticket_fields->session_secret.size)); + RESULT_GUARD(s2n_tls13_serialize_keying_material_expiration(conn, current_time, out)); + + uint32_t server_max_early_data = 0; + RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, server_max_early_data)); + if (server_max_early_data > 0) { + uint8_t application_protocol_len = strlen(conn->application_protocol); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, application_protocol_len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, (uint8_t *) conn->application_protocol, application_protocol_len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, conn->server_early_data_context.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write(out, &conn->server_early_data_context)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + if (s2n_resume_protocol_version(conn) < S2N_TLS13) { + RESULT_GUARD_POSIX(s2n_tls12_serialize_resumption_state(conn, out)); + } else { + RESULT_GUARD(s2n_tls13_serialize_resumption_state(conn, out)); + } + return S2N_RESULT_OK; +} + +static int s2n_tls12_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + uint8_t protocol_version = 0; + uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + + S2N_ERROR_IF(s2n_stuffer_data_available(from) < S2N_TLS12_STATE_SIZE_IN_BYTES - sizeof(uint8_t), S2N_ERR_STUFFER_OUT_OF_DATA); + + POSIX_GUARD(s2n_stuffer_read_uint8(from, &protocol_version)); + S2N_ERROR_IF(protocol_version != conn->actual_protocol_version, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + POSIX_GUARD(s2n_stuffer_read_bytes(from, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN), S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + uint64_t now = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); + + uint64_t then = 0; + POSIX_GUARD(s2n_stuffer_read_uint64(from, &then)); + S2N_ERROR_IF(then > now, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + S2N_ERROR_IF(now - then > conn->config->session_state_lifetime_in_nanos, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + + if (s2n_stuffer_data_available(from)) { + uint8_t ems_negotiated = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(from, &ems_negotiated)); + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *# o If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + *# + *# o If the original session used the "extended_master_secret" + *# extension but the new ClientHello does not contain it, the server + *# MUST abort the abbreviated handshake. + **/ + if (conn->ems_negotiated != ems_negotiated) { + /* The session ticket needs to have the same EMS state as the current session. If it doesn't + * have the same state, the current session takes the state of the session ticket and errors. + * If the deserialization process errors, we will use this state in a few extra checks + * to determine if we can fallback to a full handshake. + */ + conn->ems_negotiated = ems_negotiated; + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + } + + return S2N_SUCCESS; +} + +static int s2n_client_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) +{ + /* Serialize session ticket */ + if (conn->config->use_tickets && conn->client_ticket.size > 0) { + POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_TICKET)); + POSIX_GUARD(s2n_stuffer_write_uint16(to, conn->client_ticket.size)); + POSIX_GUARD(s2n_stuffer_write(to, &conn->client_ticket)); + } else { + /* Serialize session id */ + POSIX_ENSURE_LT(conn->actual_protocol_version, S2N_TLS13); + POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_ID)); + POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->session_id_len)); + POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->session_id, conn->session_id_len)); + } + + /* Serialize session state */ + POSIX_GUARD_RESULT(s2n_serialize_resumption_state(conn, to)); + + return 0; +} + +static S2N_RESULT s2n_tls12_client_deserialize_session_state(struct s2n_connection *conn, + struct s2n_blob *ticket, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(from); + + /* Operate on a copy of the connection to avoid mutating the connection on + * failure. We have tests in s2n_resume_test.c that prove this level of copy + * is sufficient. + */ + struct s2n_crypto_parameters *secure = conn->secure; + RESULT_ENSURE_REF(secure); + struct s2n_connection temp_conn = *conn; + struct s2n_crypto_parameters temp_secure = *secure; + temp_conn.secure = &temp_secure; + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &temp_conn.resume_protocol_version)); + + uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(from, S2N_TLS_CIPHER_SUITE_LEN); + RESULT_ENSURE_REF(cipher_suite_wire); + RESULT_GUARD_POSIX(s2n_set_cipher_as_client(&temp_conn, cipher_suite_wire)); + + uint64_t then = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &then)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, temp_conn.secrets.version.tls12.master_secret, + S2N_TLS_SECRET_LEN)); + + if (s2n_stuffer_data_available(from)) { + uint8_t ems_negotiated = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &ems_negotiated)); + temp_conn.ems_negotiated = ems_negotiated; + } + + DEFER_CLEANUP(struct s2n_blob client_ticket = { 0 }, s2n_free); + if (ticket) { + RESULT_GUARD_POSIX(s2n_dup(ticket, &client_ticket)); + } + + /* Finally, actually update the connection */ + RESULT_GUARD_POSIX(s2n_free(&conn->client_ticket)); + *secure = temp_secure; + *conn = temp_conn; + conn->secure = secure; + conn->client_ticket = client_ticket; + ZERO_TO_DISABLE_DEFER_CLEANUP(client_ticket); + + return S2N_RESULT_OK; +} + +/* `s2n_validate_ticket_age` is a best effort check that the session ticket is + * less than one week old. + * + * Clock skew between hosts or the possibility of a clock jump prevent this from + * being a precise check. + */ +static S2N_RESULT s2n_validate_ticket_age(uint64_t current_time, uint64_t ticket_issue_time) +{ + /* If the `ticket_issue_time` is in the future, then we are observing clock skew. + * We shouldn't fully reject the ticket, but we assert that the clock skew is + * less than some MAX_ALLOWED_CLOCK_SKEW_SEC + */ + if (current_time < ticket_issue_time) { + uint64_t clock_skew_in_nanos = ticket_issue_time - current_time; + uint64_t clock_skew_in_seconds = clock_skew_in_nanos / ONE_SEC_IN_NANOS; + RESULT_ENSURE(clock_skew_in_seconds <= MAX_ALLOWED_CLOCK_SKEW_SEC, S2N_ERR_INVALID_SESSION_TICKET); + } else { + uint64_t ticket_age_in_nanos = current_time - ticket_issue_time; + uint64_t ticket_age_in_sec = ticket_age_in_nanos / ONE_SEC_IN_NANOS; + RESULT_ENSURE(ticket_age_in_sec <= ONE_WEEK_IN_SEC, S2N_ERR_INVALID_SESSION_TICKET); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_tls13_deserialize_session_state(struct s2n_connection *conn, struct s2n_blob *psk_identity, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(psk_identity); + RESULT_ENSURE_REF(from); + + DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe); + RESULT_GUARD(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(&psk, psk_identity->data, psk_identity->size)); + + uint8_t protocol_version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &protocol_version)); + RESULT_ENSURE_GTE(protocol_version, S2N_TLS13); + + uint8_t iana_id[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, iana_id, S2N_TLS_CIPHER_SUITE_LEN)); + struct s2n_cipher_suite *cipher_suite = NULL; + RESULT_GUARD(s2n_cipher_suite_from_iana(iana_id, sizeof(iana_id), &cipher_suite)); + RESULT_ENSURE_REF(cipher_suite); + psk.hmac_alg = cipher_suite->prf_alg; + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.ticket_issue_time)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Clients MUST NOT cache + *# tickets for longer than 7 days, regardless of the ticket_lifetime, + *# and MAY delete tickets earlier based on local policy. + */ + uint64_t current_time = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); + RESULT_GUARD(s2n_validate_ticket_age(current_time, psk.ticket_issue_time)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &psk.ticket_age_add)); + + uint8_t secret_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &secret_len)); + RESULT_ENSURE_LTE(secret_len, S2N_TLS_SECRET_LEN); + uint8_t *secret_data = s2n_stuffer_raw_read(from, secret_len); + RESULT_ENSURE_REF(secret_data); + RESULT_GUARD_POSIX(s2n_psk_set_secret(&psk, secret_data, secret_len)); + + if (conn->mode == S2N_SERVER) { + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.keying_material_expiration)); + RESULT_ENSURE(psk.keying_material_expiration > current_time, S2N_ERR_KEYING_MATERIAL_EXPIRED); + } + + uint32_t max_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &max_early_data_size)); + if (max_early_data_size > 0) { + RESULT_GUARD_POSIX(s2n_psk_configure_early_data(&psk, max_early_data_size, + iana_id[0], iana_id[1])); + + uint8_t app_proto_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &app_proto_size)); + uint8_t *app_proto_data = s2n_stuffer_raw_read(from, app_proto_size); + RESULT_ENSURE_REF(app_proto_data); + RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(&psk, app_proto_data, app_proto_size)); + + uint16_t early_data_context_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(from, &early_data_context_size)); + uint8_t *early_data_context_data = s2n_stuffer_raw_read(from, early_data_context_size); + RESULT_ENSURE_REF(early_data_context_data); + RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(&psk, early_data_context_data, early_data_context_size)); + } + + /* Make sure that this connection is configured for resumption PSKs, not external PSKs */ + RESULT_GUARD(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_RESUMPTION)); + /* Remove all previously-set PSKs. To keep the session ticket API behavior consistent + * across protocol versions, we currently only support setting a single resumption PSK. */ + RESULT_GUARD(s2n_psk_parameters_wipe(&conn->psk_params)); + RESULT_GUARD_POSIX(s2n_connection_append_psk(conn, &psk)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_deserialize_resumption_state(struct s2n_connection *conn, + struct s2n_blob *ticket, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(from); + + uint8_t format = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &format)); + + if (format == S2N_SERIALIZED_FORMAT_TLS12_V3) { + if (conn->mode == S2N_SERVER) { + RESULT_GUARD_POSIX(s2n_tls12_deserialize_resumption_state(conn, from)); + } else { + RESULT_GUARD(s2n_tls12_client_deserialize_session_state(conn, ticket, from)); + } + } else if (format == S2N_SERIALIZED_FORMAT_TLS13_V1) { + RESULT_GUARD(s2n_tls13_deserialize_session_state(conn, ticket, from)); + } else { + RESULT_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + conn->set_session = true; + return S2N_RESULT_OK; +} + +static int s2n_client_deserialize_with_session_id(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + uint8_t session_id_len = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(from, &session_id_len)); + + if (session_id_len == 0 || session_id_len > S2N_TLS_SESSION_ID_MAX_LEN + || session_id_len > s2n_stuffer_data_available(from)) { + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + + conn->session_id_len = session_id_len; + POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->session_id, session_id_len)); + + POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, NULL, from)); + + return 0; +} + +static int s2n_client_deserialize_with_session_ticket(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + uint16_t session_ticket_len = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(from, &session_ticket_len)); + + if (session_ticket_len == 0 || session_ticket_len > s2n_stuffer_data_available(from)) { + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + + struct s2n_blob session_ticket = { 0 }; + uint8_t *session_ticket_bytes = s2n_stuffer_raw_read(from, session_ticket_len); + POSIX_ENSURE_REF(session_ticket_bytes); + POSIX_GUARD(s2n_blob_init(&session_ticket, session_ticket_bytes, session_ticket_len)); + + POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, &session_ticket, from)); + return 0; +} + +static int s2n_client_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + uint8_t format = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(from, &format)); + + switch (format) { + case S2N_STATE_WITH_SESSION_ID: + POSIX_GUARD(s2n_client_deserialize_with_session_id(conn, from)); + break; + case S2N_STATE_WITH_SESSION_TICKET: + POSIX_GUARD(s2n_client_deserialize_with_session_ticket(conn, from)); + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + + return 0; +} + +int s2n_resume_from_cache(struct s2n_connection *conn) +{ + S2N_ERROR_IF(conn->session_id_len == 0, S2N_ERR_SESSION_ID_TOO_SHORT); + S2N_ERROR_IF(conn->session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); + + uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob entry = { 0 }; + POSIX_GUARD(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + uint64_t size = entry.size; + int result = conn->config->cache_retrieve(conn, conn->config->cache_retrieve_data, conn->session_id, conn->session_id_len, entry.data, &size); + if (result == S2N_CALLBACK_BLOCKED) { + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + POSIX_ENSURE(result >= S2N_SUCCESS, S2N_ERR_CANCELLED); + + S2N_ERROR_IF(size != entry.size, S2N_ERR_SIZE_MISMATCH); + + struct s2n_stuffer from = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&from, &entry)); + POSIX_GUARD(s2n_stuffer_write(&from, &entry)); + POSIX_GUARD_RESULT(s2n_resume_decrypt_session(conn, &from)); + + return 0; +} + +S2N_RESULT s2n_store_to_cache(struct s2n_connection *conn) +{ + uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob entry = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + struct s2n_stuffer to = { 0 }; + + /* session_id_len should always be >0 since either the Client provided a SessionId or the Server generated a new + * one for the Client */ + RESULT_ENSURE(conn->session_id_len > 0, S2N_ERR_SESSION_ID_TOO_SHORT); + RESULT_ENSURE(conn->session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); + + RESULT_GUARD_POSIX(s2n_stuffer_init(&to, &entry)); + + struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); + RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &to)); + + /* Store to the cache */ + conn->config->cache_store(conn, conn->config->cache_store_data, S2N_TLS_SESSION_CACHE_TTL, conn->session_id, conn->session_id_len, entry.data, entry.size); + + return S2N_RESULT_OK; +} + +int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(session); + + DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free); + /* size_t is 64-bit integer on 64-bit system, while s2n_alloc's length parameter is a 32-bit integer */ + POSIX_ENSURE(length <= UINT32_MAX, S2N_ERR_INVALID_ARGUMENT); + POSIX_GUARD(s2n_alloc(&session_data, length)); + POSIX_CHECKED_MEMCPY(session_data.data, session, length); + + struct s2n_stuffer from = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&from, &session_data)); + POSIX_GUARD(s2n_stuffer_write(&from, &session_data)); + POSIX_GUARD(s2n_client_deserialize_resumption_state(conn, &from)); + return 0; +} + +int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(session); + + const int len = s2n_connection_get_session_length(conn); + POSIX_GUARD(len); + + if (len == 0) { + return 0; + } + + POSIX_ENSURE((size_t) len <= max_length, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); + + struct s2n_blob serialized_data = { 0 }; + POSIX_GUARD(s2n_blob_init(&serialized_data, session, len)); + POSIX_GUARD(s2n_blob_zero(&serialized_data)); + + struct s2n_stuffer to = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&to, &serialized_data)); + POSIX_GUARD(s2n_client_serialize_resumption_state(conn, &to)); + + return len; +} + +int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + S2N_ERROR_IF(!(conn->config->use_tickets && conn->client_ticket.size > 0), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED); + + /* Session resumption using session ticket */ + return conn->ticket_lifetime_hint; +} + +S2N_RESULT s2n_connection_get_session_state_size(struct s2n_connection *conn, size_t *state_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(state_size); + + if (s2n_resume_protocol_version(conn) < S2N_TLS13) { + *state_size = S2N_TLS12_STATE_SIZE_IN_BYTES; + return S2N_RESULT_OK; + } + + *state_size = S2N_TLS13_FIXED_STATE_SIZE; + + uint8_t secret_size = 0; + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + *state_size += secret_size; + + uint32_t server_max_early_data = 0; + RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); + if (server_max_early_data > 0) { + *state_size += S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE + + strlen(conn->application_protocol) + + conn->server_early_data_context.size; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_get_session_length_impl(struct s2n_connection *conn, size_t *length) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_REF(length); + *length = 0; + + if (conn->config->use_tickets && conn->client_ticket.size > 0) { + size_t session_state_size = 0; + RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); + *length = S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + session_state_size; + } else if (conn->session_id_len > 0 && conn->actual_protocol_version < S2N_TLS13) { + *length = S2N_STATE_FORMAT_LEN + sizeof(conn->session_id_len) + conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES; + } + return S2N_RESULT_OK; +} + +int s2n_connection_get_session_length(struct s2n_connection *conn) +{ + size_t length = 0; + if (s2n_result_is_ok(s2n_connection_get_session_length_impl(conn, &length))) { + return length; + } + return 0; +} + +int s2n_connection_is_session_resumed(struct s2n_connection *conn) +{ + return conn && IS_RESUMPTION_HANDSHAKE(conn) + && (conn->actual_protocol_version < S2N_TLS13 || conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION); +} + +int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->actual_protocol_version >= S2N_TLS13) { + return (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)); + } else { + return IS_OCSP_STAPLED(conn); + } +} + +S2N_RESULT s2n_config_is_encrypt_key_available(struct s2n_config *config) +{ + RESULT_ENSURE_REF(config); + + uint64_t now = 0; + struct s2n_ticket_key *ticket_key = NULL; + RESULT_GUARD(s2n_config_wall_clock(config, &now)); + RESULT_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + RESULT_GUARD(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + for (uint32_t i = ticket_keys_len; i > 0; i--) { + uint32_t idx = i - 1; + RESULT_GUARD(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); + uint64_t key_intro_time = ticket_key->intro_timestamp; + + if (key_intro_time <= now + && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { + return S2N_RESULT_OK; + } + } + + RESULT_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); +} + +/* This function is used in s2n_get_ticket_encrypt_decrypt_key to compute the weight + * of the keys and to choose a single key from all of the encrypt-decrypt keys. + * Higher the weight of the key, higher the probability of being picked. + */ +int s2n_compute_weight_of_encrypt_decrypt_keys(struct s2n_config *config, + uint8_t *encrypt_decrypt_keys_index, + uint8_t num_encrypt_decrypt_keys, + uint64_t now) +{ + double total_weight = 0; + struct s2n_ticket_key_weight ticket_keys_weight[S2N_MAX_TICKET_KEYS]; + struct s2n_ticket_key *ticket_key = NULL; + + /* Compute weight of encrypt-decrypt keys */ + for (int i = 0; i < num_encrypt_decrypt_keys; i++) { + POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[i], (void **) &ticket_key)); + + uint64_t key_intro_time = ticket_key->intro_timestamp; + uint64_t key_encryption_peak_time = key_intro_time + (config->encrypt_decrypt_key_lifetime_in_nanos / 2); + + /* The % of encryption using this key is linearly increasing */ + if (now < key_encryption_peak_time) { + ticket_keys_weight[i].key_weight = now - key_intro_time; + } else { + /* The % of encryption using this key is linearly decreasing */ + ticket_keys_weight[i].key_weight = (config->encrypt_decrypt_key_lifetime_in_nanos / 2) - (now - key_encryption_peak_time); + } + + ticket_keys_weight[i].key_index = encrypt_decrypt_keys_index[i]; + total_weight += ticket_keys_weight[i].key_weight; + } + + /* Pick a random number in [0, 1). Using 53 bits (IEEE 754 double-precision floats). */ + uint64_t random_int = 0; + POSIX_GUARD_RESULT(s2n_public_random(pow(2, 53), &random_int)); + double random = (double) random_int / (double) pow(2, 53); + + /* Compute cumulative weight of encrypt-decrypt keys */ + for (int i = 0; i < num_encrypt_decrypt_keys; i++) { + ticket_keys_weight[i].key_weight = ticket_keys_weight[i].key_weight / total_weight; + + if (i > 0) { + ticket_keys_weight[i].key_weight += ticket_keys_weight[i - 1].key_weight; + } + + if (ticket_keys_weight[i].key_weight > random) { + return ticket_keys_weight[i].key_index; + } + } + + POSIX_BAIL(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED); +} + +/* This function is used in s2n_resume_encrypt_session_ticket in order for s2n to + * choose a key in encrypt-decrypt state from all of the keys added to config + */ +struct s2n_ticket_key *s2n_get_ticket_encrypt_decrypt_key(struct s2n_config *config) +{ + uint8_t num_encrypt_decrypt_keys = 0; + uint8_t encrypt_decrypt_keys_index[S2N_MAX_TICKET_KEYS] = { 0 }; + struct s2n_ticket_key *ticket_key = NULL; + + uint64_t now = 0; + PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + PTR_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + for (uint32_t i = ticket_keys_len; i > 0; i--) { + uint32_t idx = i - 1; + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); + uint64_t key_intro_time = ticket_key->intro_timestamp; + + /* A key can be used at its intro time (<=) and it can be used up to (<) + * its expiration time. + */ + if (key_intro_time <= now + && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { + encrypt_decrypt_keys_index[num_encrypt_decrypt_keys] = idx; + num_encrypt_decrypt_keys++; + } + } + + if (num_encrypt_decrypt_keys == 0) { + PTR_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + } + + if (num_encrypt_decrypt_keys == 1) { + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[0], (void **) &ticket_key)); + return ticket_key; + } + + int8_t idx = 0; + PTR_GUARD_POSIX(idx = s2n_compute_weight_of_encrypt_decrypt_keys(config, encrypt_decrypt_keys_index, num_encrypt_decrypt_keys, now)); + + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); + return ticket_key; +} + +/* This function is used in s2n_resume_decrypt_session in order for s2n to + * find the matching key that was used for encryption. + */ +struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint8_t name[S2N_TICKET_KEY_NAME_LEN]) +{ + uint64_t now = 0; + struct s2n_ticket_key *ticket_key = NULL; + PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + PTR_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + for (uint32_t i = 0; i < ticket_keys_len; i++) { + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); + + if (s2n_constant_time_equals(ticket_key->key_name, name, S2N_TICKET_KEY_NAME_LEN)) { + /* Check to see if the key has expired */ + if (now >= ticket_key->intro_timestamp + + config->encrypt_decrypt_key_lifetime_in_nanos + + config->decrypt_key_lifetime_in_nanos) { + return NULL; + } + + return ticket_key; + } + } + + 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_ticket_key *key, struct s2n_stuffer *to) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(to); + + 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, 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)); + RESULT_GUARD(s2n_aes256_gcm.set_encryption_key(&aes_ticket_key, &aes_key_blob)); + + /* Ensure we never encrypt with a zero-filled key */ + uint8_t zero_block[S2N_AES256_KEY_LEN] = { 0 }; + RESULT_ENSURE(!s2n_constant_time_equals(key->aes_key, zero_block, S2N_AES256_KEY_LEN), + S2N_ERR_KEY_CHECK); + + /* Initialize Additional Authenticated Data */ + uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; + struct s2n_blob aad_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); + struct s2n_stuffer aad = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); + 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 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); + RESULT_GUARD(s2n_get_public_random_data(&iv)); + RESULT_GUARD_POSIX(s2n_stuffer_write(to, &iv)); + + /* Write serialized session state */ + uint32_t plaintext_state_size = s2n_stuffer_data_available(to); + RESULT_GUARD(s2n_serialize_resumption_state(conn, to)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(to, S2N_TLS_GCM_TAG_LEN)); + + /* Initialize blob to be encrypted */ + struct s2n_blob state_blob = { 0 }; + struct s2n_stuffer copy_for_encryption = *to; + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(©_for_encryption, plaintext_state_size)); + uint32_t state_blob_size = s2n_stuffer_data_available(©_for_encryption); + uint8_t *state_blob_data = s2n_stuffer_raw_read(©_for_encryption, state_blob_size); + RESULT_ENSURE_REF(state_blob_data); + RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, state_blob_data, state_blob_size)); + + RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.encrypt(&aes_ticket_key, &iv, &aad_blob, &state_blob, &state_blob)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(from); + RESULT_ENSURE_REF(conn->config); + + /* 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))); + + struct s2n_ticket_key *key = s2n_find_ticket_key(conn->config, key_name); + /* 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 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); + RESULT_GUARD_POSIX(s2n_stuffer_read(from, &iv)); + + /* Initialize AES key */ + struct s2n_blob aes_key_blob = { 0 }; + 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)); + RESULT_GUARD(s2n_aes256_gcm.set_decryption_key(&aes_ticket_key, &aes_key_blob)); + + /* Initialize Additional Authenticated Data */ + uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; + struct s2n_blob aad_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); + struct s2n_stuffer aad = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); + 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))); + + /* Initialize blob to be decrypted */ + struct s2n_blob en_blob = { 0 }; + uint32_t en_blob_size = s2n_stuffer_data_available(from); + uint8_t *en_blob_data = s2n_stuffer_raw_read(from, en_blob_size); + RESULT_ENSURE_REF(en_blob_data); + RESULT_GUARD_POSIX(s2n_blob_init(&en_blob, en_blob_data, en_blob_size)); + + RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.decrypt(&aes_ticket_key, &iv, &aad_blob, &en_blob, &en_blob)); + + /* Parse decrypted state */ + struct s2n_blob state_blob = { 0 }; + uint32_t state_blob_size = en_blob_size - S2N_TLS_GCM_TAG_LEN; + RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, en_blob.data, state_blob_size)); + struct s2n_stuffer state_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&state_stuffer, &state_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&state_stuffer, state_blob_size)); + RESULT_GUARD(s2n_deserialize_resumption_state(conn, &from->blob, &state_stuffer)); + + return S2N_RESULT_OK; +} + +/* This function is used to remove all or just one expired key from server config */ +int s2n_config_wipe_expired_ticket_crypto_keys(struct s2n_config *config, int8_t expired_key_index) +{ + int num_of_expired_keys = 0; + int expired_keys_index[S2N_MAX_TICKET_KEYS]; + struct s2n_ticket_key *ticket_key = NULL; + + if (expired_key_index != -1) { + expired_keys_index[num_of_expired_keys] = expired_key_index; + num_of_expired_keys++; + + goto end; + } + + uint64_t now = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + POSIX_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + for (uint32_t i = 0; i < ticket_keys_len; i++) { + POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); + if (now >= ticket_key->intro_timestamp + + config->encrypt_decrypt_key_lifetime_in_nanos + + config->decrypt_key_lifetime_in_nanos) { + expired_keys_index[num_of_expired_keys] = i; + num_of_expired_keys++; + } + } + +end: + for (int j = 0; j < num_of_expired_keys; j++) { + POSIX_GUARD_RESULT(s2n_array_remove(config->ticket_keys, expired_keys_index[j] - j)); + } + + return 0; +} + +int s2n_config_store_ticket_key(struct s2n_config *config, struct s2n_ticket_key *key) +{ + uint32_t ticket_keys_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + /* The ticket key name and secret must both be unique. */ + for (uint32_t i = 0; i < ticket_keys_len; i++) { + struct s2n_ticket_key *other_key = NULL; + POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &other_key)); + POSIX_ENSURE(!s2n_constant_time_equals(key->key_name, other_key->key_name, s2n_array_len(key->key_name)), + S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + POSIX_ENSURE(!s2n_constant_time_equals(key->aes_key, other_key->aes_key, s2n_array_len(key->aes_key)), + S2N_ERR_TICKET_KEY_NOT_UNIQUE); + } + + POSIX_GUARD_RESULT(s2n_array_insert_and_copy(config->ticket_keys, ticket_keys_len, key)); + return S2N_SUCCESS; +} + +int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num) +{ + POSIX_ENSURE_REF(config); + + config->initial_tickets_to_send = num; + POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, true)); + + return S2N_SUCCESS; +} + +int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_psk_validate_keying_material(conn)); + + uint32_t out = conn->tickets_to_send + num; + POSIX_ENSURE(out <= UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); + conn->tickets_to_send = out; + + return S2N_SUCCESS; +} + +int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(num); + POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); + *num = conn->tickets_sent; + return S2N_SUCCESS; +} + +int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(conn); + conn->server_keying_material_lifetime = lifetime_in_secs; + return S2N_SUCCESS; +} + +int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx) +{ + POSIX_ENSURE_MUT(config); + + config->session_ticket_cb = callback; + config->session_ticket_ctx = ctx; + return S2N_SUCCESS; +} + +int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len) +{ + POSIX_ENSURE_REF(ticket); + POSIX_ENSURE_MUT(data_len); + + *data_len = ticket->ticket_data.size; + return S2N_SUCCESS; +} + +int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data) +{ + POSIX_ENSURE_REF(ticket); + POSIX_ENSURE_MUT(data); + + POSIX_ENSURE(ticket->ticket_data.size <= max_data_len, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); + POSIX_CHECKED_MEMCPY(data, ticket->ticket_data.data, ticket->ticket_data.size); + + return S2N_SUCCESS; +} + +int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime) +{ + POSIX_ENSURE_REF(ticket); + POSIX_ENSURE_REF(session_lifetime); + + *session_lifetime = ticket->session_lifetime; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_send.c b/tls/s2n_send.c index 27d1afc6a20..1e5cfe3e8f0 100644 --- a/tls/s2n_send.c +++ b/tls/s2n_send.c @@ -1,274 +1,275 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "crypto/s2n_cipher.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_io.h" -#include "utils/s2n_safety.h" - -/* - * Determine whether there is currently sufficient space in the send buffer to construct - * another record, or if we need to flush now. - * - * We only buffer multiple records when sending application data, NOT when - * sending handshake messages or alerts. If the next record is a post-handshake message - * or an alert, then the send buffer will be flushed regardless of the result of this method. - * Therefore we don't need to consider the size of any potential KeyUpdate messages, - * NewSessionTicket messages, or Alerts. - */ -bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size) -{ - /* Always flush if not buffering multiple records. */ - if (!conn->multirecord_send) { - return true; - } - - /* Flush if all data has been sent. */ - ssize_t remaining_payload_size = total_message_size - conn->current_user_data_consumed; - if (remaining_payload_size <= 0) { - return true; - } - - uint16_t max_payload_size = 0; - if (!s2n_result_is_ok(s2n_record_max_write_payload_size(conn, &max_payload_size))) { - /* When in doubt, flush */ - return true; - } - max_payload_size = S2N_MIN(max_payload_size, remaining_payload_size); - - uint16_t max_write_size = 0; - if (!s2n_result_is_ok(s2n_record_max_write_size(conn, max_payload_size, &max_write_size))) { - /* When in doubt, flush */ - return true; - } - - /* Flush if the stuffer can't store the max possible record size without growing. - * - * However, the stuffer is allocated when the record is sent, so if the stuffer - * hasn't been allocated, assume it will have enough space. - */ - uint32_t available_space = s2n_stuffer_space_remaining(&conn->out); - if (available_space < max_write_size && !s2n_stuffer_is_freed(&conn->out)) { - return true; - } - - return false; -} - -int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(blocked); - *blocked = S2N_BLOCKED_ON_WRITE; - - /* Write any data that's already pending */ - while (s2n_stuffer_data_available(&conn->out)) { - errno = 0; - int w = s2n_connection_send_stuffer(&conn->out, conn, s2n_stuffer_data_available(&conn->out)); - POSIX_GUARD_RESULT(s2n_io_check_write_result(w)); - conn->wire_bytes_out += w; - } - POSIX_GUARD(s2n_stuffer_rewrite(&conn->out)); - - if (conn->reader_warning_out) { - POSIX_GUARD_RESULT(s2n_alerts_write_warning(conn)); - conn->reader_warning_out = 0; - POSIX_GUARD(s2n_flush(conn, blocked)); - } - - *blocked = S2N_NOT_BLOCKED; - return 0; -} - -S2N_RESULT s2n_sendv_with_offset_total_size(const struct iovec *bufs, ssize_t count, - ssize_t offs, ssize_t *total_size_out) -{ - RESULT_ENSURE_REF(total_size_out); - if (count > 0) { - RESULT_ENSURE_REF(bufs); - } - - size_t total_size = 0; - for (ssize_t i = 0; i < count; i++) { - size_t iov_len = bufs[i].iov_len; - /* Account for any offset */ - if (offs > 0) { - size_t offs_consumed = S2N_MIN((size_t) offs, iov_len); - iov_len -= offs_consumed; - offs -= offs_consumed; - } - RESULT_ENSURE(S2N_ADD_IS_OVERFLOW_SAFE(total_size, iov_len, SIZE_MAX), - S2N_ERR_INVALID_ARGUMENT); - total_size += iov_len; - } - - /* We must have fully accounted for the offset, or else the offset is larger - * than the available data and our inputs are invalid. - */ - RESULT_ENSURE(offs == 0, S2N_ERR_INVALID_ARGUMENT); - - RESULT_ENSURE(total_size <= SSIZE_MAX, S2N_ERR_INVALID_ARGUMENT); - *total_size_out = total_size; - return S2N_RESULT_OK; -} - -ssize_t s2n_sendv_with_offset_impl(struct s2n_connection *conn, const struct iovec *bufs, - ssize_t count, ssize_t offs, s2n_blocked_status *blocked) -{ - ssize_t user_data_sent = 0, total_size = 0; - - POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE), S2N_ERR_CLOSED); - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); - - /* Flush any pending I/O */ - POSIX_GUARD(s2n_flush(conn, blocked)); - - if (conn->ktls_send_enabled) { - return s2n_ktls_sendv_with_offset(conn, bufs, count, offs, blocked); - } - - /* Acknowledge consumed and flushed user data as sent */ - user_data_sent = conn->current_user_data_consumed; - - *blocked = S2N_BLOCKED_ON_WRITE; - - uint16_t max_payload_size = 0; - POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_payload_size)); - - /* TLS 1.0 and SSLv3 are vulnerable to the so-called Beast attack. Work - * around this by splitting messages into one byte records, and then - * the remainder can follow as usual. - */ - int cbcHackUsed = 0; - - struct s2n_crypto_parameters *writer = conn->server; - if (conn->mode == S2N_CLIENT) { - writer = conn->client; - } - - POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count, offs, &total_size)); - /* Defensive check against an invalid retry */ - POSIX_ENSURE(conn->current_user_data_consumed <= total_size, S2N_ERR_SEND_SIZE); - POSIX_GUARD_RESULT(s2n_early_data_validate_send(conn, total_size)); - - if (conn->dynamic_record_timeout_threshold > 0) { - uint64_t elapsed = 0; - POSIX_GUARD_RESULT(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); - /* Reset record size back to a single segment after threshold seconds of inactivity */ - if (elapsed - conn->last_write_elapsed > (uint64_t) conn->dynamic_record_timeout_threshold * 1000000000) { - conn->active_application_bytes_consumed = 0; - } - conn->last_write_elapsed = elapsed; - } - - /* Now write the data we were asked to send this round */ - while (total_size - conn->current_user_data_consumed) { - ssize_t to_write = S2N_MIN(total_size - conn->current_user_data_consumed, max_payload_size); - - /* If dynamic record size is enabled, - * use small TLS records that fit into a single TCP segment for the threshold bytes of data - */ - if (conn->active_application_bytes_consumed < (uint64_t) conn->dynamic_record_resize_threshold) { - uint16_t min_payload_size = 0; - POSIX_GUARD_RESULT(s2n_record_min_write_payload_size(conn, &min_payload_size)); - to_write = S2N_MIN(min_payload_size, to_write); - } - - /* Don't split messages in server mode for interoperability with naive clients. - * Some clients may have expectations based on the amount of content in the first record. - */ - if (conn->actual_protocol_version < S2N_TLS11 - && writer->cipher_suite->record_alg->cipher->type == S2N_CBC && conn->mode != S2N_SERVER) { - if (to_write > 1 && cbcHackUsed == 0) { - to_write = 1; - cbcHackUsed = 1; - } - } - - POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); - - /* Write and encrypt the record */ - int written_to_record = s2n_record_writev(conn, TLS_APPLICATION_DATA, bufs, count, - conn->current_user_data_consumed + offs, to_write); - POSIX_GUARD(written_to_record); - conn->current_user_data_consumed += written_to_record; - conn->active_application_bytes_consumed += written_to_record; - - /* Send it, unless we're waiting for more records */ - if (s2n_should_flush(conn, total_size)) { - if (s2n_flush(conn, blocked) < 0) { - if (s2n_errno == S2N_ERR_IO_BLOCKED && user_data_sent > 0) { - /* We successfully sent >0 user bytes on the wire, but not the full requested payload - * because we became blocked on I/O. Acknowledge the data sent. */ - - conn->current_user_data_consumed -= user_data_sent; - return user_data_sent; - } else { - S2N_ERROR_PRESERVE_ERRNO(); - } - } - - /* Acknowledge consumed and flushed user data as sent */ - user_data_sent = conn->current_user_data_consumed; - } - } - - /* If everything has been written, then there's no user data pending */ - conn->current_user_data_consumed = 0; - - *blocked = S2N_NOT_BLOCKED; - return total_size; -} - -ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, - ssize_t offs, s2n_blocked_status *blocked) -{ - POSIX_ENSURE(!conn->send_in_use, S2N_ERR_REENTRANCY); - conn->send_in_use = true; - - ssize_t result = s2n_sendv_with_offset_impl(conn, bufs, count, offs, blocked); - POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); - - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); - - conn->send_in_use = false; - return result; -} - -ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked) -{ - return s2n_sendv_with_offset(conn, bufs, count, 0, blocked); -} - -ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked) -{ - struct iovec iov; - iov.iov_base = (void *) (uintptr_t) buf; - iov.iov_len = size; - return s2n_sendv_with_offset(conn, &iov, 1, 0, blocked); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "crypto/s2n_cipher.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_io.h" +#include "utils/s2n_safety.h" + +/* + * Determine whether there is currently sufficient space in the send buffer to construct + * another record, or if we need to flush now. + * + * We only buffer multiple records when sending application data, NOT when + * sending handshake messages or alerts. If the next record is a post-handshake message + * or an alert, then the send buffer will be flushed regardless of the result of this method. + * Therefore we don't need to consider the size of any potential KeyUpdate messages, + * NewSessionTicket messages, or Alerts. + */ +bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size) +{ + /* Always flush if not buffering multiple records. */ + if (!conn->multirecord_send) { + return true; + } + + /* Flush if all data has been sent. */ + ssize_t remaining_payload_size = total_message_size - conn->current_user_data_consumed; + if (remaining_payload_size <= 0) { + return true; + } + + uint16_t max_payload_size = 0; + if (!s2n_result_is_ok(s2n_record_max_write_payload_size(conn, &max_payload_size))) { + /* When in doubt, flush */ + return true; + } + max_payload_size = S2N_MIN(max_payload_size, remaining_payload_size); + + uint16_t max_write_size = 0; + if (!s2n_result_is_ok(s2n_record_max_write_size(conn, max_payload_size, &max_write_size))) { + /* When in doubt, flush */ + return true; + } + + /* Flush if the stuffer can't store the max possible record size without growing. + * + * However, the stuffer is allocated when the record is sent, so if the stuffer + * hasn't been allocated, assume it will have enough space. + */ + uint32_t available_space = s2n_stuffer_space_remaining(&conn->out); + if (available_space < max_write_size && !s2n_stuffer_is_freed(&conn->out)) { + return true; + } + + return false; +} + +int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(blocked); + *blocked = S2N_BLOCKED_ON_WRITE; + + /* Write any data that's already pending */ + while (s2n_stuffer_data_available(&conn->out)) { + errno = 0; + int w = s2n_connection_send_stuffer(&conn->out, conn, s2n_stuffer_data_available(&conn->out)); + POSIX_GUARD_RESULT(s2n_io_check_write_result(w)); + conn->wire_bytes_out += w; + } + POSIX_GUARD(s2n_stuffer_rewrite(&conn->out)); + + if (conn->reader_warning_out) { + POSIX_GUARD_RESULT(s2n_alerts_write_warning(conn)); + conn->reader_warning_out = 0; + POSIX_GUARD(s2n_flush(conn, blocked)); + } + + *blocked = S2N_NOT_BLOCKED; + return 0; +} + +S2N_RESULT s2n_sendv_with_offset_total_size(const struct iovec *bufs, ssize_t count, + ssize_t offs, ssize_t *total_size_out) +{ + RESULT_ENSURE_REF(total_size_out); + if (count > 0) { + RESULT_ENSURE_REF(bufs); + } + + size_t total_size = 0; + for (ssize_t i = 0; i < count; i++) { + size_t iov_len = bufs[i].iov_len; + /* Account for any offset */ + if (offs > 0) { + size_t offs_consumed = S2N_MIN((size_t) offs, iov_len); + iov_len -= offs_consumed; + offs -= offs_consumed; + } + RESULT_ENSURE(S2N_ADD_IS_OVERFLOW_SAFE(total_size, iov_len, SIZE_MAX), + S2N_ERR_INVALID_ARGUMENT); + total_size += iov_len; + } + + /* We must have fully accounted for the offset, or else the offset is larger + * than the available data and our inputs are invalid. + */ + RESULT_ENSURE(offs == 0, S2N_ERR_INVALID_ARGUMENT); + + RESULT_ENSURE(total_size <= SSIZE_MAX, S2N_ERR_INVALID_ARGUMENT); + *total_size_out = total_size; + return S2N_RESULT_OK; +} + +ssize_t s2n_sendv_with_offset_impl(struct s2n_connection *conn, const struct iovec *bufs, + ssize_t count, ssize_t offs, s2n_blocked_status *blocked) +{ + ssize_t user_data_sent = 0, total_size = 0; + + POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE), S2N_ERR_CLOSED); + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); + + /* Flush any pending I/O */ + POSIX_GUARD(s2n_flush(conn, blocked)); + + if (conn->ktls_send_enabled) { + return s2n_ktls_sendv_with_offset(conn, bufs, count, offs, blocked); + } + + /* Acknowledge consumed and flushed user data as sent */ + user_data_sent = conn->current_user_data_consumed; + + *blocked = S2N_BLOCKED_ON_WRITE; + + uint16_t max_payload_size = 0; + POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_payload_size)); + + /* TLS 1.0 and SSLv3 are vulnerable to the so-called Beast attack. Work + * around this by splitting messages into one byte records, and then + * the remainder can follow as usual. + */ + int cbcHackUsed = 0; + + struct s2n_crypto_parameters *writer = conn->server; + if (conn->mode == S2N_CLIENT) { + writer = conn->client; + } + + POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count, offs, &total_size)); + /* Defensive check against an invalid retry */ + POSIX_ENSURE(conn->current_user_data_consumed <= total_size, S2N_ERR_SEND_SIZE); + POSIX_GUARD_RESULT(s2n_early_data_validate_send(conn, total_size)); + + if (conn->dynamic_record_timeout_threshold > 0) { + uint64_t elapsed = 0; + POSIX_GUARD_RESULT(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); + /* Reset record size back to a single segment after threshold seconds of inactivity */ + if (elapsed - conn->last_write_elapsed > (uint64_t) conn->dynamic_record_timeout_threshold * 1000000000) { + conn->active_application_bytes_consumed = 0; + } + conn->last_write_elapsed = elapsed; + } + + /* Now write the data we were asked to send this round */ + while (total_size - conn->current_user_data_consumed) { + ssize_t to_write = S2N_MIN(total_size - conn->current_user_data_consumed, max_payload_size); + + /* If dynamic record size is enabled, + * use small TLS records that fit into a single TCP segment for the threshold bytes of data + */ + if (conn->active_application_bytes_consumed < (uint64_t) conn->dynamic_record_resize_threshold) { + uint16_t min_payload_size = 0; + POSIX_GUARD_RESULT(s2n_record_min_write_payload_size(conn, &min_payload_size)); + to_write = S2N_MIN(min_payload_size, to_write); + } + + /* Don't split messages in server mode for interoperability with naive clients. + * Some clients may have expectations based on the amount of content in the first record. + */ + if (conn->actual_protocol_version < S2N_TLS11 + && writer->cipher_suite->record_alg->cipher->type == S2N_CBC && conn->mode != S2N_SERVER) { + if (to_write > 1 && cbcHackUsed == 0) { + to_write = 1; + cbcHackUsed = 1; + } + } + + POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); + + /* Write and encrypt the record */ + int written_to_record = s2n_record_writev(conn, TLS_APPLICATION_DATA, bufs, count, + conn->current_user_data_consumed + offs, to_write); + POSIX_GUARD(written_to_record); + conn->current_user_data_consumed += written_to_record; + conn->active_application_bytes_consumed += written_to_record; + + /* Send it, unless we're waiting for more records */ + if (s2n_should_flush(conn, total_size)) { + if (s2n_flush(conn, blocked) < 0) { + if (s2n_errno == S2N_ERR_IO_BLOCKED && user_data_sent > 0) { + /* We successfully sent >0 user bytes on the wire, but not the full requested payload + * because we became blocked on I/O. Acknowledge the data sent. */ + + conn->current_user_data_consumed -= user_data_sent; + return user_data_sent; + } else { + S2N_ERROR_PRESERVE_ERRNO(); + } + } + + /* Acknowledge consumed and flushed user data as sent */ + user_data_sent = conn->current_user_data_consumed; + } + } + + /* If everything has been written, then there's no user data pending */ + conn->current_user_data_consumed = 0; + + *blocked = S2N_NOT_BLOCKED; + return total_size; +} + +ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, + ssize_t offs, s2n_blocked_status *blocked) +{ + POSIX_ENSURE(!conn->send_in_use, S2N_ERR_REENTRANCY); + conn->send_in_use = true; + + ssize_t result = s2n_sendv_with_offset_impl(conn, bufs, count, offs, blocked); + POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); + + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); + + conn->send_in_use = false; + return result; +} + +ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked) +{ + return s2n_sendv_with_offset(conn, bufs, count, 0, blocked); +} + +ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked) +{ + struct iovec iov; + iov.iov_base = (void *) (uintptr_t) buf; + iov.iov_len = size; + return s2n_sendv_with_offset(conn, &iov, 1, 0, blocked); +} diff --git a/tls/s2n_server_hello.c b/tls/s2n_server_hello.c index 9b8bb8a2208..221a10e714b 100644 --- a/tls/s2n_server_hello.c +++ b/tls/s2n_server_hello.c @@ -1,343 +1,344 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_server_extensions.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls13_key_schedule.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -/* From RFC5246 7.4.1.2. */ -#define S2N_TLS_COMPRESSION_METHOD_NULL 0 - -/* From RFC8446 4.1.3. */ -#define S2N_DOWNGRADE_PROTECTION_SIZE 8 -const uint8_t tls12_downgrade_protection_bytes[] = { - 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01 -}; - -const uint8_t tls11_downgrade_protection_bytes[] = { - 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x00 -}; - -static int s2n_random_value_is_hello_retry(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - POSIX_ENSURE(s2n_constant_time_equals(hello_retry_req_random, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN), - S2N_ERR_INVALID_HELLO_RETRY); - - return S2N_SUCCESS; -} - -static int s2n_client_detect_downgrade_mechanism(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; - - /* Detect downgrade attacks according to RFC 8446 section 4.1.3 */ - if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version == S2N_TLS12) { - if (s2n_constant_time_equals(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { - POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - } - } else if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version <= S2N_TLS11) { - if (s2n_constant_time_equals(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { - POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - } - } - - return 0; -} - -static int s2n_server_add_downgrade_mechanism(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; - - /* Protect against downgrade attacks according to RFC 8446 section 4.1.3 */ - if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version == S2N_TLS12) { - /* TLS1.3 servers MUST use a special random value when negotiating TLS1.2 */ - POSIX_CHECKED_MEMCPY(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); - } else if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version <= S2N_TLS11) { - /* TLS1.3 servers MUST, use a special random value when negotiating TLS1.1 or below */ - POSIX_CHECKED_MEMCPY(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); - } - - return 0; -} - -static int s2n_server_hello_parse(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - struct s2n_stuffer *in = &conn->handshake.io; - uint8_t compression_method = 0; - uint8_t session_id_len = 0; - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN]; - - POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - POSIX_GUARD(s2n_stuffer_read_bytes(in, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); - - uint8_t legacy_version = (uint8_t) (protocol_version[0] * 10) + protocol_version[1]; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 - *# Upon receiving a message with type server_hello, implementations MUST - *# first examine the Random value and, if it matches this value, process - *# it as described in Section 4.1.4). - **/ - if (s2n_random_value_is_hello_retry(conn) == S2N_SUCCESS) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# If a client receives a second - *# HelloRetryRequest in the same connection (i.e., where the ClientHello - *# was itself in response to a HelloRetryRequest), it MUST abort the - *# handshake with an "unexpected_message" alert. - **/ - POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_INVALID_HELLO_RETRY); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version - **/ - POSIX_ENSURE(legacy_version == S2N_TLS12, S2N_ERR_INVALID_HELLO_RETRY); - - POSIX_GUARD(s2n_set_hello_retry_required(conn)); - } - - POSIX_GUARD(s2n_stuffer_read_uint8(in, &session_id_len)); - S2N_ERROR_IF(session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_stuffer_read_bytes(in, session_id, session_id_len)); - - uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(in, S2N_TLS_CIPHER_SUITE_LEN); - POSIX_ENSURE_REF(cipher_suite_wire); - - POSIX_GUARD(s2n_stuffer_read_uint8(in, &compression_method)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 - *# legacy_compression_method: A single byte which MUST have the - *# value 0. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version, legacy_session_id_echo, cipher_suite, and - *# legacy_compression_method - **/ - S2N_ERROR_IF(compression_method != S2N_TLS_COMPRESSION_METHOD_NULL, S2N_ERR_BAD_MESSAGE); - - bool session_ids_match = session_id_len != 0 && session_id_len == conn->session_id_len - && s2n_constant_time_equals(session_id, conn->session_id, session_id_len); - if (!session_ids_match) { - conn->ems_negotiated = false; - } - - POSIX_GUARD(s2n_server_extensions_recv(conn, in)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# The server's extensions MUST contain "supported_versions". - **/ - if (s2n_is_hello_retry_message(conn)) { - s2n_extension_type_id supported_versions_id = s2n_unsupported_extension; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); - POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_responses_received, supported_versions_id), - S2N_ERR_MISSING_EXTENSION); - } - - if (conn->server_protocol_version >= S2N_TLS13) { - POSIX_ENSURE(!conn->handshake.renegotiation, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.3 - *# A client which - *# receives a legacy_session_id_echo field that does not match what - *# it sent in the ClientHello MUST abort the handshake with an - *# "illegal_parameter" alert. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version, legacy_session_id_echo - **/ - POSIX_ENSURE(session_ids_match || (session_id_len == 0 && conn->session_id_len == 0), S2N_ERR_BAD_MESSAGE); - - conn->actual_protocol_version = conn->server_protocol_version; - POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); - - /* Erase TLS 1.2 client session ticket which might have been set for session resumption */ - POSIX_GUARD(s2n_free(&conn->client_ticket)); - } else { - conn->server_protocol_version = legacy_version; - - POSIX_ENSURE(!s2n_client_detect_downgrade_mechanism(conn), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - /* Hello retries are only supported in >=TLS1.3. */ - POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_BAD_MESSAGE); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.3 - *# A client that attempts to send 0-RTT data MUST fail a connection if - *# it receives a ServerHello with TLS 1.2 or older. - */ - POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - - if (conn->server_protocol_version < security_policy->minimum_protocol_version - || conn->server_protocol_version > conn->client_protocol_version) { - POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); - POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); - - /* - *= https://www.rfc-editor.org/rfc/rfc5077#section-3.4 - *# If the server accepts the ticket - *# and the Session ID is not empty, then it MUST respond with the same - *# Session ID present in the ClientHello. This allows the client to - *# easily differentiate when the server is resuming a session from when - *# it is falling back to a full handshake. - */ - if (session_ids_match) { - /* check if the resumed session state is valid */ - POSIX_ENSURE(conn->resume_protocol_version == conn->actual_protocol_version, S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite_wire, S2N_TLS_CIPHER_SUITE_LEN), - S2N_ERR_BAD_MESSAGE); - - /* Session is resumed */ - conn->client_session_resumed = 1; - } else { - conn->session_id_len = session_id_len; - POSIX_CHECKED_MEMCPY(conn->session_id, session_id, session_id_len); - POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); - /* Erase master secret which might have been set for session resumption */ - POSIX_CHECKED_MEMSET((uint8_t *) conn->secrets.version.tls12.master_secret, 0, S2N_TLS_SECRET_LEN); - - /* Erase client session ticket which might have been set for session resumption */ - POSIX_GUARD(s2n_free(&conn->client_ticket)); - } - } - - /* If it is not possible to accept early data on this connection - * (for example, because no PSK was negotiated) we need to reject early data now. - * Otherwise, early data logic may make certain invalid assumptions about the - * state of the connection (for example, that the prf is the early data prf). - */ - POSIX_GUARD_RESULT(s2n_early_data_accept_or_reject(conn)); - if (conn->early_data_state == S2N_EARLY_DATA_REJECTED) { - POSIX_GUARD_RESULT(s2n_tls13_key_schedule_reset(conn)); - } - - return 0; -} - -int s2n_server_hello_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* Read the message off the wire */ - POSIX_GUARD(s2n_server_hello_parse(conn)); - - conn->actual_protocol_version_established = 1; - - POSIX_GUARD(s2n_conn_set_handshake_type(conn)); - - /* If this is a HelloRetryRequest, we don't process the ServerHello. - * Instead we proceed with retry logic. */ - if (s2n_is_hello_retry_message(conn)) { - POSIX_GUARD(s2n_server_hello_retry_recv(conn)); - return 0; - } - - if (conn->actual_protocol_version < S2N_TLS13 && s2n_connection_is_session_resumed(conn)) { - POSIX_GUARD(s2n_prf_key_expansion(conn)); - } - - /* Update the required hashes for this connection */ - POSIX_GUARD(s2n_conn_update_required_handshake_hashes(conn)); - - return 0; -} - -int s2n_server_hello_write_message(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - /* The actual_protocol_version is set while processing the CLIENT_HELLO message, so - * it could be S2N_TLS13. SERVER_HELLO should always respond with the legacy version. - * https://tools.ietf.org/html/rfc8446#section-4.1.3 */ - const uint16_t legacy_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - protocol_version[0] = (uint8_t) (legacy_protocol_version / 10); - protocol_version[1] = (uint8_t) (legacy_protocol_version % 10); - - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, conn->session_id_len)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->session_id, conn->session_id_len)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, S2N_TLS_COMPRESSION_METHOD_NULL)); - - return 0; -} - -int s2n_server_hello_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - struct s2n_stuffer server_random = { 0 }; - struct s2n_blob b = { 0 }; - POSIX_GUARD(s2n_blob_init(&b, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); - - /* Create the server random data */ - POSIX_GUARD(s2n_stuffer_init(&server_random, &b)); - - struct s2n_blob rand_data = { 0 }; - POSIX_GUARD(s2n_blob_init(&rand_data, s2n_stuffer_raw_write(&server_random, S2N_TLS_RANDOM_DATA_LEN), S2N_TLS_RANDOM_DATA_LEN)); - POSIX_ENSURE_REF(rand_data.data); - POSIX_GUARD_RESULT(s2n_get_public_random_data(&rand_data)); - - /* Add a downgrade detection mechanism if required */ - POSIX_GUARD(s2n_server_add_downgrade_mechanism(conn)); - - POSIX_GUARD(s2n_server_hello_write_message(conn)); - - POSIX_GUARD(s2n_server_extensions_send(conn, &conn->handshake.io)); - - conn->actual_protocol_version_established = 1; - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_server_extensions.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls13_key_schedule.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +/* From RFC5246 7.4.1.2. */ +#define S2N_TLS_COMPRESSION_METHOD_NULL 0 + +/* From RFC8446 4.1.3. */ +#define S2N_DOWNGRADE_PROTECTION_SIZE 8 +const uint8_t tls12_downgrade_protection_bytes[] = { + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01 +}; + +const uint8_t tls11_downgrade_protection_bytes[] = { + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x00 +}; + +static int s2n_random_value_is_hello_retry(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + POSIX_ENSURE(s2n_constant_time_equals(hello_retry_req_random, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN), + S2N_ERR_INVALID_HELLO_RETRY); + + return S2N_SUCCESS; +} + +static int s2n_client_detect_downgrade_mechanism(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; + + /* Detect downgrade attacks according to RFC 8446 section 4.1.3 */ + if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version == S2N_TLS12) { + if (s2n_constant_time_equals(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { + POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + } + } else if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version <= S2N_TLS11) { + if (s2n_constant_time_equals(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { + POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + } + } + + return 0; +} + +static int s2n_server_add_downgrade_mechanism(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; + + /* Protect against downgrade attacks according to RFC 8446 section 4.1.3 */ + if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version == S2N_TLS12) { + /* TLS1.3 servers MUST use a special random value when negotiating TLS1.2 */ + POSIX_CHECKED_MEMCPY(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); + } else if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version <= S2N_TLS11) { + /* TLS1.3 servers MUST, use a special random value when negotiating TLS1.1 or below */ + POSIX_CHECKED_MEMCPY(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); + } + + return 0; +} + +static int s2n_server_hello_parse(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + struct s2n_stuffer *in = &conn->handshake.io; + uint8_t compression_method = 0; + uint8_t session_id_len = 0; + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN]; + + POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + POSIX_GUARD(s2n_stuffer_read_bytes(in, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); + + uint8_t legacy_version = (uint8_t) (protocol_version[0] * 10) + protocol_version[1]; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 + *# Upon receiving a message with type server_hello, implementations MUST + *# first examine the Random value and, if it matches this value, process + *# it as described in Section 4.1.4). + **/ + if (s2n_random_value_is_hello_retry(conn) == S2N_SUCCESS) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# If a client receives a second + *# HelloRetryRequest in the same connection (i.e., where the ClientHello + *# was itself in response to a HelloRetryRequest), it MUST abort the + *# handshake with an "unexpected_message" alert. + **/ + POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_INVALID_HELLO_RETRY); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version + **/ + POSIX_ENSURE(legacy_version == S2N_TLS12, S2N_ERR_INVALID_HELLO_RETRY); + + POSIX_GUARD(s2n_set_hello_retry_required(conn)); + } + + POSIX_GUARD(s2n_stuffer_read_uint8(in, &session_id_len)); + S2N_ERROR_IF(session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_stuffer_read_bytes(in, session_id, session_id_len)); + + uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(in, S2N_TLS_CIPHER_SUITE_LEN); + POSIX_ENSURE_REF(cipher_suite_wire); + + POSIX_GUARD(s2n_stuffer_read_uint8(in, &compression_method)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 + *# legacy_compression_method: A single byte which MUST have the + *# value 0. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo, cipher_suite, and + *# legacy_compression_method + **/ + S2N_ERROR_IF(compression_method != S2N_TLS_COMPRESSION_METHOD_NULL, S2N_ERR_BAD_MESSAGE); + + bool session_ids_match = session_id_len != 0 && session_id_len == conn->session_id_len + && s2n_constant_time_equals(session_id, conn->session_id, session_id_len); + if (!session_ids_match) { + conn->ems_negotiated = false; + } + + POSIX_GUARD(s2n_server_extensions_recv(conn, in)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# The server's extensions MUST contain "supported_versions". + **/ + if (s2n_is_hello_retry_message(conn)) { + s2n_extension_type_id supported_versions_id = s2n_unsupported_extension; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_responses_received, supported_versions_id), + S2N_ERR_MISSING_EXTENSION); + } + + if (conn->server_protocol_version >= S2N_TLS13) { + POSIX_ENSURE(!conn->handshake.renegotiation, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.3 + *# A client which + *# receives a legacy_session_id_echo field that does not match what + *# it sent in the ClientHello MUST abort the handshake with an + *# "illegal_parameter" alert. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo + **/ + POSIX_ENSURE(session_ids_match || (session_id_len == 0 && conn->session_id_len == 0), S2N_ERR_BAD_MESSAGE); + + conn->actual_protocol_version = conn->server_protocol_version; + POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); + + /* Erase TLS 1.2 client session ticket which might have been set for session resumption */ + POSIX_GUARD(s2n_free(&conn->client_ticket)); + } else { + conn->server_protocol_version = legacy_version; + + POSIX_ENSURE(!s2n_client_detect_downgrade_mechanism(conn), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /* Hello retries are only supported in >=TLS1.3. */ + POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_BAD_MESSAGE); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.3 + *# A client that attempts to send 0-RTT data MUST fail a connection if + *# it receives a ServerHello with TLS 1.2 or older. + */ + POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + + if (conn->server_protocol_version < security_policy->minimum_protocol_version + || conn->server_protocol_version > conn->client_protocol_version) { + POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); + POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); + + /* + *= https://www.rfc-editor.org/rfc/rfc5077#section-3.4 + *# If the server accepts the ticket + *# and the Session ID is not empty, then it MUST respond with the same + *# Session ID present in the ClientHello. This allows the client to + *# easily differentiate when the server is resuming a session from when + *# it is falling back to a full handshake. + */ + if (session_ids_match) { + /* check if the resumed session state is valid */ + POSIX_ENSURE(conn->resume_protocol_version == conn->actual_protocol_version, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite_wire, S2N_TLS_CIPHER_SUITE_LEN), + S2N_ERR_BAD_MESSAGE); + + /* Session is resumed */ + conn->client_session_resumed = 1; + } else { + conn->session_id_len = session_id_len; + POSIX_CHECKED_MEMCPY(conn->session_id, session_id, session_id_len); + POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); + /* Erase master secret which might have been set for session resumption */ + POSIX_CHECKED_MEMSET((uint8_t *) conn->secrets.version.tls12.master_secret, 0, S2N_TLS_SECRET_LEN); + + /* Erase client session ticket which might have been set for session resumption */ + POSIX_GUARD(s2n_free(&conn->client_ticket)); + } + } + + /* If it is not possible to accept early data on this connection + * (for example, because no PSK was negotiated) we need to reject early data now. + * Otherwise, early data logic may make certain invalid assumptions about the + * state of the connection (for example, that the prf is the early data prf). + */ + POSIX_GUARD_RESULT(s2n_early_data_accept_or_reject(conn)); + if (conn->early_data_state == S2N_EARLY_DATA_REJECTED) { + POSIX_GUARD_RESULT(s2n_tls13_key_schedule_reset(conn)); + } + + return 0; +} + +int s2n_server_hello_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* Read the message off the wire */ + POSIX_GUARD(s2n_server_hello_parse(conn)); + + conn->actual_protocol_version_established = 1; + + POSIX_GUARD(s2n_conn_set_handshake_type(conn)); + + /* If this is a HelloRetryRequest, we don't process the ServerHello. + * Instead we proceed with retry logic. */ + if (s2n_is_hello_retry_message(conn)) { + POSIX_GUARD(s2n_server_hello_retry_recv(conn)); + return 0; + } + + if (conn->actual_protocol_version < S2N_TLS13 && s2n_connection_is_session_resumed(conn)) { + POSIX_GUARD(s2n_prf_key_expansion(conn)); + } + + /* Update the required hashes for this connection */ + POSIX_GUARD(s2n_conn_update_required_handshake_hashes(conn)); + + return 0; +} + +int s2n_server_hello_write_message(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + /* The actual_protocol_version is set while processing the CLIENT_HELLO message, so + * it could be S2N_TLS13. SERVER_HELLO should always respond with the legacy version. + * https://tools.ietf.org/html/rfc8446#section-4.1.3 */ + const uint16_t legacy_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + protocol_version[0] = (uint8_t) (legacy_protocol_version / 10); + protocol_version[1] = (uint8_t) (legacy_protocol_version % 10); + + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, conn->session_id_len)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->session_id, conn->session_id_len)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, S2N_TLS_COMPRESSION_METHOD_NULL)); + + return 0; +} + +int s2n_server_hello_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + struct s2n_stuffer server_random = { 0 }; + struct s2n_blob b = { 0 }; + POSIX_GUARD(s2n_blob_init(&b, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); + + /* Create the server random data */ + POSIX_GUARD(s2n_stuffer_init(&server_random, &b)); + + struct s2n_blob rand_data = { 0 }; + POSIX_GUARD(s2n_blob_init(&rand_data, s2n_stuffer_raw_write(&server_random, S2N_TLS_RANDOM_DATA_LEN), S2N_TLS_RANDOM_DATA_LEN)); + POSIX_ENSURE_REF(rand_data.data); + POSIX_GUARD_RESULT(s2n_get_public_random_data(&rand_data)); + + /* Add a downgrade detection mechanism if required */ + POSIX_GUARD(s2n_server_add_downgrade_mechanism(conn)); + + POSIX_GUARD(s2n_server_hello_write_message(conn)); + + POSIX_GUARD(s2n_server_extensions_send(conn, &conn->handshake.io)); + + conn->actual_protocol_version_established = 1; + + return 0; +} diff --git a/tls/s2n_server_new_session_ticket.c b/tls/s2n_server_new_session_ticket.c index d61df50cb9a..7fa5379a4d1 100644 --- a/tls/s2n_server_new_session_ticket.c +++ b/tls/s2n_server_new_session_ticket.c @@ -1,443 +1,444 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -/* - * The maximum size of the NewSessionTicket message, not taking into account the - * ticket itself. - * - * To get the actual maximum size required for the NewSessionTicket message, we'll need - * to add the size of the ticket, which is much less predictable. - * - * This constant is enforced via unit tests. - */ -#define S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE 112 - -int s2n_server_nst_recv(struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_stuffer_read_uint32(&conn->handshake.io, &conn->ticket_lifetime_hint)); - - uint16_t session_ticket_len = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(&conn->handshake.io, &session_ticket_len)); - - if (session_ticket_len > 0) { - POSIX_GUARD(s2n_realloc(&conn->client_ticket, session_ticket_len)); - - POSIX_GUARD(s2n_stuffer_read(&conn->handshake.io, &conn->client_ticket)); - - if (conn->config->session_ticket_cb != NULL) { - size_t session_len = s2n_connection_get_session_length(conn); - - /* Alloc some memory for the serialized session ticket */ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&mem, - S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + S2N_TLS12_STATE_SIZE_IN_BYTES)); - - POSIX_GUARD(s2n_connection_get_session(conn, mem.data, session_len)); - uint32_t session_lifetime = s2n_connection_get_session_ticket_lifetime_hint(conn); - - struct s2n_session_ticket ticket = { .ticket_data = mem, .session_lifetime = session_lifetime }; - - POSIX_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, - S2N_ERR_CANCELLED); - } - } - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_generate_ticket_lifetime(struct s2n_connection *conn, uint64_t key_intro_time, - uint32_t *ticket_lifetime) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_MUT(ticket_lifetime); - - uint64_t now = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, &now)); - - /* Calculate ticket key age */ - RESULT_ENSURE_GTE(now, key_intro_time); - uint64_t ticket_key_age_in_nanos = now - key_intro_time; - - /* Calculate remaining key lifetime */ - uint64_t key_lifetime_in_nanos = conn->config->encrypt_decrypt_key_lifetime_in_nanos + conn->config->decrypt_key_lifetime_in_nanos; - RESULT_ENSURE_GTE(key_lifetime_in_nanos, ticket_key_age_in_nanos); - uint32_t remaining_key_lifetime = (key_lifetime_in_nanos - ticket_key_age_in_nanos) / ONE_SEC_IN_NANOS; - - uint32_t session_lifetime = conn->config->session_state_lifetime_in_nanos / ONE_SEC_IN_NANOS; - - /* Min of remaining key lifetime and session */ - uint32_t min_lifetime = S2N_MIN(remaining_key_lifetime, session_lifetime); - - /* In TLS1.3 we take into account keying material lifetime */ - if (conn->actual_protocol_version == S2N_TLS13) { - uint32_t key_material_lifetime = conn->server_keying_material_lifetime; - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - if (chosen_psk) { - RESULT_ENSURE_GTE(chosen_psk->keying_material_expiration, now); - uint32_t psk_key_material_lifetime = (chosen_psk->keying_material_expiration - now) / ONE_SEC_IN_NANOS; - key_material_lifetime = S2N_MIN(key_material_lifetime, psk_key_material_lifetime); - } - min_lifetime = S2N_MIN(min_lifetime, key_material_lifetime); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Servers MUST NOT use any value greater than - *# 604800 seconds (7 days). - **/ - *ticket_lifetime = S2N_MIN(min_lifetime, ONE_WEEK_IN_SEC); - - return S2N_RESULT_OK; -} - -int s2n_server_nst_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; - struct s2n_blob session_ticket = { 0 }; - POSIX_GUARD(s2n_blob_init(&session_ticket, data, sizeof(data))); - - uint32_t lifetime_hint_in_secs = 0; - - /* Send a zero-length ticket in the NewSessionTicket message if the server changes - * its mind mid-handshake or if there are no valid encrypt keys currently available. - * - *= https://www.rfc-editor.org/rfc/rfc5077#section-3.3 - *# If the server determines that it does not want to include a - *# ticket after it has included the SessionTicket extension in the - *# ServerHello, then it sends a zero-length ticket in the - *# NewSessionTicket handshake message. - **/ - if (s2n_result_is_error(s2n_server_nst_write(conn, &lifetime_hint_in_secs, &session_ticket))) { - POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, 0)); - POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, 0)); - return S2N_SUCCESS; - } - - POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, lifetime_hint_in_secs)); - POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, session_ticket.size)); - POSIX_GUARD(s2n_stuffer_write(&conn->handshake.io, &session_ticket)); - - /* For parity with TLS1.3, track the single ticket sent. - * This simplifies s2n_connection_get_tickets_sent. - */ - conn->tickets_sent++; - return S2N_SUCCESS; -} - -S2N_RESULT s2n_server_nst_write(struct s2n_connection *conn, uint32_t *lifetime_hint_in_secs, - struct s2n_blob *session_ticket) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE(s2n_server_sending_nst(conn), S2N_ERR_SENDING_NST); - - struct s2n_stuffer output = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&output, session_ticket)); - - struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); - RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); - - RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, lifetime_hint_in_secs)); - RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &output)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_server_nst_send(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); - - /* Usually tickets are sent immediately after the handshake. - * If possible, reuse the handshake IO stuffer before it's wiped. - * - * Note: handshake.io isn't explicitly dedicated to only reading or only writing, - * so we have to be careful using it outside of s2n_negotiate. - * If we use it for writing here, we CAN'T use it for reading any post-handshake messages. - */ - struct s2n_stuffer *nst_stuffer = &conn->handshake.io; - - if (conn->mode != S2N_SERVER || !conn->config->use_tickets) { - return S2N_RESULT_OK; - } - - /* Legacy behavior is that the s2n server sends a NST even if the client did not indicate support - * for resumption or does not support the psk_dhe_ke mode. This is potentially wasteful so we - * choose to not extend this behavior to QUIC. - */ - if (conn->quic_enabled && conn->psk_params.psk_ke_mode != S2N_PSK_DHE_KE) { - return S2N_RESULT_OK; - } - - /* No-op if all tickets already sent. - * Clean up the stuffer used for the ticket to conserve memory. */ - if (conn->tickets_to_send == conn->tickets_sent) { - RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, 0)); - return S2N_RESULT_OK; - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Note that in principle it is possible to continue issuing new tickets - *# which indefinitely extend the lifetime of the keying material - *# originally derived from an initial non-PSK handshake (which was most - *# likely tied to the peer's certificate). It is RECOMMENDED that - *# implementations place limits on the total lifetime of such keying - *# material; these limits should take into account the lifetime of the - *# peer's certificate, the likelihood of intervening revocation, and the - *# time since the peer's online CertificateVerify signature. - */ - if (s2n_result_is_error(s2n_psk_validate_keying_material(conn))) { - conn->tickets_to_send = conn->tickets_sent; - return S2N_RESULT_OK; - } - - RESULT_ENSURE(conn->tickets_sent <= conn->tickets_to_send, S2N_ERR_INTEGER_OVERFLOW); - - size_t session_state_size = 0; - RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); - const size_t maximum_nst_size = session_state_size + S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE; - if (s2n_stuffer_space_remaining(nst_stuffer) < maximum_nst_size) { - RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, maximum_nst_size)); - } - - while (conn->tickets_to_send - conn->tickets_sent > 0) { - if (s2n_result_is_error(s2n_tls13_server_nst_write(conn, nst_stuffer))) { - return S2N_RESULT_OK; - } - - RESULT_GUARD(s2n_post_handshake_write_records(conn, blocked)); - } - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# A per-ticket value that is unique across all tickets - *# issued on this connection. - **/ -static S2N_RESULT s2n_generate_ticket_nonce(uint16_t value, struct s2n_blob *output) -{ - RESULT_ENSURE_MUT(output); - - struct s2n_stuffer stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, output)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&stuffer, value)); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# A securely generated, random 32-bit value that is - *# used to obscure the age of the ticket that the client includes in - *# the "pre_shared_key" extension. - **/ -static S2N_RESULT s2n_generate_ticket_age_add(struct s2n_blob *random_data, uint32_t *ticket_age_add) -{ - RESULT_ENSURE_REF(random_data); - RESULT_ENSURE_REF(ticket_age_add); - - struct s2n_stuffer stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, random_data)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&stuffer, random_data->size)); - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&stuffer, ticket_age_add)); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# The PSK associated with the ticket is computed as: - *# - *# HKDF-Expand-Label(resumption_master_secret, - *# "resumption", ticket_nonce, Hash.length) - **/ -static int s2n_generate_session_secret(struct s2n_connection *conn, struct s2n_blob *nonce, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(nonce); - POSIX_ENSURE_REF(output); - - s2n_tls13_connection_keys(secrets, conn); - struct s2n_blob master_secret = { 0 }; - POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls13.resumption_master_secret, secrets.size)); - POSIX_GUARD(s2n_realloc(output, secrets.size)); - POSIX_GUARD_RESULT(s2n_tls13_derive_session_ticket_secret(&secrets, &master_secret, nonce, output)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_tls13_server_nst_write(struct s2n_connection *conn, struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); - RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); - - struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; - - /* Write message type because session resumption in TLS13 is a post-handshake message */ - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, TLS_SERVER_NEW_SESSION_TICKET)); - - struct s2n_stuffer_reservation message_size = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint24(output, &message_size)); - - uint32_t ticket_lifetime_in_secs = 0; - RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, &ticket_lifetime_in_secs)); - - RESULT_ENSURE(ticket_lifetime_in_secs > 0, S2N_ERR_ZERO_LIFETIME_TICKET); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_lifetime_in_secs)); - - /* Get random data to use as ticket_age_add value */ - uint8_t data[sizeof(uint32_t)] = { 0 }; - struct s2n_blob random_data = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&random_data, data, sizeof(data))); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# The server MUST generate a fresh value - *# for each ticket it sends. - **/ - RESULT_GUARD(s2n_get_private_random_data(&random_data)); - RESULT_GUARD(s2n_generate_ticket_age_add(&random_data, &ticket_fields->ticket_age_add)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_fields->ticket_age_add)); - - /* Write ticket nonce */ - uint8_t nonce_data[sizeof(uint16_t)] = { 0 }; - struct s2n_blob nonce = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, sizeof(nonce_data))); - RESULT_GUARD(s2n_generate_ticket_nonce(conn->tickets_sent, &nonce)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, nonce.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, nonce.data, nonce.size)); - - /* Derive individual session ticket secret */ - RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); - - /* Write ticket */ - struct s2n_stuffer_reservation ticket_size = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(output, &ticket_size)); - RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, output)); - RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&ticket_size)); - - RESULT_GUARD_POSIX(s2n_extension_list_send(S2N_EXTENSION_LIST_NST, conn, output)); - - RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&message_size)); - - RESULT_ENSURE(conn->tickets_sent < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); - conn->tickets_sent++; - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# struct { - *# uint32 ticket_lifetime; - *# uint32 ticket_age_add; - *# opaque ticket_nonce<0..255>; - *# opaque ticket<1..2^16-1>; - *# Extension extensions<0..2^16-2>; - *# } NewSessionTicket; -**/ -S2N_RESULT s2n_tls13_server_nst_recv(struct s2n_connection *conn, struct s2n_stuffer *input) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(input); - RESULT_ENSURE_REF(conn->config); - - RESULT_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); - - if (!conn->config->use_tickets) { - return S2N_RESULT_OK; - } - struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; - - /* Handle `ticket_lifetime` field */ - uint32_t ticket_lifetime = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_lifetime)); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Servers MUST NOT use any value greater than - *# 604800 seconds (7 days). - */ - RESULT_ENSURE(ticket_lifetime <= ONE_WEEK_IN_SEC, S2N_ERR_BAD_MESSAGE); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# The value of zero indicates that the - *# ticket should be discarded immediately. - */ - if (ticket_lifetime == 0) { - return S2N_RESULT_OK; - } - conn->ticket_lifetime_hint = ticket_lifetime; - - /* Handle `ticket_age_add` field */ - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_fields->ticket_age_add)); - - /* Handle `ticket_nonce` field */ - uint8_t ticket_nonce_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(input, &ticket_nonce_len)); - uint8_t nonce_data[UINT8_MAX] = { 0 }; - struct s2n_blob nonce = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, ticket_nonce_len)); - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, nonce.data, ticket_nonce_len)); - RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); - - /* Handle `ticket` field */ - uint16_t session_ticket_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(input, &session_ticket_len)); - RESULT_ENSURE(session_ticket_len > 0, S2N_ERR_SAFETY); - RESULT_GUARD_POSIX(s2n_realloc(&conn->client_ticket, session_ticket_len)); - RESULT_GUARD_POSIX(s2n_stuffer_read(input, &conn->client_ticket)); - - /* Handle `extensions` field */ - RESULT_GUARD_POSIX(s2n_extension_list_recv(S2N_EXTENSION_LIST_NST, conn, input)); - - if (conn->config->session_ticket_cb != NULL) { - /* Retrieve serialized session data */ - const uint16_t session_state_size = s2n_connection_get_session_length(conn); - DEFER_CLEANUP(struct s2n_blob session_state = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_realloc(&session_state, session_state_size)); - RESULT_GUARD_POSIX(s2n_connection_get_session(conn, session_state.data, session_state.size)); - - struct s2n_session_ticket ticket = { - .ticket_data = session_state, - .session_lifetime = ticket_lifetime - }; - RESULT_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, - S2N_ERR_CANCELLED); - } - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +/* + * The maximum size of the NewSessionTicket message, not taking into account the + * ticket itself. + * + * To get the actual maximum size required for the NewSessionTicket message, we'll need + * to add the size of the ticket, which is much less predictable. + * + * This constant is enforced via unit tests. + */ +#define S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE 112 + +int s2n_server_nst_recv(struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_stuffer_read_uint32(&conn->handshake.io, &conn->ticket_lifetime_hint)); + + uint16_t session_ticket_len = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(&conn->handshake.io, &session_ticket_len)); + + if (session_ticket_len > 0) { + POSIX_GUARD(s2n_realloc(&conn->client_ticket, session_ticket_len)); + + POSIX_GUARD(s2n_stuffer_read(&conn->handshake.io, &conn->client_ticket)); + + if (conn->config->session_ticket_cb != NULL) { + size_t session_len = s2n_connection_get_session_length(conn); + + /* Alloc some memory for the serialized session ticket */ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&mem, + S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + S2N_TLS12_STATE_SIZE_IN_BYTES)); + + POSIX_GUARD(s2n_connection_get_session(conn, mem.data, session_len)); + uint32_t session_lifetime = s2n_connection_get_session_ticket_lifetime_hint(conn); + + struct s2n_session_ticket ticket = { .ticket_data = mem, .session_lifetime = session_lifetime }; + + POSIX_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, + S2N_ERR_CANCELLED); + } + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_generate_ticket_lifetime(struct s2n_connection *conn, uint64_t key_intro_time, + uint32_t *ticket_lifetime) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_MUT(ticket_lifetime); + + uint64_t now = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, &now)); + + /* Calculate ticket key age */ + RESULT_ENSURE_GTE(now, key_intro_time); + uint64_t ticket_key_age_in_nanos = now - key_intro_time; + + /* Calculate remaining key lifetime */ + uint64_t key_lifetime_in_nanos = conn->config->encrypt_decrypt_key_lifetime_in_nanos + conn->config->decrypt_key_lifetime_in_nanos; + RESULT_ENSURE_GTE(key_lifetime_in_nanos, ticket_key_age_in_nanos); + uint32_t remaining_key_lifetime = (key_lifetime_in_nanos - ticket_key_age_in_nanos) / ONE_SEC_IN_NANOS; + + uint32_t session_lifetime = conn->config->session_state_lifetime_in_nanos / ONE_SEC_IN_NANOS; + + /* Min of remaining key lifetime and session */ + uint32_t min_lifetime = S2N_MIN(remaining_key_lifetime, session_lifetime); + + /* In TLS1.3 we take into account keying material lifetime */ + if (conn->actual_protocol_version == S2N_TLS13) { + uint32_t key_material_lifetime = conn->server_keying_material_lifetime; + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + if (chosen_psk) { + RESULT_ENSURE_GTE(chosen_psk->keying_material_expiration, now); + uint32_t psk_key_material_lifetime = (chosen_psk->keying_material_expiration - now) / ONE_SEC_IN_NANOS; + key_material_lifetime = S2N_MIN(key_material_lifetime, psk_key_material_lifetime); + } + min_lifetime = S2N_MIN(min_lifetime, key_material_lifetime); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Servers MUST NOT use any value greater than + *# 604800 seconds (7 days). + **/ + *ticket_lifetime = S2N_MIN(min_lifetime, ONE_WEEK_IN_SEC); + + return S2N_RESULT_OK; +} + +int s2n_server_nst_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob session_ticket = { 0 }; + POSIX_GUARD(s2n_blob_init(&session_ticket, data, sizeof(data))); + + uint32_t lifetime_hint_in_secs = 0; + + /* Send a zero-length ticket in the NewSessionTicket message if the server changes + * its mind mid-handshake or if there are no valid encrypt keys currently available. + * + *= https://www.rfc-editor.org/rfc/rfc5077#section-3.3 + *# If the server determines that it does not want to include a + *# ticket after it has included the SessionTicket extension in the + *# ServerHello, then it sends a zero-length ticket in the + *# NewSessionTicket handshake message. + **/ + if (s2n_result_is_error(s2n_server_nst_write(conn, &lifetime_hint_in_secs, &session_ticket))) { + POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, 0)); + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, 0)); + return S2N_SUCCESS; + } + + POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, lifetime_hint_in_secs)); + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, session_ticket.size)); + POSIX_GUARD(s2n_stuffer_write(&conn->handshake.io, &session_ticket)); + + /* For parity with TLS1.3, track the single ticket sent. + * This simplifies s2n_connection_get_tickets_sent. + */ + conn->tickets_sent++; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_server_nst_write(struct s2n_connection *conn, uint32_t *lifetime_hint_in_secs, + struct s2n_blob *session_ticket) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE(s2n_server_sending_nst(conn), S2N_ERR_SENDING_NST); + + struct s2n_stuffer output = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&output, session_ticket)); + + struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); + RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + + RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, lifetime_hint_in_secs)); + RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &output)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_server_nst_send(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); + + /* Usually tickets are sent immediately after the handshake. + * If possible, reuse the handshake IO stuffer before it's wiped. + * + * Note: handshake.io isn't explicitly dedicated to only reading or only writing, + * so we have to be careful using it outside of s2n_negotiate. + * If we use it for writing here, we CAN'T use it for reading any post-handshake messages. + */ + struct s2n_stuffer *nst_stuffer = &conn->handshake.io; + + if (conn->mode != S2N_SERVER || !conn->config->use_tickets) { + return S2N_RESULT_OK; + } + + /* Legacy behavior is that the s2n server sends a NST even if the client did not indicate support + * for resumption or does not support the psk_dhe_ke mode. This is potentially wasteful so we + * choose to not extend this behavior to QUIC. + */ + if (conn->quic_enabled && conn->psk_params.psk_ke_mode != S2N_PSK_DHE_KE) { + return S2N_RESULT_OK; + } + + /* No-op if all tickets already sent. + * Clean up the stuffer used for the ticket to conserve memory. */ + if (conn->tickets_to_send == conn->tickets_sent) { + RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, 0)); + return S2N_RESULT_OK; + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Note that in principle it is possible to continue issuing new tickets + *# which indefinitely extend the lifetime of the keying material + *# originally derived from an initial non-PSK handshake (which was most + *# likely tied to the peer's certificate). It is RECOMMENDED that + *# implementations place limits on the total lifetime of such keying + *# material; these limits should take into account the lifetime of the + *# peer's certificate, the likelihood of intervening revocation, and the + *# time since the peer's online CertificateVerify signature. + */ + if (s2n_result_is_error(s2n_psk_validate_keying_material(conn))) { + conn->tickets_to_send = conn->tickets_sent; + return S2N_RESULT_OK; + } + + RESULT_ENSURE(conn->tickets_sent <= conn->tickets_to_send, S2N_ERR_INTEGER_OVERFLOW); + + size_t session_state_size = 0; + RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); + const size_t maximum_nst_size = session_state_size + S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE; + if (s2n_stuffer_space_remaining(nst_stuffer) < maximum_nst_size) { + RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, maximum_nst_size)); + } + + while (conn->tickets_to_send - conn->tickets_sent > 0) { + if (s2n_result_is_error(s2n_tls13_server_nst_write(conn, nst_stuffer))) { + return S2N_RESULT_OK; + } + + RESULT_GUARD(s2n_post_handshake_write_records(conn, blocked)); + } + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# A per-ticket value that is unique across all tickets + *# issued on this connection. + **/ +static S2N_RESULT s2n_generate_ticket_nonce(uint16_t value, struct s2n_blob *output) +{ + RESULT_ENSURE_MUT(output); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, output)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&stuffer, value)); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# A securely generated, random 32-bit value that is + *# used to obscure the age of the ticket that the client includes in + *# the "pre_shared_key" extension. + **/ +static S2N_RESULT s2n_generate_ticket_age_add(struct s2n_blob *random_data, uint32_t *ticket_age_add) +{ + RESULT_ENSURE_REF(random_data); + RESULT_ENSURE_REF(ticket_age_add); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, random_data)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&stuffer, random_data->size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&stuffer, ticket_age_add)); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# The PSK associated with the ticket is computed as: + *# + *# HKDF-Expand-Label(resumption_master_secret, + *# "resumption", ticket_nonce, Hash.length) + **/ +static int s2n_generate_session_secret(struct s2n_connection *conn, struct s2n_blob *nonce, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(nonce); + POSIX_ENSURE_REF(output); + + s2n_tls13_connection_keys(secrets, conn); + struct s2n_blob master_secret = { 0 }; + POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls13.resumption_master_secret, secrets.size)); + POSIX_GUARD(s2n_realloc(output, secrets.size)); + POSIX_GUARD_RESULT(s2n_tls13_derive_session_ticket_secret(&secrets, &master_secret, nonce, output)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_tls13_server_nst_write(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); + RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + + struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; + + /* Write message type because session resumption in TLS13 is a post-handshake message */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, TLS_SERVER_NEW_SESSION_TICKET)); + + struct s2n_stuffer_reservation message_size = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint24(output, &message_size)); + + uint32_t ticket_lifetime_in_secs = 0; + RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, &ticket_lifetime_in_secs)); + + RESULT_ENSURE(ticket_lifetime_in_secs > 0, S2N_ERR_ZERO_LIFETIME_TICKET); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_lifetime_in_secs)); + + /* Get random data to use as ticket_age_add value */ + uint8_t data[sizeof(uint32_t)] = { 0 }; + struct s2n_blob random_data = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&random_data, data, sizeof(data))); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# The server MUST generate a fresh value + *# for each ticket it sends. + **/ + RESULT_GUARD(s2n_get_private_random_data(&random_data)); + RESULT_GUARD(s2n_generate_ticket_age_add(&random_data, &ticket_fields->ticket_age_add)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_fields->ticket_age_add)); + + /* Write ticket nonce */ + uint8_t nonce_data[sizeof(uint16_t)] = { 0 }; + struct s2n_blob nonce = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, sizeof(nonce_data))); + RESULT_GUARD(s2n_generate_ticket_nonce(conn->tickets_sent, &nonce)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, nonce.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, nonce.data, nonce.size)); + + /* Derive individual session ticket secret */ + RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); + + /* Write ticket */ + struct s2n_stuffer_reservation ticket_size = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(output, &ticket_size)); + RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, output)); + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&ticket_size)); + + RESULT_GUARD_POSIX(s2n_extension_list_send(S2N_EXTENSION_LIST_NST, conn, output)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&message_size)); + + RESULT_ENSURE(conn->tickets_sent < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); + conn->tickets_sent++; + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# struct { + *# uint32 ticket_lifetime; + *# uint32 ticket_age_add; + *# opaque ticket_nonce<0..255>; + *# opaque ticket<1..2^16-1>; + *# Extension extensions<0..2^16-2>; + *# } NewSessionTicket; +**/ +S2N_RESULT s2n_tls13_server_nst_recv(struct s2n_connection *conn, struct s2n_stuffer *input) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(input); + RESULT_ENSURE_REF(conn->config); + + RESULT_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); + + if (!conn->config->use_tickets) { + return S2N_RESULT_OK; + } + struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; + + /* Handle `ticket_lifetime` field */ + uint32_t ticket_lifetime = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_lifetime)); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Servers MUST NOT use any value greater than + *# 604800 seconds (7 days). + */ + RESULT_ENSURE(ticket_lifetime <= ONE_WEEK_IN_SEC, S2N_ERR_BAD_MESSAGE); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# The value of zero indicates that the + *# ticket should be discarded immediately. + */ + if (ticket_lifetime == 0) { + return S2N_RESULT_OK; + } + conn->ticket_lifetime_hint = ticket_lifetime; + + /* Handle `ticket_age_add` field */ + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_fields->ticket_age_add)); + + /* Handle `ticket_nonce` field */ + uint8_t ticket_nonce_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(input, &ticket_nonce_len)); + uint8_t nonce_data[UINT8_MAX] = { 0 }; + struct s2n_blob nonce = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, ticket_nonce_len)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, nonce.data, ticket_nonce_len)); + RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); + + /* Handle `ticket` field */ + uint16_t session_ticket_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(input, &session_ticket_len)); + RESULT_ENSURE(session_ticket_len > 0, S2N_ERR_SAFETY); + RESULT_GUARD_POSIX(s2n_realloc(&conn->client_ticket, session_ticket_len)); + RESULT_GUARD_POSIX(s2n_stuffer_read(input, &conn->client_ticket)); + + /* Handle `extensions` field */ + RESULT_GUARD_POSIX(s2n_extension_list_recv(S2N_EXTENSION_LIST_NST, conn, input)); + + if (conn->config->session_ticket_cb != NULL) { + /* Retrieve serialized session data */ + const uint16_t session_state_size = s2n_connection_get_session_length(conn); + DEFER_CLEANUP(struct s2n_blob session_state = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_realloc(&session_state, session_state_size)); + RESULT_GUARD_POSIX(s2n_connection_get_session(conn, session_state.data, session_state.size)); + + struct s2n_session_ticket ticket = { + .ticket_data = session_state, + .session_lifetime = ticket_lifetime + }; + RESULT_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, + S2N_ERR_CANCELLED); + } + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_signature_scheme.h b/tls/s2n_signature_scheme.h index fbeb7c00145..38eb2a792f9 100644 --- a/tls/s2n_signature_scheme.h +++ b/tls/s2n_signature_scheme.h @@ -1,109 +1,111 @@ -/* - * 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 - -#include - -#include "api/s2n.h" -#include "crypto/s2n_ecc_evp.h" -#include "crypto/s2n_hash.h" -#include "crypto/s2n_signature.h" - -struct s2n_signature_scheme { - uint16_t iana_value; - const char *name; - s2n_hash_algorithm hash_alg; - s2n_signature_algorithm sig_alg; - uint8_t minimum_protocol_version; - uint8_t maximum_protocol_version; - uint16_t libcrypto_nid; - - /* Curve is only defined for TLS1.3 ECDSA Signatures */ - struct s2n_ecc_named_curve const *signature_curve; - /* Because of the different curve behavior, we should refer - * to TLS1.3 and pre-TLS1.3 versions of this scheme differently. - * Where the version is unknown, use the standard "name" field. - */ - const char *legacy_name; - const char *tls13_name; -}; - -struct s2n_signature_preferences { - uint8_t count; - const struct s2n_signature_scheme *const *signature_schemes; -}; - -extern const struct s2n_signature_scheme s2n_null_sig_scheme; - -/* RSA PKCS1 */ -/* s2n_rsa_pkcs1_md5_sha1 is not in any preference list, but it is needed since it's the default for TLS 1.0 and 1.1 if - * no SignatureScheme is sent. */ -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_md5_sha1; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha1; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha224; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha256; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha384; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha512; - -extern const struct s2n_signature_scheme s2n_ecdsa_sha1; -extern const struct s2n_signature_scheme s2n_ecdsa_sha224; -extern const struct s2n_signature_scheme s2n_ecdsa_sha256; -extern const struct s2n_signature_scheme s2n_ecdsa_sha384; -extern const struct s2n_signature_scheme s2n_ecdsa_sha512; - -/* RSA PSS */ -/* - * Use RSA-PSS-RSAE instead of RSA-PSS-PSS in order to work with older certificates. - * For more info see: https://crypto.stackexchange.com/a/58708 - */ -extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha256; -extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha384; -extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha512; -extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha256; -extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha384; -extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha512; - -/* ML-DSA: post-quantum signature schemes */ -extern const struct s2n_signature_scheme s2n_mldsa44; -extern const struct s2n_signature_scheme s2n_mldsa65; -extern const struct s2n_signature_scheme s2n_mldsa87; - -extern const struct s2n_signature_preferences s2n_signature_preferences_20250512; -extern const struct s2n_signature_preferences s2n_signature_preferences_20240501; -extern const struct s2n_signature_preferences s2n_signature_preferences_20230317; -extern const struct s2n_signature_preferences s2n_signature_preferences_20140601; -extern const struct s2n_signature_preferences s2n_signature_preferences_20200207; -extern const struct s2n_signature_preferences s2n_signature_preferences_20200207_no_sha1; -extern const struct s2n_signature_preferences s2n_signature_preferences_20201021; -extern const struct s2n_signature_preferences s2n_signature_preferences_20210816; -extern const struct s2n_signature_preferences s2n_signature_preferences_20240521; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250429; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250820; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250821; -extern const struct s2n_signature_preferences s2n_signature_preferences_20251113; -extern const struct s2n_signature_preferences s2n_signature_preferences_20260219; -extern const struct s2n_signature_preferences s2n_signature_preferences_20260220; -extern const struct s2n_signature_preferences s2n_signature_preferences_default_fips; -extern const struct s2n_signature_preferences s2n_signature_preferences_null; -extern const struct s2n_signature_preferences s2n_signature_preferences_test_all_fips; -extern const struct s2n_signature_preferences s2n_signature_preferences_all; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250813; - -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20260220; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20251113; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250512; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250429; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20201110; +/* + * 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 + +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_ecc_evp.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_signature.h" + +struct s2n_signature_scheme { + uint16_t iana_value; + const char *name; + s2n_hash_algorithm hash_alg; + s2n_signature_algorithm sig_alg; + uint8_t minimum_protocol_version; + uint8_t maximum_protocol_version; + uint16_t libcrypto_nid; + + /* Curve is only defined for TLS1.3 ECDSA Signatures */ + struct s2n_ecc_named_curve const *signature_curve; + /* Because of the different curve behavior, we should refer + * to TLS1.3 and pre-TLS1.3 versions of this scheme differently. + * Where the version is unknown, use the standard "name" field. + */ + const char *legacy_name; + const char *tls13_name; +}; + +struct s2n_signature_preferences { + uint8_t count; + const struct s2n_signature_scheme *const *signature_schemes; +}; + +extern const struct s2n_signature_scheme s2n_null_sig_scheme; + +/* RSA PKCS1 */ +/* s2n_rsa_pkcs1_md5_sha1 is not in any preference list, but it is needed since it's the default for TLS 1.0 and 1.1 if + * no SignatureScheme is sent. */ +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_md5_sha1; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha1; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha224; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha256; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha384; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha512; + +extern const struct s2n_signature_scheme s2n_ecdsa_sha1; +extern const struct s2n_signature_scheme s2n_ecdsa_sha224; +extern const struct s2n_signature_scheme s2n_ecdsa_sha256; +extern const struct s2n_signature_scheme s2n_ecdsa_sha384; +extern const struct s2n_signature_scheme s2n_ecdsa_sha512; + +/* RSA PSS */ +/* + * Use RSA-PSS-RSAE instead of RSA-PSS-PSS in order to work with older certificates. + * For more info see: https://crypto.stackexchange.com/a/58708 + */ +extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha256; +extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha384; +extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha512; +extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha256; +extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha384; +extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha512; + +/* ML-DSA: post-quantum signature schemes */ +extern const struct s2n_signature_scheme s2n_mldsa44; +extern const struct s2n_signature_scheme s2n_mldsa65; +extern const struct s2n_signature_scheme s2n_mldsa87; + +extern const struct s2n_signature_preferences s2n_signature_preferences_20250512; +extern const struct s2n_signature_preferences s2n_signature_preferences_20240501; +extern const struct s2n_signature_preferences s2n_signature_preferences_20230317; +extern const struct s2n_signature_preferences s2n_signature_preferences_20140601; +extern const struct s2n_signature_preferences s2n_signature_preferences_20200207; +extern const struct s2n_signature_preferences s2n_signature_preferences_20200207_no_sha1; +extern const struct s2n_signature_preferences s2n_signature_preferences_20201021; +extern const struct s2n_signature_preferences s2n_signature_preferences_20210816; +extern const struct s2n_signature_preferences s2n_signature_preferences_20240521; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250429; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250820; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250821; +extern const struct s2n_signature_preferences s2n_signature_preferences_20251113; +extern const struct s2n_signature_preferences s2n_signature_preferences_20260219; +extern const struct s2n_signature_preferences s2n_signature_preferences_20260220; +extern const struct s2n_signature_preferences s2n_signature_preferences_default_fips; +extern const struct s2n_signature_preferences s2n_signature_preferences_null; +extern const struct s2n_signature_preferences s2n_signature_preferences_test_all_fips; +extern const struct s2n_signature_preferences s2n_signature_preferences_all; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250813; + +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20260220; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20251113; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250512; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250429; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20201110; diff --git a/tls/s2n_tls13.h b/tls/s2n_tls13.h index d13fe3a355a..766ccc5c3fa 100644 --- a/tls/s2n_tls13.h +++ b/tls/s2n_tls13.h @@ -1,53 +1,57 @@ -/* - * 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 - -#include "api/s2n.h" -#include "tls/s2n_crypto.h" -#include "utils/s2n_compiler.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if S2N_GCC_VERSION_AT_LEAST(4, 5, 0) -S2N_API __attribute__((deprecated("The use of TLS1.3 is configured through security policies"))) int s2n_enable_tls13(); -#else -S2N_API __attribute__((deprecated)) int s2n_enable_tls13(); -#endif - -#ifdef __cplusplus -} -#endif - -/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ -extern uint8_t hello_retry_req_random[S2N_TLS_RANDOM_DATA_LEN]; - -bool s2n_use_default_tls13_config(); -bool s2n_is_tls13_fully_supported(); -int s2n_get_highest_fully_supported_tls_version(); -int s2n_enable_tls13_in_test(); -int s2n_disable_tls13_in_test(); -int s2n_reset_tls13_in_test(); -bool s2n_is_valid_tls13_cipher(const uint8_t version[2]); -S2N_RESULT s2n_connection_validate_tls13_support(struct s2n_connection *conn); -bool s2n_connection_supports_tls13(struct s2n_connection *conn); - -bool s2n_is_middlebox_compat_enabled(struct s2n_connection *conn); - -bool s2n_is_hello_retry_handshake(struct s2n_connection *conn); -bool s2n_is_hello_retry_message(struct s2n_connection *conn); -int s2n_set_hello_retry_required(struct s2n_connection *conn); +/* + * 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 + +#include "api/s2n.h" +#include "tls/s2n_crypto.h" +#include "utils/s2n_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_MSC_VER) +S2N_API __declspec(deprecated) int s2n_enable_tls13(); +#elif S2N_GCC_VERSION_AT_LEAST(4, 5, 0) +S2N_API __attribute__((deprecated("The use of TLS1.3 is configured through security policies"))) int s2n_enable_tls13(); +#else +S2N_API __attribute__((deprecated)) int s2n_enable_tls13(); +#endif + + +#ifdef __cplusplus +} +#endif + +/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ +extern uint8_t hello_retry_req_random[S2N_TLS_RANDOM_DATA_LEN]; + +bool s2n_use_default_tls13_config(); +bool s2n_is_tls13_fully_supported(); +int s2n_get_highest_fully_supported_tls_version(); +int s2n_enable_tls13_in_test(); +int s2n_disable_tls13_in_test(); +int s2n_reset_tls13_in_test(); +bool s2n_is_valid_tls13_cipher(const uint8_t version[2]); +S2N_RESULT s2n_connection_validate_tls13_support(struct s2n_connection *conn); +bool s2n_connection_supports_tls13(struct s2n_connection *conn); + +bool s2n_is_middlebox_compat_enabled(struct s2n_connection *conn); + +bool s2n_is_hello_retry_handshake(struct s2n_connection *conn); +bool s2n_is_hello_retry_message(struct s2n_connection *conn); +int s2n_set_hello_retry_required(struct s2n_connection *conn); diff --git a/tls/s2n_tls13_certificate_verify.c b/tls/s2n_tls13_certificate_verify.c index 03fdb4daf0a..4f5516804d6 100644 --- a/tls/s2n_tls13_certificate_verify.c +++ b/tls/s2n_tls13_certificate_verify.c @@ -1,211 +1,212 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_tls13_certificate_verify.h" - -#include - -#include "crypto/s2n_hash.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_async_pkey.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_safety.h" - -/** - * Specified in https://tools.ietf.org/html/rfc8446#section-4.4.3 - * - * Servers MUST send this message when authenticating via a certificate. - * Clients MUST send this message whenever authenticating via a certificate. - * When sent, this message MUST appear immediately after the Certificate - * message and immediately prior to the Finished message. - **/ - -/* 64 'space' characters (0x20) */ -const uint8_t S2N_CERT_VERIFY_PREFIX[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; -/* 'TLS 1.3, server CertificateVerify' with 0x00 separator */ -const uint8_t S2N_SERVER_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, - 0x2c, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; -/* 'TLS 1.3, client CertificateVerify' with 0x00 separator */ -const uint8_t S2N_CLIENT_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, - 0x2c, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; - -static int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme); -static int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature); -static int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, - struct s2n_stuffer *unsigned_content, s2n_mode mode); -static int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme); -static uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode); - -int s2n_tls13_cert_verify_send(struct s2n_connection *conn) -{ - S2N_ASYNC_PKEY_GUARD(conn); - - if (conn->mode == S2N_SERVER) { - /* Write digital signature */ - POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.server_cert_sig_scheme)); - } else { - /* Write digital signature */ - POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.client_cert_sig_scheme)); - } - - return 0; -} - -int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme) -{ - POSIX_ENSURE_REF(conn->handshake_params.our_chain_and_key); - - /* Write the SignatureScheme out */ - struct s2n_stuffer *out = &conn->handshake.io; - POSIX_GUARD(s2n_stuffer_write_uint16(out, chosen_sig_scheme->iana_value)); - - DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); - POSIX_GUARD(s2n_hash_new(&message_hash)); - POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); - - const struct s2n_pkey *pkey = conn->handshake_params.our_chain_and_key->private_key; - POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); - - DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, conn->mode)); - - POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, - s2n_stuffer_data_available(&unsigned_content))); - - S2N_ASYNC_PKEY_SIGN(conn, chosen_sig_scheme->sig_alg, &message_hash, s2n_tls13_write_signature); -} - -int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature) -{ - struct s2n_stuffer *out = &conn->handshake.io; - - POSIX_GUARD(s2n_stuffer_write_uint16(out, signature->size)); - POSIX_GUARD(s2n_stuffer_write_bytes(out, signature->data, signature->size)); - - return 0; -} - -int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, - struct s2n_stuffer *unsigned_content, s2n_mode mode) -{ - s2n_tls13_connection_keys(tls13_ctx, conn); - - uint8_t hash_digest_length = tls13_ctx.size; - uint8_t digest_out[S2N_MAX_DIGEST_LEN]; - - /* Get current handshake hash */ - POSIX_ENSURE_REF(conn->handshake.hashes); - struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, tls13_ctx.hash_algorithm, hash_state)); - POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, hash_digest_length)); - - /* Concatenate the content to be signed/verified */ - POSIX_GUARD(s2n_stuffer_alloc(unsigned_content, hash_digest_length + s2n_tls13_cert_verify_header_length(mode))); - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CERT_VERIFY_PREFIX, sizeof(S2N_CERT_VERIFY_PREFIX))); - - if (mode == S2N_CLIENT) { - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CLIENT_CERT_VERIFY_CONTEXT, - sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT))); - } else { - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_SERVER_CERT_VERIFY_CONTEXT, - sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT))); - } - - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, digest_out, hash_digest_length)); - - return 0; -} - -uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode) -{ - if (mode == S2N_CLIENT) { - return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT); - } - return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT); -} - -int s2n_tls13_cert_verify_recv(struct s2n_connection *conn) -{ - S2N_ASYNC_OFFLOAD_POSIX_GUARD(conn, { - POSIX_GUARD_RESULT(s2n_signature_algorithm_recv(conn, &conn->handshake.io)); - /* Read the rest of the signature and verify */ - if (conn->mode == S2N_SERVER) { - POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, - conn->handshake_params.client_cert_sig_scheme)); - } else { - POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, - conn->handshake_params.server_cert_sig_scheme)); - } - }); - return 0; -} - -int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme) -{ - struct s2n_stuffer *in = &conn->handshake.io; - DEFER_CLEANUP(struct s2n_blob signed_content = { 0 }, s2n_free); - DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); - POSIX_GUARD(s2n_hash_new(&message_hash)); - - /* Get signature size */ - uint16_t signature_size = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &signature_size)); - S2N_ERROR_IF(signature_size > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); - - /* Get wire signature */ - POSIX_GUARD(s2n_alloc(&signed_content, signature_size)); - signed_content.size = signature_size; - POSIX_GUARD(s2n_stuffer_read_bytes(in, signed_content.data, signature_size)); - - /* Verify signature. We send the opposite mode as we are trying to verify what was sent to us */ - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_SERVER)); - } else { - POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_CLIENT)); - } - - struct s2n_pkey *pkey = NULL; - if (conn->mode == S2N_CLIENT) { - pkey = &conn->handshake_params.server_public_key; - } else { - pkey = &conn->handshake_params.client_public_key; - } - - /* Assert that the public key params match the wire signature scheme */ - POSIX_ENSURE(s2n_result_is_ok(s2n_signature_scheme_params_match(conn, pkey, chosen_sig_scheme)), - S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - - POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); - POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); - POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, - s2n_stuffer_data_available(&unsigned_content))); - - POSIX_GUARD(s2n_async_pkey_verify(conn, chosen_sig_scheme->sig_alg, - &message_hash, &signed_content)); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_tls13_certificate_verify.h" + +#include + +#include "crypto/s2n_hash.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_safety.h" + +/** + * Specified in https://tools.ietf.org/html/rfc8446#section-4.4.3 + * + * Servers MUST send this message when authenticating via a certificate. + * Clients MUST send this message whenever authenticating via a certificate. + * When sent, this message MUST appear immediately after the Certificate + * message and immediately prior to the Finished message. + **/ + +/* 64 'space' characters (0x20) */ +const uint8_t S2N_CERT_VERIFY_PREFIX[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; +/* 'TLS 1.3, server CertificateVerify' with 0x00 separator */ +const uint8_t S2N_SERVER_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, + 0x2c, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; +/* 'TLS 1.3, client CertificateVerify' with 0x00 separator */ +const uint8_t S2N_CLIENT_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, + 0x2c, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; + +static int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme); +static int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature); +static int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, + struct s2n_stuffer *unsigned_content, s2n_mode mode); +static int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme); +static uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode); + +int s2n_tls13_cert_verify_send(struct s2n_connection *conn) +{ + S2N_ASYNC_PKEY_GUARD(conn); + + if (conn->mode == S2N_SERVER) { + /* Write digital signature */ + POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.server_cert_sig_scheme)); + } else { + /* Write digital signature */ + POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.client_cert_sig_scheme)); + } + + return 0; +} + +int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme) +{ + POSIX_ENSURE_REF(conn->handshake_params.our_chain_and_key); + + /* Write the SignatureScheme out */ + struct s2n_stuffer *out = &conn->handshake.io; + POSIX_GUARD(s2n_stuffer_write_uint16(out, chosen_sig_scheme->iana_value)); + + DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&message_hash)); + POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); + + const struct s2n_pkey *pkey = conn->handshake_params.our_chain_and_key->private_key; + POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); + + DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, conn->mode)); + + POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, + s2n_stuffer_data_available(&unsigned_content))); + + S2N_ASYNC_PKEY_SIGN(conn, chosen_sig_scheme->sig_alg, &message_hash, s2n_tls13_write_signature); +} + +int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature) +{ + struct s2n_stuffer *out = &conn->handshake.io; + + POSIX_GUARD(s2n_stuffer_write_uint16(out, signature->size)); + POSIX_GUARD(s2n_stuffer_write_bytes(out, signature->data, signature->size)); + + return 0; +} + +int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, + struct s2n_stuffer *unsigned_content, s2n_mode mode) +{ + s2n_tls13_connection_keys(tls13_ctx, conn); + + uint8_t hash_digest_length = tls13_ctx.size; + uint8_t digest_out[S2N_MAX_DIGEST_LEN]; + + /* Get current handshake hash */ + POSIX_ENSURE_REF(conn->handshake.hashes); + struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, tls13_ctx.hash_algorithm, hash_state)); + POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, hash_digest_length)); + + /* Concatenate the content to be signed/verified */ + POSIX_GUARD(s2n_stuffer_alloc(unsigned_content, hash_digest_length + s2n_tls13_cert_verify_header_length(mode))); + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CERT_VERIFY_PREFIX, sizeof(S2N_CERT_VERIFY_PREFIX))); + + if (mode == S2N_CLIENT) { + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CLIENT_CERT_VERIFY_CONTEXT, + sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT))); + } else { + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_SERVER_CERT_VERIFY_CONTEXT, + sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT))); + } + + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, digest_out, hash_digest_length)); + + return 0; +} + +uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode) +{ + if (mode == S2N_CLIENT) { + return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT); + } + return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT); +} + +int s2n_tls13_cert_verify_recv(struct s2n_connection *conn) +{ + S2N_ASYNC_OFFLOAD_POSIX_GUARD(conn, { + POSIX_GUARD_RESULT(s2n_signature_algorithm_recv(conn, &conn->handshake.io)); + /* Read the rest of the signature and verify */ + if (conn->mode == S2N_SERVER) { + POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, + conn->handshake_params.client_cert_sig_scheme)); + } else { + POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, + conn->handshake_params.server_cert_sig_scheme)); + } + }); + return 0; +} + +int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme) +{ + struct s2n_stuffer *in = &conn->handshake.io; + DEFER_CLEANUP(struct s2n_blob signed_content = { 0 }, s2n_free); + DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&message_hash)); + + /* Get signature size */ + uint16_t signature_size = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &signature_size)); + S2N_ERROR_IF(signature_size > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); + + /* Get wire signature */ + POSIX_GUARD(s2n_alloc(&signed_content, signature_size)); + signed_content.size = signature_size; + POSIX_GUARD(s2n_stuffer_read_bytes(in, signed_content.data, signature_size)); + + /* Verify signature. We send the opposite mode as we are trying to verify what was sent to us */ + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_SERVER)); + } else { + POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_CLIENT)); + } + + struct s2n_pkey *pkey = NULL; + if (conn->mode == S2N_CLIENT) { + pkey = &conn->handshake_params.server_public_key; + } else { + pkey = &conn->handshake_params.client_public_key; + } + + /* Assert that the public key params match the wire signature scheme */ + POSIX_ENSURE(s2n_result_is_ok(s2n_signature_scheme_params_match(conn, pkey, chosen_sig_scheme)), + S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + + POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); + POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); + POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, + s2n_stuffer_data_available(&unsigned_content))); + + POSIX_GUARD(s2n_async_pkey_verify(conn, chosen_sig_scheme->sig_alg, + &message_hash, &signed_content)); + + return 0; +} diff --git a/tls/s2n_tls13_handshake.c b/tls/s2n_tls13_handshake.c index 91bb90a7253..dae0e5ca953 100644 --- a/tls/s2n_tls13_handshake.c +++ b/tls/s2n_tls13_handshake.c @@ -1,222 +1,223 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_tls13_handshake.h" - -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_key_log.h" -#include "tls/s2n_security_policies.h" - -static int s2n_zero_sequence_number(struct s2n_connection *conn, s2n_mode mode) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - struct s2n_blob sequence_number = { 0 }; - POSIX_GUARD_RESULT(s2n_connection_get_sequence_number(conn, mode, &sequence_number)); - POSIX_GUARD(s2n_blob_zero(&sequence_number)); - return S2N_SUCCESS; -} - -int s2n_tls13_mac_verify(struct s2n_tls13_keys *keys, struct s2n_blob *finished_verify, struct s2n_blob *wire_verify) -{ - POSIX_ENSURE_REF(wire_verify->data); - POSIX_ENSURE_EQ(wire_verify->size, keys->size); - - S2N_ERROR_IF(!s2n_constant_time_equals(finished_verify->data, wire_verify->data, keys->size), S2N_ERR_BAD_MESSAGE); - - return S2N_SUCCESS; -} - -int s2n_tls13_keys_from_conn(struct s2n_tls13_keys *keys, struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_tls13_keys_init(keys, conn->secure->cipher_suite->prf_alg)); - return S2N_SUCCESS; -} - -int s2n_tls13_compute_ecc_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) -{ - POSIX_ENSURE_REF(conn); - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - POSIX_ENSURE_REF(ecc_preferences); - - struct s2n_ecc_evp_params *server_key = &conn->kex_params.server_ecc_evp_params; - POSIX_ENSURE_REF(server_key); - POSIX_ENSURE_REF(server_key->negotiated_curve); - - struct s2n_ecc_evp_params *client_key = &conn->kex_params.client_ecc_evp_params; - POSIX_ENSURE_REF(client_key); - POSIX_ENSURE_REF(client_key->negotiated_curve); - - POSIX_ENSURE_EQ(server_key->negotiated_curve, client_key->negotiated_curve); - - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_key, server_key, shared_secret)); - } else { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_key, client_key, shared_secret)); - } - - return S2N_SUCCESS; -} - -/* Computes the ECDHE+PQKEM hybrid shared secret as defined in - * https://tools.ietf.org/html/draft-stebila-tls-hybrid-design - * Also supports "pure PQ" mode when kem_group->curve == &s2n_ecc_curve_none. - */ -int s2n_tls13_compute_pq_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(shared_secret); - - /* conn->kex_params.server_ecc_evp_params should be set only during a classic/non-hybrid handshake */ - POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.negotiated_curve); - POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.evp_pkey); - - struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params; - POSIX_ENSURE_REF(server_kem_group_params); - struct s2n_ecc_evp_params *server_ecc_params = &server_kem_group_params->ecc_params; - POSIX_ENSURE_REF(server_ecc_params); - - struct s2n_kem_group_params *client_kem_group_params = &conn->kex_params.client_kem_group_params; - POSIX_ENSURE_REF(client_kem_group_params); - struct s2n_ecc_evp_params *client_ecc_params = &client_kem_group_params->ecc_params; - POSIX_ENSURE_REF(client_ecc_params); - - struct s2n_blob *pq_shared_secret = &client_kem_group_params->kem_params.shared_secret; - POSIX_ENSURE_REF(pq_shared_secret); - POSIX_ENSURE_REF(pq_shared_secret->data); - - const struct s2n_kem_group *negotiated_kem_group = conn->kex_params.server_kem_group_params.kem_group; - POSIX_ENSURE_REF(negotiated_kem_group); - POSIX_ENSURE_REF(negotiated_kem_group->kem); - - DEFER_CLEANUP(struct s2n_blob ecdhe_shared_secret = { 0 }, s2n_free_or_wipe); - - if (negotiated_kem_group->curve == &s2n_ecc_curve_none) { - POSIX_ENSURE_EQ(ecdhe_shared_secret.size, 0); - } else if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_ecc_params, server_ecc_params, &ecdhe_shared_secret)); - } else { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_ecc_params, client_ecc_params, &ecdhe_shared_secret)); - } - - POSIX_ENSURE_EQ(pq_shared_secret->size, negotiated_kem_group->kem->shared_secret_key_length); - - /* Construct the concatenated/hybrid shared secret */ - uint32_t hybrid_shared_secret_size = ecdhe_shared_secret.size + negotiated_kem_group->kem->shared_secret_key_length; - POSIX_GUARD(s2n_alloc(shared_secret, hybrid_shared_secret_size)); - struct s2n_stuffer stuffer_combiner = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, shared_secret)); - - if (negotiated_kem_group->send_kem_first) { - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); - } else { - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); - } - - return S2N_SUCCESS; -} - -int s2n_tls13_pq_hybrid_supported(struct s2n_connection *conn) -{ - return conn->kex_params.server_kem_group_params.kem_group != NULL; -} - -int s2n_tls13_compute_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) -{ - POSIX_ENSURE_REF(conn); - - if (s2n_tls13_pq_hybrid_supported(conn)) { - POSIX_GUARD(s2n_tls13_compute_pq_shared_secret(conn, shared_secret)); - } else { - POSIX_GUARD(s2n_tls13_compute_ecc_shared_secret(conn, shared_secret)); - } - - POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); - - /* It would make more sense to wipe the PSK secrets in s2n_tls13_handle_early_secret, - * but at that point we don't know whether or not the server will request a HRR request - * and we'll have to use the secrets again. - * - * Instead, wipe them here when we wipe all the other connection secrets. */ - POSIX_GUARD_RESULT(s2n_psk_parameters_wipe_secrets(&conn->psk_params)); - - return S2N_SUCCESS; -} - -int s2n_update_application_traffic_keys(struct s2n_connection *conn, s2n_mode mode, keyupdate_status status) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); - - /* get tls13 key context */ - s2n_tls13_connection_keys(keys, conn); - - struct s2n_session_key *old_key = NULL; - struct s2n_blob old_app_secret = { 0 }; - struct s2n_blob app_iv = { 0 }; - - if (mode == S2N_CLIENT) { - old_key = &conn->secure->client_key; - POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.client_app_secret, keys.size)); - POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->client_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); - } else { - old_key = &conn->secure->server_key; - POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.server_app_secret, keys.size)); - POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->server_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); - } - - /* Produce new application secret */ - s2n_stack_blob(app_secret_update, keys.size, S2N_TLS13_SECRET_MAX_LEN); - - /* Derives next generation of traffic secret */ - POSIX_GUARD(s2n_tls13_update_application_traffic_secret(&keys, &old_app_secret, &app_secret_update)); - - s2n_tls13_key_blob(app_key, conn->secure->cipher_suite->record_alg->cipher->key_material_size); - - /* Derives next generation of traffic key */ - uint8_t *count = NULL; - POSIX_GUARD(s2n_tls13_derive_traffic_keys(&keys, &app_secret_update, &app_key, &app_iv)); - if (status == RECEIVING) { - POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(old_key, &app_key)); - count = &conn->recv_key_updated; - } else { - POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(old_key, &app_key)); - count = &conn->send_key_updated; - } - - /* Increment the count. - * Don't treat overflows as errors-- we only do best-effort reporting. - */ - *count = S2N_MIN(UINT8_MAX, *count + 1); - - /* According to https://tools.ietf.org/html/rfc8446#section-5.3: - * Each sequence number is set to zero at the beginning of a connection and - * whenever the key is changed; the first record transmitted under a particular traffic key - * MUST use sequence number 0. - */ - POSIX_GUARD(s2n_zero_sequence_number(conn, mode)); - - /* Save updated secret */ - struct s2n_stuffer old_secret_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&old_secret_stuffer, &old_app_secret)); - POSIX_GUARD(s2n_stuffer_write_bytes(&old_secret_stuffer, app_secret_update.data, keys.size)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_tls13_handshake.h" + +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_key_log.h" +#include "tls/s2n_security_policies.h" + +static int s2n_zero_sequence_number(struct s2n_connection *conn, s2n_mode mode) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + struct s2n_blob sequence_number = { 0 }; + POSIX_GUARD_RESULT(s2n_connection_get_sequence_number(conn, mode, &sequence_number)); + POSIX_GUARD(s2n_blob_zero(&sequence_number)); + return S2N_SUCCESS; +} + +int s2n_tls13_mac_verify(struct s2n_tls13_keys *keys, struct s2n_blob *finished_verify, struct s2n_blob *wire_verify) +{ + POSIX_ENSURE_REF(wire_verify->data); + POSIX_ENSURE_EQ(wire_verify->size, keys->size); + + S2N_ERROR_IF(!s2n_constant_time_equals(finished_verify->data, wire_verify->data, keys->size), S2N_ERR_BAD_MESSAGE); + + return S2N_SUCCESS; +} + +int s2n_tls13_keys_from_conn(struct s2n_tls13_keys *keys, struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_tls13_keys_init(keys, conn->secure->cipher_suite->prf_alg)); + return S2N_SUCCESS; +} + +int s2n_tls13_compute_ecc_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) +{ + POSIX_ENSURE_REF(conn); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + POSIX_ENSURE_REF(ecc_preferences); + + struct s2n_ecc_evp_params *server_key = &conn->kex_params.server_ecc_evp_params; + POSIX_ENSURE_REF(server_key); + POSIX_ENSURE_REF(server_key->negotiated_curve); + + struct s2n_ecc_evp_params *client_key = &conn->kex_params.client_ecc_evp_params; + POSIX_ENSURE_REF(client_key); + POSIX_ENSURE_REF(client_key->negotiated_curve); + + POSIX_ENSURE_EQ(server_key->negotiated_curve, client_key->negotiated_curve); + + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_key, server_key, shared_secret)); + } else { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_key, client_key, shared_secret)); + } + + return S2N_SUCCESS; +} + +/* Computes the ECDHE+PQKEM hybrid shared secret as defined in + * https://tools.ietf.org/html/draft-stebila-tls-hybrid-design + * Also supports "pure PQ" mode when kem_group->curve == &s2n_ecc_curve_none. + */ +int s2n_tls13_compute_pq_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(shared_secret); + + /* conn->kex_params.server_ecc_evp_params should be set only during a classic/non-hybrid handshake */ + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.negotiated_curve); + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.evp_pkey); + + struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params; + POSIX_ENSURE_REF(server_kem_group_params); + struct s2n_ecc_evp_params *server_ecc_params = &server_kem_group_params->ecc_params; + POSIX_ENSURE_REF(server_ecc_params); + + struct s2n_kem_group_params *client_kem_group_params = &conn->kex_params.client_kem_group_params; + POSIX_ENSURE_REF(client_kem_group_params); + struct s2n_ecc_evp_params *client_ecc_params = &client_kem_group_params->ecc_params; + POSIX_ENSURE_REF(client_ecc_params); + + struct s2n_blob *pq_shared_secret = &client_kem_group_params->kem_params.shared_secret; + POSIX_ENSURE_REF(pq_shared_secret); + POSIX_ENSURE_REF(pq_shared_secret->data); + + const struct s2n_kem_group *negotiated_kem_group = conn->kex_params.server_kem_group_params.kem_group; + POSIX_ENSURE_REF(negotiated_kem_group); + POSIX_ENSURE_REF(negotiated_kem_group->kem); + + DEFER_CLEANUP(struct s2n_blob ecdhe_shared_secret = { 0 }, s2n_free_or_wipe); + + if (negotiated_kem_group->curve == &s2n_ecc_curve_none) { + POSIX_ENSURE_EQ(ecdhe_shared_secret.size, 0); + } else if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_ecc_params, server_ecc_params, &ecdhe_shared_secret)); + } else { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_ecc_params, client_ecc_params, &ecdhe_shared_secret)); + } + + POSIX_ENSURE_EQ(pq_shared_secret->size, negotiated_kem_group->kem->shared_secret_key_length); + + /* Construct the concatenated/hybrid shared secret */ + uint32_t hybrid_shared_secret_size = ecdhe_shared_secret.size + negotiated_kem_group->kem->shared_secret_key_length; + POSIX_GUARD(s2n_alloc(shared_secret, hybrid_shared_secret_size)); + struct s2n_stuffer stuffer_combiner = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, shared_secret)); + + if (negotiated_kem_group->send_kem_first) { + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); + } else { + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); + } + + return S2N_SUCCESS; +} + +int s2n_tls13_pq_hybrid_supported(struct s2n_connection *conn) +{ + return conn->kex_params.server_kem_group_params.kem_group != NULL; +} + +int s2n_tls13_compute_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) +{ + POSIX_ENSURE_REF(conn); + + if (s2n_tls13_pq_hybrid_supported(conn)) { + POSIX_GUARD(s2n_tls13_compute_pq_shared_secret(conn, shared_secret)); + } else { + POSIX_GUARD(s2n_tls13_compute_ecc_shared_secret(conn, shared_secret)); + } + + POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); + + /* It would make more sense to wipe the PSK secrets in s2n_tls13_handle_early_secret, + * but at that point we don't know whether or not the server will request a HRR request + * and we'll have to use the secrets again. + * + * Instead, wipe them here when we wipe all the other connection secrets. */ + POSIX_GUARD_RESULT(s2n_psk_parameters_wipe_secrets(&conn->psk_params)); + + return S2N_SUCCESS; +} + +int s2n_update_application_traffic_keys(struct s2n_connection *conn, s2n_mode mode, keyupdate_status status) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); + + /* get tls13 key context */ + s2n_tls13_connection_keys(keys, conn); + + struct s2n_session_key *old_key = NULL; + struct s2n_blob old_app_secret = { 0 }; + struct s2n_blob app_iv = { 0 }; + + if (mode == S2N_CLIENT) { + old_key = &conn->secure->client_key; + POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.client_app_secret, keys.size)); + POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->client_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); + } else { + old_key = &conn->secure->server_key; + POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.server_app_secret, keys.size)); + POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->server_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); + } + + /* Produce new application secret */ + s2n_stack_blob(app_secret_update, keys.size, S2N_TLS13_SECRET_MAX_LEN); + + /* Derives next generation of traffic secret */ + POSIX_GUARD(s2n_tls13_update_application_traffic_secret(&keys, &old_app_secret, &app_secret_update)); + + s2n_tls13_key_blob(app_key, conn->secure->cipher_suite->record_alg->cipher->key_material_size); + + /* Derives next generation of traffic key */ + uint8_t *count = NULL; + POSIX_GUARD(s2n_tls13_derive_traffic_keys(&keys, &app_secret_update, &app_key, &app_iv)); + if (status == RECEIVING) { + POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(old_key, &app_key)); + count = &conn->recv_key_updated; + } else { + POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(old_key, &app_key)); + count = &conn->send_key_updated; + } + + /* Increment the count. + * Don't treat overflows as errors-- we only do best-effort reporting. + */ + *count = S2N_MIN(UINT8_MAX, *count + 1); + + /* According to https://tools.ietf.org/html/rfc8446#section-5.3: + * Each sequence number is set to zero at the beginning of a connection and + * whenever the key is changed; the first record transmitted under a particular traffic key + * MUST use sequence number 0. + */ + POSIX_GUARD(s2n_zero_sequence_number(conn, mode)); + + /* Save updated secret */ + struct s2n_stuffer old_secret_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&old_secret_stuffer, &old_app_secret)); + POSIX_GUARD(s2n_stuffer_write_bytes(&old_secret_stuffer, app_secret_update.data, keys.size)); + + return S2N_SUCCESS; +} diff --git a/tls/s2n_tls13_secrets.c b/tls/s2n_tls13_secrets.c index f3272977ba7..54feefd8710 100644 --- a/tls/s2n_tls13_secrets.c +++ b/tls/s2n_tls13_secrets.c @@ -1,777 +1,778 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_tls13_secrets.h" - -#include "tls/s2n_connection.h" -#include "tls/s2n_key_log.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_bitmap.h" - -#define S2N_MAX_HASHLEN SHA384_DIGEST_LENGTH - -#define CONN_HMAC_ALG(conn) ((conn)->secure->cipher_suite->prf_alg) -#define CONN_SECRETS(conn) ((conn)->secrets.version.tls13) -#define CONN_HASHES(conn) ((conn)->handshake.hashes) - -#define CONN_SECRET(conn, secret) ( \ - (struct s2n_blob){ .data = CONN_SECRETS(conn).secret, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) -#define CONN_HASH(conn, hash) ( \ - (struct s2n_blob){ .data = CONN_HASHES(conn)->hash, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) -#define CONN_FINISHED(conn, mode) ( \ - (struct s2n_blob){ .data = (conn)->handshake.mode##_finished, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# If a given secret is not available, then the 0-value consisting of a - *# string of Hash.length bytes set to zeros is used. - */ -static uint8_t zero_value_bytes[S2N_MAX_HASHLEN] = { 0 }; -#define ZERO_VALUE(hmac_alg) ( \ - (const struct s2n_blob){ .data = zero_value_bytes, .size = s2n_get_hash_len(hmac_alg) }) - -/** - * When an operation doesn't need an actual transcript hash, - * it uses an empty transcript hash as an input instead. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# Note that in some cases a zero- - *# length Context (indicated by "") is passed to HKDF-Expand-Label - */ -#define EMPTY_CONTEXT(hmac_alg) ( \ - (const struct s2n_blob){ .data = s2n_get_empty_context(hmac_alg), .size = s2n_get_hash_len(hmac_alg) }) - -static uint8_t s2n_get_hash_len(s2n_hmac_algorithm hmac_alg) -{ - uint8_t hash_size = 0; - if (s2n_hmac_digest_size(hmac_alg, &hash_size) != S2N_SUCCESS) { - return 0; - } - return hash_size; -} - -static uint8_t *s2n_get_empty_context(s2n_hmac_algorithm hmac_alg) -{ - static uint8_t sha256_empty_digest[S2N_MAX_HASHLEN] = { 0 }; - static uint8_t sha384_empty_digest[S2N_MAX_HASHLEN] = { 0 }; - - switch (hmac_alg) { - case S2N_HMAC_SHA256: - return sha256_empty_digest; - case S2N_HMAC_SHA384: - return sha384_empty_digest; - default: - return NULL; - } -} - -static s2n_hmac_algorithm supported_hmacs[] = { - S2N_HMAC_SHA256, - S2N_HMAC_SHA384 -}; - -S2N_RESULT s2n_tls13_empty_transcripts_init() -{ - DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); - RESULT_GUARD_POSIX(s2n_hash_new(&hash)); - - s2n_hash_algorithm hash_alg = S2N_HASH_NONE; - for (size_t i = 0; i < s2n_array_len(supported_hmacs); i++) { - s2n_hmac_algorithm hmac_alg = supported_hmacs[i]; - struct s2n_blob digest = EMPTY_CONTEXT(hmac_alg); - - RESULT_GUARD_POSIX(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); - RESULT_GUARD_POSIX(s2n_hash_init(&hash, hash_alg)); - RESULT_GUARD_POSIX(s2n_hash_digest(&hash, digest.data, digest.size)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_calculate_transcript_digest(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->handshake.hashes); - - s2n_hash_algorithm hash_algorithm = S2N_HASH_NONE; - RESULT_GUARD_POSIX(s2n_hmac_hash_alg(CONN_HMAC_ALG(conn), &hash_algorithm)); - - uint8_t digest_size = 0; - RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_algorithm, &digest_size)); - - struct s2n_blob digest = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&digest, CONN_HASHES(conn)->transcript_hash_digest, digest_size)); - - struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; - RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_algorithm, hash_state)); - RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, digest.data, digest.size)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_extract_secret(s2n_hmac_algorithm hmac_alg, - const struct s2n_blob *previous_secret_material, const struct s2n_blob *new_secret_material, - struct s2n_blob *output) -{ - /* - * TODO: We should be able to reuse the prf_work_space rather - * than allocating a new HMAC every time. - * https://github.com/aws/s2n-tls/issues/3206 - */ - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); - - RESULT_GUARD_POSIX(s2n_hkdf_extract(&hmac_state, hmac_alg, - previous_secret_material, new_secret_material, output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# Derive-Secret(Secret, Label, Messages) = - *# HKDF-Expand-Label(Secret, Label, - *# Transcript-Hash(Messages), Hash.length) - */ -static S2N_RESULT s2n_derive_secret(s2n_hmac_algorithm hmac_alg, - const struct s2n_blob *previous_secret_material, const struct s2n_blob *label, const struct s2n_blob *context, - struct s2n_blob *output) -{ - /* - * TODO: We should be able to reuse the prf_work_space rather - * than allocating a new HMAC every time. - * https://github.com/aws/s2n-tls/issues/3206 - */ - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); - - output->size = s2n_get_hash_len(hmac_alg); - RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, hmac_alg, - previous_secret_material, label, context, output)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_derive_secret_with_context(struct s2n_connection *conn, - s2n_extract_secret_type_t input_secret_type, const struct s2n_blob *label, message_type_t transcript_end_msg, - struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(label); - RESULT_ENSURE_REF(output); - - RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); - RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == transcript_end_msg, S2N_ERR_SECRET_SCHEDULE_STATE); - RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), - label, &CONN_HASH(conn, transcript_hash_digest), output)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_derive_secret_without_context(struct s2n_connection *conn, - s2n_extract_secret_type_t input_secret_type, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); - RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), - &s2n_tls13_label_derived_secret, &EMPTY_CONTEXT(CONN_HMAC_ALG(conn)), output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 - *# The key used to compute the Finished message is computed from the - *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). - *# Specifically: - *# - *# finished_key = - *# HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) - **/ -static S2N_RESULT s2n_tls13_compute_finished_key(struct s2n_connection *conn, - const struct s2n_blob *base_key, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(base_key); - RESULT_ENSURE_REF(output); - - RESULT_GUARD(s2n_handshake_set_finished_len(conn, output->size)); - - /* - * TODO: We should be able to reuse the prf_work_space rather - * than allocating a new HMAC every time. - */ - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); - - RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, CONN_HMAC_ALG(conn), - base_key, &s2n_tls13_label_finished, &(struct s2n_blob){ 0 }, output)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_call_secret_callbacks(struct s2n_connection *conn, - const struct s2n_blob *secret, s2n_secret_type_t secret_type) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - - if (conn->secret_cb && (s2n_connection_is_quic_enabled(conn) || s2n_in_unit_test())) { - RESULT_GUARD_POSIX(conn->secret_cb(conn->secret_cb_context, conn, secret_type, - secret->data, secret->size)); - } - s2n_result_ignore(s2n_key_log_tls13_secret(conn, secret, secret_type)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_trigger_secret_callbacks(struct s2n_connection *conn, - const struct s2n_blob *secret, s2n_extract_secret_type_t secret_type, s2n_mode mode) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - - static const s2n_secret_type_t conversions[][2] = { - [S2N_EARLY_SECRET] = { S2N_CLIENT_EARLY_TRAFFIC_SECRET, S2N_CLIENT_EARLY_TRAFFIC_SECRET }, - [S2N_HANDSHAKE_SECRET] = { S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET, S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET }, - [S2N_MASTER_SECRET] = { S2N_SERVER_APPLICATION_TRAFFIC_SECRET, S2N_CLIENT_APPLICATION_TRAFFIC_SECRET }, - }; - s2n_secret_type_t callback_secret_type = conversions[secret_type][mode]; - - RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, callback_secret_type)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# 0 - *# | - *# v - *# PSK -> HKDF-Extract = Early Secret - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# There are multiple potential Early Secret values, depending on which - *# PSK the server ultimately selects. The client will need to compute - *# one for each potential PSK - */ -S2N_RESULT s2n_extract_early_secret(struct s2n_psk *psk) -{ - RESULT_ENSURE_REF(psk); - RESULT_GUARD_POSIX(s2n_realloc(&psk->early_secret, s2n_get_hash_len(psk->hmac_alg))); - RESULT_GUARD(s2n_extract_secret(psk->hmac_alg, - &ZERO_VALUE(psk->hmac_alg), - &psk->secret, - &psk->early_secret)); - return S2N_RESULT_OK; -} - -/* - * When we require an early secret to derive other secrets, - * either retrieve the early secret stored on the chosen / early data PSK - * or calculate one using a "zero" PSK. - */ -static S2N_RESULT s2n_extract_early_secret_for_schedule(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_psk *psk = conn->psk_params.chosen_psk; - s2n_hmac_algorithm hmac_alg = CONN_HMAC_ALG(conn); - - /* - * If the client is sending early data, then the PSK is always assumed - * to be the first PSK offered. - */ - if (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { - RESULT_GUARD(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); - RESULT_ENSURE_REF(psk); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# if no PSK is selected, it will then need - *# to compute the Early Secret corresponding to the zero PSK. - */ - if (psk == NULL) { - RESULT_GUARD(s2n_extract_secret(hmac_alg, - &ZERO_VALUE(hmac_alg), - &ZERO_VALUE(hmac_alg), - &CONN_SECRET(conn, extract_secret))); - return S2N_RESULT_OK; - } - - /* - * The early secret is required to generate or verify a PSK's binder, - * so must have already been calculated if a valid PSK exists. - * Use the early secret stored on the PSK. - */ - RESULT_ENSURE_EQ(hmac_alg, psk->hmac_alg); - RESULT_CHECKED_MEMCPY(CONN_SECRETS(conn).extract_secret, psk->early_secret.data, psk->early_secret.size); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "ext binder" | "res binder", "") - *# | = binder_key - */ -S2N_RESULT s2n_derive_binder_key(struct s2n_psk *psk, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(psk); - RESULT_ENSURE_REF(output); - - const struct s2n_blob *label = &s2n_tls13_label_resumption_psk_binder_key; - if (psk->type == S2N_PSK_TYPE_EXTERNAL) { - label = &s2n_tls13_label_external_psk_binder_key; - } - RESULT_GUARD(s2n_extract_early_secret(psk)); - RESULT_GUARD(s2n_derive_secret(psk->hmac_alg, - &psk->early_secret, - label, - &EMPTY_CONTEXT(psk->hmac_alg), - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "c e traffic", ClientHello) - *# | = client_early_traffic_secret - */ -static S2N_RESULT s2n_derive_client_early_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_EARLY_SECRET, - &s2n_tls13_label_client_early_traffic_secret, - CLIENT_HELLO, - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# v - *# Derive-Secret(., "derived", "") - *# | - *# v - *# (EC)DHE -> HKDF-Extract = Handshake Secret - */ -static S2N_RESULT s2n_extract_handshake_secret(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_blob derived_secret = { 0 }; - uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); - RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_EARLY_SECRET, &derived_secret)); - - DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free_or_wipe); - RESULT_GUARD_POSIX(s2n_tls13_compute_shared_secret(conn, &shared_secret)); - - RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), - &derived_secret, - &shared_secret, - &CONN_SECRET(conn, extract_secret))); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "c hs traffic", - *# | ClientHello...ServerHello) - *# | = client_handshake_traffic_secret - */ -static S2N_RESULT s2n_derive_client_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_HANDSHAKE_SECRET, - &s2n_tls13_label_client_handshake_traffic_secret, - SERVER_HELLO, - output)); - - /* - * The client finished key needs to be calculated using the - * same connection state as the client handshake secret. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 - *# The key used to compute the Finished message is computed from the - *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). - */ - RESULT_GUARD(s2n_tls13_compute_finished_key(conn, - output, &CONN_FINISHED(conn, client))); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "s hs traffic", - *# | ClientHello...ServerHello) - *# | = server_handshake_traffic_secret - */ -static S2N_RESULT s2n_derive_server_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_HANDSHAKE_SECRET, - &s2n_tls13_label_server_handshake_traffic_secret, - SERVER_HELLO, - output)); - - /* - * The server finished key needs to be calculated using the - * same connection state as the server handshake secret. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 - *# The key used to compute the Finished message is computed from the - *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). - */ - RESULT_GUARD(s2n_tls13_compute_finished_key(conn, - output, &CONN_FINISHED(conn, server))); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# v - *# Derive-Secret(., "derived", "") - *# | - *# v - *# 0 -> HKDF-Extract = Master Secret - */ -static S2N_RESULT s2n_extract_master_secret(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_blob derived_secret = { 0 }; - uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); - RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_HANDSHAKE_SECRET, &derived_secret)); - - RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), - &derived_secret, - &ZERO_VALUE(CONN_HMAC_ALG(conn)), - &CONN_SECRET(conn, extract_secret))); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "c ap traffic", - *# | ClientHello...server Finished) - *# | = client_application_traffic_secret_0 - */ -static S2N_RESULT s2n_derive_client_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_client_application_traffic_secret, - SERVER_FINISHED, - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "s ap traffic", - *# | ClientHello...server Finished) - *# | = server_application_traffic_secret_0 - */ -static S2N_RESULT s2n_derive_server_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_server_application_traffic_secret, - SERVER_FINISHED, - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "res master", - *# ClientHello...client Finished) - *# = resumption_master_secret - */ -S2N_RESULT s2n_derive_resumption_master_secret(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - /* Secret derivation requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_resumption_master_secret, - CLIENT_FINISHED, - &CONN_SECRET(conn, resumption_master_secret))); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "exp master", - *# | ClientHello...server Finished) - *# | = exporter_master_secret - */ -S2N_RESULT s2n_derive_exporter_master_secret(struct s2n_connection *conn, struct s2n_blob *secret) -{ - RESULT_ENSURE_REF(conn); - /* Secret derivation requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_exporter_master_secret, - SERVER_FINISHED, - secret)); - - RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, S2N_EXPORTER_SECRET)); - - return S2N_RESULT_OK; -} - -static s2n_result (*extract_methods[])(struct s2n_connection *conn) = { - [S2N_EARLY_SECRET] = &s2n_extract_early_secret_for_schedule, - [S2N_HANDSHAKE_SECRET] = &s2n_extract_handshake_secret, - [S2N_MASTER_SECRET] = &s2n_extract_master_secret, -}; - -S2N_RESULT s2n_tls13_extract_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->handshake.hashes); - RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); - - RESULT_ENSURE_GTE(secret_type, 0); - RESULT_ENSURE_LT(secret_type, s2n_array_len(extract_methods)); - - s2n_extract_secret_type_t next_secret_type = conn->secrets.extract_secret_type + 1; - for (s2n_extract_secret_type_t i = next_secret_type; i <= secret_type; i++) { - RESULT_ENSURE_REF(extract_methods[i]); - RESULT_GUARD(extract_methods[i](conn)); - conn->secrets.extract_secret_type = i; - } - - return S2N_RESULT_OK; -} - -static s2n_result (*derive_methods[][2])(struct s2n_connection *conn, struct s2n_blob *secret) = { - [S2N_EARLY_SECRET] = { &s2n_derive_client_early_traffic_secret, &s2n_derive_client_early_traffic_secret }, - [S2N_HANDSHAKE_SECRET] = { &s2n_derive_server_handshake_traffic_secret, &s2n_derive_client_handshake_traffic_secret }, - [S2N_MASTER_SECRET] = { &s2n_derive_server_application_traffic_secret, &s2n_derive_client_application_traffic_secret }, -}; - -S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, - s2n_mode mode, struct s2n_blob *secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->handshake.hashes); - RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); - - RESULT_GUARD(s2n_tls13_extract_secret(conn, secret_type)); - - RESULT_ENSURE_GTE(secret_type, 0); - RESULT_ENSURE_LT(secret_type, s2n_array_len(derive_methods)); - RESULT_ENSURE_REF(derive_methods[secret_type][mode]); - RESULT_GUARD(derive_methods[secret_type][mode](conn, secret)); - - RESULT_GUARD(s2n_trigger_secret_callbacks(conn, secret, secret_type, mode)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_secrets_clean(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - /* Secret clean requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - if (conn->actual_protocol_version < S2N_TLS13) { - return S2N_RESULT_OK; - } - - /* - * Wipe base secrets. - * Not strictly necessary, but probably safer than leaving them. - * A compromised secret additionally compromises all secrets derived from it, - * so these are the most sensitive secrets. - */ - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, extract_secret))); - conn->secrets.extract_secret_type = S2N_NONE_SECRET; - - /* Wipe other secrets no longer needed */ - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_early_secret))); - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_handshake_secret))); - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, server_handshake_secret))); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_secrets_update(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - return S2N_RESULT_OK; - } - - /* Secret update requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - message_type_t message_type = s2n_conn_get_current_message_type(conn); - switch (message_type) { - case CLIENT_HELLO: - if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED - || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, - S2N_CLIENT, &CONN_SECRET(conn, client_early_secret))); - } - break; - case SERVER_HELLO: - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, - S2N_CLIENT, &CONN_SECRET(conn, client_handshake_secret))); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, - S2N_SERVER, &CONN_SECRET(conn, server_handshake_secret))); - break; - case SERVER_FINISHED: - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, - S2N_CLIENT, &CONN_SECRET(conn, client_app_secret))); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, - S2N_SERVER, &CONN_SECRET(conn, server_app_secret))); - RESULT_GUARD(s2n_derive_exporter_master_secret(conn, - &CONN_SECRET(conn, exporter_master_secret))); - break; - case CLIENT_FINISHED: - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_derive_resumption_master_secret(conn)); - break; - default: - break; - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, - s2n_mode mode, struct s2n_blob *secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - /* Getting secrets requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - bool is_available = (secret_type <= conn->secrets.extract_secret_type) - /* Unlike the other secrets, we don't wipe the master / app secrets */ - || (secret_type == S2N_MASTER_SECRET && s2n_handshake_is_complete(conn)); - RESULT_ENSURE(is_available, S2N_ERR_SAFETY); - - uint8_t *secrets[][2] = { - [S2N_EARLY_SECRET] = { NULL, CONN_SECRETS(conn).client_early_secret }, - [S2N_HANDSHAKE_SECRET] = { CONN_SECRETS(conn).server_handshake_secret, CONN_SECRETS(conn).client_handshake_secret }, - [S2N_MASTER_SECRET] = { CONN_SECRETS(conn).server_app_secret, CONN_SECRETS(conn).client_app_secret }, - }; - RESULT_ENSURE_GT(secret_type, S2N_NONE_SECRET); - RESULT_ENSURE_LT(secret_type, s2n_array_len(secrets)); - RESULT_ENSURE_REF(secrets[secret_type][mode]); - - secret->size = s2n_get_hash_len(CONN_HMAC_ALG(conn)); - RESULT_CHECKED_MEMCPY(secret->data, secrets[secret_type][mode], secret->size); - RESULT_ENSURE_GT(secret->size, 0); - return S2N_RESULT_OK; -} - -/* - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.5 - *# The exporter value is computed as: - *# - *# TLS-Exporter(label, context_value, key_length) = - *# HKDF-Expand-Label(Derive-Secret(Secret, label, ""), - *# "exporter", Hash(context_value), key_length) - */ -int s2n_connection_tls_exporter(struct s2n_connection *conn, - const uint8_t *label_in, uint32_t label_length, - const uint8_t *context, uint32_t context_length, - uint8_t *output_in, uint32_t output_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(output_in); - POSIX_ENSURE_REF(label_in); - POSIX_ENSURE_REF(context); - POSIX_ENSURE(s2n_connection_get_protocol_version(conn) == S2N_TLS13, S2N_ERR_INVALID_STATE); - - POSIX_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - s2n_hmac_algorithm hmac_alg = conn->secure->cipher_suite->prf_alg; - - uint8_t label_bytes[S2N_MAX_HKDF_EXPAND_LABEL_LENGTH] = { 0 }; - struct s2n_blob label = { 0 }; - POSIX_ENSURE_LTE(label_length, sizeof(label_bytes)); - POSIX_CHECKED_MEMCPY(label_bytes, label_in, label_length); - POSIX_GUARD(s2n_blob_init(&label, label_bytes, label_length)); - - uint8_t derived_secret_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob derived_secret = { 0 }; - POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); - POSIX_GUARD(s2n_blob_init(&derived_secret, - derived_secret_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); - POSIX_GUARD_RESULT(s2n_derive_secret(hmac_alg, &CONN_SECRET(conn, exporter_master_secret), - &label, &EMPTY_CONTEXT(hmac_alg), &derived_secret)); - - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - POSIX_GUARD(s2n_hmac_new(&hmac_state)); - - DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); - POSIX_GUARD(s2n_hash_new(&hash)); - - s2n_hash_algorithm hash_alg = { 0 }; - POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); - uint8_t digest_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob digest = { 0 }; - POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); - POSIX_GUARD(s2n_blob_init(&digest, digest_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); - - POSIX_GUARD(s2n_hash_init(&hash, hash_alg)); - POSIX_GUARD(s2n_hash_update(&hash, context, context_length)); - POSIX_GUARD(s2n_hash_digest(&hash, digest.data, digest.size)); - - struct s2n_blob output = { 0 }; - POSIX_GUARD(s2n_blob_init(&output, output_in, output_length)); - POSIX_GUARD(s2n_hkdf_expand_label(&hmac_state, hmac_alg, - &derived_secret, &s2n_tls13_label_exporter, &digest, &output)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_tls13_secrets.h" + +#include "tls/s2n_connection.h" +#include "tls/s2n_key_log.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_bitmap.h" + +#define S2N_MAX_HASHLEN SHA384_DIGEST_LENGTH + +#define CONN_HMAC_ALG(conn) ((conn)->secure->cipher_suite->prf_alg) +#define CONN_SECRETS(conn) ((conn)->secrets.version.tls13) +#define CONN_HASHES(conn) ((conn)->handshake.hashes) + +#define CONN_SECRET(conn, secret) ( \ + (struct s2n_blob){ .data = CONN_SECRETS(conn).secret, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) +#define CONN_HASH(conn, hash) ( \ + (struct s2n_blob){ .data = CONN_HASHES(conn)->hash, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) +#define CONN_FINISHED(conn, mode) ( \ + (struct s2n_blob){ .data = (conn)->handshake.mode##_finished, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# If a given secret is not available, then the 0-value consisting of a + *# string of Hash.length bytes set to zeros is used. + */ +static uint8_t zero_value_bytes[S2N_MAX_HASHLEN] = { 0 }; +#define ZERO_VALUE(hmac_alg) ( \ + (const struct s2n_blob){ .data = zero_value_bytes, .size = s2n_get_hash_len(hmac_alg) }) + +/** + * When an operation doesn't need an actual transcript hash, + * it uses an empty transcript hash as an input instead. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# Note that in some cases a zero- + *# length Context (indicated by "") is passed to HKDF-Expand-Label + */ +#define EMPTY_CONTEXT(hmac_alg) ( \ + (const struct s2n_blob){ .data = s2n_get_empty_context(hmac_alg), .size = s2n_get_hash_len(hmac_alg) }) + +static uint8_t s2n_get_hash_len(s2n_hmac_algorithm hmac_alg) +{ + uint8_t hash_size = 0; + if (s2n_hmac_digest_size(hmac_alg, &hash_size) != S2N_SUCCESS) { + return 0; + } + return hash_size; +} + +static uint8_t *s2n_get_empty_context(s2n_hmac_algorithm hmac_alg) +{ + static uint8_t sha256_empty_digest[S2N_MAX_HASHLEN] = { 0 }; + static uint8_t sha384_empty_digest[S2N_MAX_HASHLEN] = { 0 }; + + switch (hmac_alg) { + case S2N_HMAC_SHA256: + return sha256_empty_digest; + case S2N_HMAC_SHA384: + return sha384_empty_digest; + default: + return NULL; + } +} + +static s2n_hmac_algorithm supported_hmacs[] = { + S2N_HMAC_SHA256, + S2N_HMAC_SHA384 +}; + +S2N_RESULT s2n_tls13_empty_transcripts_init() +{ + DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); + RESULT_GUARD_POSIX(s2n_hash_new(&hash)); + + s2n_hash_algorithm hash_alg = S2N_HASH_NONE; + for (size_t i = 0; i < s2n_array_len(supported_hmacs); i++) { + s2n_hmac_algorithm hmac_alg = supported_hmacs[i]; + struct s2n_blob digest = EMPTY_CONTEXT(hmac_alg); + + RESULT_GUARD_POSIX(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); + RESULT_GUARD_POSIX(s2n_hash_init(&hash, hash_alg)); + RESULT_GUARD_POSIX(s2n_hash_digest(&hash, digest.data, digest.size)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_calculate_transcript_digest(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->handshake.hashes); + + s2n_hash_algorithm hash_algorithm = S2N_HASH_NONE; + RESULT_GUARD_POSIX(s2n_hmac_hash_alg(CONN_HMAC_ALG(conn), &hash_algorithm)); + + uint8_t digest_size = 0; + RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_algorithm, &digest_size)); + + struct s2n_blob digest = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&digest, CONN_HASHES(conn)->transcript_hash_digest, digest_size)); + + struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; + RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_algorithm, hash_state)); + RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, digest.data, digest.size)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_extract_secret(s2n_hmac_algorithm hmac_alg, + const struct s2n_blob *previous_secret_material, const struct s2n_blob *new_secret_material, + struct s2n_blob *output) +{ + /* + * TODO: We should be able to reuse the prf_work_space rather + * than allocating a new HMAC every time. + * https://github.com/aws/s2n-tls/issues/3206 + */ + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); + + RESULT_GUARD_POSIX(s2n_hkdf_extract(&hmac_state, hmac_alg, + previous_secret_material, new_secret_material, output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# Derive-Secret(Secret, Label, Messages) = + *# HKDF-Expand-Label(Secret, Label, + *# Transcript-Hash(Messages), Hash.length) + */ +static S2N_RESULT s2n_derive_secret(s2n_hmac_algorithm hmac_alg, + const struct s2n_blob *previous_secret_material, const struct s2n_blob *label, const struct s2n_blob *context, + struct s2n_blob *output) +{ + /* + * TODO: We should be able to reuse the prf_work_space rather + * than allocating a new HMAC every time. + * https://github.com/aws/s2n-tls/issues/3206 + */ + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); + + output->size = s2n_get_hash_len(hmac_alg); + RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, hmac_alg, + previous_secret_material, label, context, output)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_derive_secret_with_context(struct s2n_connection *conn, + s2n_extract_secret_type_t input_secret_type, const struct s2n_blob *label, message_type_t transcript_end_msg, + struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(label); + RESULT_ENSURE_REF(output); + + RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); + RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == transcript_end_msg, S2N_ERR_SECRET_SCHEDULE_STATE); + RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), + label, &CONN_HASH(conn, transcript_hash_digest), output)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_derive_secret_without_context(struct s2n_connection *conn, + s2n_extract_secret_type_t input_secret_type, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); + RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), + &s2n_tls13_label_derived_secret, &EMPTY_CONTEXT(CONN_HMAC_ALG(conn)), output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 + *# The key used to compute the Finished message is computed from the + *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). + *# Specifically: + *# + *# finished_key = + *# HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) + **/ +static S2N_RESULT s2n_tls13_compute_finished_key(struct s2n_connection *conn, + const struct s2n_blob *base_key, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(base_key); + RESULT_ENSURE_REF(output); + + RESULT_GUARD(s2n_handshake_set_finished_len(conn, output->size)); + + /* + * TODO: We should be able to reuse the prf_work_space rather + * than allocating a new HMAC every time. + */ + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); + + RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, CONN_HMAC_ALG(conn), + base_key, &s2n_tls13_label_finished, &(struct s2n_blob){ 0 }, output)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_call_secret_callbacks(struct s2n_connection *conn, + const struct s2n_blob *secret, s2n_secret_type_t secret_type) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + + if (conn->secret_cb && (s2n_connection_is_quic_enabled(conn) || s2n_in_unit_test())) { + RESULT_GUARD_POSIX(conn->secret_cb(conn->secret_cb_context, conn, secret_type, + secret->data, secret->size)); + } + s2n_result_ignore(s2n_key_log_tls13_secret(conn, secret, secret_type)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_trigger_secret_callbacks(struct s2n_connection *conn, + const struct s2n_blob *secret, s2n_extract_secret_type_t secret_type, s2n_mode mode) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + + static const s2n_secret_type_t conversions[][2] = { + [S2N_EARLY_SECRET] = { S2N_CLIENT_EARLY_TRAFFIC_SECRET, S2N_CLIENT_EARLY_TRAFFIC_SECRET }, + [S2N_HANDSHAKE_SECRET] = { S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET, S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET }, + [S2N_MASTER_SECRET] = { S2N_SERVER_APPLICATION_TRAFFIC_SECRET, S2N_CLIENT_APPLICATION_TRAFFIC_SECRET }, + }; + s2n_secret_type_t callback_secret_type = conversions[secret_type][mode]; + + RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, callback_secret_type)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# 0 + *# | + *# v + *# PSK -> HKDF-Extract = Early Secret + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# There are multiple potential Early Secret values, depending on which + *# PSK the server ultimately selects. The client will need to compute + *# one for each potential PSK + */ +S2N_RESULT s2n_extract_early_secret(struct s2n_psk *psk) +{ + RESULT_ENSURE_REF(psk); + RESULT_GUARD_POSIX(s2n_realloc(&psk->early_secret, s2n_get_hash_len(psk->hmac_alg))); + RESULT_GUARD(s2n_extract_secret(psk->hmac_alg, + &ZERO_VALUE(psk->hmac_alg), + &psk->secret, + &psk->early_secret)); + return S2N_RESULT_OK; +} + +/* + * When we require an early secret to derive other secrets, + * either retrieve the early secret stored on the chosen / early data PSK + * or calculate one using a "zero" PSK. + */ +static S2N_RESULT s2n_extract_early_secret_for_schedule(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_psk *psk = conn->psk_params.chosen_psk; + s2n_hmac_algorithm hmac_alg = CONN_HMAC_ALG(conn); + + /* + * If the client is sending early data, then the PSK is always assumed + * to be the first PSK offered. + */ + if (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { + RESULT_GUARD(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + RESULT_ENSURE_REF(psk); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# if no PSK is selected, it will then need + *# to compute the Early Secret corresponding to the zero PSK. + */ + if (psk == NULL) { + RESULT_GUARD(s2n_extract_secret(hmac_alg, + &ZERO_VALUE(hmac_alg), + &ZERO_VALUE(hmac_alg), + &CONN_SECRET(conn, extract_secret))); + return S2N_RESULT_OK; + } + + /* + * The early secret is required to generate or verify a PSK's binder, + * so must have already been calculated if a valid PSK exists. + * Use the early secret stored on the PSK. + */ + RESULT_ENSURE_EQ(hmac_alg, psk->hmac_alg); + RESULT_CHECKED_MEMCPY(CONN_SECRETS(conn).extract_secret, psk->early_secret.data, psk->early_secret.size); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "ext binder" | "res binder", "") + *# | = binder_key + */ +S2N_RESULT s2n_derive_binder_key(struct s2n_psk *psk, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(psk); + RESULT_ENSURE_REF(output); + + const struct s2n_blob *label = &s2n_tls13_label_resumption_psk_binder_key; + if (psk->type == S2N_PSK_TYPE_EXTERNAL) { + label = &s2n_tls13_label_external_psk_binder_key; + } + RESULT_GUARD(s2n_extract_early_secret(psk)); + RESULT_GUARD(s2n_derive_secret(psk->hmac_alg, + &psk->early_secret, + label, + &EMPTY_CONTEXT(psk->hmac_alg), + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "c e traffic", ClientHello) + *# | = client_early_traffic_secret + */ +static S2N_RESULT s2n_derive_client_early_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_EARLY_SECRET, + &s2n_tls13_label_client_early_traffic_secret, + CLIENT_HELLO, + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# v + *# Derive-Secret(., "derived", "") + *# | + *# v + *# (EC)DHE -> HKDF-Extract = Handshake Secret + */ +static S2N_RESULT s2n_extract_handshake_secret(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_blob derived_secret = { 0 }; + uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); + RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_EARLY_SECRET, &derived_secret)); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free_or_wipe); + RESULT_GUARD_POSIX(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), + &derived_secret, + &shared_secret, + &CONN_SECRET(conn, extract_secret))); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "c hs traffic", + *# | ClientHello...ServerHello) + *# | = client_handshake_traffic_secret + */ +static S2N_RESULT s2n_derive_client_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_HANDSHAKE_SECRET, + &s2n_tls13_label_client_handshake_traffic_secret, + SERVER_HELLO, + output)); + + /* + * The client finished key needs to be calculated using the + * same connection state as the client handshake secret. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 + *# The key used to compute the Finished message is computed from the + *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). + */ + RESULT_GUARD(s2n_tls13_compute_finished_key(conn, + output, &CONN_FINISHED(conn, client))); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "s hs traffic", + *# | ClientHello...ServerHello) + *# | = server_handshake_traffic_secret + */ +static S2N_RESULT s2n_derive_server_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_HANDSHAKE_SECRET, + &s2n_tls13_label_server_handshake_traffic_secret, + SERVER_HELLO, + output)); + + /* + * The server finished key needs to be calculated using the + * same connection state as the server handshake secret. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 + *# The key used to compute the Finished message is computed from the + *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). + */ + RESULT_GUARD(s2n_tls13_compute_finished_key(conn, + output, &CONN_FINISHED(conn, server))); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# v + *# Derive-Secret(., "derived", "") + *# | + *# v + *# 0 -> HKDF-Extract = Master Secret + */ +static S2N_RESULT s2n_extract_master_secret(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_blob derived_secret = { 0 }; + uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); + RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_HANDSHAKE_SECRET, &derived_secret)); + + RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), + &derived_secret, + &ZERO_VALUE(CONN_HMAC_ALG(conn)), + &CONN_SECRET(conn, extract_secret))); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "c ap traffic", + *# | ClientHello...server Finished) + *# | = client_application_traffic_secret_0 + */ +static S2N_RESULT s2n_derive_client_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_client_application_traffic_secret, + SERVER_FINISHED, + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "s ap traffic", + *# | ClientHello...server Finished) + *# | = server_application_traffic_secret_0 + */ +static S2N_RESULT s2n_derive_server_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_server_application_traffic_secret, + SERVER_FINISHED, + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "res master", + *# ClientHello...client Finished) + *# = resumption_master_secret + */ +S2N_RESULT s2n_derive_resumption_master_secret(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + /* Secret derivation requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_resumption_master_secret, + CLIENT_FINISHED, + &CONN_SECRET(conn, resumption_master_secret))); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "exp master", + *# | ClientHello...server Finished) + *# | = exporter_master_secret + */ +S2N_RESULT s2n_derive_exporter_master_secret(struct s2n_connection *conn, struct s2n_blob *secret) +{ + RESULT_ENSURE_REF(conn); + /* Secret derivation requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_exporter_master_secret, + SERVER_FINISHED, + secret)); + + RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, S2N_EXPORTER_SECRET)); + + return S2N_RESULT_OK; +} + +static s2n_result (*extract_methods[])(struct s2n_connection *conn) = { + [S2N_EARLY_SECRET] = &s2n_extract_early_secret_for_schedule, + [S2N_HANDSHAKE_SECRET] = &s2n_extract_handshake_secret, + [S2N_MASTER_SECRET] = &s2n_extract_master_secret, +}; + +S2N_RESULT s2n_tls13_extract_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->handshake.hashes); + RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); + + RESULT_ENSURE_GTE(secret_type, 0); + RESULT_ENSURE_LT(secret_type, s2n_array_len(extract_methods)); + + s2n_extract_secret_type_t next_secret_type = conn->secrets.extract_secret_type + 1; + for (s2n_extract_secret_type_t i = next_secret_type; i <= secret_type; i++) { + RESULT_ENSURE_REF(extract_methods[i]); + RESULT_GUARD(extract_methods[i](conn)); + conn->secrets.extract_secret_type = i; + } + + return S2N_RESULT_OK; +} + +static s2n_result (*derive_methods[][2])(struct s2n_connection *conn, struct s2n_blob *secret) = { + [S2N_EARLY_SECRET] = { &s2n_derive_client_early_traffic_secret, &s2n_derive_client_early_traffic_secret }, + [S2N_HANDSHAKE_SECRET] = { &s2n_derive_server_handshake_traffic_secret, &s2n_derive_client_handshake_traffic_secret }, + [S2N_MASTER_SECRET] = { &s2n_derive_server_application_traffic_secret, &s2n_derive_client_application_traffic_secret }, +}; + +S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->handshake.hashes); + RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); + + RESULT_GUARD(s2n_tls13_extract_secret(conn, secret_type)); + + RESULT_ENSURE_GTE(secret_type, 0); + RESULT_ENSURE_LT(secret_type, s2n_array_len(derive_methods)); + RESULT_ENSURE_REF(derive_methods[secret_type][mode]); + RESULT_GUARD(derive_methods[secret_type][mode](conn, secret)); + + RESULT_GUARD(s2n_trigger_secret_callbacks(conn, secret, secret_type, mode)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_secrets_clean(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + /* Secret clean requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + if (conn->actual_protocol_version < S2N_TLS13) { + return S2N_RESULT_OK; + } + + /* + * Wipe base secrets. + * Not strictly necessary, but probably safer than leaving them. + * A compromised secret additionally compromises all secrets derived from it, + * so these are the most sensitive secrets. + */ + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, extract_secret))); + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + + /* Wipe other secrets no longer needed */ + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_early_secret))); + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_handshake_secret))); + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, server_handshake_secret))); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_secrets_update(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + return S2N_RESULT_OK; + } + + /* Secret update requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + message_type_t message_type = s2n_conn_get_current_message_type(conn); + switch (message_type) { + case CLIENT_HELLO: + if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED + || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, + S2N_CLIENT, &CONN_SECRET(conn, client_early_secret))); + } + break; + case SERVER_HELLO: + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, + S2N_CLIENT, &CONN_SECRET(conn, client_handshake_secret))); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, + S2N_SERVER, &CONN_SECRET(conn, server_handshake_secret))); + break; + case SERVER_FINISHED: + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, + S2N_CLIENT, &CONN_SECRET(conn, client_app_secret))); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, + S2N_SERVER, &CONN_SECRET(conn, server_app_secret))); + RESULT_GUARD(s2n_derive_exporter_master_secret(conn, + &CONN_SECRET(conn, exporter_master_secret))); + break; + case CLIENT_FINISHED: + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_derive_resumption_master_secret(conn)); + break; + default: + break; + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + /* Getting secrets requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + bool is_available = (secret_type <= conn->secrets.extract_secret_type) + /* Unlike the other secrets, we don't wipe the master / app secrets */ + || (secret_type == S2N_MASTER_SECRET && s2n_handshake_is_complete(conn)); + RESULT_ENSURE(is_available, S2N_ERR_SAFETY); + + uint8_t *secrets[][2] = { + [S2N_EARLY_SECRET] = { NULL, CONN_SECRETS(conn).client_early_secret }, + [S2N_HANDSHAKE_SECRET] = { CONN_SECRETS(conn).server_handshake_secret, CONN_SECRETS(conn).client_handshake_secret }, + [S2N_MASTER_SECRET] = { CONN_SECRETS(conn).server_app_secret, CONN_SECRETS(conn).client_app_secret }, + }; + RESULT_ENSURE_GT(secret_type, S2N_NONE_SECRET); + RESULT_ENSURE_LT(secret_type, s2n_array_len(secrets)); + RESULT_ENSURE_REF(secrets[secret_type][mode]); + + secret->size = s2n_get_hash_len(CONN_HMAC_ALG(conn)); + RESULT_CHECKED_MEMCPY(secret->data, secrets[secret_type][mode], secret->size); + RESULT_ENSURE_GT(secret->size, 0); + return S2N_RESULT_OK; +} + +/* + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.5 + *# The exporter value is computed as: + *# + *# TLS-Exporter(label, context_value, key_length) = + *# HKDF-Expand-Label(Derive-Secret(Secret, label, ""), + *# "exporter", Hash(context_value), key_length) + */ +int s2n_connection_tls_exporter(struct s2n_connection *conn, + const uint8_t *label_in, uint32_t label_length, + const uint8_t *context, uint32_t context_length, + uint8_t *output_in, uint32_t output_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(output_in); + POSIX_ENSURE_REF(label_in); + POSIX_ENSURE_REF(context); + POSIX_ENSURE(s2n_connection_get_protocol_version(conn) == S2N_TLS13, S2N_ERR_INVALID_STATE); + + POSIX_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + s2n_hmac_algorithm hmac_alg = conn->secure->cipher_suite->prf_alg; + + uint8_t label_bytes[S2N_MAX_HKDF_EXPAND_LABEL_LENGTH] = { 0 }; + struct s2n_blob label = { 0 }; + POSIX_ENSURE_LTE(label_length, sizeof(label_bytes)); + POSIX_CHECKED_MEMCPY(label_bytes, label_in, label_length); + POSIX_GUARD(s2n_blob_init(&label, label_bytes, label_length)); + + uint8_t derived_secret_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob derived_secret = { 0 }; + POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); + POSIX_GUARD(s2n_blob_init(&derived_secret, + derived_secret_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); + POSIX_GUARD_RESULT(s2n_derive_secret(hmac_alg, &CONN_SECRET(conn, exporter_master_secret), + &label, &EMPTY_CONTEXT(hmac_alg), &derived_secret)); + + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + POSIX_GUARD(s2n_hmac_new(&hmac_state)); + + DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&hash)); + + s2n_hash_algorithm hash_alg = { 0 }; + POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); + uint8_t digest_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest = { 0 }; + POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); + POSIX_GUARD(s2n_blob_init(&digest, digest_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); + + POSIX_GUARD(s2n_hash_init(&hash, hash_alg)); + POSIX_GUARD(s2n_hash_update(&hash, context, context_length)); + POSIX_GUARD(s2n_hash_digest(&hash, digest.data, digest.size)); + + struct s2n_blob output = { 0 }; + POSIX_GUARD(s2n_blob_init(&output, output_in, output_length)); + POSIX_GUARD(s2n_hkdf_expand_label(&hmac_state, hmac_alg, + &derived_secret, &s2n_tls13_label_exporter, &digest, &output)); + + return S2N_SUCCESS; +} diff --git a/tls/s2n_x509_validator.c b/tls/s2n_x509_validator.c index 771ca31979d..ed74ccc7b4d 100644 --- a/tls/s2n_x509_validator.c +++ b/tls/s2n_x509_validator.c @@ -1,1160 +1,1169 @@ -/* - * 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 -#include - -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_openssl_x509.h" -#include "crypto/s2n_pkey.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crl.h" -#include "tls/s2n_security_policies.h" -#include "utils/s2n_result.h" -#include "utils/s2n_rfc5952.h" -#include "utils/s2n_safety.h" - -#if S2N_OCSP_STAPLING_SUPPORTED - #include -DEFINE_POINTER_CLEANUP_FUNC(OCSP_RESPONSE *, OCSP_RESPONSE_free); -DEFINE_POINTER_CLEANUP_FUNC(OCSP_BASICRESP *, OCSP_BASICRESP_free); - -#endif - -#ifndef X509_V_FLAG_PARTIAL_CHAIN - #define X509_V_FLAG_PARTIAL_CHAIN 0x80000 -#endif - -#define DEFAULT_MAX_CHAIN_DEPTH 7 -/* Time used by default for nextUpdate if none provided in OCSP: 1 hour since thisUpdate. */ -#define DEFAULT_OCSP_NEXT_UPDATE_PERIOD 3600 - -/* s2n's internal clock measures epoch-nanoseconds stored with a uint64_t. The - * maximum representable timestamp is Sunday, July 21, 2554. time_t measures - * epoch-seconds in a int64_t or int32_t (platform dependent). If time_t is an - * int32_t, the maximum representable timestamp is January 19, 2038. - * - * This means that converting from the internal clock to a time_t is not safe, - * because the internal clock might hold a value that is too large to represent - * in a time_t. This constant represents the largest internal clock value that - * can be safely represented as a time_t. - */ -#define MAX_32_TIMESTAMP_NANOS 2147483647 * ONE_SEC_IN_NANOS - -#define OSSL_VERIFY_CALLBACK_IGNORE_ERROR 1 - -DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(X509_CRL) *, sk_X509_CRL_free); -DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(GENERAL_NAME) *, GENERAL_NAMES_free); - -uint8_t s2n_x509_ocsp_stapling_supported(void) -{ - return S2N_OCSP_STAPLING_SUPPORTED; -} - -void s2n_x509_trust_store_init_empty(struct s2n_x509_trust_store *store) -{ - store->trust_store = NULL; -} - -uint8_t s2n_x509_trust_store_has_certs(struct s2n_x509_trust_store *store) -{ - return store->trust_store ? (uint8_t) 1 : (uint8_t) 0; -} - -int s2n_x509_trust_store_add_pem(struct s2n_x509_trust_store *store, const char *pem) -{ - POSIX_ENSURE_REF(store); - POSIX_ENSURE_REF(pem); - - if (!store->trust_store) { - store->trust_store = X509_STORE_new(); - POSIX_ENSURE_REF(store->trust_store); - } - - DEFER_CLEANUP(struct s2n_stuffer pem_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer der_out_stuffer = { 0 }, s2n_stuffer_free); - - POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&pem_in_stuffer, pem)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&der_out_stuffer, 2048)); - - do { - DEFER_CLEANUP(struct s2n_blob next_cert = { 0 }, s2n_free); - - POSIX_GUARD(s2n_stuffer_certificate_from_pem(&pem_in_stuffer, &der_out_stuffer)); - POSIX_GUARD(s2n_alloc(&next_cert, s2n_stuffer_data_available(&der_out_stuffer))); - POSIX_GUARD(s2n_stuffer_read(&der_out_stuffer, &next_cert)); - - const uint8_t *data = next_cert.data; - DEFER_CLEANUP(X509 *ca_cert = d2i_X509(NULL, &data, next_cert.size), X509_free_pointer); - S2N_ERROR_IF(ca_cert == NULL, S2N_ERR_DECODE_CERTIFICATE); - - if (!X509_STORE_add_cert(store->trust_store, ca_cert)) { - unsigned long error = ERR_get_error(); - POSIX_ENSURE(ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE, S2N_ERR_DECODE_CERTIFICATE); - } - } while (s2n_stuffer_data_available(&pem_in_stuffer)); - - return 0; -} - -int s2n_x509_trust_store_from_ca_file(struct s2n_x509_trust_store *store, const char *ca_pem_filename, const char *ca_dir) -{ - if (!store->trust_store) { - store->trust_store = X509_STORE_new(); - POSIX_ENSURE_REF(store->trust_store); - } - - int err_code = X509_STORE_load_locations(store->trust_store, ca_pem_filename, ca_dir); - if (!err_code) { - s2n_x509_trust_store_wipe(store); - POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); - } - - return 0; -} - -void s2n_x509_trust_store_wipe(struct s2n_x509_trust_store *store) -{ - if (store->trust_store) { - X509_STORE_free(store->trust_store); - store->trust_store = NULL; - store->loaded_system_certs = false; - } -} - -int s2n_x509_validator_init_no_x509_validation(struct s2n_x509_validator *validator) -{ - POSIX_ENSURE_REF(validator); - validator->trust_store = NULL; - validator->store_ctx = NULL; - validator->skip_cert_validation = 1; - validator->check_stapled_ocsp = 0; - validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; - validator->state = INIT; - validator->cert_chain_from_wire = sk_X509_new_null(); - validator->crl_lookup_list = NULL; - validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; - validator->cert_validation_cb_invoked = false; - - return 0; -} - -int s2n_x509_validator_init(struct s2n_x509_validator *validator, struct s2n_x509_trust_store *trust_store, uint8_t check_ocsp) -{ - POSIX_ENSURE_REF(trust_store); - validator->trust_store = trust_store; - validator->skip_cert_validation = 0; - validator->check_stapled_ocsp = check_ocsp; - validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; - validator->store_ctx = NULL; - if (validator->trust_store->trust_store) { - validator->store_ctx = X509_STORE_CTX_new(); - POSIX_ENSURE_REF(validator->store_ctx); - } - validator->cert_chain_from_wire = sk_X509_new_null(); - validator->state = INIT; - validator->crl_lookup_list = NULL; - validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; - validator->cert_validation_cb_invoked = false; - - return 0; -} - -static inline void wipe_cert_chain(STACK_OF(X509) *cert_chain) -{ - if (cert_chain) { - sk_X509_pop_free(cert_chain, X509_free); - } -} - -int s2n_x509_validator_wipe(struct s2n_x509_validator *validator) -{ - if (validator->store_ctx) { - X509_STORE_CTX_free(validator->store_ctx); - validator->store_ctx = NULL; - } - wipe_cert_chain(validator->cert_chain_from_wire); - validator->cert_chain_from_wire = NULL; - validator->trust_store = NULL; - validator->skip_cert_validation = 0; - validator->state = UNINIT; - validator->max_chain_depth = 0; - if (validator->crl_lookup_list) { - POSIX_GUARD_RESULT(s2n_array_free(validator->crl_lookup_list)); - validator->crl_lookup_list = NULL; - } - - return S2N_SUCCESS; -} - -int s2n_x509_validator_set_max_chain_depth(struct s2n_x509_validator *validator, uint16_t max_depth) -{ - POSIX_ENSURE_REF(validator); - S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); - - validator->max_chain_depth = max_depth; - return 0; -} - -static S2N_RESULT s2n_verify_host_information_san_entry(struct s2n_connection *conn, GENERAL_NAME *current_name, bool *san_found) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(current_name); - RESULT_ENSURE_REF(san_found); - - if (current_name->type == GEN_DNS || current_name->type == GEN_URI) { - *san_found = true; - - const char *name = (const char *) ASN1_STRING_data(current_name->d.ia5); - RESULT_ENSURE_REF(name); - int name_len = ASN1_STRING_length(current_name->d.ia5); - RESULT_ENSURE_GT(name_len, 0); - - RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; - } - - if (current_name->type == GEN_IPADD) { - *san_found = true; - - /* try to validate an IP address if it's in the subject alt name. */ - const unsigned char *ip_addr = current_name->d.iPAddress->data; - RESULT_ENSURE_REF(ip_addr); - int ip_addr_len = current_name->d.iPAddress->length; - RESULT_ENSURE_GT(ip_addr_len, 0); - - RESULT_STACK_BLOB(address, INET6_ADDRSTRLEN + 1, INET6_ADDRSTRLEN + 1); - - if (ip_addr_len == 4) { - RESULT_GUARD(s2n_inet_ntop(AF_INET, ip_addr, &address)); - } else if (ip_addr_len == 16) { - RESULT_GUARD(s2n_inet_ntop(AF_INET6, ip_addr, &address)); - } else { - /* we aren't able to parse this value so skip it */ - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } - - /* strlen should be safe here since we made sure we were null terminated AND that inet_ntop succeeded */ - const char *name = (const char *) address.data; - size_t name_len = strlen(name); - - RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; - } - - /* we don't understand this entry type so skip it */ - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); -} - -static S2N_RESULT s2n_verify_host_information_san(struct s2n_connection *conn, X509 *public_cert, bool *san_found) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(public_cert); - RESULT_ENSURE_REF(san_found); - - *san_found = false; - - DEFER_CLEANUP(STACK_OF(GENERAL_NAME) *names_list = NULL, GENERAL_NAMES_free_pointer); - names_list = X509_get_ext_d2i(public_cert, NID_subject_alt_name, NULL, NULL); - RESULT_ENSURE(names_list, S2N_ERR_CERT_UNTRUSTED); - - int n = sk_GENERAL_NAME_num(names_list); - RESULT_ENSURE(n > 0, S2N_ERR_CERT_UNTRUSTED); - - s2n_result result = S2N_RESULT_OK; - for (int i = 0; i < n; i++) { - GENERAL_NAME *current_name = sk_GENERAL_NAME_value(names_list, i); - - /* return success on the first entry that passes verification */ - result = s2n_verify_host_information_san_entry(conn, current_name, san_found); - if (s2n_result_is_ok(result)) { - return S2N_RESULT_OK; - } - } - - /* if an error was set by one of the entries, then just propagate the error from the last SAN entry call */ - RESULT_GUARD(result); - - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); -} - -static S2N_RESULT s2n_verify_host_information_common_name(struct s2n_connection *conn, X509 *public_cert, bool *cn_found) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(public_cert); - RESULT_ENSURE_REF(cn_found); - - X509_NAME *subject_name = X509_get_subject_name(public_cert); - RESULT_ENSURE(subject_name, S2N_ERR_CERT_UNTRUSTED); - - int curr_idx = -1; - while (true) { - int next_idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, curr_idx); - if (next_idx >= 0) { - curr_idx = next_idx; - } else { - break; - } - } - - RESULT_ENSURE(curr_idx >= 0, S2N_ERR_CERT_UNTRUSTED); - - ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, curr_idx)); - RESULT_ENSURE(common_name, S2N_ERR_CERT_UNTRUSTED); - - /* X520CommonName allows the following ANSI string types per RFC 5280 Appendix A.1 */ - RESULT_ENSURE(ASN1_STRING_type(common_name) == V_ASN1_TELETEXSTRING - || ASN1_STRING_type(common_name) == V_ASN1_PRINTABLESTRING - || ASN1_STRING_type(common_name) == V_ASN1_UNIVERSALSTRING - || ASN1_STRING_type(common_name) == V_ASN1_UTF8STRING - || ASN1_STRING_type(common_name) == V_ASN1_BMPSTRING, - S2N_ERR_CERT_UNTRUSTED); - - /* at this point we have a valid CN value */ - *cn_found = true; - - char peer_cn[255] = { 0 }; - int cn_len = ASN1_STRING_length(common_name); - RESULT_ENSURE_GT(cn_len, 0); - uint32_t len = (uint32_t) cn_len; - RESULT_ENSURE_LTE(len, s2n_array_len(peer_cn) - 1); - RESULT_CHECKED_MEMCPY(peer_cn, ASN1_STRING_data(common_name), len); - - /* According to https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4, - * the CN fallback only applies to fully qualified DNS domain names. - * - * An IP address is not a fully qualified DNS domain name. Per RFC 6125 - * section 6.2.1, IP reference identities must only be matched against - * iPAddress SAN entries, never against CN values. Reject the CN if it - * parses as an IPv4 or IPv6 address. - */ - unsigned char ip_buf[sizeof(struct in6_addr)] = { 0 }; - if (inet_pton(AF_INET, peer_cn, ip_buf) == 1 || inet_pton(AF_INET6, peer_cn, ip_buf) == 1) { - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } - - RESULT_ENSURE(conn->verify_host_fn(peer_cn, len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; -} - -/* - * For each name in the cert. Iterate them. Call the callback. If one returns true, then consider it validated, - * if none of them return true, the cert is considered invalid. - */ -static S2N_RESULT s2n_verify_host_information(struct s2n_connection *conn, X509 *public_cert) -{ - bool entry_found = false; - - /* Check SubjectAltNames before CommonName as per RFC 6125 6.4.4 */ - s2n_result result = s2n_verify_host_information_san(conn, public_cert, &entry_found); - - /* - *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 - *# As noted, a client MUST NOT seek a match for a reference identifier - *# of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, - *# URI-ID, or any application-specific identifier types supported by the - *# client. - */ - if (entry_found) { - return result; - } - - /* - *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 - *# Therefore, if and only if the presented identifiers do not include a - *# DNS-ID, SRV-ID, URI-ID, or any application-specific identifier types - *# supported by the client, then the client MAY as a last resort check - *# for a string whose form matches that of a fully qualified DNS domain - *# name in a Common Name field of the subject field (i.e., a CN-ID). - */ - result = s2n_verify_host_information_common_name(conn, public_cert, &entry_found); - if (entry_found) { - return result; - } - - /* make a null-terminated string in case the callback tries to use strlen */ - const char *name = ""; - size_t name_len = 0; - - /* at this point, we don't have anything to identify the certificate with so pass an empty string to the callback */ - RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, - struct s2n_blob *asn1_cert) -{ - uint32_t certificate_size = 0; - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(cert_chain_in_stuffer, &certificate_size)); - RESULT_ENSURE(certificate_size > 0, S2N_ERR_CERT_INVALID); - RESULT_ENSURE(certificate_size <= s2n_stuffer_data_available(cert_chain_in_stuffer), S2N_ERR_CERT_INVALID); - - asn1_cert->size = certificate_size; - asn1_cert->data = s2n_stuffer_raw_read(cert_chain_in_stuffer, certificate_size); - RESULT_ENSURE_REF(asn1_cert->data); - - return S2N_RESULT_OK; -} - -/** -* Validates that each certificate in a peer's cert chain contains only signature algorithms in a security policy's -* certificate_signatures_preference list. -*/ -S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(cert); - - const struct s2n_security_policy *security_policy = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); - - /** - * We only restrict the signature algorithm on the certificates in the - * peer's certificate chain if the certificate_signature_preferences field - * is set in the security policy. This is contrary to the RFC, which - * specifies that the signatures in the "signature_algorithms" extension - * apply to signatures in the certificate chain in certain scenarios, so RFC - * compliance would imply validating that the certificate chain signature - * algorithm matches one of the algorithms specified in the - * "signature_algorithms" extension. - * - *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.2 - *= type=exception - *= reason=not implemented due to lack of utility - *# If the client provided a "signature_algorithms" extension, then all - *# certificates provided by the server MUST be signed by a - *# hash/signature algorithm pair that appears in that extension. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.3 - *= type=exception - *= reason=not implemented due to lack of utility - *# If no "signature_algorithms_cert" extension is present, then the - *# "signature_algorithms" extension also applies to signatures appearing in - *# certificates. - */ - struct s2n_cert_info info = { 0 }; - RESULT_GUARD(s2n_openssl_x509_get_cert_info(cert, &info)); - - bool certificate_preferences_defined = security_policy->certificate_signature_preferences != NULL - || security_policy->certificate_key_preferences != NULL; - if (certificate_preferences_defined && !info.self_signed && conn->actual_protocol_version == S2N_TLS13) { - /* Ensure that the certificate signature does not use SHA-1. While this check - * would ideally apply to all connections, we only enforce it when certificate - * preferences exist to stay backwards compatible. - */ - RESULT_ENSURE(info.signature_digest_nid != NID_sha1, S2N_ERR_CERT_UNTRUSTED); - } - - if (!info.self_signed) { - RESULT_GUARD(s2n_security_policy_validate_cert_signature(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); - } - RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_get_validated_cert_chain(const struct s2n_x509_validator *validator, - struct s2n_validated_cert_chain *validated_cert_chain) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validated_cert_chain); - - RESULT_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_INVALID_CERT_STATE); - RESULT_ENSURE_REF(validator->store_ctx); - -#if S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN - /* X509_STORE_CTX_get0_chain is used when available, since it returns a pointer to the - * validated cert chain in the X509_STORE_CTX, avoiding an allocation/copy. - */ - validated_cert_chain->stack = X509_STORE_CTX_get0_chain(validator->store_ctx); -#else - /* Otherwise, X509_STORE_CTX_get1_chain is used instead, which allocates a new cert chain. */ - validated_cert_chain->stack = X509_STORE_CTX_get1_chain(validator->store_ctx); -#endif - - RESULT_ENSURE_REF(validated_cert_chain->stack); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_x509_validator_validated_cert_chain_free(struct s2n_validated_cert_chain *validated_cert_chain) -{ - RESULT_ENSURE_REF(validated_cert_chain); - -#if !S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN - /* When X509_STORE_CTX_get0_chain isn't supported, X509_STORE_CTX_get1_chain is used instead, - * which allocates a new cert chain that is owned by s2n-tls and MUST be freed. - * - * X509_STORE_CTX_get0_chain returns a pointer to the cert chain within the X509_STORE_CTX, - * which is NOT owned by s2n-tls and MUST NOT be manually freed. - */ - RESULT_GUARD(s2n_openssl_x509_stack_pop_free(&validated_cert_chain->stack)); -#endif - - /* Even though the cert chain reference is still valid in the case that get0_chain is used, set - * it to null for consistency with the get1_chain case. - */ - validated_cert_chain->stack = NULL; - - return S2N_RESULT_OK; -} - -/* Validates that the root certificate uses a key allowed by the security policy - * certificate preferences. - */ -static S2N_RESULT s2n_x509_validator_check_root_cert(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(conn); - - const struct s2n_security_policy *security_policy = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); - RESULT_ENSURE_REF(security_policy); - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - STACK_OF(X509) *cert_chain = validated_cert_chain.stack; - RESULT_ENSURE_REF(cert_chain); - - const int certs_in_chain = sk_X509_num(cert_chain); - RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_CERT_UNTRUSTED); - X509 *root = sk_X509_value(cert_chain, certs_in_chain - 1); - RESULT_ENSURE_REF(root); - - struct s2n_cert_info info = { 0 }; - RESULT_GUARD(s2n_openssl_x509_get_cert_info(root, &info)); - - RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_read_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len) -{ - RESULT_ENSURE(validator->skip_cert_validation || s2n_x509_trust_store_has_certs(validator->trust_store), S2N_ERR_CERT_UNTRUSTED); - RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); - - struct s2n_blob cert_chain_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); - DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); - - RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); - - while (s2n_stuffer_data_available(&cert_chain_in_stuffer) - && sk_X509_num(validator->cert_chain_from_wire) < validator->max_chain_depth) { - struct s2n_blob asn1_cert = { 0 }; - RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); - - /* We only do the trailing byte validation when parsing the leaf cert to - * match historical s2n-tls behavior. - */ - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - if (sk_X509_num(validator->cert_chain_from_wire) == 0) { - RESULT_GUARD(s2n_openssl_x509_parse(&asn1_cert, &cert)); - } else { - RESULT_GUARD(s2n_openssl_x509_parse_without_length_validation(&asn1_cert, &cert)); - } - - if (!validator->skip_cert_validation) { - RESULT_GUARD(s2n_x509_validator_check_cert_preferences(conn, cert)); - } - - /* add the cert to the chain */ - RESULT_ENSURE(sk_X509_push(validator->cert_chain_from_wire, cert) > 0, - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - /* After the cert is added to cert_chain_from_wire, it will be freed - * with the call to s2n_x509_validator_wipe. We disable the cleanup - * function since cleanup is no longer "owned" by cert. - */ - ZERO_TO_DISABLE_DEFER_CLEANUP(cert); - - /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ - if (conn->actual_protocol_version >= S2N_TLS13) { - s2n_parsed_extensions_list parsed_extensions_list = { 0 }; - RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); - } - } - - /* if this occurred we exceeded validator->max_chain_depth */ - RESULT_ENSURE(validator->skip_cert_validation || s2n_stuffer_data_available(&cert_chain_in_stuffer) == 0, - S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); - RESULT_ENSURE(sk_X509_num(validator->cert_chain_from_wire) > 0, S2N_ERR_NO_CERT_FOUND); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_process_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len) -{ - RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); - - RESULT_GUARD(s2n_x509_validator_read_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); - - if (validator->skip_cert_validation) { - return S2N_RESULT_OK; - } - - X509 *leaf = sk_X509_value(validator->cert_chain_from_wire, 0); - RESULT_ENSURE_REF(leaf); - - if (conn->verify_host_fn) { - RESULT_GUARD(s2n_verify_host_information(conn, leaf)); - } - - RESULT_GUARD_OSSL(X509_STORE_CTX_init(validator->store_ctx, validator->trust_store->trust_store, leaf, - validator->cert_chain_from_wire), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - if (conn->config->crl_lookup_cb) { - RESULT_GUARD(s2n_crl_invoke_lookup_callbacks(conn, validator)); - RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); - } - - validator->state = READY_TO_VERIFY; - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_set_no_check_time_flag(struct s2n_x509_validator *validator) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validator->store_ctx); - - X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); - RESULT_ENSURE_REF(param); - -#ifdef S2N_LIBCRYPTO_SUPPORTS_FLAG_NO_CHECK_TIME - RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_NO_CHECK_TIME), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); -#else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -#endif - - return S2N_RESULT_OK; -} - -int s2n_disable_time_validation_ossl_verify_callback(int default_ossl_ret, X509_STORE_CTX *ctx) -{ - int err = X509_STORE_CTX_get_error(ctx); - switch (err) { - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_CERT_HAS_EXPIRED: - return OSSL_VERIFY_CALLBACK_IGNORE_ERROR; - default: - break; - } - - /* If CRL validation is enabled, setting the time validation verify callback will override the - * CRL verify callback. The CRL verify callback is manually triggered to work around this - * issue. - * - * The CRL verify callback ignores validation errors exclusively for CRL timestamp fields. So, - * if CRL validation isn't enabled, the CRL verify callback is a no-op. - */ - return s2n_crl_ossl_verify_callback(default_ossl_ret, ctx); -} - -static S2N_RESULT s2n_x509_validator_disable_time_validation(struct s2n_connection *conn, - struct s2n_x509_validator *validator) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validator->store_ctx); - - /* Setting an X509_STORE verify callback is not recommended with AWS-LC: - * https://github.com/aws/aws-lc/blob/aa90e509f2e940916fbe9fdd469a4c90c51824f6/include/openssl/x509.h#L2980-L2990 - * - * If the libcrypto supports the ability to disable time validation with an X509_VERIFY_PARAM - * NO_CHECK_TIME flag, this method is preferred. - * - * However, older versions of AWS-LC and OpenSSL 1.0.2 do not support this flag. In this case, - * an X509_STORE verify callback is used. This is acceptable in older versions of AWS-LC - * because the versions are fixed, and updates to AWS-LC will not break the callback - * implementation. - */ - if (s2n_libcrypto_supports_flag_no_check_time()) { - RESULT_GUARD(s2n_x509_validator_set_no_check_time_flag(validator)); - } else { - X509_STORE_CTX_set_verify_cb(validator->store_ctx, - s2n_disable_time_validation_ossl_verify_callback); - } - - return S2N_RESULT_OK; -} - -int s2n_no_op_verify_custom_crit_oids_cb(X509_STORE_CTX *ctx, X509 *x509, STACK_OF(ASN1_OBJECT) *oids) -{ - return 1; -} - -static S2N_RESULT s2n_x509_validator_add_custom_extensions(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validator->store_ctx); - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - if (conn->config->custom_x509_extension_oids) { -#if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_OID - size_t custom_oid_count = sk_ASN1_OBJECT_num(conn->config->custom_x509_extension_oids); - for (size_t i = 0; i < custom_oid_count; i++) { - ASN1_OBJECT *critical_oid = sk_ASN1_OBJECT_value(conn->config->custom_x509_extension_oids, i); - RESULT_ENSURE_REF(critical_oid); - RESULT_GUARD_OSSL(X509_STORE_CTX_add_custom_crit_oid(validator->store_ctx, critical_oid), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - } - /* To enable AWS-LC accepting custom extensions, an X509_STORE_CTX_verify_crit_oids_cb must be set. - * See https://github.com/aws/aws-lc/blob/f0b4afedd7d45fc2517643d890b654856c57f994/include/openssl/x509.h#L2913-L2918. - * - * The `X509_STORE_CTX_verify_crit_oids_cb` callback can be used to implement the validation for the - * custom certificate extensions. However, s2n-tls consumers are expected to implement this validation - * in the `s2n_cert_validation_callback` instead. So, a no-op callback is provided to AWS-LC. - */ - X509_STORE_CTX_set_verify_crit_oids(validator->store_ctx, s2n_no_op_verify_custom_crit_oids_cb); -#else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -#endif - } - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_verify_intent_for_cert(struct s2n_connection *conn, X509 *cert, bool is_leaf) -{ - RESULT_ENSURE_REF(cert); - - /* The X509_PURPOSE values indicate the purpose that certificates must specify. For servers, - * received client certificates MUST have a TLS client purpose. For clients, received server - * certificates MUST have a TLS server purpose. - */ - int purpose = X509_PURPOSE_SSL_CLIENT; - if (conn->mode == S2N_CLIENT) { - purpose = X509_PURPOSE_SSL_SERVER; - } - - RESULT_GUARD_OSSL(X509_check_purpose(cert, purpose, !is_leaf), S2N_ERR_CERT_INTENT_INVALID); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_verify_intent(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - if (conn->config->disable_x509_intent_verification) { - return S2N_RESULT_OK; - } - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - - int cert_count = sk_X509_num(validated_cert_chain.stack); - RESULT_ENSURE_GT(cert_count, 0); - - /* The validated cert chain returned from the libcrypto includes the trust anchor. The trust - * anchor is omitted from intent verification since its TLS intent is implicitly indicated by - * its presence in the s2n-tls trust store. - */ - cert_count -= 1; - - for (int i = 0; i < cert_count; i++) { - X509 *cert = sk_X509_value(validated_cert_chain.stack, i); - RESULT_ENSURE_REF(cert); - - bool is_leaf = (i == 0); - RESULT_GUARD(s2n_x509_validator_verify_intent_for_cert(conn, cert, is_leaf)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_verify_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE(validator->state == READY_TO_VERIFY, S2N_ERR_INVALID_CERT_STATE); - - X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); - X509_VERIFY_PARAM_set_depth(param, validator->max_chain_depth); - - DEFER_CLEANUP(STACK_OF(X509_CRL) *crl_stack = NULL, sk_X509_CRL_free_pointer); - - if (conn->config->crl_lookup_cb) { - X509_STORE_CTX_set_verify_cb(validator->store_ctx, s2n_crl_ossl_verify_callback); - - crl_stack = sk_X509_CRL_new_null(); - RESULT_GUARD(s2n_crl_get_crls_from_lookup_list(validator, crl_stack)); - - /* Set the CRL list that the libcrypto will use to validate certificates with */ - X509_STORE_CTX_set0_crls(validator->store_ctx, crl_stack); - - /* Enable CRL validation for certificates in X509_verify_cert */ - RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - /* Enable CRL validation for all certificates, not just the leaf */ - RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK_ALL), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - } - - /* Disabling time validation may set a NO_CHECK_TIME flag on the X509_STORE_CTX. Calling - * X509_STORE_CTX_set_time will override this flag. To prevent this, X509_STORE_CTX_set_time is - * only called if time validation is enabled. - */ - if (conn->config->disable_x509_time_validation) { - RESULT_GUARD(s2n_x509_validator_disable_time_validation(conn, validator)); - } else { - uint64_t current_sys_time = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time)); - if (sizeof(time_t) == 4) { - /* cast value to uint64_t to prevent overflow errors */ - RESULT_ENSURE_LTE(current_sys_time, (uint64_t) MAX_32_TIMESTAMP_NANOS); - } - - /* this wants seconds not nanoseconds */ - time_t current_time = (time_t) (current_sys_time / ONE_SEC_IN_NANOS); - X509_STORE_CTX_set_time(validator->store_ctx, 0, current_time); - } - - /* It's assumed that if a valid certificate chain is received with an issuer that's present in - * the trust store, the certificate chain should be trusted. This should be the case even if - * the issuer in the trust store isn't a root certificate. Setting the PARTIAL_CHAIN flag - * allows the libcrypto to trust certificates in the trust store that aren't root certificates. - */ - X509_STORE_CTX_set_flags(validator->store_ctx, X509_V_FLAG_PARTIAL_CHAIN); - - RESULT_GUARD(s2n_x509_validator_add_custom_extensions(validator, conn)); - - int verify_ret = X509_verify_cert(validator->store_ctx); - if (verify_ret <= 0) { - int ossl_error = X509_STORE_CTX_get_error(validator->store_ctx); - switch (ossl_error) { - case X509_V_ERR_CERT_NOT_YET_VALID: - RESULT_BAIL(S2N_ERR_CERT_NOT_YET_VALID); - case X509_V_ERR_CERT_HAS_EXPIRED: - RESULT_BAIL(S2N_ERR_CERT_EXPIRED); - case X509_V_ERR_CERT_REVOKED: - RESULT_BAIL(S2N_ERR_CERT_REVOKED); - case X509_V_ERR_UNABLE_TO_GET_CRL: - case X509_V_ERR_DIFFERENT_CRL_SCOPE: - RESULT_BAIL(S2N_ERR_CRL_LOOKUP_FAILED); - case X509_V_ERR_CRL_SIGNATURE_FAILURE: - RESULT_BAIL(S2N_ERR_CRL_SIGNATURE); - case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: - RESULT_BAIL(S2N_ERR_CRL_ISSUER); - case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: - RESULT_BAIL(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); - case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: - RESULT_BAIL(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION); - default: - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } - } - - validator->state = VALIDATED; - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_parse_leaf_certificate_extensions(struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len, - s2n_parsed_extensions_list *first_certificate_extensions) -{ - /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ - RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); - - struct s2n_blob cert_chain_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); - DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); - - RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); - - struct s2n_blob asn1_cert = { 0 }; - RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); - - s2n_parsed_extensions_list parsed_extensions_list = { 0 }; - RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); - *first_certificate_extensions = parsed_extensions_list; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_validate_cert_chain_pre_cb(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - switch (validator->state) { - case INIT: - break; - case AWAITING_CRL_CALLBACK: - RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); - break; - default: - RESULT_BAIL(S2N_ERR_INVALID_CERT_STATE); - } - - if (validator->state == INIT) { - RESULT_GUARD(s2n_x509_validator_process_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); - } - - if (validator->state == READY_TO_VERIFY) { - RESULT_GUARD(s2n_x509_validator_verify_cert_chain(validator, conn)); - RESULT_GUARD(s2n_x509_validator_verify_intent(validator, conn)); - RESULT_GUARD(s2n_x509_validator_check_root_cert(validator, conn)); - } - - if (conn->actual_protocol_version >= S2N_TLS13) { - /* Only process certificate extensions received in the first certificate. Extensions received in all other - * certificates are ignored. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.2 - *# If an extension applies to the entire chain, it SHOULD be included in - *# the first CertificateEntry. - */ - s2n_parsed_extensions_list first_certificate_extensions = { 0 }; - RESULT_GUARD(s2n_x509_validator_parse_leaf_certificate_extensions(conn, cert_chain_in, cert_chain_len, &first_certificate_extensions)); - RESULT_GUARD_POSIX(s2n_extension_list_process(S2N_EXTENSION_LIST_CERTIFICATE, conn, &first_certificate_extensions)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_handle_cert_validation_callback_result(struct s2n_x509_validator *validator) -{ - RESULT_ENSURE_REF(validator); - - if (!validator->cert_validation_info.finished) { - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - - RESULT_ENSURE(validator->cert_validation_info.accepted, S2N_ERR_CERT_REJECTED); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_pkey_type *pkey_type, struct s2n_pkey *public_key_out) -{ - RESULT_ENSURE_REF(validator); - - if (validator->cert_validation_cb_invoked) { - RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); - } else { - RESULT_GUARD(s2n_x509_validator_validate_cert_chain_pre_cb(validator, conn, cert_chain_in, cert_chain_len)); - - if (conn->config->cert_validation_cb) { - RESULT_ENSURE(conn->config->cert_validation_cb(conn, &(validator->cert_validation_info), conn->config->cert_validation_ctx) == S2N_SUCCESS, - S2N_ERR_CANCELLED); - validator->cert_validation_cb_invoked = true; - RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); - } - } - - /* retrieve information from leaf cert */ - RESULT_ENSURE_GT(sk_X509_num(validator->cert_chain_from_wire), 0); - X509 *leaf_cert = sk_X509_value(validator->cert_chain_from_wire, 0); - RESULT_ENSURE_REF(leaf_cert); - DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); - RESULT_GUARD(s2n_pkey_from_x509(leaf_cert, &public_key, pkey_type)); - - *public_key_out = public_key; - - /* Reset the old struct, so we don't clean up public_key_out */ - ZERO_TO_DISABLE_DEFER_CLEANUP(public_key); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_validate_cert_stapled_ocsp_response(struct s2n_x509_validator *validator, - struct s2n_connection *conn, const uint8_t *ocsp_response_raw, uint32_t ocsp_response_length) -{ - if (validator->skip_cert_validation || !validator->check_stapled_ocsp) { - validator->state = OCSP_VALIDATED; - return S2N_RESULT_OK; - } - - RESULT_ENSURE(validator->state == VALIDATED, S2N_ERR_INVALID_CERT_STATE); - -#if !S2N_OCSP_STAPLING_SUPPORTED - /* Default to safety */ - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); -#else - - RESULT_ENSURE_REF(ocsp_response_raw); - - DEFER_CLEANUP(OCSP_RESPONSE *ocsp_response = d2i_OCSP_RESPONSE(NULL, &ocsp_response_raw, ocsp_response_length), - OCSP_RESPONSE_free_pointer); - RESULT_ENSURE(ocsp_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); - - int ocsp_status = OCSP_response_status(ocsp_response); - RESULT_ENSURE(ocsp_status == OCSP_RESPONSE_STATUS_SUCCESSFUL, S2N_ERR_CERT_UNTRUSTED); - - DEFER_CLEANUP(OCSP_BASICRESP *basic_response = OCSP_response_get1_basic(ocsp_response), OCSP_BASICRESP_free_pointer); - RESULT_ENSURE(basic_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - STACK_OF(X509) *cert_chain = validated_cert_chain.stack; - RESULT_ENSURE_REF(cert_chain); - - const int certs_in_chain = sk_X509_num(cert_chain); - RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_NO_CERT_FOUND); - - /* leaf is the top: not the bottom. */ - X509 *subject = sk_X509_value(cert_chain, 0); - X509 *issuer = NULL; - /* find the issuer in the chain. If it's not there. Fail everything. */ - for (int i = 0; i < certs_in_chain; ++i) { - X509 *issuer_candidate = sk_X509_value(cert_chain, i); - const int issuer_value = X509_check_issued(issuer_candidate, subject); - - if (issuer_value == X509_V_OK) { - issuer = issuer_candidate; - break; - } - } - RESULT_ENSURE(issuer != NULL, S2N_ERR_CERT_UNTRUSTED); - - /* Important: this checks that the stapled ocsp response CAN be verified, not that it has been verified. */ - const int ocsp_verify_res = OCSP_basic_verify(basic_response, cert_chain, validator->trust_store->trust_store, 0); - RESULT_GUARD_OSSL(ocsp_verify_res, S2N_ERR_CERT_UNTRUSTED); - - /* do the crypto checks on the response.*/ - int status = 0; - int reason = 0; - - /* SHA-1 is the only supported hash algorithm for the CertID due to its established use in - * OCSP responders. - */ - OCSP_CERTID *cert_id = OCSP_cert_to_id(EVP_sha1(), subject, issuer); - RESULT_ENSURE_REF(cert_id); - - /** - *= https://www.rfc-editor.org/rfc/rfc6960#section-2.4 - *# - *# thisUpdate The most recent time at which the status being - *# indicated is known by the responder to have been - *# correct. - *# - *# nextUpdate The time at or before which newer information will be - *# available about the status of the certificate. - **/ - ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; - /* Actual verification of the response */ - const int ocsp_resp_find_status_res = OCSP_resp_find_status(basic_response, cert_id, &status, &reason, &revtime, &thisupd, &nextupd); - OCSP_CERTID_free(cert_id); - RESULT_GUARD_OSSL(ocsp_resp_find_status_res, S2N_ERR_CERT_UNTRUSTED); - - uint64_t current_sys_time_nanoseconds = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time_nanoseconds)); - if (sizeof(time_t) == 4) { - /* cast value to uint64_t to prevent overflow errors */ - RESULT_ENSURE_LTE(current_sys_time_nanoseconds, (uint64_t) MAX_32_TIMESTAMP_NANOS); - } - /* convert the current_sys_time (which is in nanoseconds) to seconds */ - time_t current_sys_time_seconds = (time_t) (current_sys_time_nanoseconds / ONE_SEC_IN_NANOS); - - DEFER_CLEANUP(ASN1_GENERALIZEDTIME *current_sys_time = ASN1_GENERALIZEDTIME_set(NULL, current_sys_time_seconds), s2n_openssl_asn1_time_free_pointer); - RESULT_ENSURE_REF(current_sys_time); - - /** - * It is fine to use ASN1_TIME functions with ASN1_GENERALIZEDTIME structures - * From openssl documentation: - * It is recommended that functions starting with ASN1_TIME be used instead - * of those starting with ASN1_UTCTIME or ASN1_GENERALIZEDTIME. The - * functions starting with ASN1_UTCTIME and ASN1_GENERALIZEDTIME act only on - * that specific time format. The functions starting with ASN1_TIME will - * operate on either format. - * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_generalizedtime.html - * - * ASN1_TIME_compare has a much nicer API, but is not available in Openssl - * 1.0.1, so we use ASN1_TIME_diff. - */ - int pday = 0; - int psec = 0; - RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, thisupd, current_sys_time), S2N_ERR_CERT_UNTRUSTED); - /* ensure that current_time is after or the same as "this update" */ - RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_INVALID); - - /* ensure that current_time is before or the same as "next update" */ - if (nextupd) { - RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, current_sys_time, nextupd), S2N_ERR_CERT_UNTRUSTED); - RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_EXPIRED); - } else { - /** - * if nextupd isn't present, assume that nextupd is - * DEFAULT_OCSP_NEXT_UPDATE_PERIOD after thisupd. This means that if the - * current time is more than DEFAULT_OCSP_NEXT_UPDATE_PERIOD - * seconds ahead of thisupd, we consider it invalid. We already compared - * current_sys_time to thisupd, so reuse those values - */ - uint64_t seconds_after_thisupd = pday * (3600 * 24) + psec; - RESULT_ENSURE(seconds_after_thisupd < DEFAULT_OCSP_NEXT_UPDATE_PERIOD, S2N_ERR_CERT_EXPIRED); - } - - switch (status) { - case V_OCSP_CERTSTATUS_GOOD: - validator->state = OCSP_VALIDATED; - return S2N_RESULT_OK; - case V_OCSP_CERTSTATUS_REVOKED: - RESULT_BAIL(S2N_ERR_CERT_REVOKED); - default: - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } -#endif /* S2N_OCSP_STAPLING_SUPPORTED */ -} - -bool s2n_x509_validator_is_cert_chain_validated(const struct s2n_x509_validator *validator) -{ - return validator && (validator->state == VALIDATED || validator->state == OCSP_VALIDATED); -} - -int s2n_cert_validation_accept(struct s2n_cert_validation_info *info) -{ - POSIX_ENSURE_REF(info); - POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); - - info->finished = true; - info->accepted = true; - - return S2N_SUCCESS; -} - -int s2n_cert_validation_reject(struct s2n_cert_validation_info *info) -{ - POSIX_ENSURE_REF(info); - POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); - - info->finished = true; - info->accepted = false; - - return S2N_SUCCESS; -} +/* + * 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. + */ + +#if !defined(_MSC_VER) +#include +#else +#include +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_openssl_x509.h" +#include "crypto/s2n_pkey.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crl.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_result.h" +#include "utils/s2n_rfc5952.h" +#include "utils/s2n_safety.h" + +#if S2N_OCSP_STAPLING_SUPPORTED + #include +DEFINE_POINTER_CLEANUP_FUNC(OCSP_RESPONSE *, OCSP_RESPONSE_free); +DEFINE_POINTER_CLEANUP_FUNC(OCSP_BASICRESP *, OCSP_BASICRESP_free); + +#endif + +#ifndef X509_V_FLAG_PARTIAL_CHAIN + #define X509_V_FLAG_PARTIAL_CHAIN 0x80000 +#endif + +#define DEFAULT_MAX_CHAIN_DEPTH 7 +/* Time used by default for nextUpdate if none provided in OCSP: 1 hour since thisUpdate. */ +#define DEFAULT_OCSP_NEXT_UPDATE_PERIOD 3600 + +/* s2n's internal clock measures epoch-nanoseconds stored with a uint64_t. The + * maximum representable timestamp is Sunday, July 21, 2554. time_t measures + * epoch-seconds in a int64_t or int32_t (platform dependent). If time_t is an + * int32_t, the maximum representable timestamp is January 19, 2038. + * + * This means that converting from the internal clock to a time_t is not safe, + * because the internal clock might hold a value that is too large to represent + * in a time_t. This constant represents the largest internal clock value that + * can be safely represented as a time_t. + */ +#define MAX_32_TIMESTAMP_NANOS 2147483647 * ONE_SEC_IN_NANOS + +#define OSSL_VERIFY_CALLBACK_IGNORE_ERROR 1 + +DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(X509_CRL) *, sk_X509_CRL_free); +DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(GENERAL_NAME) *, GENERAL_NAMES_free); + +uint8_t s2n_x509_ocsp_stapling_supported(void) +{ + return S2N_OCSP_STAPLING_SUPPORTED; +} + +void s2n_x509_trust_store_init_empty(struct s2n_x509_trust_store *store) +{ + store->trust_store = NULL; +} + +uint8_t s2n_x509_trust_store_has_certs(struct s2n_x509_trust_store *store) +{ + return store->trust_store ? (uint8_t) 1 : (uint8_t) 0; +} + +int s2n_x509_trust_store_add_pem(struct s2n_x509_trust_store *store, const char *pem) +{ + POSIX_ENSURE_REF(store); + POSIX_ENSURE_REF(pem); + + if (!store->trust_store) { + store->trust_store = X509_STORE_new(); + POSIX_ENSURE_REF(store->trust_store); + } + + DEFER_CLEANUP(struct s2n_stuffer pem_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer der_out_stuffer = { 0 }, s2n_stuffer_free); + + POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&pem_in_stuffer, pem)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&der_out_stuffer, 2048)); + + do { + DEFER_CLEANUP(struct s2n_blob next_cert = { 0 }, s2n_free); + + POSIX_GUARD(s2n_stuffer_certificate_from_pem(&pem_in_stuffer, &der_out_stuffer)); + POSIX_GUARD(s2n_alloc(&next_cert, s2n_stuffer_data_available(&der_out_stuffer))); + POSIX_GUARD(s2n_stuffer_read(&der_out_stuffer, &next_cert)); + + const uint8_t *data = next_cert.data; + DEFER_CLEANUP(X509 *ca_cert = d2i_X509(NULL, &data, next_cert.size), X509_free_pointer); + S2N_ERROR_IF(ca_cert == NULL, S2N_ERR_DECODE_CERTIFICATE); + + if (!X509_STORE_add_cert(store->trust_store, ca_cert)) { + unsigned long error = ERR_get_error(); + POSIX_ENSURE(ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE, S2N_ERR_DECODE_CERTIFICATE); + } + } while (s2n_stuffer_data_available(&pem_in_stuffer)); + + return 0; +} + +int s2n_x509_trust_store_from_ca_file(struct s2n_x509_trust_store *store, const char *ca_pem_filename, const char *ca_dir) +{ + if (!store->trust_store) { + store->trust_store = X509_STORE_new(); + POSIX_ENSURE_REF(store->trust_store); + } + + int err_code = X509_STORE_load_locations(store->trust_store, ca_pem_filename, ca_dir); + if (!err_code) { + s2n_x509_trust_store_wipe(store); + POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); + } + + return 0; +} + +void s2n_x509_trust_store_wipe(struct s2n_x509_trust_store *store) +{ + if (store->trust_store) { + X509_STORE_free(store->trust_store); + store->trust_store = NULL; + store->loaded_system_certs = false; + } +} + +int s2n_x509_validator_init_no_x509_validation(struct s2n_x509_validator *validator) +{ + POSIX_ENSURE_REF(validator); + validator->trust_store = NULL; + validator->store_ctx = NULL; + validator->skip_cert_validation = 1; + validator->check_stapled_ocsp = 0; + validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; + validator->state = INIT; + validator->cert_chain_from_wire = sk_X509_new_null(); + validator->crl_lookup_list = NULL; + validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; + validator->cert_validation_cb_invoked = false; + + return 0; +} + +int s2n_x509_validator_init(struct s2n_x509_validator *validator, struct s2n_x509_trust_store *trust_store, uint8_t check_ocsp) +{ + POSIX_ENSURE_REF(trust_store); + validator->trust_store = trust_store; + validator->skip_cert_validation = 0; + validator->check_stapled_ocsp = check_ocsp; + validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; + validator->store_ctx = NULL; + if (validator->trust_store->trust_store) { + validator->store_ctx = X509_STORE_CTX_new(); + POSIX_ENSURE_REF(validator->store_ctx); + } + validator->cert_chain_from_wire = sk_X509_new_null(); + validator->state = INIT; + validator->crl_lookup_list = NULL; + validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; + validator->cert_validation_cb_invoked = false; + + return 0; +} + +static inline void wipe_cert_chain(STACK_OF(X509) *cert_chain) +{ + if (cert_chain) { + sk_X509_pop_free(cert_chain, X509_free); + } +} + +int s2n_x509_validator_wipe(struct s2n_x509_validator *validator) +{ + if (validator->store_ctx) { + X509_STORE_CTX_free(validator->store_ctx); + validator->store_ctx = NULL; + } + wipe_cert_chain(validator->cert_chain_from_wire); + validator->cert_chain_from_wire = NULL; + validator->trust_store = NULL; + validator->skip_cert_validation = 0; + validator->state = UNINIT; + validator->max_chain_depth = 0; + if (validator->crl_lookup_list) { + POSIX_GUARD_RESULT(s2n_array_free(validator->crl_lookup_list)); + validator->crl_lookup_list = NULL; + } + + return S2N_SUCCESS; +} + +int s2n_x509_validator_set_max_chain_depth(struct s2n_x509_validator *validator, uint16_t max_depth) +{ + POSIX_ENSURE_REF(validator); + S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); + + validator->max_chain_depth = max_depth; + return 0; +} + +static S2N_RESULT s2n_verify_host_information_san_entry(struct s2n_connection *conn, GENERAL_NAME *current_name, bool *san_found) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(current_name); + RESULT_ENSURE_REF(san_found); + + if (current_name->type == GEN_DNS || current_name->type == GEN_URI) { + *san_found = true; + + const char *name = (const char *) ASN1_STRING_data(current_name->d.ia5); + RESULT_ENSURE_REF(name); + int name_len = ASN1_STRING_length(current_name->d.ia5); + RESULT_ENSURE_GT(name_len, 0); + + RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; + } + + if (current_name->type == GEN_IPADD) { + *san_found = true; + + /* try to validate an IP address if it's in the subject alt name. */ + const unsigned char *ip_addr = current_name->d.iPAddress->data; + RESULT_ENSURE_REF(ip_addr); + int ip_addr_len = current_name->d.iPAddress->length; + RESULT_ENSURE_GT(ip_addr_len, 0); + + RESULT_STACK_BLOB(address, INET6_ADDRSTRLEN + 1, INET6_ADDRSTRLEN + 1); + + if (ip_addr_len == 4) { + RESULT_GUARD(s2n_inet_ntop(AF_INET, ip_addr, &address)); + } else if (ip_addr_len == 16) { + RESULT_GUARD(s2n_inet_ntop(AF_INET6, ip_addr, &address)); + } else { + /* we aren't able to parse this value so skip it */ + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } + + /* strlen should be safe here since we made sure we were null terminated AND that inet_ntop succeeded */ + const char *name = (const char *) address.data; + size_t name_len = strlen(name); + + RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; + } + + /* we don't understand this entry type so skip it */ + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); +} + +static S2N_RESULT s2n_verify_host_information_san(struct s2n_connection *conn, X509 *public_cert, bool *san_found) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(public_cert); + RESULT_ENSURE_REF(san_found); + + *san_found = false; + + DEFER_CLEANUP(STACK_OF(GENERAL_NAME) *names_list = NULL, GENERAL_NAMES_free_pointer); + names_list = X509_get_ext_d2i(public_cert, NID_subject_alt_name, NULL, NULL); + RESULT_ENSURE(names_list, S2N_ERR_CERT_UNTRUSTED); + + int n = sk_GENERAL_NAME_num(names_list); + RESULT_ENSURE(n > 0, S2N_ERR_CERT_UNTRUSTED); + + s2n_result result = S2N_RESULT_OK; + for (int i = 0; i < n; i++) { + GENERAL_NAME *current_name = sk_GENERAL_NAME_value(names_list, i); + + /* return success on the first entry that passes verification */ + result = s2n_verify_host_information_san_entry(conn, current_name, san_found); + if (s2n_result_is_ok(result)) { + return S2N_RESULT_OK; + } + } + + /* if an error was set by one of the entries, then just propagate the error from the last SAN entry call */ + RESULT_GUARD(result); + + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); +} + +static S2N_RESULT s2n_verify_host_information_common_name(struct s2n_connection *conn, X509 *public_cert, bool *cn_found) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(public_cert); + RESULT_ENSURE_REF(cn_found); + + X509_NAME *subject_name = X509_get_subject_name(public_cert); + RESULT_ENSURE(subject_name, S2N_ERR_CERT_UNTRUSTED); + + int curr_idx = -1; + while (true) { + int next_idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, curr_idx); + if (next_idx >= 0) { + curr_idx = next_idx; + } else { + break; + } + } + + RESULT_ENSURE(curr_idx >= 0, S2N_ERR_CERT_UNTRUSTED); + + ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, curr_idx)); + RESULT_ENSURE(common_name, S2N_ERR_CERT_UNTRUSTED); + + /* X520CommonName allows the following ANSI string types per RFC 5280 Appendix A.1 */ + RESULT_ENSURE(ASN1_STRING_type(common_name) == V_ASN1_TELETEXSTRING + || ASN1_STRING_type(common_name) == V_ASN1_PRINTABLESTRING + || ASN1_STRING_type(common_name) == V_ASN1_UNIVERSALSTRING + || ASN1_STRING_type(common_name) == V_ASN1_UTF8STRING + || ASN1_STRING_type(common_name) == V_ASN1_BMPSTRING, + S2N_ERR_CERT_UNTRUSTED); + + /* at this point we have a valid CN value */ + *cn_found = true; + + char peer_cn[255] = { 0 }; + int cn_len = ASN1_STRING_length(common_name); + RESULT_ENSURE_GT(cn_len, 0); + uint32_t len = (uint32_t) cn_len; + RESULT_ENSURE_LTE(len, s2n_array_len(peer_cn) - 1); + RESULT_CHECKED_MEMCPY(peer_cn, ASN1_STRING_data(common_name), len); + + /* According to https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4, + * the CN fallback only applies to fully qualified DNS domain names. + * + * An IP address is not a fully qualified DNS domain name. Per RFC 6125 + * section 6.2.1, IP reference identities must only be matched against + * iPAddress SAN entries, never against CN values. Reject the CN if it + * parses as an IPv4 or IPv6 address. + */ + unsigned char ip_buf[sizeof(struct in6_addr)] = { 0 }; + if (inet_pton(AF_INET, peer_cn, ip_buf) == 1 || inet_pton(AF_INET6, peer_cn, ip_buf) == 1) { + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } + + RESULT_ENSURE(conn->verify_host_fn(peer_cn, len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; +} + +/* + * For each name in the cert. Iterate them. Call the callback. If one returns true, then consider it validated, + * if none of them return true, the cert is considered invalid. + */ +static S2N_RESULT s2n_verify_host_information(struct s2n_connection *conn, X509 *public_cert) +{ + bool entry_found = false; + + /* Check SubjectAltNames before CommonName as per RFC 6125 6.4.4 */ + s2n_result result = s2n_verify_host_information_san(conn, public_cert, &entry_found); + + /* + *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 + *# As noted, a client MUST NOT seek a match for a reference identifier + *# of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, + *# URI-ID, or any application-specific identifier types supported by the + *# client. + */ + if (entry_found) { + return result; + } + + /* + *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 + *# Therefore, if and only if the presented identifiers do not include a + *# DNS-ID, SRV-ID, URI-ID, or any application-specific identifier types + *# supported by the client, then the client MAY as a last resort check + *# for a string whose form matches that of a fully qualified DNS domain + *# name in a Common Name field of the subject field (i.e., a CN-ID). + */ + result = s2n_verify_host_information_common_name(conn, public_cert, &entry_found); + if (entry_found) { + return result; + } + + /* make a null-terminated string in case the callback tries to use strlen */ + const char *name = ""; + size_t name_len = 0; + + /* at this point, we don't have anything to identify the certificate with so pass an empty string to the callback */ + RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, + struct s2n_blob *asn1_cert) +{ + uint32_t certificate_size = 0; + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(cert_chain_in_stuffer, &certificate_size)); + RESULT_ENSURE(certificate_size > 0, S2N_ERR_CERT_INVALID); + RESULT_ENSURE(certificate_size <= s2n_stuffer_data_available(cert_chain_in_stuffer), S2N_ERR_CERT_INVALID); + + asn1_cert->size = certificate_size; + asn1_cert->data = s2n_stuffer_raw_read(cert_chain_in_stuffer, certificate_size); + RESULT_ENSURE_REF(asn1_cert->data); + + return S2N_RESULT_OK; +} + +/** +* Validates that each certificate in a peer's cert chain contains only signature algorithms in a security policy's +* certificate_signatures_preference list. +*/ +S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(cert); + + const struct s2n_security_policy *security_policy = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); + + /** + * We only restrict the signature algorithm on the certificates in the + * peer's certificate chain if the certificate_signature_preferences field + * is set in the security policy. This is contrary to the RFC, which + * specifies that the signatures in the "signature_algorithms" extension + * apply to signatures in the certificate chain in certain scenarios, so RFC + * compliance would imply validating that the certificate chain signature + * algorithm matches one of the algorithms specified in the + * "signature_algorithms" extension. + * + *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.2 + *= type=exception + *= reason=not implemented due to lack of utility + *# If the client provided a "signature_algorithms" extension, then all + *# certificates provided by the server MUST be signed by a + *# hash/signature algorithm pair that appears in that extension. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.3 + *= type=exception + *= reason=not implemented due to lack of utility + *# If no "signature_algorithms_cert" extension is present, then the + *# "signature_algorithms" extension also applies to signatures appearing in + *# certificates. + */ + struct s2n_cert_info info = { 0 }; + RESULT_GUARD(s2n_openssl_x509_get_cert_info(cert, &info)); + + bool certificate_preferences_defined = security_policy->certificate_signature_preferences != NULL + || security_policy->certificate_key_preferences != NULL; + if (certificate_preferences_defined && !info.self_signed && conn->actual_protocol_version == S2N_TLS13) { + /* Ensure that the certificate signature does not use SHA-1. While this check + * would ideally apply to all connections, we only enforce it when certificate + * preferences exist to stay backwards compatible. + */ + RESULT_ENSURE(info.signature_digest_nid != NID_sha1, S2N_ERR_CERT_UNTRUSTED); + } + + if (!info.self_signed) { + RESULT_GUARD(s2n_security_policy_validate_cert_signature(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); + } + RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_get_validated_cert_chain(const struct s2n_x509_validator *validator, + struct s2n_validated_cert_chain *validated_cert_chain) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validated_cert_chain); + + RESULT_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_INVALID_CERT_STATE); + RESULT_ENSURE_REF(validator->store_ctx); + +#if S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN + /* X509_STORE_CTX_get0_chain is used when available, since it returns a pointer to the + * validated cert chain in the X509_STORE_CTX, avoiding an allocation/copy. + */ + validated_cert_chain->stack = X509_STORE_CTX_get0_chain(validator->store_ctx); +#else + /* Otherwise, X509_STORE_CTX_get1_chain is used instead, which allocates a new cert chain. */ + validated_cert_chain->stack = X509_STORE_CTX_get1_chain(validator->store_ctx); +#endif + + RESULT_ENSURE_REF(validated_cert_chain->stack); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_x509_validator_validated_cert_chain_free(struct s2n_validated_cert_chain *validated_cert_chain) +{ + RESULT_ENSURE_REF(validated_cert_chain); + +#if !S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN + /* When X509_STORE_CTX_get0_chain isn't supported, X509_STORE_CTX_get1_chain is used instead, + * which allocates a new cert chain that is owned by s2n-tls and MUST be freed. + * + * X509_STORE_CTX_get0_chain returns a pointer to the cert chain within the X509_STORE_CTX, + * which is NOT owned by s2n-tls and MUST NOT be manually freed. + */ + RESULT_GUARD(s2n_openssl_x509_stack_pop_free(&validated_cert_chain->stack)); +#endif + + /* Even though the cert chain reference is still valid in the case that get0_chain is used, set + * it to null for consistency with the get1_chain case. + */ + validated_cert_chain->stack = NULL; + + return S2N_RESULT_OK; +} + +/* Validates that the root certificate uses a key allowed by the security policy + * certificate preferences. + */ +static S2N_RESULT s2n_x509_validator_check_root_cert(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(conn); + + const struct s2n_security_policy *security_policy = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); + RESULT_ENSURE_REF(security_policy); + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + STACK_OF(X509) *cert_chain = validated_cert_chain.stack; + RESULT_ENSURE_REF(cert_chain); + + const int certs_in_chain = sk_X509_num(cert_chain); + RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_CERT_UNTRUSTED); + X509 *root = sk_X509_value(cert_chain, certs_in_chain - 1); + RESULT_ENSURE_REF(root); + + struct s2n_cert_info info = { 0 }; + RESULT_GUARD(s2n_openssl_x509_get_cert_info(root, &info)); + + RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_read_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len) +{ + RESULT_ENSURE(validator->skip_cert_validation || s2n_x509_trust_store_has_certs(validator->trust_store), S2N_ERR_CERT_UNTRUSTED); + RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); + + struct s2n_blob cert_chain_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); + DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); + + RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); + + while (s2n_stuffer_data_available(&cert_chain_in_stuffer) + && sk_X509_num(validator->cert_chain_from_wire) < validator->max_chain_depth) { + struct s2n_blob asn1_cert = { 0 }; + RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); + + /* We only do the trailing byte validation when parsing the leaf cert to + * match historical s2n-tls behavior. + */ + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + if (sk_X509_num(validator->cert_chain_from_wire) == 0) { + RESULT_GUARD(s2n_openssl_x509_parse(&asn1_cert, &cert)); + } else { + RESULT_GUARD(s2n_openssl_x509_parse_without_length_validation(&asn1_cert, &cert)); + } + + if (!validator->skip_cert_validation) { + RESULT_GUARD(s2n_x509_validator_check_cert_preferences(conn, cert)); + } + + /* add the cert to the chain */ + RESULT_ENSURE(sk_X509_push(validator->cert_chain_from_wire, cert) > 0, + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + /* After the cert is added to cert_chain_from_wire, it will be freed + * with the call to s2n_x509_validator_wipe. We disable the cleanup + * function since cleanup is no longer "owned" by cert. + */ + ZERO_TO_DISABLE_DEFER_CLEANUP(cert); + + /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ + if (conn->actual_protocol_version >= S2N_TLS13) { + s2n_parsed_extensions_list parsed_extensions_list = { 0 }; + RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); + } + } + + /* if this occurred we exceeded validator->max_chain_depth */ + RESULT_ENSURE(validator->skip_cert_validation || s2n_stuffer_data_available(&cert_chain_in_stuffer) == 0, + S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); + RESULT_ENSURE(sk_X509_num(validator->cert_chain_from_wire) > 0, S2N_ERR_NO_CERT_FOUND); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_process_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len) +{ + RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); + + RESULT_GUARD(s2n_x509_validator_read_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); + + if (validator->skip_cert_validation) { + return S2N_RESULT_OK; + } + + X509 *leaf = sk_X509_value(validator->cert_chain_from_wire, 0); + RESULT_ENSURE_REF(leaf); + + if (conn->verify_host_fn) { + RESULT_GUARD(s2n_verify_host_information(conn, leaf)); + } + + RESULT_GUARD_OSSL(X509_STORE_CTX_init(validator->store_ctx, validator->trust_store->trust_store, leaf, + validator->cert_chain_from_wire), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + if (conn->config->crl_lookup_cb) { + RESULT_GUARD(s2n_crl_invoke_lookup_callbacks(conn, validator)); + RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); + } + + validator->state = READY_TO_VERIFY; + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_set_no_check_time_flag(struct s2n_x509_validator *validator) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validator->store_ctx); + + X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); + RESULT_ENSURE_REF(param); + +#ifdef S2N_LIBCRYPTO_SUPPORTS_FLAG_NO_CHECK_TIME + RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_NO_CHECK_TIME), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); +#else + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +#endif + + return S2N_RESULT_OK; +} + +int s2n_disable_time_validation_ossl_verify_callback(int default_ossl_ret, X509_STORE_CTX *ctx) +{ + int err = X509_STORE_CTX_get_error(ctx); + switch (err) { + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + return OSSL_VERIFY_CALLBACK_IGNORE_ERROR; + default: + break; + } + + /* If CRL validation is enabled, setting the time validation verify callback will override the + * CRL verify callback. The CRL verify callback is manually triggered to work around this + * issue. + * + * The CRL verify callback ignores validation errors exclusively for CRL timestamp fields. So, + * if CRL validation isn't enabled, the CRL verify callback is a no-op. + */ + return s2n_crl_ossl_verify_callback(default_ossl_ret, ctx); +} + +static S2N_RESULT s2n_x509_validator_disable_time_validation(struct s2n_connection *conn, + struct s2n_x509_validator *validator) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validator->store_ctx); + + /* Setting an X509_STORE verify callback is not recommended with AWS-LC: + * https://github.com/aws/aws-lc/blob/aa90e509f2e940916fbe9fdd469a4c90c51824f6/include/openssl/x509.h#L2980-L2990 + * + * If the libcrypto supports the ability to disable time validation with an X509_VERIFY_PARAM + * NO_CHECK_TIME flag, this method is preferred. + * + * However, older versions of AWS-LC and OpenSSL 1.0.2 do not support this flag. In this case, + * an X509_STORE verify callback is used. This is acceptable in older versions of AWS-LC + * because the versions are fixed, and updates to AWS-LC will not break the callback + * implementation. + */ + if (s2n_libcrypto_supports_flag_no_check_time()) { + RESULT_GUARD(s2n_x509_validator_set_no_check_time_flag(validator)); + } else { + X509_STORE_CTX_set_verify_cb(validator->store_ctx, + s2n_disable_time_validation_ossl_verify_callback); + } + + return S2N_RESULT_OK; +} + +int s2n_no_op_verify_custom_crit_oids_cb(X509_STORE_CTX *ctx, X509 *x509, STACK_OF(ASN1_OBJECT) *oids) +{ + return 1; +} + +static S2N_RESULT s2n_x509_validator_add_custom_extensions(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validator->store_ctx); + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + if (conn->config->custom_x509_extension_oids) { +#if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_OID + size_t custom_oid_count = sk_ASN1_OBJECT_num(conn->config->custom_x509_extension_oids); + for (size_t i = 0; i < custom_oid_count; i++) { + ASN1_OBJECT *critical_oid = sk_ASN1_OBJECT_value(conn->config->custom_x509_extension_oids, i); + RESULT_ENSURE_REF(critical_oid); + RESULT_GUARD_OSSL(X509_STORE_CTX_add_custom_crit_oid(validator->store_ctx, critical_oid), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + } + /* To enable AWS-LC accepting custom extensions, an X509_STORE_CTX_verify_crit_oids_cb must be set. + * See https://github.com/aws/aws-lc/blob/f0b4afedd7d45fc2517643d890b654856c57f994/include/openssl/x509.h#L2913-L2918. + * + * The `X509_STORE_CTX_verify_crit_oids_cb` callback can be used to implement the validation for the + * custom certificate extensions. However, s2n-tls consumers are expected to implement this validation + * in the `s2n_cert_validation_callback` instead. So, a no-op callback is provided to AWS-LC. + */ + X509_STORE_CTX_set_verify_crit_oids(validator->store_ctx, s2n_no_op_verify_custom_crit_oids_cb); +#else + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +#endif + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_verify_intent_for_cert(struct s2n_connection *conn, X509 *cert, bool is_leaf) +{ + RESULT_ENSURE_REF(cert); + + /* The X509_PURPOSE values indicate the purpose that certificates must specify. For servers, + * received client certificates MUST have a TLS client purpose. For clients, received server + * certificates MUST have a TLS server purpose. + */ + int purpose = X509_PURPOSE_SSL_CLIENT; + if (conn->mode == S2N_CLIENT) { + purpose = X509_PURPOSE_SSL_SERVER; + } + + RESULT_GUARD_OSSL(X509_check_purpose(cert, purpose, !is_leaf), S2N_ERR_CERT_INTENT_INVALID); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_verify_intent(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + if (conn->config->disable_x509_intent_verification) { + return S2N_RESULT_OK; + } + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + + int cert_count = sk_X509_num(validated_cert_chain.stack); + RESULT_ENSURE_GT(cert_count, 0); + + /* The validated cert chain returned from the libcrypto includes the trust anchor. The trust + * anchor is omitted from intent verification since its TLS intent is implicitly indicated by + * its presence in the s2n-tls trust store. + */ + cert_count -= 1; + + for (int i = 0; i < cert_count; i++) { + X509 *cert = sk_X509_value(validated_cert_chain.stack, i); + RESULT_ENSURE_REF(cert); + + bool is_leaf = (i == 0); + RESULT_GUARD(s2n_x509_validator_verify_intent_for_cert(conn, cert, is_leaf)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_verify_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE(validator->state == READY_TO_VERIFY, S2N_ERR_INVALID_CERT_STATE); + + X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); + X509_VERIFY_PARAM_set_depth(param, validator->max_chain_depth); + + DEFER_CLEANUP(STACK_OF(X509_CRL) *crl_stack = NULL, sk_X509_CRL_free_pointer); + + if (conn->config->crl_lookup_cb) { + X509_STORE_CTX_set_verify_cb(validator->store_ctx, s2n_crl_ossl_verify_callback); + + crl_stack = sk_X509_CRL_new_null(); + RESULT_GUARD(s2n_crl_get_crls_from_lookup_list(validator, crl_stack)); + + /* Set the CRL list that the libcrypto will use to validate certificates with */ + X509_STORE_CTX_set0_crls(validator->store_ctx, crl_stack); + + /* Enable CRL validation for certificates in X509_verify_cert */ + RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + /* Enable CRL validation for all certificates, not just the leaf */ + RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK_ALL), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + } + + /* Disabling time validation may set a NO_CHECK_TIME flag on the X509_STORE_CTX. Calling + * X509_STORE_CTX_set_time will override this flag. To prevent this, X509_STORE_CTX_set_time is + * only called if time validation is enabled. + */ + if (conn->config->disable_x509_time_validation) { + RESULT_GUARD(s2n_x509_validator_disable_time_validation(conn, validator)); + } else { + uint64_t current_sys_time = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time)); + if (sizeof(time_t) == 4) { + /* cast value to uint64_t to prevent overflow errors */ + RESULT_ENSURE_LTE(current_sys_time, (uint64_t) MAX_32_TIMESTAMP_NANOS); + } + + /* this wants seconds not nanoseconds */ + time_t current_time = (time_t) (current_sys_time / ONE_SEC_IN_NANOS); + X509_STORE_CTX_set_time(validator->store_ctx, 0, current_time); + } + + /* It's assumed that if a valid certificate chain is received with an issuer that's present in + * the trust store, the certificate chain should be trusted. This should be the case even if + * the issuer in the trust store isn't a root certificate. Setting the PARTIAL_CHAIN flag + * allows the libcrypto to trust certificates in the trust store that aren't root certificates. + */ + X509_STORE_CTX_set_flags(validator->store_ctx, X509_V_FLAG_PARTIAL_CHAIN); + + RESULT_GUARD(s2n_x509_validator_add_custom_extensions(validator, conn)); + + int verify_ret = X509_verify_cert(validator->store_ctx); + if (verify_ret <= 0) { + int ossl_error = X509_STORE_CTX_get_error(validator->store_ctx); + switch (ossl_error) { + case X509_V_ERR_CERT_NOT_YET_VALID: + RESULT_BAIL(S2N_ERR_CERT_NOT_YET_VALID); + case X509_V_ERR_CERT_HAS_EXPIRED: + RESULT_BAIL(S2N_ERR_CERT_EXPIRED); + case X509_V_ERR_CERT_REVOKED: + RESULT_BAIL(S2N_ERR_CERT_REVOKED); + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_DIFFERENT_CRL_SCOPE: + RESULT_BAIL(S2N_ERR_CRL_LOOKUP_FAILED); + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + RESULT_BAIL(S2N_ERR_CRL_SIGNATURE); + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + RESULT_BAIL(S2N_ERR_CRL_ISSUER); + case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: + RESULT_BAIL(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); + case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: + RESULT_BAIL(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION); + default: + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } + } + + validator->state = VALIDATED; + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_parse_leaf_certificate_extensions(struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len, + s2n_parsed_extensions_list *first_certificate_extensions) +{ + /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ + RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); + + struct s2n_blob cert_chain_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); + DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); + + RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); + + struct s2n_blob asn1_cert = { 0 }; + RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); + + s2n_parsed_extensions_list parsed_extensions_list = { 0 }; + RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); + *first_certificate_extensions = parsed_extensions_list; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_validate_cert_chain_pre_cb(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + switch (validator->state) { + case INIT: + break; + case AWAITING_CRL_CALLBACK: + RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); + break; + default: + RESULT_BAIL(S2N_ERR_INVALID_CERT_STATE); + } + + if (validator->state == INIT) { + RESULT_GUARD(s2n_x509_validator_process_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); + } + + if (validator->state == READY_TO_VERIFY) { + RESULT_GUARD(s2n_x509_validator_verify_cert_chain(validator, conn)); + RESULT_GUARD(s2n_x509_validator_verify_intent(validator, conn)); + RESULT_GUARD(s2n_x509_validator_check_root_cert(validator, conn)); + } + + if (conn->actual_protocol_version >= S2N_TLS13) { + /* Only process certificate extensions received in the first certificate. Extensions received in all other + * certificates are ignored. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.2 + *# If an extension applies to the entire chain, it SHOULD be included in + *# the first CertificateEntry. + */ + s2n_parsed_extensions_list first_certificate_extensions = { 0 }; + RESULT_GUARD(s2n_x509_validator_parse_leaf_certificate_extensions(conn, cert_chain_in, cert_chain_len, &first_certificate_extensions)); + RESULT_GUARD_POSIX(s2n_extension_list_process(S2N_EXTENSION_LIST_CERTIFICATE, conn, &first_certificate_extensions)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_handle_cert_validation_callback_result(struct s2n_x509_validator *validator) +{ + RESULT_ENSURE_REF(validator); + + if (!validator->cert_validation_info.finished) { + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + + RESULT_ENSURE(validator->cert_validation_info.accepted, S2N_ERR_CERT_REJECTED); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_pkey_type *pkey_type, struct s2n_pkey *public_key_out) +{ + RESULT_ENSURE_REF(validator); + + if (validator->cert_validation_cb_invoked) { + RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); + } else { + RESULT_GUARD(s2n_x509_validator_validate_cert_chain_pre_cb(validator, conn, cert_chain_in, cert_chain_len)); + + if (conn->config->cert_validation_cb) { + RESULT_ENSURE(conn->config->cert_validation_cb(conn, &(validator->cert_validation_info), conn->config->cert_validation_ctx) == S2N_SUCCESS, + S2N_ERR_CANCELLED); + validator->cert_validation_cb_invoked = true; + RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); + } + } + + /* retrieve information from leaf cert */ + RESULT_ENSURE_GT(sk_X509_num(validator->cert_chain_from_wire), 0); + X509 *leaf_cert = sk_X509_value(validator->cert_chain_from_wire, 0); + RESULT_ENSURE_REF(leaf_cert); + DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); + RESULT_GUARD(s2n_pkey_from_x509(leaf_cert, &public_key, pkey_type)); + + *public_key_out = public_key; + + /* Reset the old struct, so we don't clean up public_key_out */ + ZERO_TO_DISABLE_DEFER_CLEANUP(public_key); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_validate_cert_stapled_ocsp_response(struct s2n_x509_validator *validator, + struct s2n_connection *conn, const uint8_t *ocsp_response_raw, uint32_t ocsp_response_length) +{ + if (validator->skip_cert_validation || !validator->check_stapled_ocsp) { + validator->state = OCSP_VALIDATED; + return S2N_RESULT_OK; + } + + RESULT_ENSURE(validator->state == VALIDATED, S2N_ERR_INVALID_CERT_STATE); + +#if !S2N_OCSP_STAPLING_SUPPORTED + /* Default to safety */ + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); +#else + + RESULT_ENSURE_REF(ocsp_response_raw); + + DEFER_CLEANUP(OCSP_RESPONSE *ocsp_response = d2i_OCSP_RESPONSE(NULL, &ocsp_response_raw, ocsp_response_length), + OCSP_RESPONSE_free_pointer); + RESULT_ENSURE(ocsp_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); + + int ocsp_status = OCSP_response_status(ocsp_response); + RESULT_ENSURE(ocsp_status == OCSP_RESPONSE_STATUS_SUCCESSFUL, S2N_ERR_CERT_UNTRUSTED); + + DEFER_CLEANUP(OCSP_BASICRESP *basic_response = OCSP_response_get1_basic(ocsp_response), OCSP_BASICRESP_free_pointer); + RESULT_ENSURE(basic_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + STACK_OF(X509) *cert_chain = validated_cert_chain.stack; + RESULT_ENSURE_REF(cert_chain); + + const int certs_in_chain = sk_X509_num(cert_chain); + RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_NO_CERT_FOUND); + + /* leaf is the top: not the bottom. */ + X509 *subject = sk_X509_value(cert_chain, 0); + X509 *issuer = NULL; + /* find the issuer in the chain. If it's not there. Fail everything. */ + for (int i = 0; i < certs_in_chain; ++i) { + X509 *issuer_candidate = sk_X509_value(cert_chain, i); + const int issuer_value = X509_check_issued(issuer_candidate, subject); + + if (issuer_value == X509_V_OK) { + issuer = issuer_candidate; + break; + } + } + RESULT_ENSURE(issuer != NULL, S2N_ERR_CERT_UNTRUSTED); + + /* Important: this checks that the stapled ocsp response CAN be verified, not that it has been verified. */ + const int ocsp_verify_res = OCSP_basic_verify(basic_response, cert_chain, validator->trust_store->trust_store, 0); + RESULT_GUARD_OSSL(ocsp_verify_res, S2N_ERR_CERT_UNTRUSTED); + + /* do the crypto checks on the response.*/ + int status = 0; + int reason = 0; + + /* SHA-1 is the only supported hash algorithm for the CertID due to its established use in + * OCSP responders. + */ + OCSP_CERTID *cert_id = OCSP_cert_to_id(EVP_sha1(), subject, issuer); + RESULT_ENSURE_REF(cert_id); + + /** + *= https://www.rfc-editor.org/rfc/rfc6960#section-2.4 + *# + *# thisUpdate The most recent time at which the status being + *# indicated is known by the responder to have been + *# correct. + *# + *# nextUpdate The time at or before which newer information will be + *# available about the status of the certificate. + **/ + ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; + /* Actual verification of the response */ + const int ocsp_resp_find_status_res = OCSP_resp_find_status(basic_response, cert_id, &status, &reason, &revtime, &thisupd, &nextupd); + OCSP_CERTID_free(cert_id); + RESULT_GUARD_OSSL(ocsp_resp_find_status_res, S2N_ERR_CERT_UNTRUSTED); + + uint64_t current_sys_time_nanoseconds = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time_nanoseconds)); + if (sizeof(time_t) == 4) { + /* cast value to uint64_t to prevent overflow errors */ + RESULT_ENSURE_LTE(current_sys_time_nanoseconds, (uint64_t) MAX_32_TIMESTAMP_NANOS); + } + /* convert the current_sys_time (which is in nanoseconds) to seconds */ + time_t current_sys_time_seconds = (time_t) (current_sys_time_nanoseconds / ONE_SEC_IN_NANOS); + + DEFER_CLEANUP(ASN1_GENERALIZEDTIME *current_sys_time = ASN1_GENERALIZEDTIME_set(NULL, current_sys_time_seconds), s2n_openssl_asn1_time_free_pointer); + RESULT_ENSURE_REF(current_sys_time); + + /** + * It is fine to use ASN1_TIME functions with ASN1_GENERALIZEDTIME structures + * From openssl documentation: + * It is recommended that functions starting with ASN1_TIME be used instead + * of those starting with ASN1_UTCTIME or ASN1_GENERALIZEDTIME. The + * functions starting with ASN1_UTCTIME and ASN1_GENERALIZEDTIME act only on + * that specific time format. The functions starting with ASN1_TIME will + * operate on either format. + * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_generalizedtime.html + * + * ASN1_TIME_compare has a much nicer API, but is not available in Openssl + * 1.0.1, so we use ASN1_TIME_diff. + */ + int pday = 0; + int psec = 0; + RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, thisupd, current_sys_time), S2N_ERR_CERT_UNTRUSTED); + /* ensure that current_time is after or the same as "this update" */ + RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_INVALID); + + /* ensure that current_time is before or the same as "next update" */ + if (nextupd) { + RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, current_sys_time, nextupd), S2N_ERR_CERT_UNTRUSTED); + RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_EXPIRED); + } else { + /** + * if nextupd isn't present, assume that nextupd is + * DEFAULT_OCSP_NEXT_UPDATE_PERIOD after thisupd. This means that if the + * current time is more than DEFAULT_OCSP_NEXT_UPDATE_PERIOD + * seconds ahead of thisupd, we consider it invalid. We already compared + * current_sys_time to thisupd, so reuse those values + */ + uint64_t seconds_after_thisupd = pday * (3600 * 24) + psec; + RESULT_ENSURE(seconds_after_thisupd < DEFAULT_OCSP_NEXT_UPDATE_PERIOD, S2N_ERR_CERT_EXPIRED); + } + + switch (status) { + case V_OCSP_CERTSTATUS_GOOD: + validator->state = OCSP_VALIDATED; + return S2N_RESULT_OK; + case V_OCSP_CERTSTATUS_REVOKED: + RESULT_BAIL(S2N_ERR_CERT_REVOKED); + default: + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ +} + +bool s2n_x509_validator_is_cert_chain_validated(const struct s2n_x509_validator *validator) +{ + return validator && (validator->state == VALIDATED || validator->state == OCSP_VALIDATED); +} + +int s2n_cert_validation_accept(struct s2n_cert_validation_info *info) +{ + POSIX_ENSURE_REF(info); + POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); + + info->finished = true; + info->accepted = true; + + return S2N_SUCCESS; +} + +int s2n_cert_validation_reject(struct s2n_cert_validation_info *info) +{ + POSIX_ENSURE_REF(info); + POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); + + info->finished = true; + info->accepted = false; + + return S2N_SUCCESS; +} diff --git a/utils/s2n_array.c b/utils/s2n_array.c index dd2d9e94613..f43d60fb652 100644 --- a/utils/s2n_array.c +++ b/utils/s2n_array.c @@ -1,221 +1,222 @@ -/* - * 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 "utils/s2n_array.h" - -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_array_validate(const struct s2n_array *array) -{ - uint32_t mem_size = 0; - RESULT_ENSURE_REF(array); - RESULT_GUARD(s2n_blob_validate(&array->mem)); - RESULT_ENSURE_NE(array->element_size, 0); - RESULT_GUARD_POSIX(s2n_mul_overflow(array->len, array->element_size, &mem_size)); - RESULT_ENSURE_GTE(array->mem.size, mem_size); - RESULT_ENSURE(S2N_IMPLIES(array->mem.size, array->mem.growable), S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_array_enlarge(struct s2n_array *array, uint32_t capacity) -{ - RESULT_ENSURE_REF(array); - - /* Acquire the memory */ - uint32_t mem_needed = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, capacity, &mem_needed)); - RESULT_GUARD_POSIX(s2n_realloc(&array->mem, mem_needed)); - - /* Zero the extened part */ - uint32_t array_elements_size = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, array->len, &array_elements_size)); - RESULT_CHECKED_MEMSET(array->mem.data + array_elements_size, 0, array->mem.size - array_elements_size); - RESULT_POSTCONDITION(s2n_array_validate(array)); - return S2N_RESULT_OK; -} - -struct s2n_array *s2n_array_new(uint32_t element_size) -{ - struct s2n_array *array = s2n_array_new_with_capacity(element_size, S2N_INITIAL_ARRAY_SIZE); - PTR_ENSURE_REF(array); - - return array; -} - -struct s2n_array *s2n_array_new_with_capacity(uint32_t element_size, uint32_t capacity) -{ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_array))); - - DEFER_CLEANUP(struct s2n_array *array = (void *) mem.data, s2n_array_free_p); - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - PTR_GUARD_RESULT(s2n_array_init_with_capacity(array, element_size, capacity)); - - struct s2n_array *array_ret = array; - ZERO_TO_DISABLE_DEFER_CLEANUP(array); - - return array_ret; -} - -S2N_RESULT s2n_array_init(struct s2n_array *array, uint32_t element_size) -{ - RESULT_ENSURE_REF(array); - - RESULT_GUARD(s2n_array_init_with_capacity(array, element_size, 0)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_init_with_capacity(struct s2n_array *array, uint32_t element_size, uint32_t capacity) -{ - RESULT_ENSURE_REF(array); - - *array = (struct s2n_array){ .element_size = element_size }; - - RESULT_GUARD(s2n_array_enlarge(array, capacity)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_pushback(struct s2n_array *array, void **element) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_REF(element); - return s2n_array_insert(array, array->len, element); -} - -S2N_RESULT s2n_array_get(struct s2n_array *array, uint32_t idx, void **element) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_REF(element); - RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); - *element = array->mem.data + (array->element_size * idx); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_insert_and_copy(struct s2n_array *array, uint32_t idx, void *element) -{ - void *insert_location = NULL; - RESULT_GUARD(s2n_array_insert(array, idx, &insert_location)); - RESULT_CHECKED_MEMCPY(insert_location, element, array->element_size); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_insert(struct s2n_array *array, uint32_t idx, void **element) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_REF(element); - /* index == len is ok since we're about to add one element */ - RESULT_ENSURE(idx <= array->len, S2N_ERR_ARRAY_INDEX_OOB); - - /* We are about to add one more element to the array. Add capacity if necessary */ - uint32_t current_capacity = 0; - RESULT_GUARD(s2n_array_capacity(array, ¤t_capacity)); - - if (array->len >= current_capacity) { - /* Enlarge the array */ - uint32_t new_capacity = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(current_capacity, 2, &new_capacity)); - new_capacity = S2N_MAX(new_capacity, S2N_INITIAL_ARRAY_SIZE); - RESULT_GUARD(s2n_array_enlarge(array, new_capacity)); - } - - /* If we are adding at an existing index, slide everything down. */ - if (idx < array->len) { - uint32_t size = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx, array->element_size, &size)); - memmove(array->mem.data + array->element_size * (idx + 1), - array->mem.data + array->element_size * idx, - size); - } - - *element = array->mem.data + array->element_size * idx; - array->len++; - - RESULT_POSTCONDITION(s2n_array_validate(array)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_remove(struct s2n_array *array, uint32_t idx) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); - - /* If the removed element is the last one, no need to move anything. - * Otherwise, shift everything down */ - if (idx != array->len - 1) { - uint32_t size = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx - 1, array->element_size, &size)); - memmove(array->mem.data + array->element_size * idx, - array->mem.data + array->element_size * (idx + 1), - size); - } - array->len--; - - /* After shifting, zero the last element */ - RESULT_CHECKED_MEMSET(array->mem.data + array->element_size * array->len, - 0, - array->element_size); - - RESULT_POSTCONDITION(s2n_array_validate(array)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_num_elements(struct s2n_array *array, uint32_t *len) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_MUT(len); - - *len = array->len; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_capacity(struct s2n_array *array, uint32_t *capacity) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_MUT(capacity); - - *capacity = array->mem.size / array->element_size; - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_array_free_p(struct s2n_array **parray) -{ - RESULT_ENSURE_REF(parray); - - struct s2n_array *array = *parray; - if (array == NULL) { - return S2N_RESULT_OK; - } - - /* Free the elements */ - RESULT_GUARD_POSIX(s2n_free(&array->mem)); - - /* And finally the array */ - RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) parray, sizeof(struct s2n_array))); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_free(struct s2n_array *array) -{ - RESULT_ENSURE_REF(array); - return s2n_array_free_p(&array); -} +#include "utils/s2n_prelude.h" +/* + * 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 "utils/s2n_array.h" + +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_array_validate(const struct s2n_array *array) +{ + uint32_t mem_size = 0; + RESULT_ENSURE_REF(array); + RESULT_GUARD(s2n_blob_validate(&array->mem)); + RESULT_ENSURE_NE(array->element_size, 0); + RESULT_GUARD_POSIX(s2n_mul_overflow(array->len, array->element_size, &mem_size)); + RESULT_ENSURE_GTE(array->mem.size, mem_size); + RESULT_ENSURE(S2N_IMPLIES(array->mem.size, array->mem.growable), S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_array_enlarge(struct s2n_array *array, uint32_t capacity) +{ + RESULT_ENSURE_REF(array); + + /* Acquire the memory */ + uint32_t mem_needed = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, capacity, &mem_needed)); + RESULT_GUARD_POSIX(s2n_realloc(&array->mem, mem_needed)); + + /* Zero the extened part */ + uint32_t array_elements_size = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, array->len, &array_elements_size)); + RESULT_CHECKED_MEMSET(array->mem.data + array_elements_size, 0, array->mem.size - array_elements_size); + RESULT_POSTCONDITION(s2n_array_validate(array)); + return S2N_RESULT_OK; +} + +struct s2n_array *s2n_array_new(uint32_t element_size) +{ + struct s2n_array *array = s2n_array_new_with_capacity(element_size, S2N_INITIAL_ARRAY_SIZE); + PTR_ENSURE_REF(array); + + return array; +} + +struct s2n_array *s2n_array_new_with_capacity(uint32_t element_size, uint32_t capacity) +{ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_array))); + + DEFER_CLEANUP(struct s2n_array *array = (void *) mem.data, s2n_array_free_p); + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + PTR_GUARD_RESULT(s2n_array_init_with_capacity(array, element_size, capacity)); + + struct s2n_array *array_ret = array; + ZERO_TO_DISABLE_DEFER_CLEANUP(array); + + return array_ret; +} + +S2N_RESULT s2n_array_init(struct s2n_array *array, uint32_t element_size) +{ + RESULT_ENSURE_REF(array); + + RESULT_GUARD(s2n_array_init_with_capacity(array, element_size, 0)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_init_with_capacity(struct s2n_array *array, uint32_t element_size, uint32_t capacity) +{ + RESULT_ENSURE_REF(array); + + *array = (struct s2n_array){ .element_size = element_size }; + + RESULT_GUARD(s2n_array_enlarge(array, capacity)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_pushback(struct s2n_array *array, void **element) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_REF(element); + return s2n_array_insert(array, array->len, element); +} + +S2N_RESULT s2n_array_get(struct s2n_array *array, uint32_t idx, void **element) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_REF(element); + RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); + *element = array->mem.data + (array->element_size * idx); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_insert_and_copy(struct s2n_array *array, uint32_t idx, void *element) +{ + void *insert_location = NULL; + RESULT_GUARD(s2n_array_insert(array, idx, &insert_location)); + RESULT_CHECKED_MEMCPY(insert_location, element, array->element_size); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_insert(struct s2n_array *array, uint32_t idx, void **element) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_REF(element); + /* index == len is ok since we're about to add one element */ + RESULT_ENSURE(idx <= array->len, S2N_ERR_ARRAY_INDEX_OOB); + + /* We are about to add one more element to the array. Add capacity if necessary */ + uint32_t current_capacity = 0; + RESULT_GUARD(s2n_array_capacity(array, ¤t_capacity)); + + if (array->len >= current_capacity) { + /* Enlarge the array */ + uint32_t new_capacity = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(current_capacity, 2, &new_capacity)); + new_capacity = S2N_MAX(new_capacity, S2N_INITIAL_ARRAY_SIZE); + RESULT_GUARD(s2n_array_enlarge(array, new_capacity)); + } + + /* If we are adding at an existing index, slide everything down. */ + if (idx < array->len) { + uint32_t size = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx, array->element_size, &size)); + memmove(array->mem.data + array->element_size * (idx + 1), + array->mem.data + array->element_size * idx, + size); + } + + *element = array->mem.data + array->element_size * idx; + array->len++; + + RESULT_POSTCONDITION(s2n_array_validate(array)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_remove(struct s2n_array *array, uint32_t idx) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); + + /* If the removed element is the last one, no need to move anything. + * Otherwise, shift everything down */ + if (idx != array->len - 1) { + uint32_t size = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx - 1, array->element_size, &size)); + memmove(array->mem.data + array->element_size * idx, + array->mem.data + array->element_size * (idx + 1), + size); + } + array->len--; + + /* After shifting, zero the last element */ + RESULT_CHECKED_MEMSET(array->mem.data + array->element_size * array->len, + 0, + array->element_size); + + RESULT_POSTCONDITION(s2n_array_validate(array)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_num_elements(struct s2n_array *array, uint32_t *len) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_MUT(len); + + *len = array->len; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_capacity(struct s2n_array *array, uint32_t *capacity) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_MUT(capacity); + + *capacity = array->mem.size / array->element_size; + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_array_free_p(struct s2n_array **parray) +{ + RESULT_ENSURE_REF(parray); + + struct s2n_array *array = *parray; + if (array == NULL) { + return S2N_RESULT_OK; + } + + /* Free the elements */ + RESULT_GUARD_POSIX(s2n_free(&array->mem)); + + /* And finally the array */ + RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) parray, sizeof(struct s2n_array))); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_free(struct s2n_array *array) +{ + RESULT_ENSURE_REF(array); + return s2n_array_free_p(&array); +} diff --git a/utils/s2n_blob.c b/utils/s2n_blob.c index 79704c6f846..648bf8085ba 100644 --- a/utils/s2n_blob.c +++ b/utils/s2n_blob.c @@ -1,92 +1,93 @@ -/* - * 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 "utils/s2n_blob.h" - -#include -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_blob_validate(const struct s2n_blob *b) -{ - RESULT_ENSURE_REF(b); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->size == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->allocated == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 0, b->allocated == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 1, b->allocated > 0 || b->size == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable != 0, b->size <= b->allocated), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->allocated), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->size), S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -/** - * Initialize a blob to reference some data. - * - * `b` will not free `data`. The caller is responsible for making sure that - * `data` outlives `b`. - */ -int s2n_blob_init(struct s2n_blob *b, uint8_t *data, uint32_t size) -{ - POSIX_ENSURE_REF(b); - POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); - *b = (struct s2n_blob){ .data = data, .size = size, .allocated = 0, .growable = 0 }; - POSIX_POSTCONDITION(s2n_blob_validate(b)); - return S2N_SUCCESS; -} - -int s2n_blob_zero(struct s2n_blob *b) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - POSIX_CHECKED_MEMSET(b->data, 0, S2N_MAX(b->allocated, b->size)); - POSIX_POSTCONDITION(s2n_blob_validate(b)); - return S2N_SUCCESS; -} - -/** - * Set `slice` to reference some portion of `b`. - * - * The caller is responsible for ensuring that the data pointed to by `b` outlives - * `slice`. - */ -int s2n_blob_slice(const struct s2n_blob *b, struct s2n_blob *slice, uint32_t offset, uint32_t size) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - POSIX_PRECONDITION(s2n_blob_validate(slice)); - - uint32_t slice_size = 0; - POSIX_GUARD(s2n_add_overflow(offset, size, &slice_size)); - POSIX_ENSURE(b->size >= slice_size, S2N_ERR_SIZE_MISMATCH); - slice->data = (b->data) ? (b->data + offset) : NULL; - slice->size = size; - slice->growable = 0; - slice->allocated = 0; - - POSIX_POSTCONDITION(s2n_blob_validate(slice)); - return S2N_SUCCESS; -} - -int s2n_blob_char_to_lower(struct s2n_blob *b) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - for (size_t i = 0; i < b->size; i++) { - b->data[i] = tolower(b->data[i]); - } - POSIX_POSTCONDITION(s2n_blob_validate(b)); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "utils/s2n_blob.h" + +#include +#include + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_blob_validate(const struct s2n_blob *b) +{ + RESULT_ENSURE_REF(b); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->size == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->allocated == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 0, b->allocated == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 1, b->allocated > 0 || b->size == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable != 0, b->size <= b->allocated), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->allocated), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->size), S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +/** + * Initialize a blob to reference some data. + * + * `b` will not free `data`. The caller is responsible for making sure that + * `data` outlives `b`. + */ +int s2n_blob_init(struct s2n_blob *b, uint8_t *data, uint32_t size) +{ + POSIX_ENSURE_REF(b); + POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); + *b = (struct s2n_blob){ .data = data, .size = size, .allocated = 0, .growable = 0 }; + POSIX_POSTCONDITION(s2n_blob_validate(b)); + return S2N_SUCCESS; +} + +int s2n_blob_zero(struct s2n_blob *b) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + POSIX_CHECKED_MEMSET(b->data, 0, S2N_MAX(b->allocated, b->size)); + POSIX_POSTCONDITION(s2n_blob_validate(b)); + return S2N_SUCCESS; +} + +/** + * Set `slice` to reference some portion of `b`. + * + * The caller is responsible for ensuring that the data pointed to by `b` outlives + * `slice`. + */ +int s2n_blob_slice(const struct s2n_blob *b, struct s2n_blob *slice, uint32_t offset, uint32_t size) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + POSIX_PRECONDITION(s2n_blob_validate(slice)); + + uint32_t slice_size = 0; + POSIX_GUARD(s2n_add_overflow(offset, size, &slice_size)); + POSIX_ENSURE(b->size >= slice_size, S2N_ERR_SIZE_MISMATCH); + slice->data = (b->data) ? (b->data + offset) : NULL; + slice->size = size; + slice->growable = 0; + slice->allocated = 0; + + POSIX_POSTCONDITION(s2n_blob_validate(slice)); + return S2N_SUCCESS; +} + +int s2n_blob_char_to_lower(struct s2n_blob *b) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + for (size_t i = 0; i < b->size; i++) { + b->data[i] = tolower(b->data[i]); + } + POSIX_POSTCONDITION(s2n_blob_validate(b)); + return S2N_SUCCESS; +} diff --git a/utils/s2n_compiler.h b/utils/s2n_compiler.h index 1b5a1309971..17b27aaace8 100644 --- a/utils/s2n_compiler.h +++ b/utils/s2n_compiler.h @@ -1,21 +1,34 @@ -/* - * 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 - -#define S2N_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) - -#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) \ - ((S2N_GCC_VERSION) >= ((major) * 10000 + (minor) * 100 + (patch_level))) +/* + * 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 + + +#if defined(_MSC_VER) +#define S2N_GCC_VERSION 0 +#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) 0 +#else +#define S2N_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) \ + ((S2N_GCC_VERSION) >= ((major) * 10000 + (minor) * 100 + (patch_level))) +#endif + + +#if defined(_MSC_VER) +#ifndef __builtin_expect +#define __builtin_expect(x, y) (x) +#endif +#endif diff --git a/utils/s2n_ensure.h b/utils/s2n_ensure.h index b7247eadf78..86c8aa14725 100644 --- a/utils/s2n_ensure.h +++ b/utils/s2n_ensure.h @@ -1,158 +1,198 @@ -/* - * 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 - -#define s2n_likely(x) __builtin_expect(!!(x), 1) -#define s2n_unlikely(x) __builtin_expect(!!(x), 0) - -/** - * s2n_ensure provides low-level safety check functionality - * - * This should only consumed directly by s2n_safety. - * - * Note: This module can be replaced by static analyzer implementation - * to insert additional safety checks. - */ - -/** - * Ensures `cond` is true, otherwise `action` will be performed - */ -#define __S2N_ENSURE(cond, action) \ - do { \ - if (!(cond)) { \ - action; \ - } \ - } while (0) - -#define __S2N_ENSURE_LIKELY(cond, action) \ - do { \ - if (s2n_unlikely(!(cond))) { \ - action; \ - } \ - } while (0) - -#ifdef NDEBUG - #define __S2N_ENSURE_DEBUG(cond, action) \ - do { \ - } while (0) -#else - #define __S2N_ENSURE_DEBUG(cond, action) __S2N_ENSURE_LIKELY((cond), action) -#endif - -#define __S2N_ENSURE_PRECONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) - -#ifdef NDEBUG - #define __S2N_ENSURE_POSTCONDITION(result) (S2N_RESULT_OK) -#else - #define __S2N_ENSURE_POSTCONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) -#endif - -#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ - do { \ - __typeof(n) __tmp_n = (n); \ - if (s2n_likely(__tmp_n)) { \ - void *r = s2n_ensure_memmove_trace((d), (s), (__tmp_n)); \ - guard(r); \ - } \ - } while (0) - -#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ - do { \ - __typeof(n) __tmp_n = (n); \ - if (s2n_likely(__tmp_n)) { \ - __typeof(d) __tmp_d = (d); \ - guard(__tmp_d); \ - memset(__tmp_d, (c), __tmp_n); \ - } \ - } while (0) - -#if defined(S2N_DIAGNOSTICS_PUSH_SUPPORTED) && defined(S2N_DIAGNOSTICS_POP_SUPPORTED) - #define __S2N_ENSURE_CHECKED_RETURN(v) \ - do { \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic error \"-Wconversion\"") return v; \ - _Pragma("GCC diagnostic pop") \ - } while (0) -#else - #define __S2N_ENSURE_CHECKED_RETURN(v) return v -#endif - -void *s2n_ensure_memmove_trace(void *to, const void *from, size_t size); - -/** - * These macros should not be used in validate functions. - * All validate functions are also used in assumptions for CBMC proofs, - * which should not contain __CPROVER_*_ok primitives. The use of these primitives - * in assumptions may lead to spurious results. - * When the code is being verified using CBMC, these properties are formally verified; - * When the code is built in debug mode, they are checked as much as possible using assertions. - * When the code is built in production mode, non-fatal properties are not checked. - * Violations of these properties are undefined behaviour. - */ -#ifdef CBMC - #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || __CPROVER_r_ok((base), (len))) - #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || __CPROVER_w_ok((base), (len))) -#else - /* the C runtime does not give a way to check these properties, - * but we can at least check for nullness. */ - #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) - #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) -#endif /* CBMC */ - -/** - * These macros can safely be used in validate functions. - */ -#define S2N_MEM_IS_READABLE(base, len) (((len) == 0) || (base) != NULL) -#define S2N_MEM_IS_WRITABLE(base, len) (((len) == 0) || (base) != NULL) -#define S2N_OBJECT_PTR_IS_READABLE(ptr) ((ptr) != NULL) -#define S2N_OBJECT_PTR_IS_WRITABLE(ptr) ((ptr) != NULL) - -/** - * If `a` is true, then `b` must be true. - */ -#define S2N_IMPLIES(a, b) (!(a) || (b)) -/** - * If and only if (iff) is a biconditional logical connective between statements a and b. - * Equivalent to (S2N_IMPLIES(a, b) && S2N_IMPLIES(b, a)). - */ -#define S2N_IFF(a, b) (!!(a) == !!(b)) - -/** - * These macros are used to specify code contracts in CBMC proofs. - * Define function contracts. - * When the code is being verified using CBMC, these contracts are formally verified; - * When the code is built in production mode, contracts are not checked. - * 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(...) - #define CONTRACT_RETURN_VALUE -#endif +/* + * 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 + +#define s2n_likely(x) __builtin_expect(!!(x), 1) +#define s2n_unlikely(x) __builtin_expect(!!(x), 0) + +/** + * s2n_ensure provides low-level safety check functionality + * + * This should only consumed directly by s2n_safety. + * + * Note: This module can be replaced by static analyzer implementation + * to insert additional safety checks. + */ + +/** + * Ensures `cond` is true, otherwise `action` will be performed + */ +#define __S2N_ENSURE(cond, action) \ + do { \ + if (!(cond)) { \ + action; \ + } \ + } while (0) + +#define __S2N_ENSURE_LIKELY(cond, action) \ + do { \ + if (s2n_unlikely(!(cond))) { \ + action; \ + } \ + } while (0) + +#ifdef NDEBUG + #define __S2N_ENSURE_DEBUG(cond, action) \ + do { \ + } while (0) +#else + #define __S2N_ENSURE_DEBUG(cond, action) __S2N_ENSURE_LIKELY((cond), action) +#endif + +#define __S2N_ENSURE_PRECONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) + +#ifdef NDEBUG + #define __S2N_ENSURE_POSTCONDITION(result) (S2N_RESULT_OK) +#else + #define __S2N_ENSURE_POSTCONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) +#endif + +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + void *r = s2n_ensure_memmove_trace((d), (s), (n)); \ + guard(r); \ + } \ + } while (0) +#else +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + void *r = s2n_ensure_memmove_trace((d), (s), (n)); \ + guard(r); \ + } \ + } while (0) +#else +#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ + do { \ + __typeof(n) __tmp_n = (n); \ + if (s2n_likely(__tmp_n)) { \ + void *r = s2n_ensure_memmove_trace((d), (s), (__tmp_n)); \ + guard(r); \ + } \ + } while (0) +#endif +#endif + +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + guard((d)); \ + memset((d), (c), (n)); \ + } \ + } while (0) +#else +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + guard((d)); \ + memset((d), (c), (n)); \ + } \ + } while (0) +#else +#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ + do { \ + __typeof(n) __tmp_n = (n); \ + if (s2n_likely(__tmp_n)) { \ + __typeof(d) __tmp_d = (d); \ + guard(__tmp_d); \ + memset(__tmp_d, (c), __tmp_n); \ + } \ + } while (0) +#endif +#endif + +#if defined(S2N_DIAGNOSTICS_PUSH_SUPPORTED) && defined(S2N_DIAGNOSTICS_POP_SUPPORTED) + #define __S2N_ENSURE_CHECKED_RETURN(v) \ + do { \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic error \"-Wconversion\"") return v; \ + _Pragma("GCC diagnostic pop") \ + } while (0) +#else + #define __S2N_ENSURE_CHECKED_RETURN(v) return v +#endif + +void *s2n_ensure_memmove_trace(void *to, const void *from, size_t size); + +/** + * These macros should not be used in validate functions. + * All validate functions are also used in assumptions for CBMC proofs, + * which should not contain __CPROVER_*_ok primitives. The use of these primitives + * in assumptions may lead to spurious results. + * When the code is being verified using CBMC, these properties are formally verified; + * When the code is built in debug mode, they are checked as much as possible using assertions. + * When the code is built in production mode, non-fatal properties are not checked. + * Violations of these properties are undefined behaviour. + */ +#ifdef CBMC + #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || __CPROVER_r_ok((base), (len))) + #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || __CPROVER_w_ok((base), (len))) +#else + /* the C runtime does not give a way to check these properties, + * but we can at least check for nullness. */ + #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) + #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) +#endif /* CBMC */ + +/** + * These macros can safely be used in validate functions. + */ +#define S2N_MEM_IS_READABLE(base, len) (((len) == 0) || (base) != NULL) +#define S2N_MEM_IS_WRITABLE(base, len) (((len) == 0) || (base) != NULL) +#define S2N_OBJECT_PTR_IS_READABLE(ptr) ((ptr) != NULL) +#define S2N_OBJECT_PTR_IS_WRITABLE(ptr) ((ptr) != NULL) + +/** + * If `a` is true, then `b` must be true. + */ +#define S2N_IMPLIES(a, b) (!(a) || (b)) +/** + * If and only if (iff) is a biconditional logical connective between statements a and b. + * Equivalent to (S2N_IMPLIES(a, b) && S2N_IMPLIES(b, a)). + */ +#define S2N_IFF(a, b) (!!(a) == !!(b)) + +/** + * These macros are used to specify code contracts in CBMC proofs. + * Define function contracts. + * When the code is being verified using CBMC, these contracts are formally verified; + * When the code is built in production mode, contracts are not checked. + * 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(...) + #define CONTRACT_RETURN_VALUE +#endif diff --git a/utils/s2n_init.c b/utils/s2n_init.c index d805d5eeb6d..8ad853ecc1f 100644 --- a/utils/s2n_init.c +++ b/utils/s2n_init.c @@ -1,155 +1,167 @@ -/* - * 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 "utils/s2n_init.h" - -#include - -#include "api/unstable/cleanup.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_locking.h" -#include "error/s2n_errno.h" -#include "openssl/opensslv.h" -#include "tls/extensions/s2n_client_key_share.h" -#include "tls/extensions/s2n_extension_type.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13_secrets.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_safety_macros.h" - -static void s2n_cleanup_atexit(void); - -static pthread_t main_thread = 0; -static bool initialized = false; -static bool atexit_cleanup = false; -int s2n_disable_atexit(void) -{ - POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); - atexit_cleanup = false; - return S2N_SUCCESS; -} - -int s2n_enable_atexit(void) -{ - atexit_cleanup = true; - return S2N_SUCCESS; -} - -int s2n_init(void) -{ - /* USAGE-GUIDE says s2n_init MUST NOT be called more than once - * Public documentation for API states s2n_init should only be called once - * https://github.com/aws/s2n-tls/issues/3446 is a result of not enforcing this - */ - POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); - - main_thread = pthread_self(); - - if (getenv("S2N_INTEG_TEST")) { - POSIX_GUARD(s2n_in_integ_test_set(true)); - } - - /* Should run before any init method that calls libcrypto methods - * to ensure we don't try to call methods that don't exist. - * It doesn't require any locks since it only deals with values that - * should be constant, so can run before s2n_locking_init. */ - POSIX_GUARD_RESULT(s2n_libcrypto_validate_runtime()); - /* Must run before any init method that allocates memory. */ - POSIX_GUARD(s2n_mem_init()); - /* Must run before any init method that calls libcrypto methods. */ - POSIX_GUARD_RESULT(s2n_locking_init()); - POSIX_GUARD(s2n_fips_init()); - POSIX_GUARD_RESULT(s2n_rand_init()); - POSIX_GUARD_RESULT(s2n_hash_algorithms_init()); - POSIX_GUARD(s2n_cipher_suites_init()); - POSIX_GUARD(s2n_security_policies_init()); - POSIX_GUARD(s2n_config_defaults_init()); - POSIX_GUARD(s2n_extension_type_init()); - POSIX_GUARD_RESULT(s2n_tls13_empty_transcripts_init()); - POSIX_GUARD_RESULT(s2n_atomic_init()); - - if (atexit_cleanup) { - POSIX_ENSURE_OK(atexit(s2n_cleanup_atexit), S2N_ERR_ATEXIT); - } - - if (getenv("S2N_PRINT_STACKTRACE")) { - s2n_stack_traces_enabled_set(true); - } - -#if defined(OPENSSL_IS_AWSLC) - CRYPTO_pre_sandbox_init(); -#endif - - initialized = true; - - return S2N_SUCCESS; -} - -static bool s2n_cleanup_atexit_impl(void) -{ - /* all of these should run, regardless of result, but the - * values to need to be consumed to prevent warnings */ - - /* the configs need to be wiped before resetting the memory callbacks */ - s2n_wipe_static_configs(); - - bool cleaned_up = s2n_result_is_ok(s2n_cipher_suites_cleanup()) - && s2n_result_is_ok(s2n_hash_algorithms_cleanup()) - && s2n_result_is_ok(s2n_rand_cleanup()) - && s2n_result_is_ok(s2n_locking_cleanup()) - && (s2n_mem_cleanup() == S2N_SUCCESS); - - initialized = !cleaned_up; - return cleaned_up; -} - -int s2n_cleanup_final(void) -{ - /* some cleanups are not idempotent (rand_cleanup, mem_cleanup) so protect */ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE(s2n_cleanup_atexit_impl(), S2N_ERR_ATEXIT); - - return S2N_SUCCESS; -} - -int s2n_cleanup(void) -{ - /* Previously cleaned up thread-local DRBG state. The custom DRBG has - * been removed, so this is now a no-op kept for API compatibility. - */ - return S2N_SUCCESS; -} - -int s2n_cleanup_thread(void) -{ - /* Thread-local DRBG state has been removed. This is now a no-op kept - * for backwards compatibility with callers of the public API. - */ - return S2N_SUCCESS; -} - -static void s2n_cleanup_atexit(void) -{ - (void) s2n_cleanup_atexit_impl(); -} - -bool s2n_is_initialized(void) -{ - return initialized; -} +/* + * 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 "utils/s2n_init.h" + +#if !defined(_MSC_VER) +#include +#else +#include +#endif + +#include "api/unstable/cleanup.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_locking.h" +#include "error/s2n_errno.h" +#include "openssl/opensslv.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13_secrets.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_safety_macros.h" + +static void s2n_cleanup_atexit(void); + +#if !defined(_MSC_VER) +static pthread_t main_thread = 0; +#else +static DWORD main_thread_id = 0; +#endif +static bool initialized = false; +static bool atexit_cleanup = false; +int s2n_disable_atexit(void) +{ + POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); + atexit_cleanup = false; + return S2N_SUCCESS; +} + +int s2n_enable_atexit(void) +{ + atexit_cleanup = true; + return S2N_SUCCESS; +} + +int s2n_init(void) +{ + /* USAGE-GUIDE says s2n_init MUST NOT be called more than once + * Public documentation for API states s2n_init should only be called once + * https://github.com/aws/s2n-tls/issues/3446 is a result of not enforcing this + */ + POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); + + #if !defined(_MSC_VER) + main_thread = pthread_self(); +#else + main_thread_id = GetCurrentThreadId(); +#endif + + if (getenv("S2N_INTEG_TEST")) { + POSIX_GUARD(s2n_in_integ_test_set(true)); + } + + /* Should run before any init method that calls libcrypto methods + * to ensure we don't try to call methods that don't exist. + * It doesn't require any locks since it only deals with values that + * should be constant, so can run before s2n_locking_init. */ + POSIX_GUARD_RESULT(s2n_libcrypto_validate_runtime()); + /* Must run before any init method that allocates memory. */ + POSIX_GUARD(s2n_mem_init()); + /* Must run before any init method that calls libcrypto methods. */ + POSIX_GUARD_RESULT(s2n_locking_init()); + POSIX_GUARD(s2n_fips_init()); + POSIX_GUARD_RESULT(s2n_rand_init()); + POSIX_GUARD_RESULT(s2n_hash_algorithms_init()); + POSIX_GUARD(s2n_cipher_suites_init()); + POSIX_GUARD(s2n_security_policies_init()); + POSIX_GUARD(s2n_config_defaults_init()); + POSIX_GUARD(s2n_extension_type_init()); + POSIX_GUARD_RESULT(s2n_tls13_empty_transcripts_init()); + POSIX_GUARD_RESULT(s2n_atomic_init()); + + if (atexit_cleanup) { + POSIX_ENSURE_OK(atexit(s2n_cleanup_atexit), S2N_ERR_ATEXIT); + } + + if (getenv("S2N_PRINT_STACKTRACE")) { + s2n_stack_traces_enabled_set(true); + } + +#if defined(OPENSSL_IS_AWSLC) + CRYPTO_pre_sandbox_init(); +#endif + + initialized = true; + + return S2N_SUCCESS; +} + +static bool s2n_cleanup_atexit_impl(void) +{ + /* all of these should run, regardless of result, but the + * values to need to be consumed to prevent warnings */ + + /* the configs need to be wiped before resetting the memory callbacks */ + s2n_wipe_static_configs(); + + bool cleaned_up = s2n_result_is_ok(s2n_cipher_suites_cleanup()) + && s2n_result_is_ok(s2n_hash_algorithms_cleanup()) + && s2n_result_is_ok(s2n_rand_cleanup()) + && s2n_result_is_ok(s2n_locking_cleanup()) + && (s2n_mem_cleanup() == S2N_SUCCESS); + + initialized = !cleaned_up; + return cleaned_up; +} + +int s2n_cleanup_final(void) +{ + /* some cleanups are not idempotent (rand_cleanup, mem_cleanup) so protect */ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE(s2n_cleanup_atexit_impl(), S2N_ERR_ATEXIT); + + return S2N_SUCCESS; +} + +int s2n_cleanup(void) +{ + /* Previously cleaned up thread-local DRBG state. The custom DRBG has + * been removed, so this is now a no-op kept for API compatibility. + */ + return S2N_SUCCESS; +} + +int s2n_cleanup_thread(void) +{ + /* Thread-local DRBG state has been removed. This is now a no-op kept + * for backwards compatibility with callers of the public API. + */ + return S2N_SUCCESS; +} + +static void s2n_cleanup_atexit(void) +{ + (void) s2n_cleanup_atexit_impl(); +} + +bool s2n_is_initialized(void) +{ + return initialized; +} diff --git a/utils/s2n_mem.c b/utils/s2n_mem.c index 8ac175b0964..c3df6ad7d40 100644 --- a/utils/s2n_mem.c +++ b/utils/s2n_mem.c @@ -1,396 +1,399 @@ -/* - * 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. - */ - -#define _DEFAULT_SOURCE 1 -#if defined(S2N_FEATURES_AVAILABLE) - #include -#endif - -#include -#include -#include -#ifndef _WIN32 - #include - #include - #include -#endif - -#include "error/s2n_errno.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -static uint32_t page_size = 4096; -static bool initialized = false; - -static int s2n_mem_init_impl(void); -static int s2n_mem_cleanup_impl(void); - -#ifdef _WIN32 -static int s2n_windows_mem_free_impl(void *ptr, uint32_t size); -static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated); -#else -static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size); -static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); -static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size); -static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); -#endif - -static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl; -static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl; -#ifdef _WIN32 -static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; -static s2n_mem_free_callback s2n_mem_free_cb = s2n_windows_mem_free_impl; -#else -static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl; -static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl; -#endif - -static int s2n_mem_init_impl(void) -{ -#ifndef _WIN32 - long sysconf_rc = sysconf(_SC_PAGESIZE); - - /* sysconf must not error, and page_size cannot be 0 */ - POSIX_ENSURE_GT(sysconf_rc, 0); - - /* page_size must be a valid uint32 */ - long max_page_size = S2N_MIN(UINT32_MAX, LONG_MAX); - POSIX_ENSURE_LTE(sysconf_rc, max_page_size); - page_size = (uint32_t) sysconf_rc; - - if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) { - s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; - s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; - } -#endif - return S2N_SUCCESS; -} - -static int s2n_mem_cleanup_impl(void) -{ - page_size = 4096; -#ifdef _WIN32 - s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; - s2n_mem_free_cb = s2n_windows_mem_free_impl; -#else - s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; - s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; -#endif - return S2N_SUCCESS; -} - -#ifdef _WIN32 -/* mlock is not supported on Windows, so these use plain malloc/free without memory locking. */ - -static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated) -{ - *ptr = malloc(requested); - POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); - *allocated = requested; - - return S2N_SUCCESS; -} - -static int s2n_windows_mem_free_impl(void *ptr, uint32_t size) -{ - free(ptr); - return S2N_SUCCESS; -} - -#else - -static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size) -{ - /* Perform a best-effort `munlock`: ignore any errors during unlocking. */ - munlock(ptr, size); - free(ptr); - return S2N_SUCCESS; -} - -static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size) -{ - free(ptr); - - return S2N_SUCCESS; -} - -static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) -{ - POSIX_ENSURE_REF(ptr); - - /* Page aligned allocation required for mlock */ - uint32_t allocate = 0; - - POSIX_GUARD(s2n_align_to(requested, page_size, &allocate)); - - *ptr = NULL; - POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC); - *allocated = allocate; - - /* - ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because - ** both need to be able to dump pages to function. It's how they map heap output. - */ - #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING) - if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) { - POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); - POSIX_BAIL(S2N_ERR_MADVISE); - } - #endif - - if (mlock(*ptr, *allocated) != 0) { - /* When mlock fails, no memory will be locked, so we don't use munlock on free */ - POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); - POSIX_BAIL(S2N_ERR_MLOCK); - } - - POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); - - return S2N_SUCCESS; -} - -static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) -{ - *ptr = malloc(requested); - POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); - *allocated = requested; - - return S2N_SUCCESS; -} - -#endif - -int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, - s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) -{ - POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); - POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback, - mem_malloc_callback, mem_free_callback)); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, - s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) -{ - RESULT_ENSURE_REF(mem_init_callback); - RESULT_ENSURE_REF(mem_cleanup_callback); - RESULT_ENSURE_REF(mem_malloc_callback); - RESULT_ENSURE_REF(mem_free_callback); - - s2n_mem_init_cb = mem_init_callback; - s2n_mem_cleanup_cb = mem_cleanup_callback; - s2n_mem_malloc_cb = mem_malloc_callback; - s2n_mem_free_cb = mem_free_callback; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback, - s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback) -{ - RESULT_ENSURE_REF(mem_init_callback); - RESULT_ENSURE_REF(mem_cleanup_callback); - RESULT_ENSURE_REF(mem_malloc_callback); - RESULT_ENSURE_REF(mem_free_callback); - - *mem_init_callback = s2n_mem_init_cb; - *mem_cleanup_callback = s2n_mem_cleanup_cb; - *mem_malloc_callback = s2n_mem_malloc_cb; - *mem_free_callback = s2n_mem_free_cb; - - return S2N_RESULT_OK; -} - -/** - * Allocate a new blob on the heap. - * - * The blob will be _growable_. - * - * This blob owns the underlying memory, which will be freed when `s2n_free` is - * called on `b`. - */ -int s2n_alloc(struct s2n_blob *b, uint32_t size) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE_REF(b); - const struct s2n_blob temp = { 0 }; - *b = temp; - POSIX_GUARD(s2n_realloc(b, size)); - return S2N_SUCCESS; -} - -/* A blob is growable if it is either explicitly marked as such, or if it contains no data */ -bool s2n_blob_is_growable(const struct s2n_blob *b) -{ - return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0)); -} - -/** - * Resize a blob to `size`. The blob must be allocated or empty. - * - * This will allocate more memory if necessary, or reuse the existing allocation - * if the requested size is smaller than the current size. - * - * If failed, *b remains unchanged. - */ -int s2n_realloc(struct s2n_blob *b, uint32_t size) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE_REF(b); - POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB); - if (size == 0) { - return s2n_free(b); - } - - /* blob already has space for the request */ - if (size <= b->allocated) { - if (size < b->size) { - /* Zero the existing blob memory before the we release it */ - struct s2n_blob slice = { 0 }; - POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size)); - POSIX_GUARD(s2n_blob_zero(&slice)); - } - - b->size = size; - return S2N_SUCCESS; - } - - struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 }; - if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) { - S2N_ERROR_PRESERVE_ERRNO(); - } - - POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC); - POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC); - - if (b->size) { - POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size); - } - - if (b->allocated) { - POSIX_GUARD(s2n_free(b)); - } - - *b = new_memory; - return S2N_SUCCESS; -} - -int s2n_free_object(uint8_t **p_data, uint32_t size) -{ - POSIX_ENSURE_REF(p_data); - - if (*p_data == NULL) { - return S2N_SUCCESS; - } - - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 }; - - /* s2n_free() will call free() even if it returns error (for a growable blob). - ** This makes sure *p_data is not used after free() */ - *p_data = NULL; - - return s2n_free(&b); -} - -/** - * Allocate enough memory for `to` to contain all the data in `from`, then copy - * the data in `from` to `to`. - */ -int s2n_dup(struct s2n_blob *from, struct s2n_blob *to) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE_REF(to); - POSIX_ENSURE_REF(from); - POSIX_ENSURE_EQ(to->size, 0); - POSIX_ENSURE_EQ(to->data, NULL); - POSIX_ENSURE_NE(from->size, 0); - POSIX_ENSURE_NE(from->data, NULL); - - POSIX_GUARD(s2n_alloc(to, from->size)); - - POSIX_CHECKED_MEMCPY(to->data, from->data, to->size); - - return S2N_SUCCESS; -} - -int s2n_mem_init(void) -{ - POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - - initialized = true; - - return S2N_SUCCESS; -} - -bool s2n_mem_is_init(void) -{ - return initialized; -} - -uint32_t s2n_mem_get_page_size(void) -{ - return page_size; -} - -int s2n_mem_cleanup(void) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - - initialized = false; - - return S2N_SUCCESS; -} - -int s2n_free(struct s2n_blob *b) -{ - /* To avoid memory leaks, don't exit the function until the memory - has been freed */ - int zero_rc = s2n_blob_zero(b); - POSIX_GUARD(s2n_free_without_wipe(b)); - return zero_rc; -} - -int s2n_free_without_wipe(struct s2n_blob *b) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB); - - if (b->data) { - void *data = b->data; - uint32_t allocated = b->allocated; - /* Set data point to NULL first to prevent potential double-free on s2n_mem_free_cb error path */ - *b = (struct s2n_blob){ 0 }; - POSIX_ENSURE(s2n_mem_free_cb(data, allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED); - } - - *b = (struct s2n_blob){ 0 }; - - return S2N_SUCCESS; -} - -int s2n_free_or_wipe(struct s2n_blob *b) -{ - POSIX_ENSURE_REF(b); - int zero_rc = s2n_blob_zero(b); - if (b->allocated) { - POSIX_GUARD(s2n_free_without_wipe(b)); - } - return zero_rc; -} +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +#define _DEFAULT_SOURCE 1 +#if defined(S2N_FEATURES_AVAILABLE) + #include +#endif + +#include +#include +#include +#ifndef _WIN32 + #include + #include +#if !defined(_MSC_VER) + #include +#endif +#endif + +#include "error/s2n_errno.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +static uint32_t page_size = 4096; +static bool initialized = false; + +static int s2n_mem_init_impl(void); +static int s2n_mem_cleanup_impl(void); + +#ifdef _WIN32 +static int s2n_windows_mem_free_impl(void *ptr, uint32_t size); +static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated); +#else +static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size); +static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); +static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size); +static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); +#endif + +static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl; +static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl; +#ifdef _WIN32 +static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; +static s2n_mem_free_callback s2n_mem_free_cb = s2n_windows_mem_free_impl; +#else +static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl; +static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl; +#endif + +static int s2n_mem_init_impl(void) +{ +#ifndef _WIN32 + long sysconf_rc = sysconf(_SC_PAGESIZE); + + /* sysconf must not error, and page_size cannot be 0 */ + POSIX_ENSURE_GT(sysconf_rc, 0); + + /* page_size must be a valid uint32 */ + long max_page_size = S2N_MIN(UINT32_MAX, LONG_MAX); + POSIX_ENSURE_LTE(sysconf_rc, max_page_size); + page_size = (uint32_t) sysconf_rc; + + if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) { + s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; + s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; + } +#endif + return S2N_SUCCESS; +} + +static int s2n_mem_cleanup_impl(void) +{ + page_size = 4096; +#ifdef _WIN32 + s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; + s2n_mem_free_cb = s2n_windows_mem_free_impl; +#else + s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; + s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; +#endif + return S2N_SUCCESS; +} + +#ifdef _WIN32 +/* mlock is not supported on Windows, so these use plain malloc/free without memory locking. */ + +static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated) +{ + *ptr = malloc(requested); + POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); + *allocated = requested; + + return S2N_SUCCESS; +} + +static int s2n_windows_mem_free_impl(void *ptr, uint32_t size) +{ + free(ptr); + return S2N_SUCCESS; +} + +#else + +static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size) +{ + /* Perform a best-effort `munlock`: ignore any errors during unlocking. */ + munlock(ptr, size); + free(ptr); + return S2N_SUCCESS; +} + +static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size) +{ + free(ptr); + + return S2N_SUCCESS; +} + +static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) +{ + POSIX_ENSURE_REF(ptr); + + /* Page aligned allocation required for mlock */ + uint32_t allocate = 0; + + POSIX_GUARD(s2n_align_to(requested, page_size, &allocate)); + + *ptr = NULL; + POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC); + *allocated = allocate; + + /* + ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because + ** both need to be able to dump pages to function. It's how they map heap output. + */ + #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING) + if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) { + POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); + POSIX_BAIL(S2N_ERR_MADVISE); + } + #endif + + if (mlock(*ptr, *allocated) != 0) { + /* When mlock fails, no memory will be locked, so we don't use munlock on free */ + POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); + POSIX_BAIL(S2N_ERR_MLOCK); + } + + POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); + + return S2N_SUCCESS; +} + +static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) +{ + *ptr = malloc(requested); + POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); + *allocated = requested; + + return S2N_SUCCESS; +} + +#endif + +int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, + s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) +{ + POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); + POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback, + mem_malloc_callback, mem_free_callback)); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, + s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) +{ + RESULT_ENSURE_REF(mem_init_callback); + RESULT_ENSURE_REF(mem_cleanup_callback); + RESULT_ENSURE_REF(mem_malloc_callback); + RESULT_ENSURE_REF(mem_free_callback); + + s2n_mem_init_cb = mem_init_callback; + s2n_mem_cleanup_cb = mem_cleanup_callback; + s2n_mem_malloc_cb = mem_malloc_callback; + s2n_mem_free_cb = mem_free_callback; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback, + s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback) +{ + RESULT_ENSURE_REF(mem_init_callback); + RESULT_ENSURE_REF(mem_cleanup_callback); + RESULT_ENSURE_REF(mem_malloc_callback); + RESULT_ENSURE_REF(mem_free_callback); + + *mem_init_callback = s2n_mem_init_cb; + *mem_cleanup_callback = s2n_mem_cleanup_cb; + *mem_malloc_callback = s2n_mem_malloc_cb; + *mem_free_callback = s2n_mem_free_cb; + + return S2N_RESULT_OK; +} + +/** + * Allocate a new blob on the heap. + * + * The blob will be _growable_. + * + * This blob owns the underlying memory, which will be freed when `s2n_free` is + * called on `b`. + */ +int s2n_alloc(struct s2n_blob *b, uint32_t size) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE_REF(b); + const struct s2n_blob temp = { 0 }; + *b = temp; + POSIX_GUARD(s2n_realloc(b, size)); + return S2N_SUCCESS; +} + +/* A blob is growable if it is either explicitly marked as such, or if it contains no data */ +bool s2n_blob_is_growable(const struct s2n_blob *b) +{ + return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0)); +} + +/** + * Resize a blob to `size`. The blob must be allocated or empty. + * + * This will allocate more memory if necessary, or reuse the existing allocation + * if the requested size is smaller than the current size. + * + * If failed, *b remains unchanged. + */ +int s2n_realloc(struct s2n_blob *b, uint32_t size) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE_REF(b); + POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB); + if (size == 0) { + return s2n_free(b); + } + + /* blob already has space for the request */ + if (size <= b->allocated) { + if (size < b->size) { + /* Zero the existing blob memory before the we release it */ + struct s2n_blob slice = { 0 }; + POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size)); + POSIX_GUARD(s2n_blob_zero(&slice)); + } + + b->size = size; + return S2N_SUCCESS; + } + + struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 }; + if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) { + S2N_ERROR_PRESERVE_ERRNO(); + } + + POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC); + POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC); + + if (b->size) { + POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size); + } + + if (b->allocated) { + POSIX_GUARD(s2n_free(b)); + } + + *b = new_memory; + return S2N_SUCCESS; +} + +int s2n_free_object(uint8_t **p_data, uint32_t size) +{ + POSIX_ENSURE_REF(p_data); + + if (*p_data == NULL) { + return S2N_SUCCESS; + } + + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 }; + + /* s2n_free() will call free() even if it returns error (for a growable blob). + ** This makes sure *p_data is not used after free() */ + *p_data = NULL; + + return s2n_free(&b); +} + +/** + * Allocate enough memory for `to` to contain all the data in `from`, then copy + * the data in `from` to `to`. + */ +int s2n_dup(struct s2n_blob *from, struct s2n_blob *to) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE_REF(to); + POSIX_ENSURE_REF(from); + POSIX_ENSURE_EQ(to->size, 0); + POSIX_ENSURE_EQ(to->data, NULL); + POSIX_ENSURE_NE(from->size, 0); + POSIX_ENSURE_NE(from->data, NULL); + + POSIX_GUARD(s2n_alloc(to, from->size)); + + POSIX_CHECKED_MEMCPY(to->data, from->data, to->size); + + return S2N_SUCCESS; +} + +int s2n_mem_init(void) +{ + POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + + initialized = true; + + return S2N_SUCCESS; +} + +bool s2n_mem_is_init(void) +{ + return initialized; +} + +uint32_t s2n_mem_get_page_size(void) +{ + return page_size; +} + +int s2n_mem_cleanup(void) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + + initialized = false; + + return S2N_SUCCESS; +} + +int s2n_free(struct s2n_blob *b) +{ + /* To avoid memory leaks, don't exit the function until the memory + has been freed */ + int zero_rc = s2n_blob_zero(b); + POSIX_GUARD(s2n_free_without_wipe(b)); + return zero_rc; +} + +int s2n_free_without_wipe(struct s2n_blob *b) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB); + + if (b->data) { + void *data = b->data; + uint32_t allocated = b->allocated; + /* Set data point to NULL first to prevent potential double-free on s2n_mem_free_cb error path */ + *b = (struct s2n_blob){ 0 }; + POSIX_ENSURE(s2n_mem_free_cb(data, allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED); + } + + *b = (struct s2n_blob){ 0 }; + + return S2N_SUCCESS; +} + +int s2n_free_or_wipe(struct s2n_blob *b) +{ + POSIX_ENSURE_REF(b); + int zero_rc = s2n_blob_zero(b); + if (b->allocated) { + POSIX_GUARD(s2n_free_without_wipe(b)); + } + return zero_rc; +} diff --git a/utils/s2n_prelude.h b/utils/s2n_prelude.h index b508beb5d86..8333ad1af43 100644 --- a/utils/s2n_prelude.h +++ b/utils/s2n_prelude.h @@ -1,49 +1,54 @@ -/* - * 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 - -/* Let modules know that the prelude was included */ -#define _S2N_PRELUDE_INCLUDED - -/* Define the POSIX API we are targeting */ -#ifndef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 200809L -#endif - -/** - * If we're building in release mode make sure _FORTIFY_SOURCE is set - * See: https://www.gnu.org/software/libc/manual/html_node/Source-Fortification.html - * https://man7.org/linux/man-pages/man7/feature_test_macros.7.html - * - * NOTE: _FORTIFY_SOURCE can only be set when optimizations are enabled. - * https://sourceware.org/git/?p=glibc.git;a=commit;f=include/features.h;h=05c2c9618f583ea4acd69b3fe5ae2a2922dd2ddc - */ -#if !defined(_FORTIFY_SOURCE) && defined(S2N_BUILD_RELEASE) - #define _FORTIFY_SOURCE 2 -#endif - -#if ((__GNUC__ >= 4) || defined(__clang__)) && defined(S2N_EXPORTS) - /** - * Marks a function as belonging to the public s2n API. - * - * See: https://gcc.gnu.org/wiki/Visibility - */ - #define S2N_API __attribute__((visibility("default"))) -#endif - -/* These replace the use of MIN/MAX from , which is not available on Windows. */ -#define S2N_MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define S2N_MAX(a, b) (((a) > (b)) ? (a) : (b)) +/* + * 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 + +/* Let modules know that the prelude was included */ +#define _S2N_PRELUDE_INCLUDED + +/* Define the POSIX API we are targeting */ +#ifndef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 200809L +#endif + +/** + * If we're building in release mode make sure _FORTIFY_SOURCE is set + * See: https://www.gnu.org/software/libc/manual/html_node/Source-Fortification.html + * https://man7.org/linux/man-pages/man7/feature_test_macros.7.html + * + * NOTE: _FORTIFY_SOURCE can only be set when optimizations are enabled. + * https://sourceware.org/git/?p=glibc.git;a=commit;f=include/features.h;h=05c2c9618f583ea4acd69b3fe5ae2a2922dd2ddc + */ +#if !defined(_FORTIFY_SOURCE) && defined(S2N_BUILD_RELEASE) + #define _FORTIFY_SOURCE 2 +#endif + +#ifndef S2N_API +#if ((__GNUC__ >= 4) || defined(__clang__)) && defined(S2N_EXPORTS) + #define S2N_API __attribute__((visibility("default"))) +#elif defined(_MSC_VER) + #if defined(S2N_EXPORTS) + #define S2N_API __declspec(dllexport) + #else + #define S2N_API __declspec(dllimport) + #endif +#else + #define S2N_API +#endif +#endif + +/* These replace the use of MIN/MAX from , which is not available on Windows. */ +#define S2N_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define S2N_MAX(a, b) (((a) > (b)) ? (a) : (b)) diff --git a/utils/s2n_random.c b/utils/s2n_random.c index 4679e63040a..16b4fdac7bf 100644 --- a/utils/s2n_random.c +++ b/utils/s2n_random.c @@ -1,390 +1,416 @@ -/* - * 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. - */ - -/* - * _XOPEN_SOURCE is needed for resolving the constant O_CLOEXEC in some - * environments. We use _XOPEN_SOURCE instead of _GNU_SOURCE because - * _GNU_SOURCE is not portable and breaks when attempting to build rust - * bindings on MacOS. - * - * https://man7.org/linux/man-pages/man2/open.2.html - * The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not - * specified in POSIX.1-2001, but are specified in POSIX.1-2008. - * Since glibc 2.12, one can obtain their definitions by defining - * either _POSIX_C_SOURCE with a value greater than or equal to - * 200809L or _XOPEN_SOURCE with a value greater than or equal to - * 700. In glibc 2.11 and earlier, one obtains the definitions by - * defining _GNU_SOURCE. - * - * We use two feature probes to detect the need to perform this workaround. - * It is only applied if we can't get CLOEXEC without it and the build doesn't - * fail with _XOPEN_SOURCE being defined. - * - * # Relevent Links - * - * - POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799 - * - https://stackoverflow.com/a/5724485 - * - https://stackoverflow.com/a/5583764 - */ -#if !defined(S2N_CLOEXEC_SUPPORTED) && defined(S2N_CLOEXEC_XOPEN_SUPPORTED) && !defined(_XOPEN_SOURCE) - #define _XOPEN_SOURCE 700 - #include - #undef _XOPEN_SOURCE -#else - #include -#endif -#include -#include -/* LibreSSL requires include. - * https://github.com/aws/s2n-tls/issues/153#issuecomment-129651643 - */ -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_libcrypto.h" -#include "error/s2n_errno.h" -#include "s2n_io.h" -#include "utils/s2n_init.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_random.h" -#include "utils/s2n_result.h" -#include "utils/s2n_safety.h" - -#if defined(O_CLOEXEC) - #define ENTROPY_FLAGS O_RDONLY | O_CLOEXEC -#else - #define ENTROPY_FLAGS O_RDONLY -#endif - -/* One second in nanoseconds */ -#define ONE_S INT64_C(1000000000) - -/* Placeholder value for an uninitialized entropy file descriptor */ -#define UNINITIALIZED_ENTROPY_FD -1 - -static struct s2n_rand_device s2n_dev_urandom = { - .source = "/dev/urandom", - .fd = UNINITIALIZED_ENTROPY_FD, -}; - -static int s2n_rand_init_cb_impl(void); -static int s2n_rand_cleanup_cb_impl(void); -static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size); - -static int s2n_rand_entropy_fd_close_ptr(int *fd) -{ - if (fd && *fd != UNINITIALIZED_ENTROPY_FD) { - close(*fd); - } - return S2N_SUCCESS; -} - -/* - * Use libcrypto for randomness when the linked libcrypto supports at least - * one of RAND_priv_bytes or RAND_public_bytes, or when the libcrypto is - * AWS-LC (whose RAND_bytes is trusted even in older FIPS builds that lack - * the distinct pub/priv APIs). For older libcryptos that lack both and are - * not AWS-LC (e.g. OpenSSL 1.0.2), fall back to system random (/dev/urandom) - * to avoid depending on the weaker single-stream PRNG. - */ -bool s2n_use_libcrypto_rand(void) -{ -#if defined(S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND) || defined(S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND) - return true; -#elif defined(OPENSSL_IS_AWSLC) - /* Older AWS-LC FIPS builds (e.g. aws-lc-fips-2022) may lack - * RAND_priv_bytes/RAND_public_bytes but still provide a trusted - * RAND_bytes implementation that we can defer to. - */ - return true; -#else - return false; -#endif -} - -static S2N_RESULT s2n_get_libcrypto_private_random_data(struct s2n_blob *out_blob) -{ - RESULT_GUARD_PTR(out_blob); - RESULT_ENSURE_REF(out_blob->data); -#if S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND - RESULT_GUARD_OSSL(RAND_priv_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#else - /* Libcryptos that support RAND_public_bytes but not RAND_priv_bytes still - * provide RAND_bytes. OpenSSL 1.0.2 (which lacks both) is handled by - * s2n_use_libcrypto_rand() returning false, so this path is only reached - * by libcryptos that have at least RAND_public_bytes. - */ - RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#endif - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_get_libcrypto_public_random_data(struct s2n_blob *out_blob) -{ - RESULT_GUARD_PTR(out_blob); - RESULT_ENSURE_REF(out_blob->data); -#if S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND - RESULT_GUARD_OSSL(RAND_public_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#else - RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#endif - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_get_system_random_data(struct s2n_blob *blob) -{ - RESULT_GUARD_PTR(blob); - RESULT_GUARD_PTR(blob->data); - - /* This function sets s2n_errno on failure */ - RESULT_GUARD_POSIX(s2n_rand_get_entropy_from_urandom(blob->data, blob->size)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob) -{ - if (s2n_use_libcrypto_rand()) { - RESULT_GUARD(s2n_get_libcrypto_public_random_data(blob)); - } else { - RESULT_GUARD(s2n_get_system_random_data(blob)); - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob) -{ - if (s2n_use_libcrypto_rand()) { - RESULT_GUARD(s2n_get_libcrypto_private_random_data(blob)); - } else { - RESULT_GUARD(s2n_get_system_random_data(blob)); - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_rand_get_urandom_for_test(struct s2n_rand_device **device) -{ - RESULT_ENSURE_REF(device); - RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST); - *device = &s2n_dev_urandom; - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_rand_device_open(struct s2n_rand_device *device) -{ - RESULT_ENSURE_REF(device); - RESULT_ENSURE_REF(device->source); - - DEFER_CLEANUP(int fd = -1, s2n_rand_entropy_fd_close_ptr); - S2N_IO_RETRY_EINTR(fd, open(device->source, ENTROPY_FLAGS)); - RESULT_ENSURE(fd >= 0, S2N_ERR_OPEN_RANDOM); - - struct stat st = { 0 }; - RESULT_ENSURE(fstat(fd, &st) == 0, S2N_ERR_OPEN_RANDOM); - device->dev = st.st_dev; - device->ino = st.st_ino; - device->mode = st.st_mode; - device->rdev = st.st_rdev; - - device->fd = fd; - - /* Disable closing the file descriptor with defer cleanup */ - fd = UNINITIALIZED_ENTROPY_FD; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_rand_device_validate(struct s2n_rand_device *device) -{ - RESULT_ENSURE_REF(device); - RESULT_ENSURE_NE(device->fd, UNINITIALIZED_ENTROPY_FD); - - /* Ensure that the random device is still valid by comparing it to the current file descriptor - * status. From: - * https://github.com/openssl/openssl/blob/260d97229c467d17934ca3e2e0455b1b5c0994a6/providers/implementations/rands/seeding/rand_unix.c#L513 - */ - struct stat st = { 0 }; - RESULT_ENSURE(fstat(device->fd, &st) == 0, S2N_ERR_OPEN_RANDOM); - RESULT_ENSURE_EQ(device->dev, st.st_dev); - RESULT_ENSURE_EQ(device->ino, st.st_ino); - RESULT_ENSURE_EQ(device->rdev, st.st_rdev); - - /* Ensure that the mode is the same (equal to 0 when xor'd), but don't check the permission bits. */ - mode_t permission_mask = ~(S_IRWXU | S_IRWXG | S_IRWXO); - RESULT_ENSURE_EQ((device->mode ^ st.st_mode) & permission_mask, 0); - - return S2N_RESULT_OK; -} - -static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) -{ - POSIX_ENSURE_REF(ptr); - POSIX_ENSURE(s2n_dev_urandom.fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED); - - /* It's possible that the file descriptor pointing to /dev/urandom was closed or changed from - * when it was last opened. Ensure that the file descriptor is still valid, and if it isn't, - * re-open it before getting entropy. - * - * If the file descriptor is invalid and the process doesn't have access to /dev/urandom (as is - * the case within a chroot tree), an error is raised here before attempting to indefinitely - * read. - */ - if (s2n_result_is_error(s2n_rand_device_validate(&s2n_dev_urandom))) { - POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); - } - - uint8_t *data = ptr; - uint32_t n = size; - struct timespec sleep_time = { .tv_sec = 0, .tv_nsec = 0 }; - long backoff = 1; - - while (n) { - errno = 0; - int r = read(s2n_dev_urandom.fd, data, n); - if (r <= 0) { - /* - * A non-blocking read() on /dev/urandom should "never" fail, - * except for EINTR. If it does, briefly pause and use - * exponential backoff to avoid creating a tight spinning loop. - * - * iteration delay - * --------- ----------------- - * 1 10 nsec - * 2 100 nsec - * 3 1,000 nsec - * 4 10,000 nsec - * 5 100,000 nsec - * 6 1,000,000 nsec - * 7 10,000,000 nsec - * 8 99,999,999 nsec - * 9 99,999,999 nsec - * ... - */ - if (errno != EINTR) { - backoff = S2N_MIN(backoff * 10, ONE_S - 1); - sleep_time.tv_nsec = backoff; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - } - - continue; - } - - data += r; - n -= r; - } - - return S2N_SUCCESS; -} - -/* - * Return a random number in the range [0, bound) - */ -S2N_RESULT s2n_public_random(int64_t bound, uint64_t *output) -{ - uint64_t r = 0; - - RESULT_ENSURE_GT(bound, 0); - - while (1) { - struct s2n_blob blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&blob, (void *) &r, sizeof(r))); - RESULT_GUARD(s2n_get_public_random_data(&blob)); - - /* Imagine an int was one byte and UINT_MAX was 256. If the - * caller asked for s2n_random(129, ...) we'd end up in - * trouble. Each number in the range 0...127 would be twice - * as likely as 128. That's because r == 0 % 129 -> 0, and - * r == 129 % 129 -> 0, but only r == 128 returns 128, - * r == 257 is out of range. - * - * To de-bias the dice, we discard values of r that are higher - * that the highest multiple of 'bound' an int can support. If - * bound is a uint, then in the worst case we discard 50% - 1 r's. - * But since 'bound' is an int and INT_MAX is <= UINT_MAX / 2, - * in the worst case we discard 25% - 1 r's. - */ - if (r < (UINT64_MAX - (UINT64_MAX % bound))) { - *output = r % bound; - return S2N_RESULT_OK; - } - } -} - -static int s2n_rand_init_cb_impl(void) -{ - POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_rand_init(void) -{ - /* Only open /dev/urandom when we actually need it for randomness. - * When libcrypto handles randomness, avoid the extra syscall and fd. - */ - if (!s2n_use_libcrypto_rand()) { - RESULT_ENSURE(s2n_rand_init_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - } - - return S2N_RESULT_OK; -} - -static int s2n_rand_cleanup_cb_impl(void) -{ - if (s2n_dev_urandom.fd == UNINITIALIZED_ENTROPY_FD) { - return S2N_SUCCESS; - } - - if (s2n_result_is_ok(s2n_rand_device_validate(&s2n_dev_urandom))) { - POSIX_GUARD(close(s2n_dev_urandom.fd)); - } - s2n_dev_urandom.fd = UNINITIALIZED_ENTROPY_FD; - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_rand_cleanup(void) -{ - if (!s2n_use_libcrypto_rand()) { - RESULT_ENSURE(s2n_rand_cleanup_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - } - - return S2N_RESULT_OK; -} - -int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback, - s2n_rand_cleanup_callback rand_cleanup_callback, - s2n_rand_seed_callback rand_seed_callback, - s2n_rand_mix_callback rand_mix_callback) -{ - /* Custom random callbacks are no longer supported. Randomness is now - * delegated directly to libcrypto or /dev/urandom. This stub is kept - * for backwards compatibility. - */ - (void) rand_init_callback; - (void) rand_cleanup_callback; - (void) rand_seed_callback; - (void) rand_mix_callback; - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +#if defined(_MSC_VER) +#pragma comment(lib, "bcrypt.lib") +#include +#include +#include "error/s2n_errno.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) +{ + POSIX_ENSURE_REF(ptr); + if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, (PUCHAR)ptr, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG))) { + POSIX_BAIL(S2N_ERR_RANDOM_UNINITIALIZED); + } + return S2N_SUCCESS; +} + +static int s2n_rand_init_cb_impl(void) { return S2N_SUCCESS; } +static int s2n_rand_cleanup_cb_impl(void) { return S2N_SUCCESS; } + +#else +/* + * 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. + */ + +/* + * _XOPEN_SOURCE is needed for resolving the constant O_CLOEXEC in some + * environments. We use _XOPEN_SOURCE instead of _GNU_SOURCE because + * _GNU_SOURCE is not portable and breaks when attempting to build rust + * bindings on MacOS. + * + * https://man7.org/linux/man-pages/man2/open.2.html + * The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not + * specified in POSIX.1-2001, but are specified in POSIX.1-2008. + * Since glibc 2.12, one can obtain their definitions by defining + * either _POSIX_C_SOURCE with a value greater than or equal to + * 200809L or _XOPEN_SOURCE with a value greater than or equal to + * 700. In glibc 2.11 and earlier, one obtains the definitions by + * defining _GNU_SOURCE. + * + * We use two feature probes to detect the need to perform this workaround. + * It is only applied if we can't get CLOEXEC without it and the build doesn't + * fail with _XOPEN_SOURCE being defined. + * + * # Relevent Links + * + * - POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799 + * - https://stackoverflow.com/a/5724485 + * - https://stackoverflow.com/a/5583764 + */ +#if !defined(S2N_CLOEXEC_SUPPORTED) && defined(S2N_CLOEXEC_XOPEN_SUPPORTED) && !defined(_XOPEN_SOURCE) + #define _XOPEN_SOURCE 700 + #include + #undef _XOPEN_SOURCE +#else + #include +#endif +#include +#include +/* LibreSSL requires include. + * https://github.com/aws/s2n-tls/issues/153#issuecomment-129651643 + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_libcrypto.h" +#include "error/s2n_errno.h" +#include "s2n_io.h" +#include "utils/s2n_init.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_random.h" +#include "utils/s2n_result.h" +#include "utils/s2n_safety.h" + +#if defined(O_CLOEXEC) + #define ENTROPY_FLAGS O_RDONLY | O_CLOEXEC +#else + #define ENTROPY_FLAGS O_RDONLY +#endif + +/* One second in nanoseconds */ +#define ONE_S INT64_C(1000000000) + +/* Placeholder value for an uninitialized entropy file descriptor */ +#define UNINITIALIZED_ENTROPY_FD -1 + +static struct s2n_rand_device s2n_dev_urandom = { + .source = "/dev/urandom", + .fd = UNINITIALIZED_ENTROPY_FD, +}; + +static int s2n_rand_init_cb_impl(void); +static int s2n_rand_cleanup_cb_impl(void); +static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size); + +static int s2n_rand_entropy_fd_close_ptr(int *fd) +{ + if (fd && *fd != UNINITIALIZED_ENTROPY_FD) { + close(*fd); + } + return S2N_SUCCESS; +} + +/* + * Use libcrypto for randomness when the linked libcrypto supports at least + * one of RAND_priv_bytes or RAND_public_bytes, or when the libcrypto is + * AWS-LC (whose RAND_bytes is trusted even in older FIPS builds that lack + * the distinct pub/priv APIs). For older libcryptos that lack both and are + * not AWS-LC (e.g. OpenSSL 1.0.2), fall back to system random (/dev/urandom) + * to avoid depending on the weaker single-stream PRNG. + */ +bool s2n_use_libcrypto_rand(void) +{ +#if defined(S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND) || defined(S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND) + return true; +#elif defined(OPENSSL_IS_AWSLC) + /* Older AWS-LC FIPS builds (e.g. aws-lc-fips-2022) may lack + * RAND_priv_bytes/RAND_public_bytes but still provide a trusted + * RAND_bytes implementation that we can defer to. + */ + return true; +#else + return false; +#endif +} + +static S2N_RESULT s2n_get_libcrypto_private_random_data(struct s2n_blob *out_blob) +{ + RESULT_GUARD_PTR(out_blob); + RESULT_ENSURE_REF(out_blob->data); +#if S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND + RESULT_GUARD_OSSL(RAND_priv_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#else + /* Libcryptos that support RAND_public_bytes but not RAND_priv_bytes still + * provide RAND_bytes. OpenSSL 1.0.2 (which lacks both) is handled by + * s2n_use_libcrypto_rand() returning false, so this path is only reached + * by libcryptos that have at least RAND_public_bytes. + */ + RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#endif + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_get_libcrypto_public_random_data(struct s2n_blob *out_blob) +{ + RESULT_GUARD_PTR(out_blob); + RESULT_ENSURE_REF(out_blob->data); +#if S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND + RESULT_GUARD_OSSL(RAND_public_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#else + RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#endif + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_get_system_random_data(struct s2n_blob *blob) +{ + RESULT_GUARD_PTR(blob); + RESULT_GUARD_PTR(blob->data); + + /* This function sets s2n_errno on failure */ + RESULT_GUARD_POSIX(s2n_rand_get_entropy_from_urandom(blob->data, blob->size)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob) +{ + if (s2n_use_libcrypto_rand()) { + RESULT_GUARD(s2n_get_libcrypto_public_random_data(blob)); + } else { + RESULT_GUARD(s2n_get_system_random_data(blob)); + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob) +{ + if (s2n_use_libcrypto_rand()) { + RESULT_GUARD(s2n_get_libcrypto_private_random_data(blob)); + } else { + RESULT_GUARD(s2n_get_system_random_data(blob)); + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_rand_get_urandom_for_test(struct s2n_rand_device **device) +{ + RESULT_ENSURE_REF(device); + RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST); + *device = &s2n_dev_urandom; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_rand_device_open(struct s2n_rand_device *device) +{ + RESULT_ENSURE_REF(device); + RESULT_ENSURE_REF(device->source); + + DEFER_CLEANUP(int fd = -1, s2n_rand_entropy_fd_close_ptr); + S2N_IO_RETRY_EINTR(fd, open(device->source, ENTROPY_FLAGS)); + RESULT_ENSURE(fd >= 0, S2N_ERR_OPEN_RANDOM); + + struct stat st = { 0 }; + RESULT_ENSURE(fstat(fd, &st) == 0, S2N_ERR_OPEN_RANDOM); + device->dev = st.st_dev; + device->ino = st.st_ino; + device->mode = st.st_mode; + device->rdev = st.st_rdev; + + device->fd = fd; + + /* Disable closing the file descriptor with defer cleanup */ + fd = UNINITIALIZED_ENTROPY_FD; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_rand_device_validate(struct s2n_rand_device *device) +{ + RESULT_ENSURE_REF(device); + RESULT_ENSURE_NE(device->fd, UNINITIALIZED_ENTROPY_FD); + + /* Ensure that the random device is still valid by comparing it to the current file descriptor + * status. From: + * https://github.com/openssl/openssl/blob/260d97229c467d17934ca3e2e0455b1b5c0994a6/providers/implementations/rands/seeding/rand_unix.c#L513 + */ + struct stat st = { 0 }; + RESULT_ENSURE(fstat(device->fd, &st) == 0, S2N_ERR_OPEN_RANDOM); + RESULT_ENSURE_EQ(device->dev, st.st_dev); + RESULT_ENSURE_EQ(device->ino, st.st_ino); + RESULT_ENSURE_EQ(device->rdev, st.st_rdev); + + /* Ensure that the mode is the same (equal to 0 when xor'd), but don't check the permission bits. */ + mode_t permission_mask = ~(S_IRWXU | S_IRWXG | S_IRWXO); + RESULT_ENSURE_EQ((device->mode ^ st.st_mode) & permission_mask, 0); + + return S2N_RESULT_OK; +} + +static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) +{ + POSIX_ENSURE_REF(ptr); + POSIX_ENSURE(s2n_dev_urandom.fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED); + + /* It's possible that the file descriptor pointing to /dev/urandom was closed or changed from + * when it was last opened. Ensure that the file descriptor is still valid, and if it isn't, + * re-open it before getting entropy. + * + * If the file descriptor is invalid and the process doesn't have access to /dev/urandom (as is + * the case within a chroot tree), an error is raised here before attempting to indefinitely + * read. + */ + if (s2n_result_is_error(s2n_rand_device_validate(&s2n_dev_urandom))) { + POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); + } + + uint8_t *data = ptr; + uint32_t n = size; + struct timespec sleep_time = { .tv_sec = 0, .tv_nsec = 0 }; + long backoff = 1; + + while (n) { + errno = 0; + int r = read(s2n_dev_urandom.fd, data, n); + if (r <= 0) { + /* + * A non-blocking read() on /dev/urandom should "never" fail, + * except for EINTR. If it does, briefly pause and use + * exponential backoff to avoid creating a tight spinning loop. + * + * iteration delay + * --------- ----------------- + * 1 10 nsec + * 2 100 nsec + * 3 1,000 nsec + * 4 10,000 nsec + * 5 100,000 nsec + * 6 1,000,000 nsec + * 7 10,000,000 nsec + * 8 99,999,999 nsec + * 9 99,999,999 nsec + * ... + */ + if (errno != EINTR) { + backoff = S2N_MIN(backoff * 10, ONE_S - 1); + sleep_time.tv_nsec = backoff; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + } + + continue; + } + + data += r; + n -= r; + } + + return S2N_SUCCESS; +} + +/* + * Return a random number in the range [0, bound) + */ +S2N_RESULT s2n_public_random(int64_t bound, uint64_t *output) +{ + uint64_t r = 0; + + RESULT_ENSURE_GT(bound, 0); + + while (1) { + struct s2n_blob blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&blob, (void *) &r, sizeof(r))); + RESULT_GUARD(s2n_get_public_random_data(&blob)); + + /* Imagine an int was one byte and UINT_MAX was 256. If the + * caller asked for s2n_random(129, ...) we'd end up in + * trouble. Each number in the range 0...127 would be twice + * as likely as 128. That's because r == 0 % 129 -> 0, and + * r == 129 % 129 -> 0, but only r == 128 returns 128, + * r == 257 is out of range. + * + * To de-bias the dice, we discard values of r that are higher + * that the highest multiple of 'bound' an int can support. If + * bound is a uint, then in the worst case we discard 50% - 1 r's. + * But since 'bound' is an int and INT_MAX is <= UINT_MAX / 2, + * in the worst case we discard 25% - 1 r's. + */ + if (r < (UINT64_MAX - (UINT64_MAX % bound))) { + *output = r % bound; + return S2N_RESULT_OK; + } + } +} + +static int s2n_rand_init_cb_impl(void) +{ + POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_rand_init(void) +{ + /* Only open /dev/urandom when we actually need it for randomness. + * When libcrypto handles randomness, avoid the extra syscall and fd. + */ + if (!s2n_use_libcrypto_rand()) { + RESULT_ENSURE(s2n_rand_init_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + } + + return S2N_RESULT_OK; +} + +static int s2n_rand_cleanup_cb_impl(void) +{ + if (s2n_dev_urandom.fd == UNINITIALIZED_ENTROPY_FD) { + return S2N_SUCCESS; + } + + if (s2n_result_is_ok(s2n_rand_device_validate(&s2n_dev_urandom))) { + POSIX_GUARD(close(s2n_dev_urandom.fd)); + } + s2n_dev_urandom.fd = UNINITIALIZED_ENTROPY_FD; + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_rand_cleanup(void) +{ + if (!s2n_use_libcrypto_rand()) { + RESULT_ENSURE(s2n_rand_cleanup_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + } + + return S2N_RESULT_OK; +} + +int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback, + s2n_rand_cleanup_callback rand_cleanup_callback, + s2n_rand_seed_callback rand_seed_callback, + s2n_rand_mix_callback rand_mix_callback) +{ + /* Custom random callbacks are no longer supported. Randomness is now + * delegated directly to libcrypto or /dev/urandom. This stub is kept + * for backwards compatibility. + */ + (void) rand_init_callback; + (void) rand_cleanup_callback; + (void) rand_seed_callback; + (void) rand_mix_callback; + return S2N_SUCCESS; +} + +#endif \ No newline at end of file diff --git a/utils/s2n_random.h b/utils/s2n_random.h index 2efa741249d..4aa6cc6ff39 100644 --- a/utils/s2n_random.h +++ b/utils/s2n_random.h @@ -1,35 +1,37 @@ -/* - * 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 - -#include "utils/s2n_blob.h" -#include "utils/s2n_result.h" - -struct s2n_rand_device { - const char *source; - int fd; - dev_t dev; - ino_t ino; - mode_t mode; - dev_t rdev; -}; - -S2N_RESULT s2n_rand_init(void); -S2N_RESULT s2n_rand_cleanup(void); -S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob); -S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob); -S2N_RESULT s2n_public_random(int64_t max, uint64_t *output); -bool s2n_use_libcrypto_rand(void); +/* + * 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 + +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" + +#if !defined(_MSC_VER) +struct s2n_rand_device { + const char *source; + int fd; + dev_t dev; + ino_t ino; + mode_t mode; + dev_t rdev; +}; +#endif + +S2N_RESULT s2n_rand_init(void); +S2N_RESULT s2n_rand_cleanup(void); +S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob); +S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob); +S2N_RESULT s2n_public_random(int64_t max, uint64_t *output); +bool s2n_use_libcrypto_rand(void); diff --git a/utils/s2n_rfc5952.c b/utils/s2n_rfc5952.c index 5ed078a51dc..d3a56b94775 100644 --- a/utils/s2n_rfc5952.c +++ b/utils/s2n_rfc5952.c @@ -1,130 +1,138 @@ -/* - * 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 "utils/s2n_rfc5952.h" - -#include -#include -#include - -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" - -static uint8_t dec[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; -static uint8_t hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - -S2N_RESULT s2n_inet_ntop(int af, const void *addr, struct s2n_blob *dst) -{ - const uint8_t *bytes = addr; - uint8_t *cursor = dst->data; - - if (af == AF_INET) { - RESULT_ENSURE(dst->size >= sizeof("111.222.333.444"), S2N_ERR_SIZE_MISMATCH); - - for (int i = 0; i < 4; i++) { - if (bytes[i] / 100) { - *cursor++ = dec[bytes[i] / 100]; - } - if (bytes[i] >= 10) { - *cursor++ = dec[(bytes[i] % 100) / 10]; - } - *cursor++ = dec[(bytes[i] % 10)]; - *cursor++ = '.'; - } - - *--cursor = '\0'; - - return S2N_RESULT_OK; - } - - if (af == AF_INET6) { - RESULT_ENSURE(dst->size >= sizeof("1111:2222:3333:4444:5555:6666:7777:8888"), S2N_ERR_SIZE_MISMATCH); - - /* See Section 4 of RFC5952 for the rules we are going to follow here - * - * Here's the general algorithm: - * - * 1/ Treat the bytes as 8 16-bit fields - * 2/ Find the longest run of 16-bit fields. - * 3/ or if there are two or more equal length longest runs, go with the left-most run - * 4/ Make that run :: - * 5/ Print the remaining 16-bit fields in lowercase hex, no leading zeroes - */ - - uint16_t octets[8] = { 0 }; - - int longest_run_start = 0; - int longest_run_length = 0; - int current_run_length = 0; - - /* 2001:db8::1:0:0:1 */ - - /* Find the longest run of zeroes */ - for (int i = 0; i < 8; i++) { - octets[i] = (bytes[i * 2] << 8) + bytes[(i * 2) + 1]; - - if (octets[i]) { - current_run_length = 0; - } else { - current_run_length++; - } - - if (current_run_length > longest_run_length) { - longest_run_length = current_run_length; - longest_run_start = (i - current_run_length) + 1; - } - } - - for (int i = 0; i < 8; i++) { - if (i == longest_run_start && longest_run_length > 1) { - if (i == 0) { - *cursor++ = ':'; - } - - if (longest_run_length == 8) { - *cursor++ = ':'; - } - - i += longest_run_length - 1; - - } else { - uint8_t nibbles[4] = { (octets[i] & 0xF000) >> 12, - (octets[i] & 0x0F00) >> 8, - (octets[i] & 0x00F0) >> 4, - (octets[i] & 0x000F) }; - - /* Skip up to three leading zeroes */ - int j = 0; - for (j = 0; j < 3; j++) { - if (nibbles[j]) { - break; - } - } - - for (; j < 4; j++) { - *cursor++ = hex[nibbles[j]]; - } - } - - *cursor++ = ':'; - } - - *--cursor = '\0'; - - return S2N_RESULT_OK; - } - - RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); -} +/* + * 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 "utils/s2n_rfc5952.h" + +#if defined(_MSC_VER) +#include +#include +#endif + + +#include +#if !defined(_MSC_VER) +#include +#endif +#include + +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" + +static uint8_t dec[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; +static uint8_t hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +S2N_RESULT s2n_inet_ntop(int af, const void *addr, struct s2n_blob *dst) +{ + const uint8_t *bytes = addr; + uint8_t *cursor = dst->data; + + if (af == AF_INET) { + RESULT_ENSURE(dst->size >= sizeof("111.222.333.444"), S2N_ERR_SIZE_MISMATCH); + + for (int i = 0; i < 4; i++) { + if (bytes[i] / 100) { + *cursor++ = dec[bytes[i] / 100]; + } + if (bytes[i] >= 10) { + *cursor++ = dec[(bytes[i] % 100) / 10]; + } + *cursor++ = dec[(bytes[i] % 10)]; + *cursor++ = '.'; + } + + *--cursor = '\0'; + + return S2N_RESULT_OK; + } + + if (af == AF_INET6) { + RESULT_ENSURE(dst->size >= sizeof("1111:2222:3333:4444:5555:6666:7777:8888"), S2N_ERR_SIZE_MISMATCH); + + /* See Section 4 of RFC5952 for the rules we are going to follow here + * + * Here's the general algorithm: + * + * 1/ Treat the bytes as 8 16-bit fields + * 2/ Find the longest run of 16-bit fields. + * 3/ or if there are two or more equal length longest runs, go with the left-most run + * 4/ Make that run :: + * 5/ Print the remaining 16-bit fields in lowercase hex, no leading zeroes + */ + + uint16_t octets[8] = { 0 }; + + int longest_run_start = 0; + int longest_run_length = 0; + int current_run_length = 0; + + /* 2001:db8::1:0:0:1 */ + + /* Find the longest run of zeroes */ + for (int i = 0; i < 8; i++) { + octets[i] = (bytes[i * 2] << 8) + bytes[(i * 2) + 1]; + + if (octets[i]) { + current_run_length = 0; + } else { + current_run_length++; + } + + if (current_run_length > longest_run_length) { + longest_run_length = current_run_length; + longest_run_start = (i - current_run_length) + 1; + } + } + + for (int i = 0; i < 8; i++) { + if (i == longest_run_start && longest_run_length > 1) { + if (i == 0) { + *cursor++ = ':'; + } + + if (longest_run_length == 8) { + *cursor++ = ':'; + } + + i += longest_run_length - 1; + + } else { + uint8_t nibbles[4] = { (octets[i] & 0xF000) >> 12, + (octets[i] & 0x0F00) >> 8, + (octets[i] & 0x00F0) >> 4, + (octets[i] & 0x000F) }; + + /* Skip up to three leading zeroes */ + int j = 0; + for (j = 0; j < 3; j++) { + if (nibbles[j]) { + break; + } + } + + for (; j < 4; j++) { + *cursor++ = hex[nibbles[j]]; + } + } + + *cursor++ = ':'; + } + + *--cursor = '\0'; + + return S2N_RESULT_OK; + } + + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); +} diff --git a/utils/s2n_safety.h b/utils/s2n_safety.h index 7a6c3af0e27..5d2cbb14422 100644 --- a/utils/s2n_safety.h +++ b/utils/s2n_safety.h @@ -1,119 +1,123 @@ -/* - * 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 - -#include -#include -#include -#include - -#include "error/s2n_errno.h" -#include "utils/s2n_ensure.h" -#include "utils/s2n_result.h" -#include "utils/s2n_safety_macros.h" - -/** - * The goal of s2n_safety is to provide helpers to perform common - * checks, which help with code readability. - */ - -/** - * Marks a case of a switch statement as able to fall through to the next case - */ -#if defined(S2N_FALL_THROUGH_SUPPORTED) - #define FALL_THROUGH __attribute__((fallthrough)) -#else - #define FALL_THROUGH ((void) 0) -#endif - -int s2n_in_unit_test_set(bool is_unit); -int s2n_in_integ_test_set(bool is_integ); -bool s2n_in_unit_test(); -bool s2n_in_test(); - -/* Returns true if a and b are equal, in constant time */ -bool s2n_constant_time_equals(const uint8_t* a, const uint8_t* b, const uint32_t len); - -/* Copy src to dst, or don't copy it, in constant time */ -int s2n_constant_time_copy_or_dont(uint8_t* dst, const uint8_t* src, uint32_t len, uint8_t dont); - -/* If src contains valid PKCS#1 v1.5 padding of exactly expectlen bytes, decode - * it into dst, otherwise leave dst alone, in constant time. - * Always returns zero. */ -int s2n_constant_time_pkcs1_unpad_or_dont(uint8_t* dst, const uint8_t* src, uint32_t srclen, uint32_t expectlen); - -/** - * Runs _thecleanup function on _thealloc once _thealloc went out of scope - */ -#define DEFER_CLEANUP(_thealloc, _thecleanup) \ - __attribute__((cleanup(_thecleanup))) _thealloc -/** - * Often we want to free memory on an error, but not on a success. - * We do this by declaring a variable with DEFER_CLEANUP, then zeroing - * that variable after success to prevent DEFER_CLEANUP from accessing - * and freeing any memory it allocated. - * - * This pattern is not intuitive, so a named macro makes it more readable. - */ -#define ZERO_TO_DISABLE_DEFER_CLEANUP(_thealloc) memset(&_thealloc, 0, sizeof(_thealloc)) - -/* We want to apply blinding whenever `action` fails. - * Unfortunately, because functions in S2N do not have a consistent return type, determining failure is difficult. - * Instead, let's rely on the consistent error handling behavior of returning from a method early on error - * and apply blinding if our tracking variable goes out of scope early. - */ -S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection** conn); -#define WITH_ERROR_BLINDING(conn, action) \ - do { \ - DEFER_CLEANUP(struct s2n_connection* _conn_to_blind = conn, s2n_connection_apply_error_blinding); \ - action; \ - /* The `if` here is to avoid a redundantInitialization warning from cppcheck */ \ - if (_conn_to_blind) { \ - _conn_to_blind = NULL; \ - } \ - } while (0) - -/* Creates cleanup function for pointers from function func which accepts a pointer. - * This is useful for DEFER_CLEANUP as it passes &_thealloc into _thecleanup function, - * so if _thealloc is a pointer _thecleanup will receive a pointer to a pointer.*/ -#define DEFINE_POINTER_CLEANUP_FUNC(type, func) \ - static inline void func##_pointer(type* p) \ - { \ - if (p && *p) \ - func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -/* This method works for ARRAYS, not for POINTERS. - * Calling sizeof on an array declared in the current function correctly returns - * the total size of the array. But once the array is passed to another function, - * it behaves like a pointer. Calling sizeof on a pointer only returns the size - * of the pointer address itself (so usually 8). - * Newer compilers (gcc >= 8.1, clang >= 8.0) will warn if the argument is a pointer. - */ -#define s2n_array_len(array) (sizeof(array) / sizeof(array[0])) - -int s2n_mul_overflow(uint32_t a, uint32_t b, uint32_t* out); - -/** - * Rounds "initial" up to a multiple of "alignment", and stores the result in "out". - * Raises an error if overflow would occur. - * NOT CONSTANT TIME. - */ -int s2n_align_to(uint32_t initial, uint32_t alignment, uint32_t* out); -int s2n_add_overflow(uint32_t a, uint32_t b, uint32_t* out); -int s2n_sub_overflow(uint32_t a, uint32_t b, uint32_t* out); -#define S2N_ADD_IS_OVERFLOW_SAFE(a, b, max) (((max) >= (a)) && ((max) - (a) >= (b))) +/* + * 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 + +#include +#include +#include +#include + +#include "error/s2n_errno.h" +#include "utils/s2n_ensure.h" +#include "utils/s2n_result.h" +#include "utils/s2n_safety_macros.h" + +/** + * The goal of s2n_safety is to provide helpers to perform common + * checks, which help with code readability. + */ + +/** + * Marks a case of a switch statement as able to fall through to the next case + */ +#if defined(S2N_FALL_THROUGH_SUPPORTED) + #define FALL_THROUGH __attribute__((fallthrough)) +#else + #define FALL_THROUGH ((void) 0) +#endif + +int s2n_in_unit_test_set(bool is_unit); +int s2n_in_integ_test_set(bool is_integ); +bool s2n_in_unit_test(); +bool s2n_in_test(); + +/* Returns true if a and b are equal, in constant time */ +bool s2n_constant_time_equals(const uint8_t* a, const uint8_t* b, const uint32_t len); + +/* Copy src to dst, or don't copy it, in constant time */ +int s2n_constant_time_copy_or_dont(uint8_t* dst, const uint8_t* src, uint32_t len, uint8_t dont); + +/* If src contains valid PKCS#1 v1.5 padding of exactly expectlen bytes, decode + * it into dst, otherwise leave dst alone, in constant time. + * Always returns zero. */ +int s2n_constant_time_pkcs1_unpad_or_dont(uint8_t* dst, const uint8_t* src, uint32_t srclen, uint32_t expectlen); + +/** + * Runs _thecleanup function on _thealloc once _thealloc went out of scope + */ +#if defined(_MSC_VER) +#define DEFER_CLEANUP(_thealloc, _thecleanup) _thealloc +#else +#define DEFER_CLEANUP(_thealloc, _thecleanup) \ + __attribute__((cleanup(_thecleanup))) _thealloc +#endif +/** + * Often we want to free memory on an error, but not on a success. + * We do this by declaring a variable with DEFER_CLEANUP, then zeroing + * that variable after success to prevent DEFER_CLEANUP from accessing + * and freeing any memory it allocated. + * + * This pattern is not intuitive, so a named macro makes it more readable. + */ +#define ZERO_TO_DISABLE_DEFER_CLEANUP(_thealloc) memset(&_thealloc, 0, sizeof(_thealloc)) + +/* We want to apply blinding whenever `action` fails. + * Unfortunately, because functions in S2N do not have a consistent return type, determining failure is difficult. + * Instead, let's rely on the consistent error handling behavior of returning from a method early on error + * and apply blinding if our tracking variable goes out of scope early. + */ +S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection** conn); +#define WITH_ERROR_BLINDING(conn, action) \ + do { \ + DEFER_CLEANUP(struct s2n_connection* _conn_to_blind = conn, s2n_connection_apply_error_blinding); \ + action; \ + /* The `if` here is to avoid a redundantInitialization warning from cppcheck */ \ + if (_conn_to_blind) { \ + _conn_to_blind = NULL; \ + } \ + } while (0) + +/* Creates cleanup function for pointers from function func which accepts a pointer. + * This is useful for DEFER_CLEANUP as it passes &_thealloc into _thecleanup function, + * so if _thealloc is a pointer _thecleanup will receive a pointer to a pointer.*/ +#define DEFINE_POINTER_CLEANUP_FUNC(type, func) \ + static inline void func##_pointer(type* p) \ + { \ + if (p && *p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +/* This method works for ARRAYS, not for POINTERS. + * Calling sizeof on an array declared in the current function correctly returns + * the total size of the array. But once the array is passed to another function, + * it behaves like a pointer. Calling sizeof on a pointer only returns the size + * of the pointer address itself (so usually 8). + * Newer compilers (gcc >= 8.1, clang >= 8.0) will warn if the argument is a pointer. + */ +#define s2n_array_len(array) (sizeof(array) / sizeof(array[0])) + +int s2n_mul_overflow(uint32_t a, uint32_t b, uint32_t* out); + +/** + * Rounds "initial" up to a multiple of "alignment", and stores the result in "out". + * Raises an error if overflow would occur. + * NOT CONSTANT TIME. + */ +int s2n_align_to(uint32_t initial, uint32_t alignment, uint32_t* out); +int s2n_add_overflow(uint32_t a, uint32_t b, uint32_t* out); +int s2n_sub_overflow(uint32_t a, uint32_t b, uint32_t* out); +#define S2N_ADD_IS_OVERFLOW_SAFE(a, b, max) (((max) >= (a)) && ((max) - (a) >= (b))) diff --git a/utils/s2n_safety_macros.h b/utils/s2n_safety_macros.h index e3896e8c360..1fed9b7e8bb 100644 --- a/utils/s2n_safety_macros.h +++ b/utils/s2n_safety_macros.h @@ -1,611 +1,659 @@ - -/* - * 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 - -/** - * DO NOT DIRECTLY MODIFY THIS FILE: - * - * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications - * should be in there. - */ - -/* clang-format off */ - -#include "error/s2n_errno.h" -#include "utils/s2n_ensure.h" -#include "utils/s2n_result.h" - -/** - * The goal of s2n_safety is to provide helpers to perform common - * checks, which help with code readability. - */ - -/* Success signal value for OpenSSL functions */ -#define _OSSL_SUCCESS 1 - -/** - * Sets the global `s2n_errno` to `error` and returns with an `S2N_RESULT_ERROR` - */ -#define RESULT_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR); } while (0) - -/** - * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` - */ -#define RESULT_ENSURE(condition, error) __S2N_ENSURE((condition), RESULT_BAIL(error)) - -/** - * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - */ -#define RESULT_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), RESULT_BAIL(error)) - -/** - * Ensures `s2n_result_is_ok(result)`, otherwise the function will `RESULT_BAIL` with `error` - * - * This can be useful for overriding the global `s2n_errno` - */ -#define RESULT_ENSURE_OK(result, error) __S2N_ENSURE(s2n_result_is_ok(result), RESULT_BAIL(error)) - -/** - * Ensures `a` is greater than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is less than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is greater than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is less than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is not equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `min <= n <= max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` - */ -#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - RESULT_ENSURE_GTE(__tmp_n, __tmp_min); \ - RESULT_ENSURE_LTE(__tmp_n, __tmp_max); \ - } while(0) - -/** - * Ensures `min < n < max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` - */ -#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - RESULT_ENSURE_GT(__tmp_n, __tmp_min); \ - RESULT_ENSURE_LT(__tmp_n, __tmp_max); \ - } while(0) - -/** - * Ensures `x` is a readable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` - */ -#define RESULT_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), RESULT_BAIL(S2N_ERR_NULL)) - -/** - * Ensures `x` is a mutable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` - */ -#define RESULT_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), RESULT_BAIL(S2N_ERR_NULL)) - -/** - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * `RESULT_PRECONDITION` should be used at the beginning of a function to make assertions about - * the provided arguments. By default, it is functionally equivalent to `RESULT_GUARD(result)` - * but can be altered by a testing environment to provide additional guarantees. - */ -#define RESULT_PRECONDITION(result) RESULT_GUARD(__S2N_ENSURE_PRECONDITION((result))) - -/** - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - * - * `RESULT_POSTCONDITION` should be used at the end of a function to make assertions about - * the resulting state. In debug mode, it is functionally equivalent to `RESULT_GUARD(result)`. - * In production builds, it becomes a no-op. This can also be altered by a testing environment - * to provide additional guarantees. - */ -#define RESULT_POSTCONDITION(result) RESULT_GUARD(__S2N_ENSURE_POSTCONDITION((result))) - -/** - * Performs a safer memcpy. - * - * The following checks are performed: - * - * * `destination` is non-null - * * `source` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by both the `destination` and `source` parameters, - * shall be at least `len` bytes. - */ -#define RESULT_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), RESULT_ENSURE_REF) - -/** - * Performs a safer memset - * - * The following checks are performed: - * - * * `destination` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by the `destination` parameter shall be at least - * `len` bytes. - */ -#define RESULT_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), RESULT_ENSURE_REF) - -/** - * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` - */ -#define RESULT_GUARD(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) - -/** - * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `RESULT_BAIL` with `error` - */ -#define RESULT_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, RESULT_BAIL(error)) - -/** - * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_RESULT_ERROR` - */ -#define RESULT_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) - -/** - * Ensures `(result) != NULL`, otherwise the function will return `S2N_RESULT_ERROR` - * - * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for RESULT_ENSURE_REF. - */ -#define RESULT_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Sets the global `s2n_errno` to `error` and returns with an `S2N_FAILURE` - */ -#define POSIX_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE); } while (0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` - */ -#define POSIX_ENSURE(condition, error) __S2N_ENSURE((condition), POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - */ -#define POSIX_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) > S2N_FAILURE`, otherwise the function will `POSIX_BAIL` with `error` - * - * This can be useful for overriding the global `s2n_errno` - */ -#define POSIX_ENSURE_OK(result, error) __S2N_ENSURE((result) > S2N_FAILURE, POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is not equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min <= n <= max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` - */ -#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - POSIX_ENSURE_GTE(__tmp_n, __tmp_min); \ - POSIX_ENSURE_LTE(__tmp_n, __tmp_max); \ - } while(0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min < n < max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` - */ -#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - POSIX_ENSURE_GT(__tmp_n, __tmp_min); \ - POSIX_ENSURE_LT(__tmp_n, __tmp_max); \ - } while(0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a readable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` - */ -#define POSIX_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), POSIX_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a mutable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` - */ -#define POSIX_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), POSIX_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * `POSIX_PRECONDITION` should be used at the beginning of a function to make assertions about - * the provided arguments. By default, it is functionally equivalent to `POSIX_GUARD_RESULT(result)` - * but can be altered by a testing environment to provide additional guarantees. - */ -#define POSIX_PRECONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - * - * `POSIX_POSTCONDITION` should be used at the end of a function to make assertions about - * the resulting state. In debug mode, it is functionally equivalent to `POSIX_GUARD_RESULT(result)`. - * In production builds, it becomes a no-op. This can also be altered by a testing environment - * to provide additional guarantees. - */ -#define POSIX_POSTCONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memcpy. - * - * The following checks are performed: - * - * * `destination` is non-null - * * `source` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by both the `destination` and `source` parameters, - * shall be at least `len` bytes. - */ -#define POSIX_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), POSIX_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memset - * - * The following checks are performed: - * - * * `destination` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by the `destination` parameter shall be at least - * `len` bytes. - */ -#define POSIX_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), POSIX_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_FAILURE` - */ -#define POSIX_GUARD(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` - */ -#define POSIX_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_FAILURE` - */ -#define POSIX_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) != NULL`, otherwise the function will return `S2N_FAILURE` - * - * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for POSIX_ENSURE_REF. - */ -#define POSIX_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Sets the global `s2n_errno` to `error` and returns with an `NULL` - */ -#define PTR_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(NULL); } while (0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` - */ -#define PTR_ENSURE(condition, error) __S2N_ENSURE((condition), PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - */ -#define PTR_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) != NULL`, otherwise the function will `PTR_BAIL` with `error` - * - * This can be useful for overriding the global `s2n_errno` - */ -#define PTR_ENSURE_OK(result, error) __S2N_ENSURE((result) != NULL, PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is not equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min <= n <= max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` - */ -#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - PTR_ENSURE_GTE(__tmp_n, __tmp_min); \ - PTR_ENSURE_LTE(__tmp_n, __tmp_max); \ - } while(0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min < n < max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` - */ -#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - PTR_ENSURE_GT(__tmp_n, __tmp_min); \ - PTR_ENSURE_LT(__tmp_n, __tmp_max); \ - } while(0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a readable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` - */ -#define PTR_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), PTR_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a mutable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` - */ -#define PTR_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), PTR_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * `PTR_PRECONDITION` should be used at the beginning of a function to make assertions about - * the provided arguments. By default, it is functionally equivalent to `PTR_GUARD_RESULT(result)` - * but can be altered by a testing environment to provide additional guarantees. - */ -#define PTR_PRECONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - * - * `PTR_POSTCONDITION` should be used at the end of a function to make assertions about - * the resulting state. In debug mode, it is functionally equivalent to `PTR_GUARD_RESULT(result)`. - * In production builds, it becomes a no-op. This can also be altered by a testing environment - * to provide additional guarantees. - */ -#define PTR_POSTCONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memcpy. - * - * The following checks are performed: - * - * * `destination` is non-null - * * `source` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by both the `destination` and `source` parameters, - * shall be at least `len` bytes. - */ -#define PTR_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), PTR_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memset - * - * The following checks are performed: - * - * * `destination` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by the `destination` parameter shall be at least - * `len` bytes. - */ -#define PTR_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), PTR_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) != NULL`, otherwise the function will return `NULL` - */ -#define PTR_GUARD(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `PTR_BAIL` with `error` - */ -#define PTR_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `NULL` - */ -#define PTR_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `NULL` - */ -#define PTR_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(NULL)) - + +/* + * 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 + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +/* clang-format off */ + +#include "error/s2n_errno.h" +#include "utils/s2n_ensure.h" +#include "utils/s2n_result.h" + +/** + * The goal of s2n_safety is to provide helpers to perform common + * checks, which help with code readability. + */ + +/* Success signal value for OpenSSL functions */ +#define _OSSL_SUCCESS 1 + +/** + * Sets the global `s2n_errno` to `error` and returns with an `S2N_RESULT_ERROR` + */ +#define RESULT_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR); } while (0) + +/** + * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + */ +#define RESULT_ENSURE(condition, error) __S2N_ENSURE((condition), RESULT_BAIL(error)) + +/** + * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define RESULT_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), RESULT_BAIL(error)) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will `RESULT_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define RESULT_ENSURE_OK(result, error) __S2N_ENSURE(s2n_result_is_ok(result), RESULT_BAIL(error)) + +/** + * Ensures `a` is greater than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is greater than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is not equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `min <= n <= max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + RESULT_ENSURE_GTE((n), (min)); \ + RESULT_ENSURE_LTE((n), (max)); \ + } while(0) +#else +#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + RESULT_ENSURE_GTE(__tmp_n, __tmp_min); \ + RESULT_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * Ensures `min < n < max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + RESULT_ENSURE_GT((n), (min)); \ + RESULT_ENSURE_LT((n), (max)); \ + } while(0) +#else +#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + RESULT_ENSURE_GT(__tmp_n, __tmp_min); \ + RESULT_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * Ensures `x` is a readable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + */ +#define RESULT_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), RESULT_BAIL(S2N_ERR_NULL)) + +/** + * Ensures `x` is a mutable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + */ +#define RESULT_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), RESULT_BAIL(S2N_ERR_NULL)) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `RESULT_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `RESULT_GUARD(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define RESULT_PRECONDITION(result) RESULT_GUARD(__S2N_ENSURE_PRECONDITION((result))) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `RESULT_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `RESULT_GUARD(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define RESULT_POSTCONDITION(result) RESULT_GUARD(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define RESULT_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), RESULT_ENSURE_REF) + +/** + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define RESULT_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), RESULT_ENSURE_REF) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) + +/** + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `RESULT_BAIL` with `error` + */ +#define RESULT_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, RESULT_BAIL(error)) + +/** + * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) + +/** + * Ensures `(result) != NULL`, otherwise the function will return `S2N_RESULT_ERROR` + * + * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for RESULT_ENSURE_REF. + */ +#define RESULT_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Sets the global `s2n_errno` to `error` and returns with an `S2N_FAILURE` + */ +#define POSIX_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE); } while (0) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + */ +#define POSIX_ENSURE(condition, error) __S2N_ENSURE((condition), POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define POSIX_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) > S2N_FAILURE`, otherwise the function will `POSIX_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define POSIX_ENSURE_OK(result, error) __S2N_ENSURE((result) > S2N_FAILURE, POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is not equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min <= n <= max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + POSIX_ENSURE_GTE((n), (min)); \ + POSIX_ENSURE_LTE((n), (max)); \ + } while(0) +#else +#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + POSIX_ENSURE_GTE(__tmp_n, __tmp_min); \ + POSIX_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min < n < max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + POSIX_ENSURE_GT((n), (min)); \ + POSIX_ENSURE_LT((n), (max)); \ + } while(0) +#else +#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + POSIX_ENSURE_GT(__tmp_n, __tmp_min); \ + POSIX_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a readable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + */ +#define POSIX_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), POSIX_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a mutable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + */ +#define POSIX_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), POSIX_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `POSIX_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `POSIX_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define POSIX_PRECONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `POSIX_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `POSIX_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define POSIX_POSTCONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define POSIX_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), POSIX_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define POSIX_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), POSIX_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` + */ +#define POSIX_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) != NULL`, otherwise the function will return `S2N_FAILURE` + * + * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for POSIX_ENSURE_REF. + */ +#define POSIX_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Sets the global `s2n_errno` to `error` and returns with an `NULL` + */ +#define PTR_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(NULL); } while (0) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + */ +#define PTR_ENSURE(condition, error) __S2N_ENSURE((condition), PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define PTR_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) != NULL`, otherwise the function will `PTR_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define PTR_ENSURE_OK(result, error) __S2N_ENSURE((result) != NULL, PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is not equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min <= n <= max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + PTR_ENSURE_GTE((n), (min)); \ + PTR_ENSURE_LTE((n), (max)); \ + } while(0) +#else +#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + PTR_ENSURE_GTE(__tmp_n, __tmp_min); \ + PTR_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min < n < max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + PTR_ENSURE_GT((n), (min)); \ + PTR_ENSURE_LT((n), (max)); \ + } while(0) +#else +#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + PTR_ENSURE_GT(__tmp_n, __tmp_min); \ + PTR_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a readable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + */ +#define PTR_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), PTR_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a mutable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + */ +#define PTR_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), PTR_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `PTR_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `PTR_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define PTR_PRECONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `PTR_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `PTR_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define PTR_POSTCONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define PTR_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), PTR_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define PTR_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), PTR_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) != NULL`, otherwise the function will return `NULL` + */ +#define PTR_GUARD(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `PTR_BAIL` with `error` + */ +#define PTR_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(NULL)) + diff --git a/utils/s2n_socket.c b/utils/s2n_socket.c index bef336e3b4c..2c7e0e8e462 100644 --- a/utils/s2n_socket.c +++ b/utils/s2n_socket.c @@ -1,228 +1,264 @@ -/* - * 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 "utils/s2n_socket.h" - -#include -#include -#include -#include - -#include "tls/s2n_connection.h" -#include "utils/s2n_safety.h" - -#if TCP_CORK - #define S2N_CORK TCP_CORK - #define S2N_CORK_ON 1 - #define S2N_CORK_OFF 0 -#elif TCP_NOPUSH - #define S2N_CORK TCP_NOPUSH - #define S2N_CORK_ON 1 - #define S2N_CORK_OFF 0 -#elif TCP_NODELAY - #define S2N_CORK TCP_NODELAY - #define S2N_CORK_ON 0 - #define S2N_CORK_OFF 1 -#endif - -int s2n_socket_quickack(struct s2n_connection *conn) -{ -#ifdef TCP_QUICKACK - POSIX_ENSURE_REF(conn); - if (!conn->managed_recv_io) { - return 0; - } - - struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; - POSIX_ENSURE_REF(r_io_ctx); - if (r_io_ctx->tcp_quickack_set) { - return 0; - } - - /* Ignore the return value, if it fails it fails */ - int optval = 1; - if (setsockopt(r_io_ctx->fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == 0) { - r_io_ctx->tcp_quickack_set = 1; - } -#endif - - return 0; -} - -int s2n_socket_write_snapshot(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - socklen_t corklen = sizeof(int); - POSIX_ENSURE_REF(conn); - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - getsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, &corklen); - POSIX_ENSURE_EQ(corklen, sizeof(int)); - w_io_ctx->original_cork_is_set = 1; -#endif - - return 0; -} - -int s2n_socket_read_snapshot(struct s2n_connection *conn) -{ -#ifdef SO_RCVLOWAT - socklen_t watlen = sizeof(int); - POSIX_ENSURE_REF(conn); - struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; - POSIX_ENSURE_REF(r_io_ctx); - - getsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, &watlen); - POSIX_ENSURE_EQ(watlen, sizeof(int)); - r_io_ctx->original_rcvlowat_is_set = 1; -#endif - - return 0; -} - -int s2n_socket_write_restore(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - POSIX_ENSURE_REF(conn); - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - if (!w_io_ctx->original_cork_is_set) { - return 0; - } - setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, sizeof(w_io_ctx->original_cork_val)); - w_io_ctx->original_cork_is_set = 0; -#endif - - return 0; -} - -int s2n_socket_read_restore(struct s2n_connection *conn) -{ -#ifdef SO_RCVLOWAT - POSIX_ENSURE_REF(conn); - struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; - POSIX_ENSURE_REF(r_io_ctx); - - if (!r_io_ctx->original_rcvlowat_is_set) { - return 0; - } - setsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, sizeof(r_io_ctx->original_rcvlowat_val)); - r_io_ctx->original_rcvlowat_is_set = 0; -#endif - - return 0; -} - -int s2n_socket_was_corked(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - /* If we're not using custom I/O and a send fd has not been set yet, return false*/ - if (!conn->managed_send_io || !conn->send) { - return 0; - } - - struct s2n_socket_write_io_context *io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(io_ctx); - - return io_ctx->original_cork_val; -} - -int s2n_socket_write_cork(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - POSIX_ENSURE_REF(conn); - int optval = S2N_CORK_ON; - - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - /* Ignore the return value, if it fails it fails */ - setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); -#endif - - return 0; -} - -int s2n_socket_write_uncork(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - POSIX_ENSURE_REF(conn); - int optval = S2N_CORK_OFF; - - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - /* Ignore the return value, if it fails it fails */ - setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); -#endif - - return 0; -} - -int s2n_socket_read(void *io_context, uint8_t *buf, uint32_t len) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(buf); - int rfd = ((struct s2n_socket_read_io_context *) io_context)->fd; - if (rfd < 0) { - errno = EBADF; - POSIX_BAIL(S2N_ERR_BAD_FD); - } - - /* Clear the quickack flag so we know to reset it */ - ((struct s2n_socket_read_io_context *) io_context)->tcp_quickack_set = 0; - - /* On success, the number of bytes read is returned. On failure, -1 is - * returned and errno is set appropriately. */ - ssize_t result = read(rfd, buf, len); - POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); - return result; -} - -int s2n_socket_write(void *io_context, const uint8_t *buf, uint32_t len) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(buf); - int wfd = ((struct s2n_socket_write_io_context *) io_context)->fd; - if (wfd < 0) { - errno = EBADF; - POSIX_BAIL(S2N_ERR_BAD_FD); - } - - /* On success, the number of bytes written is returned. On failure, -1 is - * returned and errno is set appropriately. */ - ssize_t result = write(wfd, buf, len); - POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); - return result; -} - -int s2n_socket_is_ipv6(int fd, uint8_t *ipv6) -{ - POSIX_ENSURE_REF(ipv6); - - socklen_t len = 0; - struct sockaddr_storage addr; - len = sizeof(addr); - POSIX_GUARD(getpeername(fd, (struct sockaddr *) &addr, &len)); - - *ipv6 = 0; - if (AF_INET6 == addr.ss_family) { - *ipv6 = 1; - } - - return 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 "utils/s2n_socket.h" + +#if defined(_MSC_VER) +#include +#include +#endif + + +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif + +#include "tls/s2n_connection.h" +#include "utils/s2n_safety.h" + +#if TCP_CORK + #define S2N_CORK TCP_CORK + #define S2N_CORK_ON 1 + #define S2N_CORK_OFF 0 +#elif TCP_NOPUSH + #define S2N_CORK TCP_NOPUSH + #define S2N_CORK_ON 1 + #define S2N_CORK_OFF 0 +#elif TCP_NODELAY + #define S2N_CORK TCP_NODELAY + #define S2N_CORK_ON 0 + #define S2N_CORK_OFF 1 +#endif + +int s2n_socket_quickack(struct s2n_connection *conn) +{ +#ifdef TCP_QUICKACK + POSIX_ENSURE_REF(conn); + if (!conn->managed_recv_io) { + return 0; + } + + struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; + POSIX_ENSURE_REF(r_io_ctx); + if (r_io_ctx->tcp_quickack_set) { + return 0; + } + + /* Ignore the return value, if it fails it fails */ + int optval = 1; + if (setsockopt(r_io_ctx->fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == 0) { + r_io_ctx->tcp_quickack_set = 1; + } +#endif + + return 0; +} + +int s2n_socket_write_snapshot(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + socklen_t corklen = sizeof(int); + POSIX_ENSURE_REF(conn); + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + getsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, &corklen); + POSIX_ENSURE_EQ(corklen, sizeof(int)); + w_io_ctx->original_cork_is_set = 1; +#endif + + return 0; +} + +int s2n_socket_read_snapshot(struct s2n_connection *conn) +{ +#ifdef SO_RCVLOWAT + socklen_t watlen = sizeof(int); + POSIX_ENSURE_REF(conn); + struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; + POSIX_ENSURE_REF(r_io_ctx); + + getsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, &watlen); + POSIX_ENSURE_EQ(watlen, sizeof(int)); + r_io_ctx->original_rcvlowat_is_set = 1; +#endif + + return 0; +} + +int s2n_socket_write_restore(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + POSIX_ENSURE_REF(conn); + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + if (!w_io_ctx->original_cork_is_set) { + return 0; + } + setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, sizeof(w_io_ctx->original_cork_val)); + w_io_ctx->original_cork_is_set = 0; +#endif + + return 0; +} + +int s2n_socket_read_restore(struct s2n_connection *conn) +{ +#ifdef SO_RCVLOWAT + POSIX_ENSURE_REF(conn); + struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; + POSIX_ENSURE_REF(r_io_ctx); + + if (!r_io_ctx->original_rcvlowat_is_set) { + return 0; + } + setsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, sizeof(r_io_ctx->original_rcvlowat_val)); + r_io_ctx->original_rcvlowat_is_set = 0; +#endif + + return 0; +} + +int s2n_socket_was_corked(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + /* If we're not using custom I/O and a send fd has not been set yet, return false*/ + if (!conn->managed_send_io || !conn->send) { + return 0; + } + + struct s2n_socket_write_io_context *io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(io_ctx); + + return io_ctx->original_cork_val; +} + +int s2n_socket_write_cork(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + POSIX_ENSURE_REF(conn); + int optval = S2N_CORK_ON; + + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + /* Ignore the return value, if it fails it fails */ + setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); +#endif + + return 0; +} + +int s2n_socket_write_uncork(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + POSIX_ENSURE_REF(conn); + int optval = S2N_CORK_OFF; + + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + /* Ignore the return value, if it fails it fails */ + setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); +#endif + + return 0; +} + +int s2n_socket_read(void *io_context, uint8_t *buf, uint32_t len) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(buf); + int rfd = ((struct s2n_socket_read_io_context *) io_context)->fd; + if (rfd < 0) { + errno = EBADF; + POSIX_BAIL(S2N_ERR_BAD_FD); + } + + /* Clear the quickack flag so we know to reset it */ + ((struct s2n_socket_read_io_context *) io_context)->tcp_quickack_set = 0; + + /* On success, the number of bytes read is returned. On failure, -1 is + * returned and errno is set appropriately. */ + #if defined(_MSC_VER) + ssize_t result = recv(rfd, (char *)buf, (int)len, 0); + if (result < 0) { + int wsa_err = WSAGetLastError(); + if (wsa_err == WSAEWOULDBLOCK) { errno = EWOULDBLOCK; } + else if (wsa_err == WSAECONNRESET) { errno = ECONNRESET; } + else if (wsa_err == WSAEINTR) { errno = EINTR; } + else { errno = EIO; } + } +#else + ssize_t result = read(rfd, buf, len); +#endif + POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); + return result; +} + +int s2n_socket_write(void *io_context, const uint8_t *buf, uint32_t len) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(buf); + int wfd = ((struct s2n_socket_write_io_context *) io_context)->fd; + if (wfd < 0) { + errno = EBADF; + POSIX_BAIL(S2N_ERR_BAD_FD); + } + + /* On success, the number of bytes written is returned. On failure, -1 is + * returned and errno is set appropriately. */ + #if defined(_MSC_VER) + ssize_t result = send(wfd, (const char *)buf, (int)len, 0); + if (result < 0) { + int wsa_err = WSAGetLastError(); + if (wsa_err == WSAEWOULDBLOCK) { errno = EWOULDBLOCK; } + else if (wsa_err == WSAECONNRESET) { errno = ECONNRESET; } + else if (wsa_err == WSAEINTR) { errno = EINTR; } + else { errno = EIO; } + } +#else + ssize_t result = write(wfd, buf, len); +#endif + POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); + return result; +} + +int s2n_socket_is_ipv6(int fd, uint8_t *ipv6) +{ + POSIX_ENSURE_REF(ipv6); + + socklen_t len = 0; + struct sockaddr_storage addr; + len = sizeof(addr); + POSIX_GUARD(getpeername(fd, (struct sockaddr *) &addr, &len)); + + *ipv6 = 0; + if (AF_INET6 == addr.ss_family) { + *ipv6 = 1; + } + + return 0; +} diff --git a/win_shim/mmap-windows.c b/win_shim/mmap-windows.c index 3f73bd4f62a..226868e9488 100644 --- a/win_shim/mmap-windows.c +++ b/win_shim/mmap-windows.c @@ -1,78 +1,78 @@ -/* mmap() replacement for Windows - * - * Author: Mike Frysinger - * Placed into the public domain - */ - -/* References: - * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx - * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx - * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx - * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx - */ - -#include -#include -#include - -#include "win_shim.h" - -void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) -{ - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) - return MAP_FAILED; - if (fd == -1) { - if (!(flags & MAP_ANON) || offset) - return MAP_FAILED; - } else if (flags & MAP_ANON) - return MAP_FAILED; - - DWORD flProtect; - if (prot & PROT_WRITE) { - if (prot & PROT_EXEC) - flProtect = PAGE_EXECUTE_READWRITE; - else - flProtect = PAGE_READWRITE; - } else if (prot & PROT_EXEC) { - if (prot & PROT_READ) - flProtect = PAGE_EXECUTE_READ; - else if (prot & PROT_EXEC) - flProtect = PAGE_EXECUTE; - } else - flProtect = PAGE_READONLY; - - off_t end = length + offset; - HANDLE mmap_fd, h; - if (fd == -1) - mmap_fd = INVALID_HANDLE_VALUE; - else - mmap_fd = (HANDLE)_get_osfhandle(fd); - h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); - if (h == NULL) - return MAP_FAILED; - - DWORD dwDesiredAccess; - if (prot & PROT_WRITE) - dwDesiredAccess = FILE_MAP_WRITE; - else - dwDesiredAccess = FILE_MAP_READ; - if (prot & PROT_EXEC) - dwDesiredAccess |= FILE_MAP_EXECUTE; - if (flags & MAP_PRIVATE) - dwDesiredAccess |= FILE_MAP_COPY; - void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); - if (ret == NULL) { - CloseHandle(h); - ret = MAP_FAILED; - } - return ret; -} - -void munmap(void *addr, size_t length) -{ - UnmapViewOfFile(addr); - /* ruh-ro, we leaked handle from CreateFileMapping() ... */ -} - -#undef DWORD_HI -#undef DWORD_LO +/* mmap() replacement for Windows + * + * Author: Mike Frysinger + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#include +#include +#include + +#include "win_shim.h" + +void *mmap(void *start, size_t length, int prot, int flags, int fd, size_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = (off_t)length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +void munmap(void *addr, size_t length) +{ + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} + +#undef DWORD_HI +#undef DWORD_LO diff --git a/win_shim/win_shim.h b/win_shim/win_shim.h index 9b9954ce1a9..dab5087a0bf 100644 --- a/win_shim/win_shim.h +++ b/win_shim/win_shim.h @@ -1,54 +1,87 @@ -#ifndef S2N_WIN_SHIM_H -#define S2N_WIN_SHIM_H - -#ifdef WIN32 - -#include - -struct iovec { - size_t iov_len; - void *iov_base; -}; - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif /* !MIN */ - -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif /* !MAX */ - -/* */ - -#define PROT_READ 0x1 -#define PROT_WRITE 0x2 -/* This flag is only available in WinXP+ */ -#ifdef FILE_MAP_EXECUTE - #define PROT_EXEC 0x4 -#else - #define PROT_EXEC 0x0 - #define FILE_MAP_EXECUTE 0 -#endif - -#define MAP_SHARED 0x01 -#define MAP_PRIVATE 0x02 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS -#define MAP_FAILED ((void *) -1) - -#ifdef __USE_FILE_OFFSET64 - # define DWORD_HI(x) (x >> 32) - # define DWORD_LO(x) ((x) & 0xffffffff) -#else - # define DWORD_HI(x) (0) - # define DWORD_LO(x) (x) -#endif - -void *mmap(void *, size_t, int, int, int, off_t); -void munmap(void *, size_t); - -/* */ - -#endif /* WIN32 */ - -#endif /* !S2N_WIN_SHIM_H */ +#ifndef S2N_WIN_SHIM_H +#define S2N_WIN_SHIM_H + +#ifdef WIN32 + +#include + +#include +#include +typedef SSIZE_T ssize_t; + +#ifndef SSIZE_MAX +#ifdef _WIN64 +#define SSIZE_MAX _I64_MAX +#else +#define SSIZE_MAX LONG_MAX +#endif +#endif + + +#ifndef __thread +#define __thread __declspec(thread) +#endif + + +struct iovec { + size_t iov_len; + void *iov_base; +}; + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* !MIN */ + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif /* !MAX */ + +/* */ + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE + #define PROT_EXEC 0x4 +#else + #define PROT_EXEC 0x0 + #define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +#ifdef __USE_FILE_OFFSET64 + # define DWORD_HI(x) (x >> 32) + # define DWORD_LO(x) ((x) & 0xffffffff) +#else + # define DWORD_HI(x) (0) + # define DWORD_LO(x) (x) +#endif + +void *mmap(void *, size_t, int, int, int, size_t); +void munmap(void *, size_t); + +/* */ + + + +#ifndef strncasecmp +#define strncasecmp _strnicmp +#endif + +#ifndef __builtin_expect +#define __builtin_expect(x, y) (x) +#endif + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif + +#endif /* WIN32 */ + +#endif /* !S2N_WIN_SHIM_H */ + From 115b08c7237c74fee2391c292d463eb9911aff74 Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Sat, 23 May 2026 18:33:17 +1200 Subject: [PATCH 4/6] Fix line endings --- .gitattributes | 1 + .gitignore | 1 + CMakeLists.txt | 1650 ++-- api/s2n.h | 8110 ++++++++--------- bin/common.c | 1156 +-- bin/echo.c | 964 +- bin/policy.c | 122 +- bin/s2nc.c | 1658 ++-- bin/s2nd.c | 1438 +-- cmake/modules/Findcrypto.cmake | 258 +- crypto/s2n_certificate.c | 1754 ++-- crypto/s2n_dhe.c | 768 +- crypto/s2n_hkdf.c | 790 +- crypto/s2n_locking.c | 240 +- crypto/s2n_openssl_x509.c | 300 +- crypto/s2n_pkey_evp.c | 748 +- error/s2n_errno.c | 1032 +-- stuffer/s2n_stuffer.c | 940 +- stuffer/s2n_stuffer.h | 484 +- stuffer/s2n_stuffer_file.c | 162 +- .../s2n_array_insert_harness.c | 124 +- .../s2n_hash_digest_size_harness.c | 66 +- .../s2n_stuffer_raw_write_harness.c | 108 +- .../s2n_stuffer_reserve_harness.c | 132 +- .../s2n_stuffer_reserve_space_harness.c | 128 +- .../s2n_stuffer_reserve_uint16_harness.c | 124 +- .../s2n_stuffer_reserve_uint24_harness.c | 122 +- .../s2n_stuffer_skip_write_harness.c | 98 +- .../s2n_stuffer_wipe_n_harness.c | 134 +- .../s2n_stuffer_write_harness.c | 128 +- .../s2n_stuffer_write_bytes_harness.c | 116 +- .../s2n_stuffer_write_hex_harness.c | 150 +- .../s2n_stuffer_write_uint16_hex_harness.c | 128 +- .../s2n_stuffer_write_uint8_hex_harness.c | 128 +- .../265b600e8ad749d27ba841994c25a73c8b12d33e | 2 +- .../8c306ddd7ba5001e3000c76750badae346ec258e | 4 +- tests/fuzz/s2n_cert_req_recv_test.c | 202 +- .../s2n_certificate_extensions_parse_test.c | 232 +- tests/fuzz/s2n_client_key_recv_fuzz_test.c | 298 +- tests/fuzz/s2n_select_server_cert_test.c | 560 +- tests/fuzz/s2n_tls13_cert_req_recv_test.c | 164 +- tests/fuzz/s2n_tls13_cert_verify_recv_test.c | 192 +- tests/pems/rsa_2048_pkcs1_cert_crlf.pem | 112 +- tests/pems/rsa_2048_pkcs1_key_crlf.pem | 56 +- tests/testlib/s2n_key_schedule_testlib.c | 158 +- tests/testlib/s2n_ktls_test_utils.c | 500 +- tests/testlib/s2n_test_certs.c | 532 +- tests/unit/s2n_async_pkey_test.c | 1452 +-- tests/unit/s2n_cbc_test.c | 220 +- tests/unit/s2n_cert_chain_and_key_load_test.c | 344 +- tests/unit/s2n_cert_chain_and_key_test.c | 458 +- tests/unit/s2n_cert_status_extension_test.c | 818 +- .../unit/s2n_cert_validation_callback_test.c | 1154 +-- tests/unit/s2n_cipher_suite_match_test.c | 2724 +++--- tests/unit/s2n_client_auth_handshake_test.c | 1066 +-- tests/unit/s2n_client_extensions_test.c | 2532 ++--- tests/unit/s2n_client_hello_recv_test.c | 1124 +-- tests/unit/s2n_client_hello_retry_test.c | 4022 ++++---- tests/unit/s2n_client_hello_test.c | 4292 ++++----- tests/unit/s2n_client_record_version_test.c | 632 +- .../s2n_client_secure_renegotiation_test.c | 620 +- tests/unit/s2n_config_test.c | 2770 +++--- .../s2n_connection_protocol_versions_test.c | 782 +- tests/unit/s2n_cookie_test.c | 754 +- tests/unit/s2n_crl_test.c | 1884 ++-- tests/unit/s2n_drain_alert_test.c | 282 +- tests/unit/s2n_handshake_invariant_test.c | 178 +- tests/unit/s2n_handshake_test.c | 1108 +-- tests/unit/s2n_handshake_type_test.c | 552 +- tests/unit/s2n_key_update_threads_test.c | 538 +- tests/unit/s2n_mem_allocator_test.c | 612 +- tests/unit/s2n_mem_usage_test.c | 420 +- tests/unit/s2n_mutual_auth_test.c | 1132 +-- tests/unit/s2n_openssl_x509_test.c | 478 +- tests/unit/s2n_optional_client_auth_test.c | 958 +- tests/unit/s2n_pem_rsa_dhe_test.c | 410 +- tests/unit/s2n_pem_test.c | 248 +- tests/unit/s2n_policy_defaults_test.c | 272 +- tests/unit/s2n_post_handshake_recv_test.c | 1032 +-- tests/unit/s2n_prf_key_material_test.c | 472 +- tests/unit/s2n_quic_support_io_test.c | 1218 +-- tests/unit/s2n_record_size_test.c | 1046 +-- tests/unit/s2n_recv_test.c | 1342 +-- .../unit/s2n_release_non_empty_buffers_test.c | 416 +- tests/unit/s2n_renegotiate_test.c | 1242 +-- tests/unit/s2n_rsa_pss_test.c | 702 +- tests/unit/s2n_seccomp_handshake_test.c | 172 +- tests/unit/s2n_self_talk_alerts_test.c | 672 +- tests/unit/s2n_self_talk_broken_pipe_test.c | 360 +- tests/unit/s2n_self_talk_certificates_test.c | 280 +- tests/unit/s2n_self_talk_custom_io_test.c | 442 +- .../s2n_self_talk_min_protocol_version_test.c | 260 +- tests/unit/s2n_self_talk_nonblocking_test.c | 786 +- tests/unit/s2n_self_talk_session_id_test.c | 1346 +-- tests/unit/s2n_self_talk_tls12_test.c | 414 +- tests/unit/s2n_send_key_update_test.c | 396 +- tests/unit/s2n_send_multirecord_test.c | 1166 +-- tests/unit/s2n_send_test.c | 1618 ++-- tests/unit/s2n_server_hello_retry_test.c | 1254 +-- tests/unit/s2n_session_ticket_test.c | 3590 ++++---- tests/unit/s2n_sslv3_test.c | 264 +- tests/unit/s2n_tls12_handshake_test.c | 1118 +-- tests/unit/s2n_tls13_cert_verify_test.c | 722 +- .../s2n_tls13_handshake_early_data_test.c | 714 +- .../s2n_tls13_handshake_state_machine_test.c | 2016 ++-- tests/unit/s2n_tls13_handshake_test.c | 664 +- tests/unit/s2n_tls13_parse_record_type_test.c | 514 +- tests/unit/s2n_tls13_pq_handshake_test.c | 1430 +-- tests/unit/s2n_tls13_server_cert_test.c | 400 +- tests/unit/s2n_tls_prf_test.c | 1058 +-- tests/unit/s2n_wildcard_hostname_test.c | 426 +- .../unit/s2n_x509_intent_verification_test.c | 1274 +-- ...09_validator_certificate_signatures_test.c | 440 +- tests/unit/s2n_x509_validator_test.c | 4618 +++++----- ...2n_x509_validator_time_verification_test.c | 574 +- tests/viz/s2n_state_machine_viz.c | 230 +- tls/extensions/s2n_client_psk.c | 844 +- tls/extensions/s2n_client_server_name.c | 206 +- .../s2n_client_supported_versions.c | 408 +- tls/extensions/s2n_extension_type.c | 516 +- tls/extensions/s2n_supported_versions.c | 78 +- tls/policy/s2n_policy_defaults.c | 258 +- tls/policy/s2n_policy_writer.c | 422 +- tls/s2n_alerts.c | 786 +- tls/s2n_async_pkey.h | 214 +- tls/s2n_cbc.c | 208 +- tls/s2n_cipher_preferences.c | 4238 ++++----- tls/s2n_client_hello.c | 2214 ++--- tls/s2n_client_key_exchange.c | 696 +- tls/s2n_config.c | 2690 +++--- tls/s2n_connection.c | 3790 ++++---- tls/s2n_early_data.c | 872 +- tls/s2n_early_data_io.c | 554 +- tls/s2n_ecc_preferences.h | 88 +- tls/s2n_fingerprint_ja4.c | 1204 +-- tls/s2n_handshake.c | 766 +- tls/s2n_handshake_io.c | 3504 +++---- tls/s2n_handshake_transcript.c | 260 +- tls/s2n_ktls.h | 162 +- tls/s2n_ktls_io.c | 998 +- tls/s2n_ocsp_stapling.c | 82 +- tls/s2n_post_handshake.c | 402 +- tls/s2n_prf.c | 1704 ++-- tls/s2n_psk.c | 1424 +-- tls/s2n_quic_support.c | 366 +- tls/s2n_record_read.c | 576 +- tls/s2n_record_read_stream.c | 188 +- tls/s2n_record_write.c | 1286 +-- tls/s2n_recv.c | 664 +- tls/s2n_resume.c | 2188 ++--- tls/s2n_send.c | 550 +- tls/s2n_server_hello.c | 688 +- tls/s2n_server_new_session_ticket.c | 888 +- tls/s2n_signature_scheme.h | 222 +- tls/s2n_tls13.h | 114 +- tls/s2n_tls13_certificate_verify.c | 424 +- tls/s2n_tls13_handshake.c | 446 +- tls/s2n_tls13_secrets.c | 1556 ++-- tls/s2n_x509_validator.c | 2338 ++--- utils/s2n_array.c | 444 +- utils/s2n_blob.c | 186 +- utils/s2n_compiler.h | 68 +- utils/s2n_ensure.h | 396 +- utils/s2n_init.c | 334 +- utils/s2n_mem.c | 798 +- utils/s2n_prelude.h | 108 +- utils/s2n_random.c | 830 +- utils/s2n_random.h | 74 +- utils/s2n_rfc5952.c | 276 +- utils/s2n_safety.h | 246 +- utils/s2n_safety_macros.h | 1318 +-- utils/s2n_socket.c | 528 +- win_shim/mmap-windows.c | 156 +- win_shim/win_shim.h | 174 +- 174 files changed, 73409 insertions(+), 73407 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..94f480de94e --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 73356385b7d..145f3c6a51c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +vcpkg* build_* cmake-build-* *.o diff --git a/CMakeLists.txt b/CMakeLists.txt index 294a6f5a817..91722dec136 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,825 +1,825 @@ -cmake_minimum_required (VERSION 3.10) -project (s2n C) - -if(POLICY CMP0077) - cmake_policy(SET CMP0077 NEW) #option does nothing when a normal variable of the same name exists. -endif() - -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") -set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") -set(INSTALL_CMAKE_DIR lib/cmake CACHE PATH "Installation directory for cmake files") - -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) - -# These Version numbers are for major updates only- we won't track minor/patch updates here. -set(VERSION_MAJOR 1) -set(VERSION_MINOR 0) -set(VERSION_PATCH 0) - -option(SEARCH_LIBCRYPTO "Set this if you want to let S2N search libcrypto for you, -otherwise a crypto target needs to be defined." ON) -option(UNSAFE_TREAT_WARNINGS_AS_ERRORS "Compiler warnings are treated as errors. Warnings may -indicate danger points where you should verify with the S2N-TLS developers that the security of -the library is not compromised. Turn this OFF to ignore warnings." ON) -option(S2N_WERROR_ALL "This option will cause all artifacts linked to libs2n to use the --Werror setting." OFF) -option(S2N_INTERN_LIBCRYPTO "This ensures that s2n-tls is compiled and deployed with a specific -version of libcrypto by interning the code and hiding symbols. This also enables s2n-tls to be -loaded in an application with an otherwise conflicting libcrypto version." OFF) -option(S2N_LTO, "Enables link time optimizations when building s2n-tls." OFF) -option(S2N_STACKTRACE "Enables stacktrace functionality in s2n-tls. Note that this functionality is -only available on platforms that support execinfo." ON) -option(S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE "No-op, retained for backwards compatibility. The custom RAND engine was removed -as part of the DRBG deprecation." ON) -option(S2N_ENFORCE_PROPER_LIBCRYPTO_FEATURE_PROBE "Assert that the feature probes are able to link to the libcrypto and -properly probe for feature support. If the feature probes are unable to properly probe for support, the build will -fail. This option ensures that s2n-tls doesn't silently build without properly probing for the support of important -features, such as TLS 1.3 support." OFF) -option(COVERAGE "Enable profiling collection for code coverage calculation" OFF) -option(BUILD_TESTING "Build tests for s2n-tls. By default only unit tests are built." ON) -option(S2N_INTEG_TESTS "Enable the integrationv2 tests" OFF) -option(S2N_FAST_INTEG_TESTS "Enable the integrationv2 with more parallelism, only has effect if S2N_INTEG_TESTS=ON" ON) -option(S2N_INSTALL_S2NC_S2ND "Install the binaries s2nc and s2nd" OFF) -option(S2N_USE_CRYPTO_SHARED_LIBS "For S2N to use shared libs in Findcrypto" OFF) -option(TSAN "Enable ThreadSanitizer to test thread safety" OFF) -option(ASAN "Enable AddressSanitizer to test memory safety" OFF) -option(SECCOMP "Link with seccomp and run seccomp tests" OFF) - -file(GLOB API_HEADERS "api/*.h") -file(GLOB API_UNSTABLE_HEADERS "api/unstable/*.h") - -file(GLOB CRYPTO_HEADERS "crypto/*.h") -file(GLOB CRYPTO_SRC "crypto/*.c") - -file(GLOB ERROR_HEADERS "error/*.h") -file(GLOB ERROR_SRC "error/*.c") - -file(GLOB STUFFER_HEADERS "stuffer/*.h") -file(GLOB STUFFER_SRC "stuffer/*.c") - -file(GLOB_RECURSE TLS_HEADERS "tls/*.h") -file(GLOB_RECURSE TLS_SRC "tls/*.c") - -file(GLOB UTILS_HEADERS "utils/*.h") -file(GLOB UTILS_SRC "utils/*.c") -if (WINDOWS) - list(APPEND UTILS_HEADERS "win_shim/win_shim.h") - list(APPEND UTILS_SRC "win_shim/mmap-windows.c") -endif (WINDOWS) - -message(STATUS "Detected CMAKE_SYSTEM_PROCESSOR as ${CMAKE_SYSTEM_PROCESSOR}") - -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - message(STATUS "Detected 32-Bit system") -else() - message(STATUS "Detected 64-Bit system") -endif() - -##be nice to visual studio users -if(MSVC) - source_group("Header Files\\s2n\\api" FILES ${API_HEADERS} ${API_UNSTABLE_HEADERS}) - source_group("Header Files\\s2n\\crypto" FILES ${CRYPTO_HEADERS}) - source_group("Header Files\\s2n\\error" FILES ${ERROR_HEADERS}) - source_group("Header Files\\s2n\\stuffer" FILES ${STUFFER_HEADERS}) - source_group("Header Files\\s2n\\tls" FILES ${TLS_HEADERS}) - source_group("Header Files\\s2n\\utils" FILES ${UTILS_HEADERS}) - - source_group("Source Files\\crypto" FILES ${CRYPTO_SRC}) - source_group("Source Files\\error" FILES ${ERROR_SRC}) - source_group("Source Files\\stuffer" FILES ${STUFFER_SRC}) - source_group("Source Files\\tls" FILES ${TLS_SRC}) - source_group("Source Files\\utils" FILES ${UTILS_SRC}) -else() - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) -endif() - -set(gcc_like "$") -set(msvc "$") - -if(APPLE) - set(OS_LIBS c Threads::Threads) -elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") - set(OS_LIBS thr execinfo) -elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") - set(OS_LIBS Threads::Threads) -elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") - set(OS_LIBS Threads::Threads kvm) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") - set(OS_LIBS Threads::Threads dl) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(OS_LIBS bcrypt) - # pthreads is stubbed for native MSVC -else() - set(OS_LIBS Threads::Threads dl rt) -endif() - -file(GLOB S2N_HEADERS - ${API_HEADERS} - ${API_UNSTABLE_HEADERS} - ${CRYPTO_HEADERS} - ${ERROR_HEADERS} - ${STUFFER_HEADERS} - ${TLS_HEADERS} - ${UTILS_HEADERS} -) - -file(GLOB S2N_SRC - ${CRYPTO_SRC} - ${ERROR_SRC} - ${STUFFER_SRC} - ${TLS_SRC} - ${UTILS_SRC} -) - -add_library(${PROJECT_NAME} ${S2N_HEADERS} ${S2N_SRC}) -set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C) - -# Version numbers are for major updates only- we won't track minor/patch updates here. -set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) - -set(CMAKE_C_FLAGS_DEBUGOPT "") - -if (NOT MSVC AND NOT CYGWIN AND NOT MSYS AND NOT MINGW) - target_compile_options(${PROJECT_NAME} PRIVATE - -pedantic - -std=gnu99 - -Wall - -Wcast-align - -Wcast-qual - -Wchar-subscripts - -Wcomment - -Wformat-security - -Wimplicit - -Wshadow - -Wsign-compare - -Wuninitialized - -Wunused - -Wwrite-strings - - # Assembler Options - -Wa,--noexecstack - - # Suppressed Warnings - -Wno-deprecated-declarations - # GCC 4 fails to parse our macros with a "missing-braces" error - -Wno-missing-braces - -Wno-strict-prototypes - -Wno-unknown-pragmas - ) -endif () - -if (S2N_WERROR_ALL) - target_compile_options(${PROJECT_NAME} PUBLIC - "$<${gcc_like}:$>" - "$<${msvc}:$>" - ) -elseif (UNSAFE_TREAT_WARNINGS_AS_ERRORS) - target_compile_options(${PROJECT_NAME} PRIVATE - "$<${gcc_like}:$>" - "$<${msvc}:$>" - ) -endif () - -if(BUILD_TESTING AND BUILD_SHARED_LIBS OR S2N_FUZZ_TEST) - target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=default) -else() - target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=hidden -DS2N_EXPORTS=1) -endif() - -if(S2N_LTO) - target_compile_options(${PROJECT_NAME} PRIVATE -flto) - # if we're building a static lib, make it easier for consuming applications to also perform LTO - if(NOT BUILD_SHARED_LIBS) - target_compile_options(${PROJECT_NAME} PRIVATE -ffunction-sections -fdata-sections) - endif() -endif() - -if(NOT APPLE AND NOT WINDOWS AND NOT CMAKE_SYSTEM_NAME STREQUAL "AIX") - set(CMAKE_SHARED_LINKER_FLAGS -Wl,-z,noexecstack,-z,relro,-z,now) -endif() - -# Whether to fail the build when compiling s2n's portable C code with non-portable assembly optimizations. Doing this -# can lead to runtime crashes if build artifacts are built on modern hardware, but deployed to older hardware without -# newer CPU instructions. s2n, by default, should be backwards compatible with older CPU types so this flag should be -# enabled in s2n's CI builds and tests, but other consumers of s2n may have stronger control of what CPU types they -# deploy to, and can enable more CPU optimizations. -if(S2N_BLOCK_NONPORTABLE_OPTIMIZATIONS) - target_compile_options(${PROJECT_NAME} PUBLIC -DS2N_BLOCK_NONPORTABLE_OPTIMIZATIONS=1) -endif() - -target_compile_options(${PROJECT_NAME} PRIVATE -fPIC) - -set(S2N_PRELUDE "${CMAKE_CURRENT_LIST_DIR}/utils/s2n_prelude.h") -target_compile_options(${PROJECT_NAME} PRIVATE -include "${S2N_PRELUDE}") - -# Match on Release, RelWithDebInfo and MinSizeRel -# See: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html#variable:CMAKE_BUILD_TYPE -if(CMAKE_BUILD_TYPE MATCHES Rel) - add_definitions(-DS2N_BUILD_RELEASE=1) -endif() - -if(NO_STACK_PROTECTOR) - target_compile_options(${PROJECT_NAME} PRIVATE -Wstack-protector -fstack-protector-all) -endif() - -if(S2N_FUZZ_TEST) - target_compile_definitions(${PROJECT_NAME} PUBLIC S2N_FUZZ_TESTING=1) - target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=fuzzer-no-link,leak,address,undefined) - target_link_libraries(${PROJECT_NAME} PUBLIC -fsanitize=fuzzer-no-link,leak,address,undefined) -endif() - -if(TSAN) - target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=thread -DS2N_THREAD_SANITIZER=1) - target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=thread) -endif() - -if(ASAN) - target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=address -DS2N_ADDRESS_SANITIZER=1) - target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=address) -endif() - -if (UBSAN) - target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=undefined -fno-sanitize-recover=all) - target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=undefined -fno-sanitize-recover=all) -endif() - -if(TSAN OR ASAN OR UBSAN) - # no-omit-frame-pointer and no-optimize-sibling-calls provide better stack traces - target_compile_options(${PROJECT_NAME} PUBLIC -fno-omit-frame-pointer -fno-optimize-sibling-calls) -endif() - -file(TO_CMAKE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules" MODULE_PATH) -set(CMAKE_MODULE_PATH "${MODULE_PATH}") - -if (COVERAGE) - # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html - # Coverage is done using LLVM source based coverage. This is only supported - # on LLVM compilers. GCC would fail with "unrecognized compile options" - # on -fprofile-instr-generate -fcoverage-mapping flags. - if (NOT ${CMAKE_C_COMPILER_ID} MATCHES Clang) - message(FATAL_ERROR "This project requires clang for coverage support. You are currently using " ${CMAKE_C_COMPILER_ID}) - endif() - target_compile_options(${PROJECT_NAME} PUBLIC -fprofile-instr-generate -fcoverage-mapping) - target_link_options(${PROJECT_NAME} PUBLIC -fprofile-instr-generate -fcoverage-mapping) -endif() - -if (NOT S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE) - # No-op: the custom RAND engine was removed as part of the DRBG - # deprecation. This option is retained so that existing builds - # setting S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE=0 continue to succeed. - message(STATUS "S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE=OFF (no-op, custom RAND engine was removed)") -endif() - -# For interning, we need to find the static libcrypto library. Cmake configs -# can branch on the variable BUILD_SHARED_LIBS to e.g. avoid having to define -# multiple targets. An example is AWS-LC: -# https://github.com/awslabs/aws-lc/blob/main/crypto/cmake/crypto-config.cmake#L5 -if (S2N_INTERN_LIBCRYPTO) - set(BUILD_SHARED_LIBS_BACKUP ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) -endif() - -# Work around target differences -if (TARGET crypto) - message(STATUS "S2N found target: crypto") - set(LINK_LIB "crypto") -elseif(DEFINED Z_VCPKG_EXECUTABLE) - find_package(OpenSSL REQUIRED) - message(STATUS "Using libcrypto from vcpkg") - set(LINK_LIB "OpenSSL::Crypto") -else() - find_package(crypto REQUIRED) - message(STATUS "Using libcrypto from the cmake path") - set(LINK_LIB "AWS::crypto") -endif() - -if (S2N_INTERN_LIBCRYPTO) - # Restore the old BUILD_SHARED_LIBS value - set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BACKUP}) - message(STATUS "Enabling libcrypto interning") -endif() - -if (NOT DEFINED CMAKE_AR) - message(STATUS "CMAKE_AR undefined, setting to `ar` by default") - SET(CMAKE_AR ar) -else() - message(STATUS "CMAKE_AR found: ${CMAKE_AR}") -endif() - -if (NOT DEFINED CMAKE_RANLIB) - message(STATUS "CMAKE_RANLIB undefined, setting to `ranlib` by default") - SET(CMAKE_RANLIB ranlib) -else() - message(STATUS "CMAKE_RANLIB found: ${CMAKE_RANLIB}") -endif() - -if (NOT DEFINED CMAKE_OBJCOPY) - message(STATUS "CMAKE_OBJCOPY undefined, setting to `objcopy` by default") - SET(CMAKE_OBJCOPY objcopy) -else() - message(STATUS "CMAKE_OBJCOPY found: ${CMAKE_OBJCOPY}") -endif() - -# Sets the result of the feature probe to `IS_AVAILABLE` -function(feature_probe_result PROBE_NAME IS_AVAILABLE) - # normalize the boolean value - if(IS_AVAILABLE) - set(NORMALIZED TRUE) - else() - set(NORMALIZED FALSE) - endif() - - # indicate the status of the probe - message(STATUS "feature ${PROBE_NAME}: ${NORMALIZED}") - # set the probe result in the parent scope for other probes - set(${PROBE_NAME} ${NORMALIZED} PARENT_SCOPE) - - # define the probe if available - if(NORMALIZED) - add_definitions(-D${PROBE_NAME}=1) - endif() -endfunction() - -# Tries to compile a feature probe and initializes the corresponding flags -function(feature_probe PROBE_NAME) - # Load the global probe flags - file(READ "${CMAKE_CURRENT_LIST_DIR}/tests/features/GLOBAL.flags" GLOBAL_FILE) - string(REPLACE "\n" "" GLOBAL_FLAGS "${GLOBAL_FILE}") - - # Load the probe's flags - file(READ "${CMAKE_CURRENT_LIST_DIR}/tests/features/${PROBE_NAME}.flags" PROBE_FILE) - string(REPLACE "\n" "" PROBE_FLAGS "${PROBE_FILE}") - - # Try to compile the probe with the given flags - try_compile( - IS_AVAILABLE - ${CMAKE_BINARY_DIR} - SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/${PROBE_NAME}.c" - LINK_LIBRARIES ${LINK_LIB} ${OS_LIBS} - CMAKE_FLAGS ${ADDITIONAL_FLAGS} - COMPILE_DEFINITIONS -I "${CMAKE_CURRENT_LIST_DIR}" -include "${CMAKE_CURRENT_LIST_DIR}/utils/s2n_prelude.h" -c ${GLOBAL_FLAGS} ${PROBE_FLAGS} - ${ARGN} - OUTPUT_VARIABLE TRY_COMPILE_OUTPUT - ) - # Uncomment the line below to get the output of the try_compile command - #message(STATUS "Output of try_compile: ${TRY_COMPILE_OUTPUT}") - - # Set the result of the probe - feature_probe_result(${PROBE_NAME} ${IS_AVAILABLE}) - - # Make sure the variable is set in the parent scope - set(${PROBE_NAME} ${IS_AVAILABLE} PARENT_SCOPE) - - # Set the flags that we used for the probe - set(${PROBE_NAME}_FLAGS ${PROBE_FLAGS} PARENT_SCOPE) - - set(${PROBE_NAME}_OUTPUT "${TRY_COMPILE_OUTPUT}" PARENT_SCOPE) -endfunction() - -# Iterate over all of the features and try to compile them -FILE(GLOB FEATURE_SRCS "${CMAKE_CURRENT_LIST_DIR}/tests/features/*.c") -list(SORT FEATURE_SRCS) -foreach(file ${FEATURE_SRCS}) - get_filename_component(feature_name ${file} NAME_WE) - feature_probe(${feature_name}) -endforeach() - -# Ensure that the feature probes were able to properly link to the libcrypto. -if(S2N_ENFORCE_PROPER_LIBCRYPTO_FEATURE_PROBE AND NOT S2N_LIBCRYPTO_SANITY_PROBE) - message(FATAL_ERROR "A sanity-check libcrypto feature probe failed, which indicates that other - feature probes were likely unable to probe the libcrypto for its supported features: - ${S2N_LIBCRYPTO_SANITY_PROBE_OUTPUT}") -endif() - -# FreeBSD might need to link to execinfo explicitly -if(NOT S2N_EXECINFO_AVAILABLE AND CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") - feature_probe(S2N_EXECINFO_AVAILABLE LINK_LIBRARIES execinfo) -endif() - -# Stack traces are only available if execinfo is -if (NOT S2N_EXECINFO_AVAILABLE) - set(S2N_STACKTRACE FALSE) -endif() -feature_probe_result(S2N_STACKTRACE ${S2N_STACKTRACE}) - -if (S2N_COMPILER_SUPPORTS_BRANCH_ALIGN AND NOT S2N_FUZZ_TEST) - target_compile_options(${PROJECT_NAME} PRIVATE "-Wa,-mbranches-within-32B-boundaries") -endif() - -if (S2N_INTERN_LIBCRYPTO) - - # Check if the AWS::crypto target has been added and handle it - if (TARGET AWS::crypto) - # Get the target library type (shared or static) - get_target_property(target_type AWS::crypto TYPE) - message(STATUS "AWS::crypto target type: ${target_type}") - - # If we didn't find the a target with static library type, fallback to - # existing crypto_STATIC_LIBRARY and crypto_INCLUDE_DIR - if (target_type STREQUAL STATIC_LIBRARY) - # We need an path to the include directory and libcrypto.a archive. - # The finder module defines these appropriately, but if we go through - # the target config we need to query this information from the target - # first. - get_target_property(crypto_STATIC_LIBRARY AWS::crypto LOCATION) - get_target_property(crypto_INCLUDE_DIR AWS::crypto INTERFACE_INCLUDE_DIRECTORIES) - endif() - endif() - - if (NOT crypto_STATIC_LIBRARY) - message(FATAL_ERROR "libcrypto interning requires a static build of libcrypto.a to be available") - endif() - - message(STATUS "crypto_STATIC_LIBRARY: ${crypto_STATIC_LIBRARY}") - message(STATUS "crypto_INCLUDE_DIR: ${crypto_INCLUDE_DIR}") - - # Don't call link_target_libraries here, just make sure the libcrypto include dir is in the path - include_directories("${crypto_INCLUDE_DIR}") - - add_custom_command( - OUTPUT libcrypto.symbols - COMMAND - # copy the static version of libcrypto - cp ${crypto_STATIC_LIBRARY} s2n_libcrypto.a && - # dump all of the symbols and prefix them with `s2n$` - bash -c "${CMAKE_NM} s2n_libcrypto.a | awk '/ [A-Z] /{if ($3) print $3\" s2n$\"$3}' | sort | uniq > libcrypto.symbols" && - # redefine the libcrypto library symbols - ${CMAKE_OBJCOPY} --redefine-syms libcrypto.symbols s2n_libcrypto.a && - rm -rf s2n_libcrypto && - mkdir s2n_libcrypto && - cd s2n_libcrypto && - # extract libcrypto objects from the archive - ${CMAKE_AR} x ../s2n_libcrypto.a && - # rename all of the object files so we don't have any object name collisions - bash -c "find . -name '*.o' -type f -print0 | xargs -0 -n1 -- basename | xargs -I{} mv {} s2n_crypto__{}" - VERBATIM - ) - - add_custom_target(s2n_libcrypto ALL - DEPENDS libcrypto.symbols - ) - add_dependencies(${PROJECT_NAME} s2n_libcrypto) - add_definitions(-DS2N_INTERN_LIBCRYPTO=1) - - if ((BUILD_SHARED_LIBS AND BUILD_TESTING) OR NOT BUILD_SHARED_LIBS) - # if libcrypto needs to be interned, rewrite libcrypto references so use of internal functions will link correctly - add_custom_command( - TARGET ${PROJECT_NAME} PRE_LINK - COMMAND - find "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}.dir" -name '*.c.o' -exec objcopy --redefine-syms libcrypto.symbols --preserve-dates {} \\\; - ) - endif() - - # copy the static libcrypto into the final artifact - if (BUILD_SHARED_LIBS) - if (BUILD_TESTING) - # if we're building tests, we export the prefixed symbols so tests can link to them - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS - "-Wl,--whole-archive s2n_libcrypto.a -Wl,--no-whole-archive") - else() - # if we're not building tests, then just copy the original archive, unmodified - set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS - "-Wl,--whole-archive ${crypto_STATIC_LIBRARY} -Wl,--no-whole-archive -Wl,--exclude-libs=ALL") - endif() - else() - # add all of the prefixed symbols to the archive - add_custom_command( - TARGET ${PROJECT_NAME} POST_BUILD - COMMAND - bash -c "${CMAKE_AR} -r $ s2n_libcrypto/*.o" - VERBATIM - ) - endif() -else() - # LINK_LIB is set above after checking targets. It handles the find_package craziness. - target_link_libraries(${PROJECT_NAME} PUBLIC ${LINK_LIB}) -endif() - -target_link_libraries(${PROJECT_NAME} PUBLIC ${OS_LIBS} m) - -target_include_directories(${PROJECT_NAME} PUBLIC $) -target_include_directories(${PROJECT_NAME} PUBLIC $ $) - -if (BUILD_TESTING AND NOT MSVC) - enable_testing() - - ############################################################################ - ################### build testlib (utility library) ######################## - ############################################################################ - - file(GLOB TESTLIB_SRC "tests/testlib/*.c") - file(GLOB EXAMPLES_SRC "docs/examples/*.c") - - add_library(testss2n STATIC ${TESTLIB_SRC} ${EXAMPLES_SRC}) - target_include_directories(testss2n PUBLIC tests) - target_compile_options(testss2n PRIVATE -std=gnu99) - # make sure all linked tests include the prelude - target_compile_options(testss2n PUBLIC -include "${S2N_PRELUDE}" -fPIC) - target_link_libraries(testss2n PUBLIC ${PROJECT_NAME}) - if (SECCOMP) - message(STATUS "Linking tests with seccomp") - target_link_libraries(testss2n PRIVATE seccomp) - target_compile_definitions(testss2n PRIVATE SECCOMP) - endif() - - if (S2N_INTERN_LIBCRYPTO) - # if libcrypto was interned, rewrite libcrypto symbols so use of internal - # functions will link correctly - add_custom_command( - TARGET testss2n POST_BUILD - COMMAND - objcopy --redefine-syms libcrypto.symbols lib/libtestss2n.a - ) - endif() - - ############################################################################ - ########################## configure unit tests ############################ - ############################################################################ - - # CTest configuration variables need to be set before include(CTest) is called - set(VALGRIND_DEFAULT " \ - --leak-check=full \ - --leak-resolution=high \ - --trace-children=yes \ - -q --error-exitcode=123 \ - --error-limit=no \ - --num-callers=40 \ - --undef-value-errors=no \ - --track-fds=yes \ - --log-fd=2 \ - --suppressions=valgrind.suppressions") - - # "pedantic valgrind" will error on memory that is "Still Reachable". - # We only run this on OpenSSL 1.1.1 because there are hundreds of false positives in other libcryptos. - # Tracking issue: https://github.com/aws/s2n-tls/issues/4777 - if ($ENV{S2N_LIBCRYPTO} MATCHES "openssl-1.1.1") - set(MEMORYCHECK_COMMAND_OPTIONS "${VALGRIND_DEFAULT} --run-libc-freeres=yes --errors-for-leak-kinds=all --show-leak-kinds=all") - else() - set(MEMORYCHECK_COMMAND_OPTIONS "${VALGRIND_DEFAULT} --run-libc-freeres=no") - endif() - - set(MEMORYCHECK_TYPE "Valgrind") - - set(UNIT_TEST_ENVS S2N_DONT_MLOCK=1) - if (TSAN OR ASAN) - set(UNIT_TEST_ENVS ${UNIT_TEST_ENVS} S2N_ADDRESS_SANITIZER=1) - endif() - if(TSAN) - set(TSAN_SUPPRESSIONS_FILE ${CMAKE_SOURCE_DIR}/tests/.tsan_suppressions) - if(NOT EXISTS ${TSAN_SUPPRESSIONS_FILE}) - message(FATAL_ERROR "TSAN suppression file ${TSAN_SUPPRESSIONS_FILE} missing") - endif() - set(TSAN_OPTIONS suppressions=${TSAN_SUPPRESSIONS_FILE}) - if(DEFINED ENV{TSAN_OPTIONS}) - set(TSAN_OPTIONS "${TSAN_OPTIONS} $ENV{TSAN_OPTIONS}") - endif() - set(UNIT_TEST_ENVS ${UNIT_TEST_ENVS} TSAN_OPTIONS=${TSAN_OPTIONS}) - endif() - if(ASAN) - # "detect_odr_violation" detects violations of the "one definition rule", - # ensuring that symbols are only defined once. - # But some of our unit tests intentionally include *.c files for testing, - # resulting in duplicate global values. - set(ASAN_OPTIONS detect_odr_violation=0) - if(DEFINED ENV{ASAN_OPTIONS}) - set(ASAN_OPTIONS "${ASAN_OPTIONS} $ENV{ASAN_OPTIONS}") - endif() - set(UNIT_TEST_ENVS ${UNIT_TEST_ENVS} ASAN_OPTIONS=${ASAN_OPTIONS}) - endif() - message(STATUS "Running tests with environment: ${UNIT_TEST_ENVS}") - - include(CTest) - - ############################################################################ - ############################ build unit tests ############################## - ############################################################################ - - file(GLOB UNITTESTS_SRC "tests/unit/*.c") - foreach(test_case ${UNITTESTS_SRC}) - # NAME_WE: name without extension - get_filename_component(test_case_name ${test_case} NAME_WE) - - add_executable(${test_case_name} ${test_case}) - target_link_libraries(${test_case_name} PRIVATE testss2n) - if (S2N_INTERN_LIBCRYPTO) - # if libcrypto was interned, rewrite libcrypto symbols so use of internal functions will link correctly - add_custom_command( - TARGET ${test_case_name} PRE_LINK - COMMAND - find . -name '${test_case_name}.c.o' -exec objcopy --redefine-syms libcrypto.symbols {} \\\; - ) - endif() - if (NOT MSVC AND NOT CYGWIN AND NOT MSYS AND NOT MINGW) - target_compile_options(${test_case_name} PRIVATE - -Wall -Wimplicit -Wunused -Wcomment -Wchar-subscripts -Wuninitialized - -Wshadow -Wcast-align -Wwrite-strings -Wformat-security - -Wno-deprecated-declarations -Wno-unknown-pragmas -Wno-deprecated -std=gnu99 -Wno-missing-braces - ) - endif() - if (UNSAFE_TREAT_WARNINGS_AS_ERRORS) - target_compile_options(${test_case_name} PRIVATE - "$<${gcc_like}:$>" - "$<${msvc}:$>" - ) - endif() - if (S2N_LTO) - target_compile_options(${test_case_name} PRIVATE -flto) - endif() - - add_test( - NAME ${test_case_name} - COMMAND $ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/unit - ) - set_property(TEST ${test_case_name} PROPERTY LABELS "unit") - set_property(TEST ${test_case_name} PROPERTY ENVIRONMENT ${UNIT_TEST_ENVS}) - - endforeach(test_case) - - ############################################################################ - ######################### build utility binaries ########################### - ############################################################################ - -if (NOT MSVC) - add_executable(s2nc "bin/s2nc.c" "bin/echo.c" "bin/https.c" "bin/common.c") - target_link_libraries(s2nc ${PROJECT_NAME}) - target_compile_options(s2nc PRIVATE -std=gnu99) - - add_executable(s2nd "bin/s2nd.c" "bin/echo.c" "bin/https.c" "bin/common.c") - target_link_libraries(s2nd ${PROJECT_NAME}) - target_compile_options(s2nd PRIVATE -std=gnu99) - - add_executable(policy "bin/policy.c") - target_link_libraries(policy ${PROJECT_NAME}) - target_compile_options(policy PRIVATE -std=gnu99) - - if(S2N_LTO) - target_compile_options(s2nc PRIVATE -flto) - target_compile_options(s2nd PRIVATE -flto) - endif() - endif() - - if (S2N_INTEG_TESTS) - find_package (Python3 COMPONENTS Interpreter Development) - file(GLOB integv2_test_files "${PROJECT_SOURCE_DIR}/tests/integrationv2/test_*.py") - set(N 1) - if (S2N_FAST_INTEG_TESTS) - set(N auto) - endif() - foreach(test_file_path ${integv2_test_files}) - get_filename_component(test_filename ${test_file_path} NAME_WE) - string(REGEX REPLACE "^test_" "integrationv2_" test_target ${test_filename}) - if (S2N_INTEG_NIX) - # For Nix and environments where LD_LIBRARY_PATH is already correct. - # We're also dropping tox and calling pytest directly, because - # Nix is already handling all of the python setup. - if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND ${test_target} STREQUAL "integrationv2_sslyze" ) - # sslyze/nassl is not available on aarch64. - message(WARNING "Skipping ${test_target} due to missing tools on ${CMAKE_SYSTEM_PROCESSOR}") - continue() - endif() - message(STATUS "Adding integ test ${test_target}") - add_test(NAME ${test_target} - COMMAND - pytest - -x -n=${N} --reruns=2 --durations=10 --cache-clear -rpfsq - --junitxml=../../build/junit/${test_target}.xml - -o log_cli=true --log-cli-level=DEBUG --provider-version=$ENV{S2N_LIBCRYPTO} - ${test_file_path} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integrationv2 - ) - else() - # For use with libcryptos built into test-deps, and not in LD_LIBRARY_PATH. - # This is a duplication of tests/integrationv2/Makefile and - # can go away once all the Nix porting is finished. - add_test(NAME ${test_target} - COMMAND - ${CMAKE_COMMAND} -E env - DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/libcrypto-root/lib:$ENV{DYLD_LIBRARY_PATH} - LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/libcrypto-root/lib:${PROJECT_SOURCE_DIR}/test-deps/openssl-1.1.1/lib:${PROJECT_SOURCE_DIR}/test-deps/gnutls37/nettle/lib:$ENV{LD_LIBRARY_PATH} - PATH=${PROJECT_SOURCE_DIR}/bin:${PROJECT_SOURCE_DIR}/test-deps/openssl-1.1.1/bin:${PROJECT_SOURCE_DIR}/test-deps/gnutls37/bin:$ENV{PATH} - PYTHONNOUSERSITE=1 - S2N_INTEG_TEST=1 - TOX_TEST_NAME=${test_file_path} - ${Python3_EXECUTABLE} -m tox - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integrationv2) - endif() - set_property(TEST ${test_target} PROPERTY LABELS "integrationv2") - set_property(TEST ${test_target} PROPERTY TIMEOUT 7200) - endforeach() - endif() - - if(S2N_FUZZ_TEST) - message(STATUS "Fuzz build enabled") - set(SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/runFuzzTest.sh") - file(GLOB FUZZ_TEST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/*.c") - - file(GLOB TESTLIB_SRC "tests/testlib/*.c") - file(GLOB TESTLIB_HEADERS "tests/testlib/*.h" "tests/s2n_test.h") - - # This must be a shared object so that symbols can be overridden by the - # fuzz test specific LD_PRELOAD libraries. - add_library(fuzztest SHARED ${TESTLIB_SRC}) - target_include_directories(fuzztest PUBLIC tests) - target_compile_options(fuzztest PUBLIC -include "${S2N_PRELUDE}" -fPIC) - target_link_libraries(fuzztest PUBLIC ${PROJECT_NAME}) - - # Set default values for fuzzing - if(DEFINED ENV{FUZZ_TIMEOUT_SEC}) - set(FUZZ_TIMEOUT_SEC $ENV{FUZZ_TIMEOUT_SEC}) - else() - set(FUZZ_TIMEOUT_SEC 60) - endif() - - # Build LD_PRELOAD shared libraries - file(GLOB LIBRARY_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/LD_PRELOAD/*.c") - foreach(SRC ${LIBRARY_SRCS}) - get_filename_component(LIB_NAME ${SRC} NAME_WE) - add_library(${LIB_NAME} SHARED ${SRC}) - target_include_directories(${LIB_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/api - ) - endforeach() - - set(CMAKE_C_COMPILER clang) - - foreach(src ${FUZZ_TEST_SRCS}) - get_filename_component(TEST_NAME ${src} NAME_WE) - - add_executable(${TEST_NAME} ${src}) - - target_compile_options(${TEST_NAME} PRIVATE - -g -Wno-unknown-pragmas -Wno-unused-result - ) - target_link_options(${TEST_NAME} PRIVATE - -fsanitize=fuzzer -lstdc++ - ) - target_link_libraries(${TEST_NAME} PRIVATE - fuzztest - ) - - add_test(NAME ${TEST_NAME} - COMMAND ${CMAKE_COMMAND} -E env - bash ${SCRIPT_PATH} - ${TEST_NAME} - ${FUZZ_TIMEOUT_SEC} - ${CMAKE_CURRENT_SOURCE_DIR} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz - ) - set_property(TEST ${TEST_NAME} PROPERTY LABELS "fuzz") - endforeach() - endif() -endif() - -#install the s2n files -install(FILES ${API_HEADERS} DESTINATION "include/" COMPONENT Development) -install(FILES ${API_UNSTABLE_HEADERS} DESTINATION "include/s2n/unstable" COMPONENT Development) - -if (UNIX AND NOT APPLE) - include(GNUInstallDirs) -elseif(NOT DEFINED CMAKE_INSTALL_LIBDIR) - set(CMAKE_INSTALL_LIBDIR "lib") -endif() - -if (S2N_INSTALL_S2NC_S2ND) - install( - TARGETS s2nc s2nd RUNTIME DESTINATION bin - ) -endif() - -install( - TARGETS ${PROJECT_NAME} - EXPORT ${PROJECT_NAME}-targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime - RUNTIME DESTINATION bin COMPONENT Runtime -) - -configure_file("cmake/${PROJECT_NAME}-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" - @ONLY) - -if (BUILD_SHARED_LIBS) - set (TARGET_DIR "shared") -else() - set (TARGET_DIR "static") -endif() - -install(EXPORT "${PROJECT_NAME}-targets" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/${TARGET_DIR}" - NAMESPACE AWS:: - COMPONENT Development) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/" - COMPONENT Development) - -install(FILES "cmake/modules/Findcrypto.cmake" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/modules/" - COMPONENT Development) +cmake_minimum_required (VERSION 3.10) +project (s2n C) + +if(POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) #option does nothing when a normal variable of the same name exists. +endif() + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") +set(INSTALL_CMAKE_DIR lib/cmake CACHE PATH "Installation directory for cmake files") + +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) + +# These Version numbers are for major updates only- we won't track minor/patch updates here. +set(VERSION_MAJOR 1) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) + +option(SEARCH_LIBCRYPTO "Set this if you want to let S2N search libcrypto for you, +otherwise a crypto target needs to be defined." ON) +option(UNSAFE_TREAT_WARNINGS_AS_ERRORS "Compiler warnings are treated as errors. Warnings may +indicate danger points where you should verify with the S2N-TLS developers that the security of +the library is not compromised. Turn this OFF to ignore warnings." ON) +option(S2N_WERROR_ALL "This option will cause all artifacts linked to libs2n to use the +-Werror setting." OFF) +option(S2N_INTERN_LIBCRYPTO "This ensures that s2n-tls is compiled and deployed with a specific +version of libcrypto by interning the code and hiding symbols. This also enables s2n-tls to be +loaded in an application with an otherwise conflicting libcrypto version." OFF) +option(S2N_LTO, "Enables link time optimizations when building s2n-tls." OFF) +option(S2N_STACKTRACE "Enables stacktrace functionality in s2n-tls. Note that this functionality is +only available on platforms that support execinfo." ON) +option(S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE "No-op, retained for backwards compatibility. The custom RAND engine was removed +as part of the DRBG deprecation." ON) +option(S2N_ENFORCE_PROPER_LIBCRYPTO_FEATURE_PROBE "Assert that the feature probes are able to link to the libcrypto and +properly probe for feature support. If the feature probes are unable to properly probe for support, the build will +fail. This option ensures that s2n-tls doesn't silently build without properly probing for the support of important +features, such as TLS 1.3 support." OFF) +option(COVERAGE "Enable profiling collection for code coverage calculation" OFF) +option(BUILD_TESTING "Build tests for s2n-tls. By default only unit tests are built." ON) +option(S2N_INTEG_TESTS "Enable the integrationv2 tests" OFF) +option(S2N_FAST_INTEG_TESTS "Enable the integrationv2 with more parallelism, only has effect if S2N_INTEG_TESTS=ON" ON) +option(S2N_INSTALL_S2NC_S2ND "Install the binaries s2nc and s2nd" OFF) +option(S2N_USE_CRYPTO_SHARED_LIBS "For S2N to use shared libs in Findcrypto" OFF) +option(TSAN "Enable ThreadSanitizer to test thread safety" OFF) +option(ASAN "Enable AddressSanitizer to test memory safety" OFF) +option(SECCOMP "Link with seccomp and run seccomp tests" OFF) + +file(GLOB API_HEADERS "api/*.h") +file(GLOB API_UNSTABLE_HEADERS "api/unstable/*.h") + +file(GLOB CRYPTO_HEADERS "crypto/*.h") +file(GLOB CRYPTO_SRC "crypto/*.c") + +file(GLOB ERROR_HEADERS "error/*.h") +file(GLOB ERROR_SRC "error/*.c") + +file(GLOB STUFFER_HEADERS "stuffer/*.h") +file(GLOB STUFFER_SRC "stuffer/*.c") + +file(GLOB_RECURSE TLS_HEADERS "tls/*.h") +file(GLOB_RECURSE TLS_SRC "tls/*.c") + +file(GLOB UTILS_HEADERS "utils/*.h") +file(GLOB UTILS_SRC "utils/*.c") +if (WINDOWS) + list(APPEND UTILS_HEADERS "win_shim/win_shim.h") + list(APPEND UTILS_SRC "win_shim/mmap-windows.c") +endif (WINDOWS) + +message(STATUS "Detected CMAKE_SYSTEM_PROCESSOR as ${CMAKE_SYSTEM_PROCESSOR}") + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + message(STATUS "Detected 32-Bit system") +else() + message(STATUS "Detected 64-Bit system") +endif() + +##be nice to visual studio users +if(MSVC) + source_group("Header Files\\s2n\\api" FILES ${API_HEADERS} ${API_UNSTABLE_HEADERS}) + source_group("Header Files\\s2n\\crypto" FILES ${CRYPTO_HEADERS}) + source_group("Header Files\\s2n\\error" FILES ${ERROR_HEADERS}) + source_group("Header Files\\s2n\\stuffer" FILES ${STUFFER_HEADERS}) + source_group("Header Files\\s2n\\tls" FILES ${TLS_HEADERS}) + source_group("Header Files\\s2n\\utils" FILES ${UTILS_HEADERS}) + + source_group("Source Files\\crypto" FILES ${CRYPTO_SRC}) + source_group("Source Files\\error" FILES ${ERROR_SRC}) + source_group("Source Files\\stuffer" FILES ${STUFFER_SRC}) + source_group("Source Files\\tls" FILES ${TLS_SRC}) + source_group("Source Files\\utils" FILES ${UTILS_SRC}) +else() + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) +endif() + +set(gcc_like "$") +set(msvc "$") + +if(APPLE) + set(OS_LIBS c Threads::Threads) +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(OS_LIBS thr execinfo) +elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") + set(OS_LIBS Threads::Threads) +elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + set(OS_LIBS Threads::Threads kvm) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(OS_LIBS Threads::Threads dl) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(OS_LIBS bcrypt) + # pthreads is stubbed for native MSVC +else() + set(OS_LIBS Threads::Threads dl rt) +endif() + +file(GLOB S2N_HEADERS + ${API_HEADERS} + ${API_UNSTABLE_HEADERS} + ${CRYPTO_HEADERS} + ${ERROR_HEADERS} + ${STUFFER_HEADERS} + ${TLS_HEADERS} + ${UTILS_HEADERS} +) + +file(GLOB S2N_SRC + ${CRYPTO_SRC} + ${ERROR_SRC} + ${STUFFER_SRC} + ${TLS_SRC} + ${UTILS_SRC} +) + +add_library(${PROJECT_NAME} ${S2N_HEADERS} ${S2N_SRC}) +set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE C) + +# Version numbers are for major updates only- we won't track minor/patch updates here. +set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) +set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) + +set(CMAKE_C_FLAGS_DEBUGOPT "") + +if (NOT MSVC AND NOT CYGWIN AND NOT MSYS AND NOT MINGW) + target_compile_options(${PROJECT_NAME} PRIVATE + -pedantic + -std=gnu99 + -Wall + -Wcast-align + -Wcast-qual + -Wchar-subscripts + -Wcomment + -Wformat-security + -Wimplicit + -Wshadow + -Wsign-compare + -Wuninitialized + -Wunused + -Wwrite-strings + + # Assembler Options + -Wa,--noexecstack + + # Suppressed Warnings + -Wno-deprecated-declarations + # GCC 4 fails to parse our macros with a "missing-braces" error + -Wno-missing-braces + -Wno-strict-prototypes + -Wno-unknown-pragmas + ) +endif () + +if (S2N_WERROR_ALL) + target_compile_options(${PROJECT_NAME} PUBLIC + "$<${gcc_like}:$>" + "$<${msvc}:$>" + ) +elseif (UNSAFE_TREAT_WARNINGS_AS_ERRORS) + target_compile_options(${PROJECT_NAME} PRIVATE + "$<${gcc_like}:$>" + "$<${msvc}:$>" + ) +endif () + +if(BUILD_TESTING AND BUILD_SHARED_LIBS OR S2N_FUZZ_TEST) + target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=default) +else() + target_compile_options(${PROJECT_NAME} PRIVATE -fvisibility=hidden -DS2N_EXPORTS=1) +endif() + +if(S2N_LTO) + target_compile_options(${PROJECT_NAME} PRIVATE -flto) + # if we're building a static lib, make it easier for consuming applications to also perform LTO + if(NOT BUILD_SHARED_LIBS) + target_compile_options(${PROJECT_NAME} PRIVATE -ffunction-sections -fdata-sections) + endif() +endif() + +if(NOT APPLE AND NOT WINDOWS AND NOT CMAKE_SYSTEM_NAME STREQUAL "AIX") + set(CMAKE_SHARED_LINKER_FLAGS -Wl,-z,noexecstack,-z,relro,-z,now) +endif() + +# Whether to fail the build when compiling s2n's portable C code with non-portable assembly optimizations. Doing this +# can lead to runtime crashes if build artifacts are built on modern hardware, but deployed to older hardware without +# newer CPU instructions. s2n, by default, should be backwards compatible with older CPU types so this flag should be +# enabled in s2n's CI builds and tests, but other consumers of s2n may have stronger control of what CPU types they +# deploy to, and can enable more CPU optimizations. +if(S2N_BLOCK_NONPORTABLE_OPTIMIZATIONS) + target_compile_options(${PROJECT_NAME} PUBLIC -DS2N_BLOCK_NONPORTABLE_OPTIMIZATIONS=1) +endif() + +target_compile_options(${PROJECT_NAME} PRIVATE -fPIC) + +set(S2N_PRELUDE "${CMAKE_CURRENT_LIST_DIR}/utils/s2n_prelude.h") +target_compile_options(${PROJECT_NAME} PRIVATE -include "${S2N_PRELUDE}") + +# Match on Release, RelWithDebInfo and MinSizeRel +# See: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html#variable:CMAKE_BUILD_TYPE +if(CMAKE_BUILD_TYPE MATCHES Rel) + add_definitions(-DS2N_BUILD_RELEASE=1) +endif() + +if(NO_STACK_PROTECTOR) + target_compile_options(${PROJECT_NAME} PRIVATE -Wstack-protector -fstack-protector-all) +endif() + +if(S2N_FUZZ_TEST) + target_compile_definitions(${PROJECT_NAME} PUBLIC S2N_FUZZ_TESTING=1) + target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=fuzzer-no-link,leak,address,undefined) + target_link_libraries(${PROJECT_NAME} PUBLIC -fsanitize=fuzzer-no-link,leak,address,undefined) +endif() + +if(TSAN) + target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=thread -DS2N_THREAD_SANITIZER=1) + target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=thread) +endif() + +if(ASAN) + target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=address -DS2N_ADDRESS_SANITIZER=1) + target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=address) +endif() + +if (UBSAN) + target_compile_options(${PROJECT_NAME} PUBLIC -fsanitize=undefined -fno-sanitize-recover=all) + target_link_options(${PROJECT_NAME} PUBLIC -fsanitize=undefined -fno-sanitize-recover=all) +endif() + +if(TSAN OR ASAN OR UBSAN) + # no-omit-frame-pointer and no-optimize-sibling-calls provide better stack traces + target_compile_options(${PROJECT_NAME} PUBLIC -fno-omit-frame-pointer -fno-optimize-sibling-calls) +endif() + +file(TO_CMAKE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules" MODULE_PATH) +set(CMAKE_MODULE_PATH "${MODULE_PATH}") + +if (COVERAGE) + # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html + # Coverage is done using LLVM source based coverage. This is only supported + # on LLVM compilers. GCC would fail with "unrecognized compile options" + # on -fprofile-instr-generate -fcoverage-mapping flags. + if (NOT ${CMAKE_C_COMPILER_ID} MATCHES Clang) + message(FATAL_ERROR "This project requires clang for coverage support. You are currently using " ${CMAKE_C_COMPILER_ID}) + endif() + target_compile_options(${PROJECT_NAME} PUBLIC -fprofile-instr-generate -fcoverage-mapping) + target_link_options(${PROJECT_NAME} PUBLIC -fprofile-instr-generate -fcoverage-mapping) +endif() + +if (NOT S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE) + # No-op: the custom RAND engine was removed as part of the DRBG + # deprecation. This option is retained so that existing builds + # setting S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE=0 continue to succeed. + message(STATUS "S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE=OFF (no-op, custom RAND engine was removed)") +endif() + +# For interning, we need to find the static libcrypto library. Cmake configs +# can branch on the variable BUILD_SHARED_LIBS to e.g. avoid having to define +# multiple targets. An example is AWS-LC: +# https://github.com/awslabs/aws-lc/blob/main/crypto/cmake/crypto-config.cmake#L5 +if (S2N_INTERN_LIBCRYPTO) + set(BUILD_SHARED_LIBS_BACKUP ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) +endif() + +# Work around target differences +if (TARGET crypto) + message(STATUS "S2N found target: crypto") + set(LINK_LIB "crypto") +elseif(DEFINED Z_VCPKG_EXECUTABLE) + find_package(OpenSSL REQUIRED) + message(STATUS "Using libcrypto from vcpkg") + set(LINK_LIB "OpenSSL::Crypto") +else() + find_package(crypto REQUIRED) + message(STATUS "Using libcrypto from the cmake path") + set(LINK_LIB "AWS::crypto") +endif() + +if (S2N_INTERN_LIBCRYPTO) + # Restore the old BUILD_SHARED_LIBS value + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BACKUP}) + message(STATUS "Enabling libcrypto interning") +endif() + +if (NOT DEFINED CMAKE_AR) + message(STATUS "CMAKE_AR undefined, setting to `ar` by default") + SET(CMAKE_AR ar) +else() + message(STATUS "CMAKE_AR found: ${CMAKE_AR}") +endif() + +if (NOT DEFINED CMAKE_RANLIB) + message(STATUS "CMAKE_RANLIB undefined, setting to `ranlib` by default") + SET(CMAKE_RANLIB ranlib) +else() + message(STATUS "CMAKE_RANLIB found: ${CMAKE_RANLIB}") +endif() + +if (NOT DEFINED CMAKE_OBJCOPY) + message(STATUS "CMAKE_OBJCOPY undefined, setting to `objcopy` by default") + SET(CMAKE_OBJCOPY objcopy) +else() + message(STATUS "CMAKE_OBJCOPY found: ${CMAKE_OBJCOPY}") +endif() + +# Sets the result of the feature probe to `IS_AVAILABLE` +function(feature_probe_result PROBE_NAME IS_AVAILABLE) + # normalize the boolean value + if(IS_AVAILABLE) + set(NORMALIZED TRUE) + else() + set(NORMALIZED FALSE) + endif() + + # indicate the status of the probe + message(STATUS "feature ${PROBE_NAME}: ${NORMALIZED}") + # set the probe result in the parent scope for other probes + set(${PROBE_NAME} ${NORMALIZED} PARENT_SCOPE) + + # define the probe if available + if(NORMALIZED) + add_definitions(-D${PROBE_NAME}=1) + endif() +endfunction() + +# Tries to compile a feature probe and initializes the corresponding flags +function(feature_probe PROBE_NAME) + # Load the global probe flags + file(READ "${CMAKE_CURRENT_LIST_DIR}/tests/features/GLOBAL.flags" GLOBAL_FILE) + string(REPLACE "\n" "" GLOBAL_FLAGS "${GLOBAL_FILE}") + + # Load the probe's flags + file(READ "${CMAKE_CURRENT_LIST_DIR}/tests/features/${PROBE_NAME}.flags" PROBE_FILE) + string(REPLACE "\n" "" PROBE_FLAGS "${PROBE_FILE}") + + # Try to compile the probe with the given flags + try_compile( + IS_AVAILABLE + ${CMAKE_BINARY_DIR} + SOURCES "${CMAKE_CURRENT_LIST_DIR}/tests/features/${PROBE_NAME}.c" + LINK_LIBRARIES ${LINK_LIB} ${OS_LIBS} + CMAKE_FLAGS ${ADDITIONAL_FLAGS} + COMPILE_DEFINITIONS -I "${CMAKE_CURRENT_LIST_DIR}" -include "${CMAKE_CURRENT_LIST_DIR}/utils/s2n_prelude.h" -c ${GLOBAL_FLAGS} ${PROBE_FLAGS} + ${ARGN} + OUTPUT_VARIABLE TRY_COMPILE_OUTPUT + ) + # Uncomment the line below to get the output of the try_compile command + #message(STATUS "Output of try_compile: ${TRY_COMPILE_OUTPUT}") + + # Set the result of the probe + feature_probe_result(${PROBE_NAME} ${IS_AVAILABLE}) + + # Make sure the variable is set in the parent scope + set(${PROBE_NAME} ${IS_AVAILABLE} PARENT_SCOPE) + + # Set the flags that we used for the probe + set(${PROBE_NAME}_FLAGS ${PROBE_FLAGS} PARENT_SCOPE) + + set(${PROBE_NAME}_OUTPUT "${TRY_COMPILE_OUTPUT}" PARENT_SCOPE) +endfunction() + +# Iterate over all of the features and try to compile them +FILE(GLOB FEATURE_SRCS "${CMAKE_CURRENT_LIST_DIR}/tests/features/*.c") +list(SORT FEATURE_SRCS) +foreach(file ${FEATURE_SRCS}) + get_filename_component(feature_name ${file} NAME_WE) + feature_probe(${feature_name}) +endforeach() + +# Ensure that the feature probes were able to properly link to the libcrypto. +if(S2N_ENFORCE_PROPER_LIBCRYPTO_FEATURE_PROBE AND NOT S2N_LIBCRYPTO_SANITY_PROBE) + message(FATAL_ERROR "A sanity-check libcrypto feature probe failed, which indicates that other + feature probes were likely unable to probe the libcrypto for its supported features: + ${S2N_LIBCRYPTO_SANITY_PROBE_OUTPUT}") +endif() + +# FreeBSD might need to link to execinfo explicitly +if(NOT S2N_EXECINFO_AVAILABLE AND CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + feature_probe(S2N_EXECINFO_AVAILABLE LINK_LIBRARIES execinfo) +endif() + +# Stack traces are only available if execinfo is +if (NOT S2N_EXECINFO_AVAILABLE) + set(S2N_STACKTRACE FALSE) +endif() +feature_probe_result(S2N_STACKTRACE ${S2N_STACKTRACE}) + +if (S2N_COMPILER_SUPPORTS_BRANCH_ALIGN AND NOT S2N_FUZZ_TEST) + target_compile_options(${PROJECT_NAME} PRIVATE "-Wa,-mbranches-within-32B-boundaries") +endif() + +if (S2N_INTERN_LIBCRYPTO) + + # Check if the AWS::crypto target has been added and handle it + if (TARGET AWS::crypto) + # Get the target library type (shared or static) + get_target_property(target_type AWS::crypto TYPE) + message(STATUS "AWS::crypto target type: ${target_type}") + + # If we didn't find the a target with static library type, fallback to + # existing crypto_STATIC_LIBRARY and crypto_INCLUDE_DIR + if (target_type STREQUAL STATIC_LIBRARY) + # We need an path to the include directory and libcrypto.a archive. + # The finder module defines these appropriately, but if we go through + # the target config we need to query this information from the target + # first. + get_target_property(crypto_STATIC_LIBRARY AWS::crypto LOCATION) + get_target_property(crypto_INCLUDE_DIR AWS::crypto INTERFACE_INCLUDE_DIRECTORIES) + endif() + endif() + + if (NOT crypto_STATIC_LIBRARY) + message(FATAL_ERROR "libcrypto interning requires a static build of libcrypto.a to be available") + endif() + + message(STATUS "crypto_STATIC_LIBRARY: ${crypto_STATIC_LIBRARY}") + message(STATUS "crypto_INCLUDE_DIR: ${crypto_INCLUDE_DIR}") + + # Don't call link_target_libraries here, just make sure the libcrypto include dir is in the path + include_directories("${crypto_INCLUDE_DIR}") + + add_custom_command( + OUTPUT libcrypto.symbols + COMMAND + # copy the static version of libcrypto + cp ${crypto_STATIC_LIBRARY} s2n_libcrypto.a && + # dump all of the symbols and prefix them with `s2n$` + bash -c "${CMAKE_NM} s2n_libcrypto.a | awk '/ [A-Z] /{if ($3) print $3\" s2n$\"$3}' | sort | uniq > libcrypto.symbols" && + # redefine the libcrypto library symbols + ${CMAKE_OBJCOPY} --redefine-syms libcrypto.symbols s2n_libcrypto.a && + rm -rf s2n_libcrypto && + mkdir s2n_libcrypto && + cd s2n_libcrypto && + # extract libcrypto objects from the archive + ${CMAKE_AR} x ../s2n_libcrypto.a && + # rename all of the object files so we don't have any object name collisions + bash -c "find . -name '*.o' -type f -print0 | xargs -0 -n1 -- basename | xargs -I{} mv {} s2n_crypto__{}" + VERBATIM + ) + + add_custom_target(s2n_libcrypto ALL + DEPENDS libcrypto.symbols + ) + add_dependencies(${PROJECT_NAME} s2n_libcrypto) + add_definitions(-DS2N_INTERN_LIBCRYPTO=1) + + if ((BUILD_SHARED_LIBS AND BUILD_TESTING) OR NOT BUILD_SHARED_LIBS) + # if libcrypto needs to be interned, rewrite libcrypto references so use of internal functions will link correctly + add_custom_command( + TARGET ${PROJECT_NAME} PRE_LINK + COMMAND + find "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}.dir" -name '*.c.o' -exec objcopy --redefine-syms libcrypto.symbols --preserve-dates {} \\\; + ) + endif() + + # copy the static libcrypto into the final artifact + if (BUILD_SHARED_LIBS) + if (BUILD_TESTING) + # if we're building tests, we export the prefixed symbols so tests can link to them + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS + "-Wl,--whole-archive s2n_libcrypto.a -Wl,--no-whole-archive") + else() + # if we're not building tests, then just copy the original archive, unmodified + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS + "-Wl,--whole-archive ${crypto_STATIC_LIBRARY} -Wl,--no-whole-archive -Wl,--exclude-libs=ALL") + endif() + else() + # add all of the prefixed symbols to the archive + add_custom_command( + TARGET ${PROJECT_NAME} POST_BUILD + COMMAND + bash -c "${CMAKE_AR} -r $ s2n_libcrypto/*.o" + VERBATIM + ) + endif() +else() + # LINK_LIB is set above after checking targets. It handles the find_package craziness. + target_link_libraries(${PROJECT_NAME} PUBLIC ${LINK_LIB}) +endif() + +target_link_libraries(${PROJECT_NAME} PUBLIC ${OS_LIBS} m) + +target_include_directories(${PROJECT_NAME} PUBLIC $) +target_include_directories(${PROJECT_NAME} PUBLIC $ $) + +if (BUILD_TESTING AND NOT MSVC) + enable_testing() + + ############################################################################ + ################### build testlib (utility library) ######################## + ############################################################################ + + file(GLOB TESTLIB_SRC "tests/testlib/*.c") + file(GLOB EXAMPLES_SRC "docs/examples/*.c") + + add_library(testss2n STATIC ${TESTLIB_SRC} ${EXAMPLES_SRC}) + target_include_directories(testss2n PUBLIC tests) + target_compile_options(testss2n PRIVATE -std=gnu99) + # make sure all linked tests include the prelude + target_compile_options(testss2n PUBLIC -include "${S2N_PRELUDE}" -fPIC) + target_link_libraries(testss2n PUBLIC ${PROJECT_NAME}) + if (SECCOMP) + message(STATUS "Linking tests with seccomp") + target_link_libraries(testss2n PRIVATE seccomp) + target_compile_definitions(testss2n PRIVATE SECCOMP) + endif() + + if (S2N_INTERN_LIBCRYPTO) + # if libcrypto was interned, rewrite libcrypto symbols so use of internal + # functions will link correctly + add_custom_command( + TARGET testss2n POST_BUILD + COMMAND + objcopy --redefine-syms libcrypto.symbols lib/libtestss2n.a + ) + endif() + + ############################################################################ + ########################## configure unit tests ############################ + ############################################################################ + + # CTest configuration variables need to be set before include(CTest) is called + set(VALGRIND_DEFAULT " \ + --leak-check=full \ + --leak-resolution=high \ + --trace-children=yes \ + -q --error-exitcode=123 \ + --error-limit=no \ + --num-callers=40 \ + --undef-value-errors=no \ + --track-fds=yes \ + --log-fd=2 \ + --suppressions=valgrind.suppressions") + + # "pedantic valgrind" will error on memory that is "Still Reachable". + # We only run this on OpenSSL 1.1.1 because there are hundreds of false positives in other libcryptos. + # Tracking issue: https://github.com/aws/s2n-tls/issues/4777 + if ($ENV{S2N_LIBCRYPTO} MATCHES "openssl-1.1.1") + set(MEMORYCHECK_COMMAND_OPTIONS "${VALGRIND_DEFAULT} --run-libc-freeres=yes --errors-for-leak-kinds=all --show-leak-kinds=all") + else() + set(MEMORYCHECK_COMMAND_OPTIONS "${VALGRIND_DEFAULT} --run-libc-freeres=no") + endif() + + set(MEMORYCHECK_TYPE "Valgrind") + + set(UNIT_TEST_ENVS S2N_DONT_MLOCK=1) + if (TSAN OR ASAN) + set(UNIT_TEST_ENVS ${UNIT_TEST_ENVS} S2N_ADDRESS_SANITIZER=1) + endif() + if(TSAN) + set(TSAN_SUPPRESSIONS_FILE ${CMAKE_SOURCE_DIR}/tests/.tsan_suppressions) + if(NOT EXISTS ${TSAN_SUPPRESSIONS_FILE}) + message(FATAL_ERROR "TSAN suppression file ${TSAN_SUPPRESSIONS_FILE} missing") + endif() + set(TSAN_OPTIONS suppressions=${TSAN_SUPPRESSIONS_FILE}) + if(DEFINED ENV{TSAN_OPTIONS}) + set(TSAN_OPTIONS "${TSAN_OPTIONS} $ENV{TSAN_OPTIONS}") + endif() + set(UNIT_TEST_ENVS ${UNIT_TEST_ENVS} TSAN_OPTIONS=${TSAN_OPTIONS}) + endif() + if(ASAN) + # "detect_odr_violation" detects violations of the "one definition rule", + # ensuring that symbols are only defined once. + # But some of our unit tests intentionally include *.c files for testing, + # resulting in duplicate global values. + set(ASAN_OPTIONS detect_odr_violation=0) + if(DEFINED ENV{ASAN_OPTIONS}) + set(ASAN_OPTIONS "${ASAN_OPTIONS} $ENV{ASAN_OPTIONS}") + endif() + set(UNIT_TEST_ENVS ${UNIT_TEST_ENVS} ASAN_OPTIONS=${ASAN_OPTIONS}) + endif() + message(STATUS "Running tests with environment: ${UNIT_TEST_ENVS}") + + include(CTest) + + ############################################################################ + ############################ build unit tests ############################## + ############################################################################ + + file(GLOB UNITTESTS_SRC "tests/unit/*.c") + foreach(test_case ${UNITTESTS_SRC}) + # NAME_WE: name without extension + get_filename_component(test_case_name ${test_case} NAME_WE) + + add_executable(${test_case_name} ${test_case}) + target_link_libraries(${test_case_name} PRIVATE testss2n) + if (S2N_INTERN_LIBCRYPTO) + # if libcrypto was interned, rewrite libcrypto symbols so use of internal functions will link correctly + add_custom_command( + TARGET ${test_case_name} PRE_LINK + COMMAND + find . -name '${test_case_name}.c.o' -exec objcopy --redefine-syms libcrypto.symbols {} \\\; + ) + endif() + if (NOT MSVC AND NOT CYGWIN AND NOT MSYS AND NOT MINGW) + target_compile_options(${test_case_name} PRIVATE + -Wall -Wimplicit -Wunused -Wcomment -Wchar-subscripts -Wuninitialized + -Wshadow -Wcast-align -Wwrite-strings -Wformat-security + -Wno-deprecated-declarations -Wno-unknown-pragmas -Wno-deprecated -std=gnu99 -Wno-missing-braces + ) + endif() + if (UNSAFE_TREAT_WARNINGS_AS_ERRORS) + target_compile_options(${test_case_name} PRIVATE + "$<${gcc_like}:$>" + "$<${msvc}:$>" + ) + endif() + if (S2N_LTO) + target_compile_options(${test_case_name} PRIVATE -flto) + endif() + + add_test( + NAME ${test_case_name} + COMMAND $ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/unit + ) + set_property(TEST ${test_case_name} PROPERTY LABELS "unit") + set_property(TEST ${test_case_name} PROPERTY ENVIRONMENT ${UNIT_TEST_ENVS}) + + endforeach(test_case) + + ############################################################################ + ######################### build utility binaries ########################### + ############################################################################ + +if (NOT MSVC) + add_executable(s2nc "bin/s2nc.c" "bin/echo.c" "bin/https.c" "bin/common.c") + target_link_libraries(s2nc ${PROJECT_NAME}) + target_compile_options(s2nc PRIVATE -std=gnu99) + + add_executable(s2nd "bin/s2nd.c" "bin/echo.c" "bin/https.c" "bin/common.c") + target_link_libraries(s2nd ${PROJECT_NAME}) + target_compile_options(s2nd PRIVATE -std=gnu99) + + add_executable(policy "bin/policy.c") + target_link_libraries(policy ${PROJECT_NAME}) + target_compile_options(policy PRIVATE -std=gnu99) + + if(S2N_LTO) + target_compile_options(s2nc PRIVATE -flto) + target_compile_options(s2nd PRIVATE -flto) + endif() + endif() + + if (S2N_INTEG_TESTS) + find_package (Python3 COMPONENTS Interpreter Development) + file(GLOB integv2_test_files "${PROJECT_SOURCE_DIR}/tests/integrationv2/test_*.py") + set(N 1) + if (S2N_FAST_INTEG_TESTS) + set(N auto) + endif() + foreach(test_file_path ${integv2_test_files}) + get_filename_component(test_filename ${test_file_path} NAME_WE) + string(REGEX REPLACE "^test_" "integrationv2_" test_target ${test_filename}) + if (S2N_INTEG_NIX) + # For Nix and environments where LD_LIBRARY_PATH is already correct. + # We're also dropping tox and calling pytest directly, because + # Nix is already handling all of the python setup. + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND ${test_target} STREQUAL "integrationv2_sslyze" ) + # sslyze/nassl is not available on aarch64. + message(WARNING "Skipping ${test_target} due to missing tools on ${CMAKE_SYSTEM_PROCESSOR}") + continue() + endif() + message(STATUS "Adding integ test ${test_target}") + add_test(NAME ${test_target} + COMMAND + pytest + -x -n=${N} --reruns=2 --durations=10 --cache-clear -rpfsq + --junitxml=../../build/junit/${test_target}.xml + -o log_cli=true --log-cli-level=DEBUG --provider-version=$ENV{S2N_LIBCRYPTO} + ${test_file_path} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integrationv2 + ) + else() + # For use with libcryptos built into test-deps, and not in LD_LIBRARY_PATH. + # This is a duplication of tests/integrationv2/Makefile and + # can go away once all the Nix porting is finished. + add_test(NAME ${test_target} + COMMAND + ${CMAKE_COMMAND} -E env + DYLD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/libcrypto-root/lib:$ENV{DYLD_LIBRARY_PATH} + LD_LIBRARY_PATH=${PROJECT_SOURCE_DIR}/libcrypto-root/lib:${PROJECT_SOURCE_DIR}/test-deps/openssl-1.1.1/lib:${PROJECT_SOURCE_DIR}/test-deps/gnutls37/nettle/lib:$ENV{LD_LIBRARY_PATH} + PATH=${PROJECT_SOURCE_DIR}/bin:${PROJECT_SOURCE_DIR}/test-deps/openssl-1.1.1/bin:${PROJECT_SOURCE_DIR}/test-deps/gnutls37/bin:$ENV{PATH} + PYTHONNOUSERSITE=1 + S2N_INTEG_TEST=1 + TOX_TEST_NAME=${test_file_path} + ${Python3_EXECUTABLE} -m tox + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integrationv2) + endif() + set_property(TEST ${test_target} PROPERTY LABELS "integrationv2") + set_property(TEST ${test_target} PROPERTY TIMEOUT 7200) + endforeach() + endif() + + if(S2N_FUZZ_TEST) + message(STATUS "Fuzz build enabled") + set(SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/runFuzzTest.sh") + file(GLOB FUZZ_TEST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/*.c") + + file(GLOB TESTLIB_SRC "tests/testlib/*.c") + file(GLOB TESTLIB_HEADERS "tests/testlib/*.h" "tests/s2n_test.h") + + # This must be a shared object so that symbols can be overridden by the + # fuzz test specific LD_PRELOAD libraries. + add_library(fuzztest SHARED ${TESTLIB_SRC}) + target_include_directories(fuzztest PUBLIC tests) + target_compile_options(fuzztest PUBLIC -include "${S2N_PRELUDE}" -fPIC) + target_link_libraries(fuzztest PUBLIC ${PROJECT_NAME}) + + # Set default values for fuzzing + if(DEFINED ENV{FUZZ_TIMEOUT_SEC}) + set(FUZZ_TIMEOUT_SEC $ENV{FUZZ_TIMEOUT_SEC}) + else() + set(FUZZ_TIMEOUT_SEC 60) + endif() + + # Build LD_PRELOAD shared libraries + file(GLOB LIBRARY_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/LD_PRELOAD/*.c") + foreach(SRC ${LIBRARY_SRCS}) + get_filename_component(LIB_NAME ${SRC} NAME_WE) + add_library(${LIB_NAME} SHARED ${SRC}) + target_include_directories(${LIB_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/api + ) + endforeach() + + set(CMAKE_C_COMPILER clang) + + foreach(src ${FUZZ_TEST_SRCS}) + get_filename_component(TEST_NAME ${src} NAME_WE) + + add_executable(${TEST_NAME} ${src}) + + target_compile_options(${TEST_NAME} PRIVATE + -g -Wno-unknown-pragmas -Wno-unused-result + ) + target_link_options(${TEST_NAME} PRIVATE + -fsanitize=fuzzer -lstdc++ + ) + target_link_libraries(${TEST_NAME} PRIVATE + fuzztest + ) + + add_test(NAME ${TEST_NAME} + COMMAND ${CMAKE_COMMAND} -E env + bash ${SCRIPT_PATH} + ${TEST_NAME} + ${FUZZ_TIMEOUT_SEC} + ${CMAKE_CURRENT_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz + ) + set_property(TEST ${TEST_NAME} PROPERTY LABELS "fuzz") + endforeach() + endif() +endif() + +#install the s2n files +install(FILES ${API_HEADERS} DESTINATION "include/" COMPONENT Development) +install(FILES ${API_UNSTABLE_HEADERS} DESTINATION "include/s2n/unstable" COMPONENT Development) + +if (UNIX AND NOT APPLE) + include(GNUInstallDirs) +elseif(NOT DEFINED CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "lib") +endif() + +if (S2N_INSTALL_S2NC_S2ND) + install( + TARGETS s2nc s2nd RUNTIME DESTINATION bin + ) +endif() + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime +) + +configure_file("cmake/${PROJECT_NAME}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + @ONLY) + +if (BUILD_SHARED_LIBS) + set (TARGET_DIR "shared") +else() + set (TARGET_DIR "static") +endif() + +install(EXPORT "${PROJECT_NAME}-targets" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/${TARGET_DIR}" + NAMESPACE AWS:: + COMPONENT Development) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/" + COMPONENT Development) + +install(FILES "cmake/modules/Findcrypto.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/modules/" + COMPONENT Development) diff --git a/api/s2n.h b/api/s2n.h index 3874df2d405..9dc67848da6 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -1,4055 +1,4055 @@ -/* - * 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. - */ - -/** - * @file s2n.h - * s2n-tls is a C99 implementation of the TLS/SSL protocols that is designed to - * be simple, small, fast, and with security as a priority.
It is released and - * licensed under the Apache License 2.0. - */ - -#pragma once - -#ifndef S2N_API - /** - * Marks a function as belonging to the public s2n API. - */ - #define S2N_API -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#if defined(_MSC_VER) || defined(__MINGW32__) -#include -#else -#include -#endif - -/** - * Function return code - */ -#define S2N_SUCCESS 0 -/** - * Function return code - */ -#define S2N_FAILURE -1 - -/** - * Callback return code - */ -#define S2N_CALLBACK_BLOCKED -2 - -/** - * s2n minimum supported TLS record major version - */ -#define S2N_MINIMUM_SUPPORTED_TLS_RECORD_MAJOR_VERSION 2 - -/** - * s2n maximum supported TLS record major version - */ -#define S2N_MAXIMUM_SUPPORTED_TLS_RECORD_MAJOR_VERSION 3 - -/** - * s2n SSL 2.0 Version Constant - */ -#define S2N_SSLv2 20 - -/** - * s2n SSL 3.0 Version Constant - */ -#define S2N_SSLv3 30 - -/** - * s2n TLS 1.0 Version Constant - */ -#define S2N_TLS10 31 - -/** - * s2n TLS 1.1 Version Constant - */ -#define S2N_TLS11 32 - -/** - * s2n TLS 1.2 Version Constant - */ -#define S2N_TLS12 33 - -/** - * s2n TLS 1.3 Version Constant - */ -#define S2N_TLS13 34 - -/** - * s2n Unknown TLS Version - */ -#define S2N_UNKNOWN_PROTOCOL_VERSION 0 - -/** - * s2n-tls functions that return 'int' return 0 to indicate success and -1 to indicate - * failure. - * - * s2n-tls functions that return pointer types return NULL in the case of - * failure. - * - * When an s2n-tls function returns a failure, s2n_errno will be set to a value - * corresponding to the error. This error value can be translated into a string - * explaining the error in English by calling s2n_strerror(s2n_errno, "EN"). - * A string containing human readable error name; can be generated with `s2n_strerror_name`. - * A string containing internal debug information, including filename and line number, can be generated with `s2n_strerror_debug`. - * A string containing only the filename and line number can be generated with `s2n_strerror_source`. - * This string is useful to include when reporting issues to the s2n-tls development team. - * - * @warning To avoid possible confusion, s2n_errno should be cleared after processing an error: `s2n_errno = S2N_ERR_T_OK` - */ -extern __thread int s2n_errno; - -/** - * This function can be used instead of trying to resolve `s2n_errno` directly - * in runtimes where thread-local variables may not be easily accessible. - * - * @returns The address of the thread-local `s2n_errno` variable - */ -S2N_API extern int *s2n_errno_location(void); - -/** - * Used to help applications determine why an s2n-tls function failed. - * - * This enum is optimized for use in C switch statements. Each value in the enum represents - * an error "category". - * - * s2n-tls organizes errors into different "types" to allow applications to handle error - * values without catching all possibilities. Applications using non-blocking I/O should check - * the error type to determine if the I/O operation failed because it would block or for some other - * error. To retrieve the type for a given error use `s2n_error_get_type()`. Applications should - * perform any error handling logic using these high level types. - * - * See the [Error Handling](https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch03-error-handling.md) section for how the errors should be interpreted. - */ -typedef enum { - /** No error */ - S2N_ERR_T_OK = 0, - /** Underlying I/O operation failed, check system errno */ - S2N_ERR_T_IO, - /** EOF */ - S2N_ERR_T_CLOSED, - /** Underlying I/O operation would block */ - S2N_ERR_T_BLOCKED, - /** Incoming Alert */ - S2N_ERR_T_ALERT, - /** Failure in some part of the TLS protocol. Ex: CBC verification failure */ - S2N_ERR_T_PROTO, - /** Error internal to s2n-tls. A precondition could have failed. */ - S2N_ERR_T_INTERNAL, - /** User input error. Ex: Providing an invalid cipher preference version */ - S2N_ERR_T_USAGE -} s2n_error_type; - -/** - * Gets the category of error from an error. - * - * s2n-tls organizes errors into different "types" to allow applications to do logic on error values without catching all possibilities. - * Applications using non-blocking I/O should check error type to determine if the I/O operation failed because - * it would block or for some other error. - * - * @param error The error from s2n. Usually this is `s2n_errno`. - * @returns An s2n_error_type - */ -S2N_API extern int s2n_error_get_type(int error); - -/** - * An opaque configuration object, used by clients and servers for holding cryptographic certificates, keys and preferences. - */ -struct s2n_config; - -/** - * An opaque connection. Used to track each s2n connection. - */ -struct s2n_connection; - -/** - * Prevents S2N from calling `OPENSSL_init_crypto`/`OPENSSL_cleanup`/`EVP_cleanup` on OpenSSL versions - * prior to 1.1.x. This allows applications or languages that also init OpenSSL to interoperate - * with S2N. - * - * @warning This function must be called BEFORE s2n_init() to have any effect. It will return an error - * if s2n is already initialized. - * - * @note If you disable this and are using a version of OpenSSL/libcrypto < 1.1.x, you will - * be responsible for library init and cleanup (specifically `OPENSSL_add_all_algorithms()` - * or `OPENSSL_init_crypto()`, and EVP_* APIs will not be usable unless the library is initialized. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_crypto_disable_init(void); - -/** - * Prevents S2N from installing an atexit handler, which allows safe shutdown of S2N from within a - * re-entrant shared library - * - * @warning This function must be called BEFORE s2n_init() to have any effect. It will return an error - * if s2n is already initialized. - * - * @note When the atexit handler is disabled, callers are responsible for calling - * s2n_cleanup_final() to perform full library cleanup before process exit. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_disable_atexit(void); - -/** - * Fetches the OpenSSL version s2n-tls was compiled with. This can be used by applications to validate at runtime - * that the versions of s2n-tls and Openssl that they have loaded are correct. - * - * @returns the version number of OpenSSL that s2n-tls was compiled with - */ -S2N_API extern unsigned long s2n_get_openssl_version(void); - -/** - * Initializes the s2n-tls library and should be called once in your application, before any other s2n-tls - * functions are called. Failure to call s2n_init() will result in errors from other s2n-tls functions. - * - * @warning This function is not thread safe and should only be called once. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_init(void); - -/** - * @deprecated This function is a no-op. It previously cleaned up thread-local - * DRBG state, but the custom DRBG has been removed. Retained for API - * compatibility. Use s2n_cleanup_final() for full library cleanup. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cleanup(void); - -/* - * Performs a complete deinitialization and cleanup of the s2n-tls library. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cleanup_final(void); - -typedef enum { - S2N_FIPS_MODE_DISABLED = 0, - S2N_FIPS_MODE_ENABLED, -} s2n_fips_mode; - -/** - * Determines whether s2n-tls is operating in FIPS mode. - * - * s2n-tls enters FIPS mode on initialization when built with a version of AWS-LC that supports - * FIPS (https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/FIPS.md). FIPS mode controls - * some internal configuration related to FIPS support, like which random number generator is used. - * - * FIPS mode does not enforce the use of FIPS-approved cryptography. Applications attempting to use - * only FIPS-approved cryptography should also ensure that s2n-tls is configured to use a security - * policy that only supports FIPS-approved cryptography. - * - * @param fips_mode Set to the FIPS mode of s2n-tls. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API extern int s2n_get_fips_mode(s2n_fips_mode *fips_mode); - -/** - * Creates a new s2n_config object. This object can (and should) be associated with many connection - * objects. - * - * The returned config will be initialized with default system certificates in its trust store. - * - * The returned config should be freed with `s2n_config_free()` after it's no longer in use by any - * connection. - * - * @returns A new configuration object suitable for configuring connections and associating certs - * and keys. - */ -S2N_API extern struct s2n_config *s2n_config_new(void); - -/** - * Creates a new s2n_config object with minimal default options. - * - * This function has better performance than `s2n_config_new()` because it does not load default - * system certificates into the trust store by default. To add system certificates to this config, - * call `s2n_config_load_system_certs()`. - * - * The returned config should be freed with `s2n_config_free()` after it's no longer in use by any - * connection. - * - * @returns A new configuration object suitable for configuring connections and associating certs - * and keys. - */ -S2N_API extern struct s2n_config *s2n_config_new_minimal(void); - -/** - * Frees the memory associated with an `s2n_config` object. - * - * @param config The configuration object being freed - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_free(struct s2n_config *config); - -/** - * Frees the DH params associated with an `s2n_config` object. - * - * @param config The configuration object with DH params being freed - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_free_dhparams(struct s2n_config *config); - -/** - * Frees the certificate chain and key associated with an `s2n_config` object. - * - * @param config The configuration object with DH params being freed - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_free_cert_chain_and_key(struct s2n_config *config); - -/** - * Callback function type used to get the system time. - * - * @param void* A pointer to arbitrary data for use within the callback - * @param uint64_t* A pointer that the callback will set to the time in nanoseconds - * The function should return 0 on success and -1 on failure. - */ -typedef int (*s2n_clock_time_nanoseconds)(void *, uint64_t *); - -/** - * Cache callback function that allows the caller to retrieve SSL session data - * from a cache. - * - * The callback function takes six arguments: - * a pointer to the s2n_connection object, - * a pointer to arbitrary data for use within the callback, - * a pointer to a key which can be used to retrieve the cached entry, - * a 64 bit unsigned integer specifying the size of this key, - * a pointer to a memory location where the value should be stored, - * and a pointer to a 64 bit unsigned integer specifying the size of this value. - * - * Initially *value_size will be set to the amount of space allocated for the value, - * the callback should set *value_size to the actual size of the data returned. - * If there is insufficient space, -1 should be returned. - * If the cache is not ready to provide data for the request, - * S2N_CALLBACK_BLOCKED should be returned. - * - * This will cause s2n_negotiate() to return S2N_BLOCKED_ON_APPLICATION_INPUT. - */ -typedef int (*s2n_cache_retrieve_callback)(struct s2n_connection *conn, void *, const void *key, uint64_t key_size, void *value, uint64_t *value_size); - -/** - * Cache callback function that allows the caller to store SSL session data in a - * cache. - * - * The callback function takes seven arguments: - * a pointer to the s2n_connection object, - * a pointer to arbitrary data for use within the callback, - * a 64-bit unsigned integer specifying the number of seconds the session data may be stored for, - * a pointer to a key which can be used to retrieve the cached entry, - * a 64 bit unsigned integer specifying the size of this key, - * a pointer to a value which should be stored, - * and a 64 bit unsigned integer specified the size of this value. - */ -typedef int (*s2n_cache_store_callback)(struct s2n_connection *conn, void *, uint64_t ttl_in_seconds, const void *key, uint64_t key_size, const void *value, uint64_t value_size); - -/** -* Cache callback function that allows the caller to set a callback function -* that will be used to delete SSL session data from a cache. -* -* The callback function takes four arguments: -* a pointer to s2n_connection object, -* a pointer to arbitrary data for use within the callback, -* a pointer to a key which can be used to delete the cached entry, -* and a 64 bit unsigned integer specifying the size of this key. -*/ -typedef int (*s2n_cache_delete_callback)(struct s2n_connection *conn, void *, const void *key, uint64_t key_size); - -/** - * Allows the caller to set a callback function that will be used to get the - * system time. The time returned should be the number of nanoseconds since the - * Unix epoch (Midnight, January 1st, 1970). - * - * s2n-tls uses this clock for timestamps. - * - * @param config The configuration object being updated - * @param clock_fn The wall clock time callback function - * @param ctx An opaque pointer that the callback will be invoked with - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx); - -/** - * Allows the caller to set a callback function that will be used to get - * monotonic time. The monotonic time is the time since an arbitrary, unspecified - * point. Unlike wall clock time, it MUST never move backwards. - * - * s2n-tls uses this clock for timers. - * - * @param config The configuration object being updated - * @param clock_fn The monotonic time callback function - * @param ctx An opaque pointer that the callback will be invoked with - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx); - -/** - * Translates an s2n_error code to a human readable string explaining the error. - * - * @param error The error code to explain. Usually this is s2n_errno - * @param lang The language to explain the error code. Pass "EN" or NULL for English. - * @returns The error string - */ -S2N_API extern const char *s2n_strerror(int error, const char *lang); - -/** - * Translates an s2n_error code to a human readable string containing internal debug - * information, including file name and line number. This function is useful when - * reporting issues to the s2n-tls development team. - * - * @param error The error code to explain. Usually this is s2n_errno - * @param lang The language to explain the error code. Pass "EN" or NULL for English. - * @returns The error string - */ -S2N_API extern const char *s2n_strerror_debug(int error, const char *lang); - -/** - * Translates an s2n_error code to a human readable string. - * - * @param error The error code to explain. Usually this is s2n_errno - * @returns The error string - */ -S2N_API extern const char *s2n_strerror_name(int error); - -/** - * Translates an s2n_error code to a filename and line number. - * - * @param error The error code to explain. Usually this is s2n_errno. - * @returns The error string. - */ -S2N_API extern const char *s2n_strerror_source(int error); - -/** - * Opaque stack trace structure. - */ -struct s2n_stacktrace; - -/** - * Checks if s2n stack trace captures are enabled. - * - * @returns True if stack traces are enabled. False if they are disabled. - */ -S2N_API extern bool s2n_stack_traces_enabled(void); - -/** - * Configures the s2n stack trace captures option. - * - * @param newval Boolean to determine if stack traces should be enabled. True to enable them. False to disable them. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_stack_traces_enabled_set(bool newval); - -/** - * Calculates the s2n stack trace. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_calculate_stacktrace(void); - -/** - * Prints the s2n stack trace to a file. The file descriptor is expected to be - * open and ready for writing. - * - * @param fptr A pointer to the file s2n-tls should write the stack trace to. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_print_stacktrace(FILE *fptr); - -/** - * Clean up the memory used to contain the stack trace. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_free_stacktrace(void); - -/** - * Export the s2n_stacktrace. - * - * @param trace A pointer to the s2n_stacktrace to fill. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_get_stacktrace(struct s2n_stacktrace *trace); - -/** - * Allows the caller to set a callback function that will be used to store SSL - * session data in a cache. - * - * @param config The configuration object being updated - * @param cache_store_callback The cache store callback function. - * @param data An opaque context pointer that the callback will be invoked with. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_cache_store_callback(struct s2n_config *config, s2n_cache_store_callback cache_store_callback, void *data); - -/** - * Allows the caller to set a callback function that will be used to retrieve SSL - * session data from a cache. - * - * @param config The configuration object being updated - * @param cache_retrieve_callback The cache retrieve callback function. - * @param data An opaque context pointer that the callback will be invoked with. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_cache_retrieve_callback(struct s2n_config *config, s2n_cache_retrieve_callback cache_retrieve_callback, void *data); - -/** - * Allows the caller to set a callback function that will be used to delete SSL - * session data from a cache. - * - * @param config The configuration object being updated - * @param cache_delete_callback The cache delete callback function. - * @param data An opaque context pointer that the callback will be invoked with. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_cache_delete_callback(struct s2n_config *config, s2n_cache_delete_callback cache_delete_callback, void *data); - -/** - * Called when `s2n_init` is executed. - */ -typedef int (*s2n_mem_init_callback)(void); - -/** - * Will be called when `s2n_cleanup` is executed. - */ -typedef int (*s2n_mem_cleanup_callback)(void); - -/** - * A function that can allocate at least `requested` bytes of memory. - * - * It stores the location of that memory in **\*ptr** and the size of the allocated - * data in **\*allocated**. The function may choose to allocate more memory - * than was requested. s2n-tls will consider all allocated memory available for - * use, and will attempt to free all allocated memory when able. - */ -typedef int (*s2n_mem_malloc_callback)(void **ptr, uint32_t requested, uint32_t *allocated); - -/** - * Frees memory allocated by s2n_mem_malloc_callback. - */ -typedef int (*s2n_mem_free_callback)(void *ptr, uint32_t size); - -/** - * Allows the caller to override s2n-tls's internal memory handling functions. - * - * @warning This function must be called before s2n_init(). - * - * @param mem_init_callback The s2n_mem_init_callback - * @param mem_cleanup_callback The s2n_mem_cleanup_callback - * @param mem_malloc_callback The s2n_mem_malloc_callback - * @param mem_free_callback The s2n_mem_free_callback - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, - s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback); - -/** - * @deprecated No longer used. See `s2n_rand_set_callbacks`. - */ -typedef int (*s2n_rand_init_callback)(void); - -/** - * @deprecated No longer used. See `s2n_rand_set_callbacks`. - */ -typedef int (*s2n_rand_cleanup_callback)(void); - -/** - * @deprecated No longer used. See `s2n_rand_set_callbacks`. - */ -typedef int (*s2n_rand_seed_callback)(void *data, uint32_t size); - -/** - * @deprecated No longer used. See `s2n_rand_set_callbacks`. - */ -typedef int (*s2n_rand_mix_callback)(void *data, uint32_t size); - -/** - * Allows the caller to override s2n-tls's entropy functions. - * - * @deprecated Custom random callbacks are no longer supported. Randomness is - * now delegated directly to libcrypto or /dev/urandom. This function is a - * no-op kept for backwards compatibility. - * - * @param rand_init_callback The s2n_rand_init_callback - * @param rand_cleanup_callback The s2n_rand_cleanup_callback - * @param rand_seed_callback The s2n_rand_seed_callback - * @param rand_mix_callback The s2n_rand_mix_callback - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback, s2n_rand_cleanup_callback rand_cleanup_callback, - s2n_rand_seed_callback rand_seed_callback, s2n_rand_mix_callback rand_mix_callback); - -/** - * TLS extensions supported by s2n-tls - */ -typedef enum { - S2N_EXTENSION_SERVER_NAME = 0, - S2N_EXTENSION_MAX_FRAG_LEN = 1, - S2N_EXTENSION_OCSP_STAPLING = 5, - S2N_EXTENSION_SUPPORTED_GROUPS = 10, - S2N_EXTENSION_EC_POINT_FORMATS = 11, - S2N_EXTENSION_SIGNATURE_ALGORITHMS = 13, - S2N_EXTENSION_ALPN = 16, - S2N_EXTENSION_CERTIFICATE_TRANSPARENCY = 18, - S2N_EXTENSION_SUPPORTED_VERSIONS = 43, - S2N_EXTENSION_RENEGOTIATION_INFO = 65281, -} s2n_tls_extension_type; - -/** - * MFL configurations from https://datatracker.ietf.org/doc/html/rfc6066#section-4. - */ -typedef enum { - S2N_TLS_MAX_FRAG_LEN_512 = 1, - S2N_TLS_MAX_FRAG_LEN_1024 = 2, - S2N_TLS_MAX_FRAG_LEN_2048 = 3, - S2N_TLS_MAX_FRAG_LEN_4096 = 4, -} s2n_max_frag_len; - -/** - * Opaque certificate type. - */ -struct s2n_cert; - -/** - * Opaque certificate chain and key type. - */ -struct s2n_cert_chain_and_key; - -/** - * Opaque key type. - */ -struct s2n_pkey; - -/** - * Opaque public key type. - */ -typedef struct s2n_pkey s2n_cert_public_key; - -/** - * Opaque private key type. - */ -typedef struct s2n_pkey s2n_cert_private_key; - -/** - * Creates a new s2n_cert_chain_and_key object. This object can be associated - * with many config objects. It is used to represent a certificate and key pair. - * - * @returns A new object used to represent a certificate-chain/key pair - */ -S2N_API extern struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void); - -/** - * Associates a certificate chain and private key with an `s2n_cert_chain_and_key` object. - * - * `cert_chain_pem` should be a PEM encoded certificate chain, with the first - * certificate in the chain being your leaf certificate. `private_key_pem` - * should be a PEM encoded private key corresponding to the leaf certificate. - * - * @note Prefer using s2n_cert_chain_and_key_load_pem_bytes. - * - * @param chain_and_key The certificate chain and private key handle - * @param chain_pem A byte array of a PEM encoded certificate chain. - * @param private_key_pem A byte array of a PEM encoded key. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem); - -/** - * Associates a certificate chain and private key with an `s2n_cert_chain_and_key` object. - * - * `cert_chain_pem` should be a PEM encoded certificate chain, with the first - * certificate in the chain being your leaf certificate. `private_key_pem` - * should be a PEM encoded private key corresponding to the leaf certificate. - * - * @param chain_and_key The certificate chain and private key handle - * @param chain_pem A byte array of a PEM encoded certificate chain. - * @param chain_pem_len Size of `chain_pem` - * @param private_key_pem A byte array of a PEM encoded key. - * @param private_key_pem_len Size of `private_key_pem` - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_load_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len, uint8_t *private_key_pem, uint32_t private_key_pem_len); - -/** - * Associates a public certificate chain with a `s2n_cert_chain_and_key` object. It does - * NOT set a private key, so the connection will need to be configured to - * [offload private key operations](https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch12-private-key-ops.md). - * - * @param chain_and_key The certificate chain and private key handle - * @param chain_pem A byte array of a PEM encoded certificate chain. - * @param chain_pem_len Size of `chain_pem` - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_load_public_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len); - -/** - * Frees the memory associated with an `s2n_cert_chain_and_key` object. - * - * @param cert_and_key The certificate chain and private key handle - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key); - -/** - * Adds a context to the `s2n_cert_chain_and_key` object. - * - * @param cert_and_key The certificate chain and private key handle - * @param ctx An opaque pointer to user supplied data. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx); - -/** - * Get the user supplied context from the `s2n_cert_chain_and_key` object. - * - * @param cert_and_key The certificate chain and private key handle - * @returns The user supplied pointer from s2n_cert_chain_and_key_set_ctx() - */ -S2N_API extern void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key); - -/** - * Get the private key from the `s2n_cert_chain_and_key` object. - * - * @param cert_and_key The certificate chain and private key handle - * @returns A pointer to the `s2n_cert_private_key` - */ -S2N_API extern s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *cert_and_key); - -/** - * Set the raw OCSP stapling data for a certificate chain. - * - * @param chain_and_key The certificate chain handle - * @param data A pointer to the raw OCSP stapling data bytes. The data will be copied. - * @param length The length of the data bytes. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); - -/** - * Set the signed certificate timestamp (SCT) for a certificate chain. - * This is used for Certificate Transparency. - * - * @param chain_and_key The certificate chain handle - * @param data A pointer to the SCT data. The data will be copied. - * @param length The length of the data bytes. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); - -/** - * A callback function that is invoked if s2n-tls cannot resolve a conflict between - * two certificates with the same domain name. This function is invoked while certificates - * are added to an `s2n_config`. - * - * Currently, the only builtin resolution for domain name conflicts is certificate type(RSA, - * ECDSA, etc). The callback should return a pointer to the `s2n_cert_chain_and_key` that - * should be used for dns name `name`. - * - * If NULL is returned, the first certificate will be used. Typically an application - * will use properties like trust and expiry to implement tiebreaking. - */ -typedef struct s2n_cert_chain_and_key *(*s2n_cert_tiebreak_callback)(struct s2n_cert_chain_and_key *cert1, struct s2n_cert_chain_and_key *cert2, uint8_t *name, uint32_t name_len); - -/** - * Sets the `s2n_cert_tiebreak_callback` for resolving domain name conflicts. - * If no callback is set, the first certificate added for a domain name will always be preferred. - * - * @param config The configuration object being updated - * @param cert_tiebreak_cb The pointer to the certificate tiebreak function - * - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb); - -/** - * Associates a certificate chain and private key with an `s2n_config` object. - * Using this API, only one cert chain of each type (like ECDSA or RSA) may be associated with a config. - * `cert_chain_pem` should be a PEM encoded certificate chain, with the first certificate - * in the chain being your server's certificate. `private_key_pem` should be a - * PEM encoded private key corresponding to the server certificate. - * - * @deprecated Use s2n_config_add_cert_chain_and_key_to_store instead. - * - * @param config The configuration object being updated - * @param cert_chain_pem A byte array of a PEM encoded certificate chain. - * @param private_key_pem A byte array of a PEM encoded key. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API extern int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem); - -/** - * The preferred method of associating a certificate chain and private key pair with an `s2n_config` object. - * This method may be called multiple times to support multiple key types (RSA, RSA-PSS, ECDSA) and multiple - * domains. On the server side, the certificate selected will be based on the incoming SNI value and the - * client's capabilities (supported ciphers). - * - * In the case of no certificate matching the client's SNI extension or if no SNI extension was sent by - * the client, the certificate from the `first` call to `s2n_config_add_cert_chain_and_key_to_store()` - * will be selected. Use `s2n_config_set_cert_chain_and_key_defaults()` to set different defaults. - * - * @warning It is not recommended to free or modify the `cert_key_pair` as any subsequent changes will be - * reflected in the config. - * - * @param config The configuration object being updated - * @param cert_key_pair The certificate chain and private key handle - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair); - -/** - * Explicitly sets certificate chain and private key pairs to be used as defaults for each auth - * method (key type). A "default" certificate is used when there is not an SNI match with any other - * configured certificate. - * - * Only one certificate can be set as the default per auth method (one RSA default, one ECDSA default, - * etc.). All previous default certificates will be cleared and re-set when this API is called. - * - * This API is called for a specific `s2n_config` object. s2n-tls will attempt to automatically choose - * default certificates for each auth method (key type) based on the order that `s2n_cert_chain_and_key` - * are added to the `s2n_config` using one of the APIs listed above. - * `s2n_config_set_cert_chain_and_key_defaults` can be called at any time; s2n-tls will clear defaults - * and no longer attempt to automatically choose any default certificates. - * - * @param config The configuration object being updated - * @param cert_key_pairs An array of certificate chain and private key handles - * @param num_cert_key_pairs The amount of handles in cert_key_pairs - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, - struct s2n_cert_chain_and_key **cert_key_pairs, uint32_t num_cert_key_pairs); - -/** - * Adds to the trust store from a CA file or directory containing trusted certificates. - * - * When configs are created with `s2n_config_new()`, the trust store is initialized with default - * system certificates. To completely override these certificates, call - * `s2n_config_wipe_trust_store()` before calling this function. - * - * @note The trust store will be initialized with the common locations for the host - * operating system by default. - * - * @warning This API uses the PEM parsing implementation from the linked libcrypto. This - * implementation will typically make a best-effort attempt to parse all of the certificates in the - * provided file or directory. This permissive approach may silently ignore malformed certificates, - * leading to possible connection failures if a certificate was expected to exist in the trust - * store but was skipped while parsing. As such, this API should only be used on PEMs that are - * known to be well-formed and parsable with the linked libcrypto, such as the system trust store. - * For all other PEMs, `s2n_config_add_pem_to_trust_store()` should be used instead, which parses - * more strictly. - * - * @param config The configuration object being updated - * @param ca_pem_filename A string for the file path of the CA PEM file. - * @param ca_dir A string for the directory of the CA PEM files. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_verification_ca_location(struct s2n_config *config, const char *ca_pem_filename, const char *ca_dir); - -/** - * Adds a PEM to the trust store. This will allocate memory, and load `pem` into the trust store. - * - * When configs are created with `s2n_config_new()`, the trust store is initialized with default - * system certificates. To completely override these certificates, call - * `s2n_config_wipe_trust_store()` before calling this function. - * - * @note This API uses the s2n-tls PEM parsing implementation, which is more strict than typical - * libcrypto implementations such as OpenSSL. An error is returned if any unexpected data is - * encountered while parsing `pem`. This allows applications to be made aware of any malformed - * certificates rather than attempt to negotiate with a partial trust store. However, some PEMs may - * need to be loaded that are not under control of the application, such as system trust stores. In - * this case, `s2n_config_set_verification_ca_location()` may be used, which performs more widely - * compatible and permissive parsing from the linked libcrypto. - * - * @param config The configuration object being updated - * @param pem The string value of the PEM certificate. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_add_pem_to_trust_store(struct s2n_config *config, const char *pem); - -/** - * Clears the trust store of all certificates. - * - * When configs are created with `s2n_config_new()`, the trust store is initialized with default - * system certificates. To completely override these certificates, call this function before - * functions like `s2n_config_set_verification_ca_location()` or - * `s2n_config_add_pem_to_trust_store()`. - * - * @param config The configuration object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_wipe_trust_store(struct s2n_config *config); - -/** - * Loads default system certificates into the trust store. - * - * `s2n_config_new_minimal()` doesn't load default system certificates into the config's trust - * store by default. If `config` was created with `s2n_config_new_minimal`, this function can be - * used to load system certificates into the trust store. - * - * @note This API will error if called on a config that has already loaded system certificates - * into its trust store, which includes all configs created with `s2n_config_new()`. - * - * @param config The configuration object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_load_system_certs(struct s2n_config *config); - -typedef enum { - S2N_VERIFY_AFTER_SIGN_DISABLED, - S2N_VERIFY_AFTER_SIGN_ENABLED -} s2n_verify_after_sign; - -/** - * Toggle whether generated signatures are verified before being sent. - * - * Although signatures produced by the underlying libcrypto should always be valid, - * hardware faults, bugs in the signing implementation, or other uncommon factors - * can cause unexpected mistakes in the final signatures. Because these mistakes - * can leak information about the private key, applications with low trust in their - * hardware or libcrypto may want to verify signatures before sending them. - * - * However, this feature will significantly impact handshake latency. - * Additionally, most libcrypto implementations already check for common errors in signatures. - */ -S2N_API extern int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after_sign mode); - -/** - * Set a custom send buffer size. - * - * This buffer is used to stage records for sending. By default, - * enough memory is allocated to hold a single record of the maximum - * size configured for the connection. With the default fragment size, - * that is about 8K bytes. - * - * Less memory can be allocated for the send buffer, but this will result in - * smaller, more fragmented records and increased overhead. While the absolute - * minimum size required is 1034 bytes, at least 2K bytes is recommended for - * reasonable record sizes. - * - * More memory can be allocated for the send buffer. This will result in s2n-tls - * buffering multiple records before sending them, reducing system write calls. - * At least 17K bytes is recommended for this use case, or at least 35K bytes - * if larger fragment sizes are used via `s2n_connection_prefer_throughput()`. - * - * @param config The configuration object being updated - * @param size The desired custom buffer size. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_send_buffer_size(struct s2n_config *config, uint32_t size); - -/** - * Enable or disable receiving of multiple TLS records in a single s2n_recv call - * - * By default, s2n-tls returns from s2n_recv() after reading a single TLS record. - * Enabling receiving of multiple records will instead cause s2n_recv() to attempt - * to read until the application-provided output buffer is full. This may be more - * efficient, especially if larger receive buffers are used. - * - * @note If this option is enabled with blocking IO, the call to s2n_recv() will - * not return until either the application-provided output buffer is full or the - * peer closes the connection. This may lead to unintentionally long waits if the - * peer does not send enough data. - * - * @param config The configuration object being updated - * @param enabled Set to `true` if multiple record receive is to be enabled; `false` to disable. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled); - -/** - * A callback function invoked (usually multiple times) during X.509 validation for each - * name encountered in the leaf certificate. - * - * Return 1 to trust that hostname or 0 to not trust the hostname. - * - * If this function returns 1, then the certificate is considered trusted and that portion - * of the X.509 validation will succeed. - * - * If no hostname results in a 1 being returned, the certificate will be untrusted and the - * validation will terminate immediately. - * - * Data is a opaque user context set in s2n_config_set_verify_host_callback() or s2n_connection_set_verify_host_callback(). - */ -typedef uint8_t (*s2n_verify_host_fn)(const char *host_name, size_t host_name_len, void *data); - -/** - * Sets the callback to use for verifying that a hostname from an X.509 certificate is trusted. - * - * The default behavior is to require that the hostname match the server name set with s2n_set_server_name(). - * This will likely lead to all client certificates being rejected, so the callback will need to be overridden when using - * client authentication. - * - * This change will be inherited by s2n_connections using this config. If a separate callback for different connections - * using the same config is desired, see s2n_connection_set_verify_host_callback(). - * - * @param config The configuration object being updated - * @param data A user supplied opaque context to pass back to the callback - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_verify_host_callback(struct s2n_config *config, s2n_verify_host_fn, void *data); - -/** - * Toggles whether or not to validate stapled OCSP responses. - * - * 1 means OCSP responses will be validated when they are encountered, while 0 means this step will - * be skipped. - * - * The default value is 1 if the underlying libCrypto implementation supports OCSP. - * - * @param config The configuration object being updated - * @param check_ocsp The desired OCSP response check configuration - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_check_stapled_ocsp_response(struct s2n_config *config, uint8_t check_ocsp); - -/** - * Disables timestamp validation for received certificates. - * - * By default, s2n-tls checks the notBefore and notAfter fields on the certificates it receives - * during the handshake. If the current date is not within the range of these fields for any - * certificate in the chain of trust, `s2n_negotiate()` will error. This validation is in - * accordance with RFC 5280, section 6.1.3 a.2: - * https://datatracker.ietf.org/doc/html/rfc5280#section-6.1.3. - * - * This API will disable this timestamp validation, permitting negotiation with peers that send - * expired certificates, or certificates that are not yet considered valid. - * - * @warning Applications calling this API should seriously consider the security implications of - * disabling this validation. The validity period of a certificate corresponds to the range of time - * in which the CA is guaranteed to maintain information regarding the certificate's revocation - * status. As such, it may not be possible to obtain accurate revocation information for - * certificates with invalid timestamps. Applications disabling this validation MUST implement - * some external method for limiting certificate lifetime. - * - * @param config The associated connection config. - * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. - */ -S2N_API extern int s2n_config_disable_x509_time_verification(struct s2n_config *config); - -/* Disable TLS intent verification for received certificates. - * - * By default, s2n-tls will verify that received certificates set Key Usage / Extended Key Usage - * fields that are consistent with the current TLS context (e.g. checking that serverAuth is set - * when verifying server certificates as a client, or checking that clientAuth is set when - * verifying client certificates as a server). This verification ensures that received certificates - * are being used for their intended purpose as specified by the issuer. - * - * This verification may be incompatible with some PKIs where intent is improperly specified. - * `s2n_config_disable_x509_intent_verification()` may be called in this case to disable the - * verification. This verification should only be disabled if it is known that all received - * certificates will be issued from a CA that intended for the certificates to be used in the given - * TLS context, despite what's indicated in the Key Usage / Extended Key Usage extensions. - * - * @note If a received certificate doesn't contain a Key Usage / Extended Key Usage extension, it's - * assumed that the issuer permits the certificate to be used for any purpose. s2n-tls will - * only reject a certificate due to invalid intent if the issuer explicitly indicates a - * purpose that is invalid for the TLS context in which it is received. - * - * @param config The associated connection config. - * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. - */ -S2N_API extern int s2n_config_disable_x509_intent_verification(struct s2n_config *config); - -/** - * Turns off all X.509 validation during the negotiation phase of the connection. This should only - * be used for testing or debugging purposes. - * - * @param config The configuration object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_disable_x509_verification(struct s2n_config *config); - -/** - * Sets the maximum allowed depth of a cert chain used for X509 validation. The default value is - * 7. If this limit is exceeded, validation will fail if s2n_config_disable_x509_verification() - * has not been called. 0 is an illegal value and will return an error. - * 1 means only a root certificate will be used. - * - * @param config The configuration object being updated - * @param max_depth The number of allowed certificates in the certificate chain - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_max_cert_chain_depth(struct s2n_config *config, uint16_t max_depth); - -/** - * Associates a set of Diffie-Hellman parameters with an `s2n_config` object. - * @note `dhparams_pem` should be PEM encoded DH parameters. - * - * @param config The configuration object being updated - * @param dhparams_pem A string containing the PEM encoded DH parameters. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem); - -/** - * Sets the security policy that includes the cipher/kem/signature/ecc preferences and - * protocol version. - * - * See the [USAGE-GUIDE.md](https://github.com/aws/s2n-tls/blob/main/docs/usage-guide) for how to use security policies. - */ -S2N_API extern int s2n_config_set_cipher_preferences(struct s2n_config *config, const char *version); - -/** - * Appends the provided application protocol to the preference list - * - * The data provided in `protocol` parameter will be copied into an internal buffer - * - * @param config The configuration object being updated - * @param protocol A pointer to a byte array value - * @param protocol_len The length of bytes that should be read from `protocol`. Note: this value cannot be 0, otherwise an error will be returned. - */ -S2N_API extern int s2n_config_append_protocol_preference(struct s2n_config *config, const uint8_t *protocol, uint8_t protocol_len); - -/** - * Sets the application protocol preferences on an `s2n_config` object. - * `protocols` is a list in order of preference, with most preferred protocol first, and of - * length `protocol_count`. - * - * When acting as an `S2N_CLIENT` the protocol list is included in the Client Hello message - * as the ALPN extension. - * - * As an `S2N_SERVER`, the list is used to negotiate a mutual application protocol with the - * client. After the negotiation for the connection has completed, the agreed upon protocol - * can be retrieved with s2n_get_application_protocol() - * - * @param config The configuration object being updated - * @param protocols The list of preferred protocols, in order of preference - * @param protocol_count The size of the protocols list - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_protocol_preferences(struct s2n_config *config, const char *const *protocols, int protocol_count); - -/** - * Enum used to define the type, if any, of certificate status request - * a connection should make during the handshake. The only supported status request type is - * OCSP, `S2N_STATUS_REQUEST_OCSP`. -*/ -typedef enum { - S2N_STATUS_REQUEST_NONE = 0, - S2N_STATUS_REQUEST_OCSP = 1 -} s2n_status_request_type; - -/** - * Sets up a connection to request the certificate status of a peer during an SSL handshake. If set - * to S2N_STATUS_REQUEST_NONE, no status request is made. - * - * @note SHA-1 is the only supported hash algorithm for the `certID` field. This is different - * from the hash algorithm used for the OCSP signature. See - * [RFC 6960](https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1) for more information. - * While unlikely to be the case, if support for a different hash algorithm is required, the - * s2n-tls validation can be disabled with `s2n_config_set_check_stapled_ocsp_response()` and the - * response can be retrieved for manual validation with `s2n_connection_get_ocsp_response()`. - * - * @param config The configuration object being updated - * @param type The desired request status type - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type); - -/** - * Enum to set Certificate Transparency Support level. - */ -typedef enum { - S2N_CT_SUPPORT_NONE = 0, - S2N_CT_SUPPORT_REQUEST = 1 -} s2n_ct_support_level; - -/** - * Set the Certificate Transparency Support level. - * - * @param config The configuration object being updated - * @param level The desired Certificate Transparency Support configuration - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_ct_support_level(struct s2n_config *config, s2n_ct_support_level level); - -/** - * Sets whether or not a connection should terminate on receiving a WARNING alert from its peer. - * - * `alert_behavior` can take the following values: - * - `S2N_ALERT_FAIL_ON_WARNINGS` default behavior: s2n-tls will terminate the connection if its peer sends a WARNING alert. - * - `S2N_ALERT_IGNORE_WARNINGS` - with the exception of `close_notify` s2n-tls will ignore all WARNING alerts and keep communicating with its peer. This setting is ignored in TLS1.3 - * - * @note TLS1.3 terminates a connection for all alerts except user_canceled. - * @warning S2N_ALERT_FAIL_ON_WARNINGS is the recommended behavior. Past TLS protocol vulnerabilities have involved downgrading alerts to warnings. - */ -typedef enum { - S2N_ALERT_FAIL_ON_WARNINGS = 0, - S2N_ALERT_IGNORE_WARNINGS = 1 -} s2n_alert_behavior; - -/** - * Sets the config's alert behavior based on the `s2n_alert_behavior` enum. - * - * @param config The configuration object being updated - * @param alert_behavior The desired alert behavior. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_behavior alert_behavior); - -/** - * Sets the extension data in the `s2n_config` object for the specified extension. - * This method will clear any existing data that is set. If the data and length - * parameters are set to NULL, no new data is set in the `s2n_config` object, - * effectively clearing existing data. - * - * @deprecated Use s2n_cert_chain_and_key_set_ocsp_data and s2n_cert_chain_and_key_set_sct_list instead. - * - * @param config The configuration object being updated - * @param type The extension type - * @param data Data for the extension - * @param length Length of the `data` buffer - */ -S2N_API extern int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_type type, const uint8_t *data, uint32_t length); - -/** - * Allows the caller to set a TLS Maximum Fragment Length extension that will be used - * to fragment outgoing messages. s2n-tls currently does not reject fragments larger - * than the configured maximum when in server mode. The TLS negotiated maximum fragment - * length overrides the preference set by the `s2n_connection_prefer_throughput` and - * `s2n_connection_prefer_low_latency`. - * - * @note Some TLS implementations do not respect their peer's max fragment length extension. - * - * @param config The configuration object being updated - * @param mfl_code The selected MFL size - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code); - -/** - * Allows the server to opt-in to accept client's TLS maximum fragment length extension - * requests. If this API is not called, and client requests the extension, server will ignore - * the request and continue TLS handshake with default maximum fragment length of 8k bytes - * - * @note Some TLS implementations do not respect their peer's max fragment length extension. - * - * @param config The configuration object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_accept_max_fragment_length(struct s2n_config *config); - -/** - * Sets the lifetime of the cached session state. The default value is 15 hours. - * - * @param config The configuration object being updated - * @param lifetime_in_secs The desired lifetime of the session state in seconds - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_session_state_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs); - -/** - * Enable or disable session resumption using session ticket. - * - * @param config The configuration object being updated - * @param enabled The configuration object being updated. Set to 1 to enable. Set to 0 to disable. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled); - -/** - * Enable or disable session caching. - * - * @note Session caching will not be turned on unless all three session cache callbacks are set - * prior to calling this function. - * - * @param config The configuration object being updated - * @param enabled The configuration object being updated. Set to 1 to enable. Set to 0 to disable. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_session_cache_onoff(struct s2n_config *config, uint8_t enabled); - -/** - * Sets how long a session ticket key will be in a state where it can be used for both encryption - * and decryption of tickets on the server side. - * - * @note The default value is 2 hours. - * @param config The configuration object being updated - * @param lifetime_in_secs The desired lifetime of decrypting and encrypting tickets in seconds - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs); - -/** - * Sets how long a session ticket key will be in a state where it can used just for decryption of - * already assigned tickets on the server side. Once decrypted, the session will resume and the - * server will issue a new session ticket encrypted using a key in encrypt-decrypt state. - * - * @note The default value is 13 hours. - * @param config The configuration object being updated - * @param lifetime_in_secs The desired lifetime of decrypting and encrypting tickets in seconds - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs); - -/** - * Adds session ticket key on the server side. It would be ideal to add new keys after every - * (encrypt_decrypt_key_lifetime_in_nanos/2) nanos because this will allow for gradual and - * linear transition of a key from encrypt-decrypt state to decrypt-only state. - * - * @param config The configuration object being updated - * @param name Name of the session ticket key that should be randomly generated to avoid collisions - * @param name_len Length of session ticket key name - * @param key Key used to perform encryption/decryption of session ticket - * @param key_len Length of the session ticket key - * @param intro_time_in_seconds_from_epoch Time at which the session ticket key is introduced. If this is 0, then intro_time_in_seconds_from_epoch is set to now. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_add_ticket_crypto_key(struct s2n_config *config, const uint8_t *name, uint32_t name_len, - uint8_t *key, uint32_t key_len, uint64_t intro_time_in_seconds_from_epoch); - -/** - * Requires that session tickets are only used when forward secrecy is possible. - * - * Restricts session resumption to TLS1.3, as the tickets used in TLS1.2 resumption are - * not forward secret. Clients should not expect to receive new session tickets and servers - * will not send new session tickets when TLS1.2 is negotiated and ticket forward secrecy is required. - * - * @note The default behavior is that forward secrecy is not required. - * - * @param config The config object being updated - * @param enabled Indicates if forward secrecy is required or not on tickets - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled); - -/** - * Sets user defined context on the `s2n_config` object. - * - * @param config The configuration object being updated - * @param ctx A pointer to the user defined ctx. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_ctx(struct s2n_config *config, void *ctx); - -/** - * Gets the user defined context from the `s2n_config` object. - * The context is set by calling s2n_config_set_ctx() - * - * @param config The configuration object being accessed - * @param ctx A pointer to the user defined ctx. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_get_ctx(struct s2n_config *config, void **ctx); - -/** - * Used to declare connections as server or client type, respectively. - */ -typedef enum { - S2N_SERVER, - S2N_CLIENT -} s2n_mode; - -/** - * Creates a new connection object. Each s2n-tls SSL/TLS connection uses - * one of these objects. These connection objects can be operated on by up - * to two threads at a time, one sender and one receiver, but neither sending - * nor receiving are atomic, so if these objects are being called by multiple - * sender or receiver threads, you must perform your own locking to ensure - * that only one sender or receiver is active at a time. - * - * The `mode` parameters specifies if the caller is a server, or is a client. - * Connections objects are re-usable across many connections, and should be - * re-used (to avoid deallocating and allocating memory). You should wipe - * connections immediately after use. - * - * @param mode The desired connection type - * @returns A s2n_connection handle - */ -S2N_API extern struct s2n_connection *s2n_connection_new(s2n_mode mode); - -/** - * Associates a configuration object with a connection. - * - * @param conn The connection object being associated - * @param config The configuration object being associated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *config); - -/** - * Sets user defined context in `s2n_connection` object. - * - * @param conn The connection object being updated - * @param ctx A pointer to the user defined context - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_ctx(struct s2n_connection *conn, void *ctx); - -/** - * Gets user defined context from a `s2n_connection` object. - * - * @param conn The connection object that contains the desired context - */ -S2N_API extern void *s2n_connection_get_ctx(struct s2n_connection *conn); - -/** - * The callback function takes a s2n-tls connection as input, which receives the ClientHello - * and the context previously provided in `s2n_config_set_client_hello_cb`. The callback can - * access any ClientHello information from the connection and use the `s2n_connection_set_config` - * call to change the config of the connection. - */ -typedef int s2n_client_hello_fn(struct s2n_connection *conn, void *ctx); - -/** - * Client Hello callback modes - * - `S2N_CLIENT_HELLO_CB_BLOCKING` (default): - * - In this mode s2n-tls expects the callback to complete its work and return the appropriate response code before the handshake continues. If any of the connection properties were changed based on the server_name extension the callback must either return a value greater than 0 or invoke `s2n_connection_server_name_extension_used`, otherwise the callback returns 0 to continue the handshake. - * - `S2N_CLIENT_HELLO_CB_NONBLOCKING`: - * - In non-blocking mode, s2n-tls expects the callback to not complete its work. If the callback returns a response code of 0, s2n-tls will return `S2N_FAILURE` with `S2N_ERR_T_BLOCKED` error type and `s2n_blocked_status` set to `S2N_BLOCKED_ON_APPLICATION_INPUT`. The handshake is paused and further calls to `s2n_negotiate` will continue to return the same error until `s2n_client_hello_cb_done` is invoked for the `s2n_connection` to resume the handshake. If any of the connection properties were changed on the basis of the server_name extension then `s2n_connection_server_name_extension_used` must be invoked before marking the callback done. - */ -typedef enum { - S2N_CLIENT_HELLO_CB_BLOCKING, - S2N_CLIENT_HELLO_CB_NONBLOCKING -} s2n_client_hello_cb_mode; - -/** - * Allows the caller to set a callback function that will be called after ClientHello was parsed. - * - * @param config The configuration object being updated - * @param client_hello_callback The client hello callback function - * @param ctx A pointer to a user defined context that the Client Hello callback will be invoked with. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_client_hello_cb(struct s2n_config *config, s2n_client_hello_fn client_hello_callback, void *ctx); - -/** - * Sets the callback execution mode. - * - * See s2n_client_hello_cb_mode for each mode's behavior. - * - * @param config The configuration object being updated - * @param cb_mode The desired callback mode - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_client_hello_cb_mode(struct s2n_config *config, s2n_client_hello_cb_mode cb_mode); - -/** - * Marks the non-blocking callback as complete. Can be invoked from within the callback when - * operating in non-blocking mode to continue the handshake. - * - * @param conn The connection object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_client_hello_cb_done(struct s2n_connection *conn); - -/** - * Must be invoked if any of the connection properties were changed on the basis of the server_name - * extension. This must be invoked before marking the Client Hello callback done. - * - * @param conn The connection object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_server_name_extension_used(struct s2n_connection *conn); - -/** - * Opaque client hello handle - */ -struct s2n_client_hello; - -/** - * Get the Client Hello from a s2n_connection. - * - * Earliest point during the handshake when this structure is available for use is in the - * client_hello_callback (see s2n_config_set_client_hello_cb()). - * - * @param conn The connection object containing the client hello - * @returns A handle to the s2n_client_hello structure holding the client hello message sent by the client during the handshake. NULL is returned if a Client Hello has not yet been received and parsed. - */ -S2N_API extern struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn); - -/** - * Creates an s2n_client_hello from bytes representing a ClientHello message. - * - * The input bytes should include the message header (message type and length), - * but not the record header. - * - * Unlike s2n_connection_get_client_hello, the s2n_client_hello returned by this - * method is owned by the application and must be freed with s2n_client_hello_free. - * - * This method does not support SSLv2 ClientHellos. - * - * @param bytes The raw bytes representing the ClientHello. - * @param size The size of raw_message. - * @returns A new s2n_client_hello on success, or NULL on failure. - */ -S2N_API extern struct s2n_client_hello *s2n_client_hello_parse_message(const uint8_t *bytes, uint32_t size); - -/** - * Frees an s2n_client_hello structure. - * - * This method should be called to free s2n_client_hellos returned by - * s2n_client_hello_parse_message. It will error if passed an s2n_client_hello - * returned by s2n_connection_get_client_hello and owned by the connection. - * - * @param ch The structure to be freed. - * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. - */ -S2N_API extern int s2n_client_hello_free(struct s2n_client_hello **ch); - -/** - * Function to determine the size of the raw Client Hello buffer. - * - * Can be used to determine the necessary size of the `out` buffer for - * s2n_client_hello_get_raw_message() - * - * @param ch The Client Hello handle - * @returns The size of the ClientHello message received by the server - */ -S2N_API extern ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client_hello *ch); - -/** - * Copies `max_length` bytes of the ClientHello message into the `out` buffer. - * The ClientHello instrumented using this function will have the Random bytes - * zero-ed out. Use s2n_client_hello_get_random to access the random bytes. - * - * Note: SSLv2 ClientHello messages follow a different structure than more modern - * ClientHello messages. See [RFC5246](https://tools.ietf.org/html/rfc5246#appendix-E.2). - * In addition, due to how s2n-tls parses SSLv2 ClientHellos, the raw message is - * missing the first three bytes (the msg_type and version) and instead begins with - * the cipher_specs. To determine whether a ClientHello is an SSLv2 ClientHello, - * you will need to use s2n_connection_get_client_hello_version(). To get the - * protocol version advertised in the SSLv2 ClientHello (which may be higher - * than SSLv2), you will need to use s2n_connection_get_client_protocol_version(). - * - * @param ch The Client Hello handle - * @param out The destination buffer for the raw Client Hello - * @param max_length The size of out in bytes - * @returns The number of copied bytes - */ -S2N_API extern ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); - -/** - * Function to determine the size of the Client Hello cipher suites. - * This can be used to allocate the `out` buffer for s2n_client_hello_get_cipher_suites(). - * - * @param ch The Client Hello handle - * @returns the number of bytes the cipher_suites takes on the ClientHello message received by the server - */ -S2N_API extern ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch); - -/** - * Copies into the `out` buffer `max_length` bytes of the cipher_suites on the ClientHello. - * - * Note: SSLv2 ClientHello cipher suites follow a different structure than modern - * ClientHello messages. See [RFC5246](https://tools.ietf.org/html/rfc5246#appendix-E.2). - * To determine whether a ClientHello is an SSLv2 ClientHello, - * you will need to use s2n_connection_get_client_hello_version(). - * - * @param ch The Client Hello handle - * @param out The destination buffer for the raw Client Hello cipher suites - * @param max_length The size of out in bytes - * @returns The number of copied bytes - */ -S2N_API extern ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); - -/** - * Function to determine the size of the Client Hello extensions. - * This can be used to allocate the `out` buffer for s2n_client_hello_get_extensions(). - * - * @param ch The Client Hello handle - * @returns the number of bytes the extensions take in the ClientHello message received by the server - */ -S2N_API extern ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch); - -/** - * Copies into the `out` buffer `max_length` bytes of the extensions in the ClientHello. - * - * @param ch The Client Hello handle - * @param out The destination buffer for the raw Client Hello extensions - * @param max_length The size of out in bytes - * @returns The number of copied bytes - */ -S2N_API extern ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); - -/** - * Query the ClientHello message received by the server. Use this function to allocate the `out` buffer for - * other client hello extension functions. - * - * @param ch A pointer to the Client Hello - * @param extension_type Indicates the desired extension - * @returns The number of bytes the given extension type takes - */ -S2N_API extern ssize_t s2n_client_hello_get_extension_length(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type); - -/** - * Copies into the `out` buffer `max_length` bytes of a given extension type on the ClientHello - * - * `ch` is a pointer to the `s2n_client_hello` of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). - * - * @param ch A pointer to the Client Hello - * @param extension_type Indicates the desired extension - * @param out A pointer to the buffer that s2n will write the client session id to. This buffer MUST be the size of `max_length` - * @param max_length The size of `out`. - * @returns The number of copied bytes - */ -S2N_API extern ssize_t s2n_client_hello_get_extension_by_id(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type, uint8_t *out, uint32_t max_length); - -/** - * Used to check if a particular extension exists in the client hello. - * - * `ch` is a pointer to the `s2n_client_hello` of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). - * - * @param ch A pointer to the client hello object - * @param extension_iana The iana value of the extension - * @param exists A pointer that will be set to whether or not the extension exists - */ -S2N_API extern int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extension_iana, bool *exists); - -/** - * Get the the ClientHello session id length in bytes - * - * `ch` is a pointer to the `s2n_client_hello` of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). - * - * @param ch A pointer to the Client Hello - * @param out_length An out pointer. s2n will set it's value to the size of the session_id in bytes. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_client_hello_get_session_id_length(struct s2n_client_hello *ch, uint32_t *out_length); - -/** - * Copies up to `max_length` bytes of the ClientHello session_id into the `out` buffer and stores the number of copied bytes in `out_length`. - * - * Retrieve the session id as sent by the client in the ClientHello message. The session id on the `s2n_connection` may change later - * when the server sends the ServerHello; see `s2n_connection_get_session_id` for how to get the final session id used for future session resumption. - * - * Use s2n_client_hello_get_session_id_length() to get the the ClientHello session id length in bytes. `ch` is a pointer to the `s2n_client_hello` - * of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). - * - * @param ch A pointer to the Client Hello - * @param out A pointer to the buffer that s2n will write the client session id to. This buffer MUST be the size of `max_length` - * @param out_length An out pointer. s2n will set it's value to the size of the session_id in bytes. - * @param max_length The size of `out`. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length); - -/** - * Get the length of the compression methods list sent in the Client Hello. - * - * @param ch A pointer to the Client Hello - * @param out_length An out pointer. Will be set to the length of the compression methods list in bytes. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_client_hello_get_compression_methods_length(struct s2n_client_hello *ch, uint32_t *out_length); - -/** - * Retrieves the list of compression methods sent in the Client Hello. - * - * Use `s2n_client_hello_get_compression_methods_length()` - * to retrieve how much memory should be allocated for the buffer in advance. - * - * @note Compression methods were removed in TLS1.3 and therefore the only valid value in this list is the - * "null" compression method when TLS1.3 is negotiated. - * - * @note s2n-tls has never supported compression methods in any TLS version and therefore a - * compression method will never be negotiated or used. - * - * @param ch A pointer to the Client Hello - * @param list A pointer to some memory that s2n will write the compression methods to. This memory MUST be the size of `list_length` - * @param list_length The size of `list`. - * @param out_length An out pointer. s2n will set its value to the size of the compression methods list in bytes. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_client_hello_get_compression_methods(struct s2n_client_hello *ch, uint8_t *list, uint32_t list_length, uint32_t *out_length); - -/** - * Access the Client Hello protocol version - * - * @note This field is a legacy field in TLS1.3 and is no longer used to negotiate the - * protocol version of the connection. It will be set to TLS1.2 even if TLS1.3 is negotiated. - * Therefore this method should only be used for logging or fingerprinting. - * - * @param ch A pointer to the client hello struct - * @param out The protocol version in the client hello. - */ -S2N_API extern int s2n_client_hello_get_legacy_protocol_version(struct s2n_client_hello *ch, uint8_t *out); - -/** - * Retrieves the client random value from the Client Hello. - * - * The client random is a 32-byte value sent by the client in the ClientHello message. - * It is used in the TLS handshake for key derivation and to prevent replay attacks. - * - * Copies up to `max_length` bytes of the client random into the `out` buffer. The number - * of bytes copied will be the minimum of `max_length` and S2N_TLS_RANDOM_DATA_LEN (32 bytes). - * - * @param ch A pointer to the client hello struct - * @param out A pointer to a buffer where the client random will be copied. - * @param max_length The maximum number of bytes to copy into the `out` buffer. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API extern int s2n_client_hello_get_random(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); - -/** - * Retrieves the supported groups received from the client in the supported groups extension. - * - * IANA values for each of the received supported groups are written to the provided `groups` - * array, and `groups_count` is set to the number of received supported groups. - * - * `groups_count_max` should be set to the maximum capacity of the `groups` array. If - * `groups_count_max` is less than the number of received supported groups, this function will - * error. To determine how large `groups` should be in advance, use - * `s2n_client_hello_get_extension_length()` with the S2N_EXTENSION_SUPPORTED_GROUPS extension - * type, and divide the value by 2. - * - * If no supported groups extension was received from the peer, or the received supported groups - * extension is malformed, this function will error. - * - * @param ch A pointer to the ClientHello. Can be retrieved from a connection via - * `s2n_connection_get_client_hello()`. - * @param groups The array to populate with the received supported groups. - * @param groups_count_max The maximum number of supported groups that can fit in the `groups` array. - * @param groups_count Returns the number of received supported groups. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API extern int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups, - uint16_t groups_count_max, uint16_t *groups_count); - -/** - * Gets the length of the first server name in a Client Hello. - * - * @param ch A pointer to the ClientHello - * @param length A pointer which will be populated with the length of the server name - */ -S2N_API extern int s2n_client_hello_get_server_name_length(struct s2n_client_hello *ch, uint16_t *length); - -/** - * Gets the first server name in a Client Hello. - * - * Use `s2n_client_hello_get_server_name_length()` to get the amount of memory needed for the buffer. - * - * @param ch A pointer to the ClientHello - * @param server_name A pointer to the memory which will be populated with the server name - * @param length The maximum amount of data that can be written to `server_name` - * @param out_length A pointer which will be populated with the size of the server name - */ -S2N_API extern int s2n_client_hello_get_server_name(struct s2n_client_hello *ch, uint8_t *server_name, uint16_t length, uint16_t *out_length); - -/** - * Sets the file descriptor for a s2n connection. - * - * @warning If the read end of the pipe is closed unexpectedly, writing to the pipe will raise a SIGPIPE signal. - * **s2n-tls does NOT handle SIGPIPE.** A SIGPIPE signal will cause the process to terminate unless it is handled - * or ignored by the application. - * @note This file-descriptor should be active and connected - * @param conn A pointer to the s2n connection - * @param fd The new file descriptor - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_fd(struct s2n_connection *conn, int fd); - -/** - * Sets the file descriptor for the read channel of an s2n connection. - * - * @warning If the read end of the pipe is closed unexpectedly, writing to the pipe will raise a SIGPIPE signal. - * **s2n-tls does NOT handle SIGPIPE.** A SIGPIPE signal will cause the process to terminate unless it is handled - * or ignored by the application. - * @note This file-descriptor should be active and connected - * @param conn A pointer to the s2n connection - * @param readfd The new read file descriptor - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_read_fd(struct s2n_connection *conn, int readfd); - -/** - * Sets the assigned file descriptor for the write channel of an s2n connection. - * - * @note This file-descriptor should be active and connected - * @param conn A pointer to the s2n connection - * @param writefd The new write file descriptor - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_write_fd(struct s2n_connection *conn, int writefd); - -/** - * Gets the assigned file descriptor for the read channel of an s2n connection. - * - * @param conn A pointer to the s2n connection - * @param readfd pointer to place the used file descriptor. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_get_read_fd(struct s2n_connection *conn, int *readfd); - -/** - * Gets the assigned file descriptor for the write channel of an s2n connection. - * - * @param conn A pointer to the s2n connection - * @param writefd pointer to place the used file descriptor. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_get_write_fd(struct s2n_connection *conn, int *writefd); - -/** - * Indicates to s2n that the connection is using corked IO. - * - * @warning This API should only be used when using managed send IO. - * - * @param conn The connection object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_use_corked_io(struct s2n_connection *conn); - -/** - * Function pointer for a user provided recv callback. - */ -typedef int s2n_recv_fn(void *io_context, uint8_t *buf, uint32_t len); - -/** - * Function pointer for a user provided send callback. - */ -typedef int s2n_send_fn(void *io_context, const uint8_t *buf, uint32_t len); - -/** - * Set a context containing anything needed in the recv callback function (for example, - * a file descriptor), the buffer holding data to be sent or received, and the length of the buffer. - * - * @note The `io_context` passed to the callbacks may be set separately using `s2n_connection_set_recv_ctx` and `s2n_connection_set_send_ctx`. - * - * @param conn The connection object being updated - * @param ctx A user provided context that the callback will be invoked with - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_recv_ctx(struct s2n_connection *conn, void *ctx); - -/** - * Set a context containing anything needed in the send callback function (for example, - * a file descriptor), the buffer holding data to be sent or received, and the length of the buffer. - * - * @note The `io_context` passed to the callbacks may be set separately using `s2n_connection_set_recv_ctx` and `s2n_connection_set_send_ctx`. - * - * @param conn The connection object being updated - * @param ctx A user provided context that the callback will be invoked with - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_send_ctx(struct s2n_connection *conn, void *ctx); - -/** - * Configure a connection to use a recv callback to receive data. - * - * @note This callback may be blocking or nonblocking. - * @note The callback may receive less than the requested length. The function should return the number - * of bytes received, or set errno and return an error code < 0. - * - * @param conn The connection object being updated - * @param recv A recv callback function pointer - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_recv_cb(struct s2n_connection *conn, s2n_recv_fn recv); - -/** - * Configure a connection to use a send callback to send data. - * - * @note This callback may be blocking or nonblocking. - * @note The callback may send less than the requested length. The function should return the - * number of bytes sent or set errno and return an error code < 0. - * - * @param conn The connection object being updated - * @param send A send callback function pointer - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_send_cb(struct s2n_connection *conn, s2n_send_fn send); - -/** - * Change the behavior of s2n-tls when sending data to prefer high throughput. - * - * Connections preferring throughput will use - * large record sizes that minimize overhead. - * - * @param conn The connection object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_prefer_throughput(struct s2n_connection *conn); - -/** - * Change the behavior of s2n-tls when sending data to prefer low latency. - * - * Connections preferring low latency will be encrypted - * using small record sizes that can be decrypted sooner by the recipient. - * - * @param conn The connection object being updated - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_prefer_low_latency(struct s2n_connection *conn); - -/** - * Configure the connection to reduce potentially expensive calls to recv. - * - * If this setting is disabled, s2n-tls will call read twice for every TLS record, - * which can be expensive but ensures that s2n-tls will always attempt to read the - * exact number of bytes it requires. If this setting is enabled, s2n-tls will - * instead reduce the number of calls to read by attempting to read as much data - * as possible in each read call, storing the extra in the existing IO buffers. - * This may cause it to request more data than will ever actually be available. - * - * There is no additional memory cost of enabling this setting. It reuses the - * existing IO buffers. - * - * This setting is disabled by default. Depending on how your application detects - * data available for reading, buffering reads may break your event loop. - * In particular, note that: - * - * 1. File descriptor reads or calls to your custom s2n_recv_cb may request more - * data than is available. Reads must return partial data when available rather - * than blocking until all requested data is available. - * - * 2. s2n_negotiate may read and buffer application data records. - * You must call s2n_recv at least once after negotiation to ensure that you - * handle any buffered data. - * - * 3. s2n_recv may read and buffer more records than it parses and decrypts. - * You must call s2n_recv until it reports S2N_ERR_T_BLOCKED, rather than just - * until it reports S2N_SUCCESS. - * - * 4. s2n_peek reports available decrypted data. It does not report any data - * buffered by this feature. However, s2n_peek_buffered does report data - * buffered by this feature. - * - * 5. s2n_connection_release_buffers will not release the input buffer if it - * contains buffered data. - * - * For example: if your event loop uses `poll`, you will receive a POLLIN event - * for your read file descriptor when new data is available. When you call s2n_recv - * to read that data, s2n-tls reads one or more TLS records from the file descriptor. - * If you stop calling s2n_recv before it reports S2N_ERR_T_BLOCKED, some of those - * records may remain in s2n-tls's read buffer. If you read part of a record, - * s2n_peek will report the remainder of that record as available. But if you don't - * read any of a record, it remains encrypted and is not reported by s2n_peek, but - * is still reported by s2n_peek_buffered. And because the data is buffered in s2n-tls - * instead of in the file descriptor, another call to `poll` will NOT report any - * more data available. Your application may hang waiting for more data. - * - * @warning This feature cannot be enabled for a connection that will enable kTLS for receiving. - * - * @warning This feature may work with blocking IO, if used carefully. Your blocking - * IO must support partial reads (so MSG_WAITALL cannot be used). You will either - * need to know exactly how much data your peer is sending, or will need to use - * `s2n_peek` and `s2n_peek_buffered` rather than relying on S2N_ERR_T_BLOCKED - * as noted in #3 above. - * - * @param conn The connection object being updated - * @param enabled Set to `true` to enable, `false` to disable. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled); - -/** - * Reports how many bytes of unprocessed TLS records are buffered after the current - * record. - * - * This is only useful when `s2n_connection_set_recv_buffering` is enabled, and - * will return 0 otherwise. - * - * `s2n_peek_buffered` is not a replacement for `s2n_peek`. While `s2n_peek` reports - * application data that is ready for the application to read with no additional - * processing, `s2n_peek_buffered` reports raw TLS records that still need to be - * parsed and likely decrypted. Those records may contain application data, but - * they may also only contain TLS control messages. - * - * When receive buffering is enabled, it is useful to imagine that an s2n-tls - * connection behaves as if it has two buffers, one for the "current record", - * and one for "additional data". - * ```text - * c -> ciphertext - * p -> plaintext - * - * |-----current record-----|-------additional------------| - * case 1 - |ccccccccccccc - * case 2 - |pppppppppppppppppppppppp| - * case 3 - |pppppppppppppppppppppppp|ccccccccccccccccccc - * ``` - * In case 1, we received a record fragment. Records can only be decrypted - * once the full record is available. So "current record" just stores the record - * fragment (ciphertext). There are currently no APIs to determine if there - * is a record fragment stored. https://github.com/aws/s2n-tls/issues/5863. `s2n_peek_buffered` - * and `s2n_peek` will both return 0 in this case. - * - * In case 2, we received a full record, which is decrypted and stored in current - * record. `s2n_peek` will return the count of plaintext bytes - * yet to be read from "current record", and `s2n_peek_buffered` will return 0 - * - * In case 3, additional bytes (another record, complete or incomplete) were - * also read off the wire. In this case `s2n_peek_buffered` will return the length - * of ciphertext bytes buffered in "additional". This is the only case in which - * `s2n_peek_buffered` will return a non-zero result. - * - * @param conn A pointer to the s2n_connection object - * @returns The number of buffered encrypted bytes - */ -S2N_API extern uint32_t s2n_peek_buffered(struct s2n_connection *conn); - -/** - * Configure the connection to free IO buffers when they are not currently in use. - * - * This configuration can be used to minimize connection memory footprint size, at the cost - * of more calls to alloc and free. Some of these costs can be mitigated by configuring s2n-tls - * to use an allocator that includes thread-local caches or lock-free allocation patterns. - * - * @param conn The connection object being update - * @param enabled Set to `true` if dynamic buffers are enabled; `false` if disabled - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_dynamic_buffers(struct s2n_connection *conn, bool enabled); - -/** - * Changes the behavior of s2n-tls when sending data to initially prefer records - * small enough to fit in single ethernet frames. - * - * When dynamic record sizing is active, the connection sends records small enough - * to fit in a single standard 1500 byte ethernet frame. Otherwise, the connection - * chooses record sizes according to the configured maximum fragment length. - * - * Dynamic record sizing is active for the first resize_threshold bytes of a connection, - * and is reactivated whenever timeout_threshold seconds pass without sending data. - * - * @param conn The connection object being updated - * @param resize_threshold The number of bytes to send before changing the record size. Maximum 8MiB. - * @param timeout_threshold Reset record size back to a single segment after threshold seconds of inactivity - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_dynamic_record_threshold(struct s2n_connection *conn, uint32_t resize_threshold, uint16_t timeout_threshold); - -/** - * Sets the callback to use for verifying that a hostname from an X.509 certificate is trusted. - * - * The default behavior is to require that the hostname match the server name set with s2n_set_server_name(). This will - * likely lead to all client certificates being rejected, so the callback will need to be overridden when using client authentication. - * - * If a single callback for different connections using the same config is desired, see s2n_config_set_verify_host_callback(). - * - * @param conn A pointer to a s2n_connection object - * @param host_fn A pointer to a callback function that s2n will invoke in order to verify the hostname of an X.509 certificate - * @param data Opaque pointer to data that the verify host function will be invoked with - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_verify_host_callback(struct s2n_connection *conn, s2n_verify_host_fn host_fn, void *data); - -/** - * Used to opt-out of s2n-tls's built-in 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. - * - * 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 should call s2n_connection_get_delay() and pause - * activity on the connection for the specified number of nanoseconds before calling - * close() or shutdown(). - */ -typedef enum { - S2N_BUILT_IN_BLINDING, - S2N_SELF_SERVICE_BLINDING -} s2n_blinding; - -/** - * Used to configure s2n-tls to either use built-in blinding (set blinding to S2N_BUILT_IN_BLINDING) or - * self-service blinding (set blinding to S2N_SELF_SERVICE_BLINDING). - * - * @param conn The connection object being updated - * @param blinding The desired blinding mode for the connection - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blinding); - -/** - * Query the connection object for the configured blinding delay. - * @param conn The connection object being updated - * @returns the number of nanoseconds an application using self-service blinding should pause before calling close() or shutdown(). - */ -S2N_API extern uint64_t s2n_connection_get_delay(struct s2n_connection *conn); - -/** - * Configures the maximum blinding delay enforced after errors. - * - * Blinding protects your application from timing side channel attacks like Lucky13. While s2n-tls - * implements other, more specific mitigations for known timing side channels, blinding is important - * as a defense against currently unknown or unreported timing attacks. - * - * Setting a maximum delay lower than the recommended default (30s) will make timing attacks against - * your application easier. The lower you set the delay, the fewer requests and less total time an - * attacker will require to execute an attack. If you must lower the delay for reasons such as client - * timeouts, then you should choose the highest value practically possible to limit your risk. - * - * If you lower the blinding delay, you should also consider implementing monitoring and filtering - * to detect and reject suspicious traffic that could be gathering timing information from a potential - * side channel. Timing attacks usually involve repeatedly triggering TLS errors. - * - * @warning Do NOT set a lower blinding delay unless you understand the risks and have other - * mitigations for timing side channels in place. - * - * @note This delay needs to be set lower than any timeouts, such as your TCP socket timeout. - * - * @param config The config object being updated. - * @param seconds The maximum number of seconds that s2n-tls will delay for in the event of a - * sensitive error. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API extern int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds); - -/** - * Sets the cipher preference override for the s2n_connection. Calling this function is not necessary - * unless you want to set the cipher preferences on the connection to something different than what is in the s2n_config. - * - * @param conn The connection object being updated - * @param version The human readable string representation of the security policy version. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_cipher_preferences(struct s2n_connection *conn, const char *version); - -/** - * Used to indicate the type of key update that is being requested. For further - * information refer to `s2n_connection_request_key_update`. -*/ -typedef enum { - S2N_KEY_UPDATE_NOT_REQUESTED = 0, - S2N_KEY_UPDATE_REQUESTED -} s2n_peer_key_update; - -/** - * Signals the connection to do a key_update at the next possible opportunity. Note that the resulting key update message - * will not be sent until `s2n_send` is called. - * - * @param conn The connection object to trigger the key update on. - * @param peer_request Indicates if a key update should also be requested - * of the peer. When set to `S2N_KEY_UPDATE_NOT_REQUESTED`, then only the sending - * key of `conn` will be updated. If set to `S2N_KEY_UPDATE_REQUESTED`, then - * the sending key of conn will be updated AND the peer will be requested to - * update their sending key. Note that s2n-tls currently only supports - * `peer_request` being set to `S2N_KEY_UPDATE_NOT_REQUESTED` and will return - * S2N_FAILURE if any other value is used. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure -*/ -S2N_API extern int s2n_connection_request_key_update(struct s2n_connection *conn, s2n_peer_key_update peer_request); -/** - * Appends the provided application protocol to the preference list - * - * The data provided in `protocol` parameter will be copied into an internal buffer - * - * @param conn The connection object being updated - * @param protocol A pointer to a slice of bytes - * @param protocol_len The length of bytes that should be read from `protocol`. Note: this value cannot be 0, otherwise an error will be returned. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_append_protocol_preference(struct s2n_connection *conn, const uint8_t *protocol, uint8_t protocol_len); - -/** - * Sets the protocol preference override for the s2n_connection. Calling this function is not necessary unless you want - * to set the protocol preferences on the connection to something different than what is in the s2n_config. - * - * @param conn The connection object being updated - * @param protocols A pointer to an array of protocol strings - * @param protocol_count The number of protocols contained in protocols - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_protocol_preferences(struct s2n_connection *conn, const char *const *protocols, int protocol_count); - -/** - * Sets the server name for the connection. - * - * The provided server name will be sent by the client to the server in the - * server_name ClientHello extension. It may be desirable for clients - * to provide this information to facilitate secure connections to - * servers that host multiple 'virtual' servers at a single underlying - * network address. - * - * s2n-tls does not place any restrictions on the provided server name. However, - * other TLS implementations might. Specifically, the TLS specification for the - * server_name extension requires that it be an ASCII-encoded DNS name without a - * trailing dot, and explicitly forbids literal IPv4 or IPv6 addresses. - * - * @param conn The connection object being queried - * @param server_name A pointer to a string containing the desired server name - * @warning `server_name` must be a NULL terminated string. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_set_server_name(struct s2n_connection *conn, const char *server_name); - -/** - * Query the connection for the selected server name. - * - * This can be used by a server to determine which server name the client is using. This function returns the first ServerName entry - * in the ServerNameList sent by the client. Subsequent entries are not returned. - * - * @param conn The connection object being queried - * @returns The server name associated with a connection, or NULL if none is found. - */ -S2N_API extern const char *s2n_get_server_name(struct s2n_connection *conn); - -/** - * Query the connection for the selected application protocol. - * - * @param conn The connection object being queried - * @returns The negotiated application protocol for a `s2n_connection`. In the event of no protocol being negotiated, NULL is returned. - */ -S2N_API extern const char *s2n_get_application_protocol(struct s2n_connection *conn); - -/** - * Query the connection for a buffer containing the OCSP response. - * - * @param conn The connection object being queried - * @param length A pointer that is set to the certificate transparency response buffer's size - * @returns A pointer to the OCSP response sent by a server during the handshake. If no status response is received, NULL is returned. - */ -S2N_API extern const uint8_t *s2n_connection_get_ocsp_response(struct s2n_connection *conn, uint32_t *length); - -/** - * Query the connection for a buffer containing the Certificate Transparency response. - * - * @param conn The connection object being queried - * @param length A pointer that is set to the certificate transparency response buffer's size - * @returns A pointer to the certificate transparency response buffer. - */ -S2N_API extern const uint8_t *s2n_connection_get_sct_list(struct s2n_connection *conn, uint32_t *length); - -/** - * Used in non-blocking mode to indicate in which direction s2n-tls became blocked on I/O before it - * returned control to the caller. This allows an application to avoid retrying s2n-tls operations - * until I/O is possible in that direction. - */ -typedef enum { - S2N_NOT_BLOCKED = 0, - S2N_BLOCKED_ON_READ, - S2N_BLOCKED_ON_WRITE, - S2N_BLOCKED_ON_APPLICATION_INPUT, - S2N_BLOCKED_ON_EARLY_DATA, -} s2n_blocked_status; - -/** - * Performs the initial "handshake" phase of a TLS connection and must be called before any s2n_recv() or s2n_send() calls. - * - * @note When using client authentication with TLS1.3, s2n_negotiate() will report a successful - * handshake to clients before the server validates the client certificate. If the server then - * rejects the client certificate, the client may later receive an alert while calling s2n_recv, - * potentially after already having sent application data with s2n_send. - * - * See the following example for guidance on calling `s2n_negotiate()`: - * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_negotiate.c - * - * @param conn A pointer to the s2n_connection object - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns S2N_SUCCESS if the handshake completed. S2N_FAILURE if the handshake encountered an error or is blocked. - */ -S2N_API extern int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked); - -/** - * Writes and encrypts `size` of `buf` data to the associated connection. s2n_send() will return the number of bytes - * written, and may indicate a partial write. - * - * @note Partial writes are possible not just for non-blocking I/O, but also for connections aborted while active. - * @note Unlike OpenSSL, repeated calls to s2n_send() should not duplicate the original parameters, but should - * update `buf` and `size` per the indication of size written. - * - * See the following example for guidance on calling `s2n_send()`: - * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_send.c - * - * @param conn A pointer to the s2n_connection object - * @param buf A pointer to a buffer that s2n will write data from - * @param size The size of buf - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. - */ -S2N_API extern ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked); - -/** - * Works in the same way as s2n_sendv_with_offset() but with the `offs` parameter implicitly assumed to be 0. - * Therefore in the partial write case, the caller would have to make sure that the `bufs` and `count` fields are modified in a way that takes - * the partial writes into account. - * - * @param conn A pointer to the s2n_connection object - * @param bufs A pointer to a vector of buffers that s2n will write data from. - * @param count The number of buffers in `bufs` - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. - */ -S2N_API extern ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked); - -/** - * Works in the same way as s2n_send() except that it accepts vectorized buffers. Will return the number of bytes written, and may indicate a partial write. Partial writes are possible not just for non-blocking I/O, but also for connections aborted while active. - * - * @note Partial writes are possible not just for non-blocking I/O, but also for connections aborted while active. - * - * @note Unlike OpenSSL, repeated calls to s2n_sendv_with_offset() should not duplicate the original parameters, but should update `bufs` and `count` per the indication of size written. - * - * See the following example for guidance on calling `s2n_sendv_with_offset()`: - * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_send.c - * - * @param conn A pointer to the s2n_connection object - * @param bufs A pointer to a vector of buffers that s2n will write data from. - * @param count The number of buffers in `bufs` - * @param offs The write cursor offset. This should be updated as data is written. See the example code. - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. - */ -S2N_API extern ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, ssize_t offs, s2n_blocked_status *blocked); - -/** - * Decrypts and reads **size* to `buf` data from the associated - * connection. - * - * @note Unlike OpenSSL, repeated calls to `s2n_recv` should not duplicate the original parameters, but should update `buf` and `size` per the indication of size read. - * - * See the following example for guidance on calling `s2n_recv()`: - * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_recv.c - * - * @param conn A pointer to the s2n_connection object - * @param buf A pointer to a buffer that s2n will place read data into. - * @param size Size of `buf` - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns The number of bytes read on success. 0 if the connection was shutdown by the peer. S2N_FAILURE on failure. - */ -S2N_API extern ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked); - -/** - * Allows users of s2n-tls to peek inside the data buffer of an s2n-tls connection to see if there more data to be read without actually reading it. - * - * This is useful when using select() on the underlying s2n-tls file descriptor with a message based application layer protocol. As a single call - * to s2n_recv may read all data off the underlying file descriptor, select() will be unable to tell you there if there is more application data - * ready for processing already loaded into the s2n-tls buffer. - * - * @note can then be used to determine if s2n_recv() needs to be called before more data comes in on the raw fd - * @param conn A pointer to the s2n_connection object - * @returns The number of bytes that can be read from the connection - */ -S2N_API extern uint32_t s2n_peek(struct s2n_connection *conn); - -/** - * Wipes and releases buffers and memory allocated during the TLS handshake. - * - * @note This function should be called after the handshake is successfully negotiated and logging or recording of handshake data is complete. - * - * @param conn A pointer to the s2n_connection object - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_free_handshake(struct s2n_connection *conn); - -/** - * Wipes and free the `in` and `out` buffers associated with a connection. - * - * @note This function may be called when a connection is - * in keep-alive or idle state to reduce memory overhead of long lived connections. - * - * @param conn A pointer to the s2n_connection object - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_release_buffers(struct s2n_connection *conn); - -/** - * Wipes an existing connection and allows it to be reused. Erases all data associated with a connection including - * pending reads. - * - * @note This function should be called after all I/O is completed and s2n_shutdown has been called. - * @note Reusing the same connection handle(s) is more performant than repeatedly calling s2n_connection_new() and s2n_connection_free(). - * - * @param conn A pointer to the s2n_connection object - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_wipe(struct s2n_connection *conn); - -/** - * Frees the memory associated with an s2n_connection - * handle. The handle is considered invalid after `s2n_connection_free` is used. - * s2n_connection_wipe() does not need to be called prior to this function. `s2n_connection_free` performs its own wipe - * of sensitive data. - * - * @param conn A pointer to the s2n_connection object - * @returns 0 on success. -1 on failure - */ -S2N_API extern int s2n_connection_free(struct s2n_connection *conn); - -/** - * Attempts a closure at the TLS layer. Does not close the underlying transport. This call may block in either direction. - * - * Unlike other TLS implementations, `s2n_shutdown` attempts a graceful shutdown by default. It will not return with success unless a close_notify alert is successfully - * sent and received. As a result, `s2n_shutdown` may fail when interacting with a non-conformant TLS implementation. - * - * Once `s2n_shutdown` is complete: - * * The s2n_connection handle cannot be used for reading for writing. - * * The underlying transport can be closed. Most likely via `shutdown()` or `close()`. - * * The s2n_connection handle can be freed via s2n_connection_free() or reused via s2n_connection_wipe() - * - * @param conn A pointer to the s2n_connection object - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_shutdown(struct s2n_connection *conn, s2n_blocked_status *blocked); - -/** - * Attempts to close the write side of the TLS connection. - * - * TLS1.3 supports closing the write side of a TLS connection while leaving the read - * side unaffected. This feature is usually referred to as "half-close". We send - * a close_notify alert, but do not wait for the peer to respond. - * - * Like `s2n_shutdown()`, this method does not affect the underlying transport. - * - * `s2n_shutdown_send()` may still be called for earlier TLS versions, but most - * TLS implementations will react by immediately discarding any pending writes and - * closing the connection. - * - * Once `s2n_shutdown_send()` is complete: - * * The s2n_connection handle CANNOT be used for writing. - * * The s2n_connection handle CAN be used for reading. - * * The write side of the underlying transport can be closed. Most likely via `shutdown()`. - * - * The application should still call `s2n_shutdown()` or wait for `s2n_recv()` to - * return 0 to indicate end-of-data before cleaning up the connection or closing - * the read side of the underlying transport. - * - * @param conn A pointer to the s2n_connection object - * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_shutdown_send(struct s2n_connection *conn, s2n_blocked_status *blocked); - -/** - * Used to declare what type of client certificate authentication to use. - * - * A s2n_connection will enforce client certificate authentication (mTLS) differently based on - * the `s2n_cert_auth_type` and `s2n_mode` (client/server) of the connection, as described below. - * - * Server behavior: - * - None (default): Will not request client authentication. - * - Optional: Request the client's certificate and validate it. If no certificate is received then - * no validation is performed. - * - Required: Request the client's certificate and validate it. Abort the handshake if a client - * certificate is not received. - * - * Client behavior: - * - None: Abort the handshake if the server requests client authentication. - * - Optional (default): Sends the client certificate if the server requests client - * authentication. No certificate is sent if the application hasn't provided a certificate. - * - Required: Send the client certificate. Abort the handshake if the server doesn't request - * client authentication or if the application hasn't provided a certificate. - */ -typedef enum { - S2N_CERT_AUTH_NONE, - S2N_CERT_AUTH_REQUIRED, - S2N_CERT_AUTH_OPTIONAL -} s2n_cert_auth_type; - -/** - * Gets Client Certificate authentication method the s2n_config object is using. - * - * @param config A pointer to a s2n_config object - * @param client_auth_type A pointer to a client auth policy. This will be updated to the s2n_config value. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_get_client_auth_type(struct s2n_config *config, s2n_cert_auth_type *client_auth_type); - -/** - * Sets whether or not a Client Certificate should be required to complete the TLS Connection. - * - * If this is set to `S2N_CERT_AUTH_OPTIONAL` the server will request a client certificate but allow the client to not provide one. - * Rejecting a client certificate when using `S2N_CERT_AUTH_OPTIONAL` will terminate the handshake. - * - * @param config A pointer to a s2n_config object - * @param client_auth_type The client auth policy for the connection - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_client_auth_type(struct s2n_config *config, s2n_cert_auth_type client_auth_type); - -/** - * Gets Client Certificate authentication method the s2n_connection object is using. - * - * @param conn A pointer to the s2n_connection object - * @param client_auth_type A pointer to a client auth policy. This will be updated to the s2n_connection value. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_get_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type *client_auth_type); - -/** - * Sets whether or not a Client Certificate should be required to complete the TLS Connection. - * - * If this is set to `S2N_CERT_AUTH_OPTIONAL` the server will request a client certificate but allow the client to not provide one. - * Rejecting a client certificate when using `S2N_CERT_AUTH_OPTIONAL` will terminate the handshake. - * - * @param conn A pointer to the s2n_connection object - * @param client_auth_type The client auth policy for the connection - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type client_auth_type); - -/** - * Gets the raw certificate chain received from the client. - * - * The retrieved certificate chain has the format described by the TLS 1.2 RFC: - * https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.2. Each certificate is a DER-encoded ASN.1 X.509, - * prepended by a 3 byte network-endian length value. Note that this format is used regardless of the connection's - * protocol version. - * - * @warning The buffer pointed to by `cert_chain_out` shares its lifetime with the s2n_connection object. - * - * @param conn A pointer to the s2n_connection object - * @param cert_chain_out A pointer that's set to the client certificate chain. - * @param cert_chain_len A pointer that's set to the size of the `cert_chain_out` buffer. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t **der_cert_chain_out, uint32_t *cert_chain_len); - -/** - * Sets the initial number of session tickets to send after a >=TLS1.3 handshake. The default value is one ticket. - * - * @param config A pointer to the config object. - * @param num The number of session tickets that will be sent. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num); - -/** - * Increases the number of session tickets to send after a >=TLS1.3 handshake. - * - * @param conn A pointer to the connection object. - * @param num The number of additional session tickets to send. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num); - -/** - * Returns the number of session tickets issued by the server. - * - * In TLS1.3, this number can be up to the limit configured by s2n_config_set_initial_ticket_count - * and s2n_connection_add_new_tickets_to_send. In earlier versions of TLS, this number will be either 0 or 1. - * - * This method only works for server connections. - * - * @param conn A pointer to the connection object. - * @param num The number of additional session tickets sent. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num); - -/** - * Sets the keying material lifetime for >=TLS1.3 session tickets so that one session doesn't get re-used ad infinitum. - * The default value is one week. - * - * @param conn A pointer to the connection object. - * @param lifetime_in_secs Lifetime of keying material in seconds. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure - */ -S2N_API extern int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs); - -struct s2n_session_ticket; - -/** - * Callback function for receiving a session ticket. - * - * This function will be called each time a session ticket is received, which may be multiple times for TLS1.3. - * - * # Safety - * - * `ctx` is a void pointer and the caller is responsible for ensuring it is cast to the correct type. - * `ticket` is valid only within the scope of this callback. - * - * @param conn A pointer to the connection object. - * @param ctx Context for the session ticket callback function. - * @param ticket Pointer to the received session ticket object. - */ -typedef int (*s2n_session_ticket_fn)(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket); - -/** - * Sets a session ticket callback to be called when a client receives a new session ticket. - * - * # Safety - * - * `callback` MUST cast `ctx` into the same type of pointer that was originally created. - * `ctx` MUST be valid for the lifetime of the config, or until a different context is set. - * - * @param config A pointer to the config object. - * @param callback The function that should be called when the callback is triggered. - * @param ctx The context to be passed when the callback is called. - */ -S2N_API extern int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx); - -/** - * Gets the length of the session ticket from a session ticket object. - * - * @param ticket Pointer to the session ticket object. - * @param data_len Pointer to be set to the length of the session ticket on success. - */ -S2N_API extern int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len); - -/** - * Gets the session ticket data from a session ticket object. - * - * # Safety - * The entire session ticket will be copied into `data` on success. Therefore, `data` MUST have enough - * memory to store the session ticket data. - * - * @param ticket Pointer to the session ticket object. - * @param max_data_len Maximum length of data that can be written to the 'data' pointer. - * @param data Pointer to where the session ticket data will be stored. - */ -S2N_API extern int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data); - -/** - * Gets the lifetime in seconds of the session ticket from a session ticket object. - * - * @param ticket Pointer to the session ticket object. - * @param session_lifetime Pointer to a variable where the lifetime of the session ticket will be stored. - */ -S2N_API extern int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime); - -/** - * De-serializes the session state and updates the connection accordingly. - * - * If this method fails, the connection should not be affected: calling s2n_negotiate - * with the connection should simply result in a full handshake. - * - * @param conn A pointer to the s2n_connection object - * @param session A pointer to a buffer of size `length` - * @param length The size of the `session` buffer - * - * @returns The number of copied bytes - */ -S2N_API extern int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length); - -/** - * Serializes the session state from connection and copies into the `session` buffer and returns the number of copied bytes - * - * @note This function is not recommended for > TLS 1.2 because in TLS1.3 - * servers can send multiple session tickets and this function will only - * return the most recently received ticket. - * - * @param conn A pointer to the s2n_connection object - * @param session A pointer to a buffer of size `max_length` - * @param max_length The size of the `session` buffer - * - * @returns The number of copied bytes - */ -S2N_API extern int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length); - -/** - * Retrieves a hint from the server indicating how long this ticket's lifetime is. - * - * @note This function is not recommended for > TLS 1.2 because in TLS1.3 - * servers can send multiple session tickets and this function will only - * return the most recently received ticket lifetime hint. - * - * @param conn A pointer to the s2n_connection object - * - * @returns The session ticket lifetime hint in seconds from the server or -1 when session ticket was not used for resumption. - */ -S2N_API extern int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn); - -/** - * Use this to query the serialized session state size before copying it into a buffer. - * - * @param conn A pointer to the s2n_connection object - * - * @returns number of bytes needed to store serialized session state - */ -S2N_API extern int s2n_connection_get_session_length(struct s2n_connection *conn); - -/** - * Gets the latest session id's length from the connection. - * - * Use this to query the session id size before copying it into a buffer. - * - * @param conn A pointer to the s2n_connection object - * - * @returns The latest session id length from the connection. Session id length will be 0 for TLS versions >= TLS1.3 as stateful session resumption has not yet been implemented in TLS1.3. - */ -S2N_API extern int s2n_connection_get_session_id_length(struct s2n_connection *conn); - -/** -* Gets the latest session id from the connection, copies it into the `session_id` buffer, and returns the number of copied bytes. -* -* The session id may change between s2n receiving the ClientHello and sending the ServerHello, but this function will always describe the latest session id. -* -* See s2n_client_hello_get_session_id() to get the session id as it was sent by the client in the ClientHello message. - * - * @param conn A pointer to the s2n_connection object - * @param session_id A pointer to a buffer of size `max_length` - * @param max_length The size of the `session_id` buffer - * - * @returns The number of copied bytes. - */ -S2N_API extern int s2n_connection_get_session_id(struct s2n_connection *conn, uint8_t *session_id, size_t max_length); - -/** - * Check if the connection was resumed from an earlier handshake. - * - * @param conn A pointer to the s2n_connection object - * - * @returns returns 1 if the handshake was abbreviated, otherwise returns 0 - */ -S2N_API extern int s2n_connection_is_session_resumed(struct s2n_connection *conn); - -/** - * Check if the connection is OCSP stapled. - * - * @param conn A pointer to the s2n_connection object - * - * @returns 1 if OCSP response was sent (if connection is in S2N_SERVER mode) or received (if connection is in S2N_CLIENT mode) during handshake, otherwise it returns 0. - */ -S2N_API extern int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn); - -/** - * TLS Signature Algorithms - RFC 5246 7.4.1.4.1 - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 - */ -typedef enum { - S2N_TLS_SIGNATURE_ANONYMOUS = 0, - S2N_TLS_SIGNATURE_RSA = 1, - S2N_TLS_SIGNATURE_ECDSA = 3, - S2N_TLS_SIGNATURE_MLDSA = 9, - - /* Use Private Range for RSA PSS since it's not defined there */ - S2N_TLS_SIGNATURE_RSA_PSS_RSAE = 224, - S2N_TLS_SIGNATURE_RSA_PSS_PSS -} s2n_tls_signature_algorithm; - -/** TLS Hash Algorithms - https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 - */ -typedef enum { - S2N_TLS_HASH_NONE = 0, - S2N_TLS_HASH_MD5 = 1, - S2N_TLS_HASH_SHA1 = 2, - S2N_TLS_HASH_SHA224 = 3, - S2N_TLS_HASH_SHA256 = 4, - S2N_TLS_HASH_SHA384 = 5, - S2N_TLS_HASH_SHA512 = 6, - - /* Use Private Range for MD5_SHA1 */ - S2N_TLS_HASH_MD5_SHA1 = 224 -} s2n_tls_hash_algorithm; - -/** - * Get the connection's selected signature algorithm. - * - * @param conn A pointer to the s2n_connection object - * @param chosen_alg A pointer to a s2n_tls_signature_algorithm object. This is an output parameter. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. - */ -S2N_API extern int s2n_connection_get_selected_signature_algorithm(struct s2n_connection *conn, s2n_tls_signature_algorithm *chosen_alg); - -/** - * Get the connection's selected digest algorithm. - * - * @param conn A pointer to the s2n_connection object - * @param chosen_alg A pointer to a s2n_tls_hash_algorithm object. This is an output parameter. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. - */ -S2N_API extern int s2n_connection_get_selected_digest_algorithm(struct s2n_connection *conn, s2n_tls_hash_algorithm *chosen_alg); - -/** - * Get the client certificate's signature algorithm. - * - * @param conn A pointer to the s2n_connection object - * @param chosen_alg A pointer to a s2n_tls_signature_algorithm object. This is an output parameter. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. - */ -S2N_API extern int s2n_connection_get_selected_client_cert_signature_algorithm(struct s2n_connection *conn, s2n_tls_signature_algorithm *chosen_alg); - -/** - * Get the client certificate's digest algorithm. - * - * @param conn A pointer to the s2n_connection object - * @param chosen_alg A pointer to a s2n_tls_hash_algorithm object. This is an output parameter. - * - * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. - */ -S2N_API extern int s2n_connection_get_selected_client_cert_digest_algorithm(struct s2n_connection *conn, s2n_tls_hash_algorithm *chosen_alg); - -/** - * Get the human readable signature scheme for the connection. - * - * This method will return: - * 1. The IANA "description" for the negotiated signature scheme. - * For example, rsa_pss_rsae_sha384 or ecdsa_secp256r1_sha256. - * 2. An unofficial description, if the server signature did not use an official - * IANA signature scheme. This description will take the form - * "legacy__". - * For example, legacy_rsa_sha224 or legacy_ecdsa_sha256. - * 3. "none", if the handshake did not include a server signature. - * This may happen if session resumption or RSA key exchange are used. - * - * If the connection has not yet performed a handshake, this method will error. - * - * A note on unofficial descriptions: If TLS1.2 or earlier is negotiated, - * an official IANA signature scheme may not be chosen. Before TLS1.3, a combination - * of "signature algorithm" and "hash algorithm" were used instead of signature schemes. - * Not all combinations were later assigned to official signature schemes. - * - * A note on ECDSA signature schemes: TLS1.3 and TLS1.2 ECDSA "signature schemes" - * share the same IANA value. However, this method assigns them different descriptions - * because the TLS1.3 versions (like ecdsa_secp256r1_sha256) imply specific curves, - * while the TLS1.2 versions (like legacy_ecdsa_sha256) do not. - * - * IANA signature schemes: - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme - * IANA signature algorithms: - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 - * IANA hash algorithms: - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 - * - * @param conn A pointer to the s2n connection - * @param group_name A pointer that will be set to the signature scheme name. - * @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. - */ -S2N_API extern int s2n_connection_get_signature_scheme(struct s2n_connection *conn, const char **scheme_name); - -/** - * Get the certificate used during the TLS handshake - * - * - If `conn` is a server connection, the certificate selected will depend on the - * ServerName sent by the client and supported ciphers. - * - If `conn` is a client connection, the certificate sent in response to a CertificateRequest - * message is returned. Currently s2n-tls supports loading only one certificate in client mode. Note that - * not all TLS endpoints will request a certificate. - * - * @param conn A pointer to the s2n_connection object - * - * @returns NULL if the certificate selection phase of the handshake has not completed or if a certificate was not requested by the peer - */ -S2N_API extern struct s2n_cert_chain_and_key *s2n_connection_get_selected_cert(struct s2n_connection *conn); - -/** - * @param chain_and_key A pointer to the s2n_cert_chain_and_key object being read. - * @param cert_length This return value represents the length of the s2n certificate chain `chain_and_key`. - * @returns the length of the s2n certificate chain `chain_and_key`. - */ -S2N_API extern int s2n_cert_chain_get_length(const struct s2n_cert_chain_and_key *chain_and_key, uint32_t *cert_length); - -/** - * Returns the certificate `out_cert` present at the index `cert_idx` of the certificate chain `chain_and_key`. - * - * Note that the index of the leaf certificate is zero. If the certificate chain `chain_and_key` is NULL or the - * certificate index value is not in the acceptable range for the input certificate chain, an error is returned. - * - * # Safety - * - * There is no memory allocation required for `out_cert` buffer prior to calling the `s2n_cert_chain_get_cert` API. - * The `out_cert` will contain the pointer to the s2n_cert initialized within the input s2n_cert_chain_and_key `chain_and_key`. - * The pointer to the output s2n certificate `out_cert` is valid until `chain_and_key` is freed up. - * If a caller wishes to persist the `out_cert` beyond the lifetime of `chain_and_key`, the contents would need to be - * copied prior to freeing `chain_and_key`. - * - * @param chain_and_key A pointer to the s2n_cert_chain_and_key object being read. - * @param out_cert A pointer to the output s2n_cert `out_cert` present at the index `cert_idx` of the certificate chain `chain_and_key`. - * @param cert_idx The certificate index for the requested certificate within the s2n certificate chain. - */ -S2N_API extern int s2n_cert_chain_get_cert(const struct s2n_cert_chain_and_key *chain_and_key, struct s2n_cert **out_cert, const uint32_t cert_idx); - -/** - * Returns the s2n certificate in DER format along with its length. - * - * The API gets the s2n certificate `cert` in DER format. The certificate is returned in the `out_cert_der` buffer. - * Here, `cert_len` represents the length of the certificate. - * - * A caller can use certificate parsing tools such as the ones provided by OpenSSL to parse the DER encoded certificate chain returned. - * - * # Safety - * - * The memory for the `out_cert_der` buffer is allocated and owned by s2n-tls. - * Since the size of the certificate can potentially be very large, a pointer to internal connection data is returned instead of - * copying the contents into a caller-provided buffer. - * - * The pointer to the output buffer `out_cert_der` is valid only while the connection exists. - * The `s2n_connection_free` API frees the memory associated with the out_cert_der buffer and after the `s2n_connection_wipe` API is - * called the memory pointed by out_cert_der is invalid. - * - * If a caller wishes to persist the `out_cert_der` beyond the lifetime of the connection, the contents would need to be - * copied prior to the connection termination. - * - * @param cert A pointer to the s2n_cert object being read. - * @param out_cert_der A pointer to the output buffer which will hold the s2n certificate `cert` in DER format. - * @param cert_length This return value represents the length of the certificate. - */ -S2N_API extern int s2n_cert_get_der(const struct s2n_cert *cert, const uint8_t **out_cert_der, uint32_t *cert_length); - -/** - * Returns the validated peer certificate chain as a `s2n_cert_chain_and_key` opaque object. - * - * The `s2n_cert_chain_and_key` parameter must be allocated by the caller using the `s2n_cert_chain_and_key_new` API - * prior to this function call and must be empty. To free the memory associated with the `s2n_cert_chain_and_key` object use the - * `s2n_cert_chain_and_key_free` API. - * - * @param conn A pointer to the s2n_connection object being read. - * @param cert_chain The returned validated peer certificate chain `cert_chain` retrieved from the s2n connection. - */ -S2N_API extern int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain); - -/** - * Returns the length of the DER encoded extension value of the ASN.1 X.509 certificate extension. - * - * @param cert A pointer to the s2n_cert object being read. - * @param oid A null-terminated cstring that contains the OID of the X.509 certificate extension to be read. - * @param ext_value_len This return value contains the length of DER encoded extension value of the ASN.1 X.509 certificate extension. - */ -S2N_API extern int s2n_cert_get_x509_extension_value_length(struct s2n_cert *cert, const uint8_t *oid, uint32_t *ext_value_len); - -/** - * Returns the DER encoding of an ASN.1 X.509 certificate extension value, it's length and a boolean critical. - * - * @param cert A pointer to the s2n_cert object being read. - * @param oid A null-terminated cstring that contains the OID of the X.509 certificate extension to be read. - * @param ext_value A pointer to the output buffer which will hold the DER encoding of an ASN.1 X.509 certificate extension value returned. - * @param ext_value_len This value is both an input and output parameter and represents the length of the output buffer `ext_value`. - * When used as an input parameter, the caller must use this parameter to convey the maximum length of `ext_value`. - * When used as an output parameter, `ext_value_len` holds the actual length of the DER encoding of the ASN.1 X.509 certificate extension value returned. - * @param critical This return value contains the boolean value for `critical`. - */ -S2N_API extern int s2n_cert_get_x509_extension_value(struct s2n_cert *cert, const uint8_t *oid, uint8_t *ext_value, uint32_t *ext_value_len, bool *critical); - -/** - * Returns the UTF8 String length of the ASN.1 X.509 certificate extension data. - * - * @param extension_data A pointer to the DER encoded ASN.1 X.509 certificate extension value being read. - * @param extension_len represents the length of the input buffer `extension_data`. - * @param utf8_str_len This return value contains the UTF8 String length of the ASN.1 X.509 certificate extension data. - */ -S2N_API extern int s2n_cert_get_utf8_string_from_extension_data_length(const uint8_t *extension_data, uint32_t extension_len, uint32_t *utf8_str_len); - -/** - * Returns the UTF8 String representation of the DER encoded ASN.1 X.509 certificate extension data. - * - * @param extension_data A pointer to the DER encoded ASN.1 X.509 certificate extension value being read. - * @param extension_len represents the length of the input buffer `extension_data`. - * @param out_data A pointer to the output buffer which will hold the UTF8 String representation of the DER encoded ASN.1 X.509 - * certificate extension data returned. - * @param out_len This value is both an input and output parameter and represents the length of the output buffer `out_data`. - * When used as an input parameter, the caller must use this parameter to convey the maximum length of `out_data`. - * When used as an output parameter, `out_len` holds the actual length of UTF8 String returned. - */ -S2N_API extern int s2n_cert_get_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len); - -/** - * Pre-shared key (PSK) Hash Algorithm - RFC 8446 Section-2.2 - */ -typedef enum { - S2N_PSK_HMAC_SHA256, - S2N_PSK_HMAC_SHA384, -} s2n_psk_hmac; - -/** - * Opaque pre shared key handle - */ -struct s2n_psk; - -/** - * Creates a new s2n external pre-shared key (PSK) object with `S2N_PSK_HMAC_SHA256` as the default - * PSK hash algorithm. An external PSK is a key established outside of TLS using a secure mutually agreed upon mechanism. - * - * Use `s2n_psk_free` to free the memory allocated to the s2n external PSK object created by this API. - * - * @returns struct s2n_psk* Returns a pointer to the newly created external PSK object. - */ -S2N_API struct s2n_psk *s2n_external_psk_new(void); - -/** - * Frees the memory associated with the external PSK object. - * - * @param psk Pointer to the PSK object to be freed. - */ -S2N_API int s2n_psk_free(struct s2n_psk **psk); - -/** - * Sets the identity for a given external PSK object. - * The identity is a unique identifier for the pre-shared secret. - * It is a non-secret value represented by raw bytes. - * - * # Safety - * - * The identity is transmitted over the network unencrypted and is a non-secret value. - * Do not include confidential information in the identity. - * - * Note that the identity is copied into s2n-tls memory and the caller is responsible for - * freeing the memory associated with the identity input. - * - * @param psk A pointer to a PSK object to be updated with the identity. - * @param identity The identity in raw bytes format to be copied. - * @param identity_size The length of the PSK identity being set. - */ -S2N_API int s2n_psk_set_identity(struct s2n_psk *psk, const uint8_t *identity, uint16_t identity_size); - -/** - * Sets the out-of-band/externally provisioned secret for a given external PSK object. - * - * # Safety - * - * Note that the secret is copied into s2n-tls memory and the caller is responsible for - * freeing the memory associated with the `secret` input. - * - * Deriving a shared secret from a password or other low-entropy source - * is not secure and is subject to dictionary attacks. - * See https://tools.ietf.org/rfc/rfc8446#section-2.2 for more information. - * - * @param psk A pointer to a PSK object to be updated with the secret. - * @param secret The secret in raw bytes format to be copied. - * @param secret_size The length of the pre-shared secret being set. - */ -S2N_API int s2n_psk_set_secret(struct s2n_psk *psk, const uint8_t *secret, uint16_t secret_size); - -/** - * Sets the hash algorithm for a given external PSK object. The supported PSK hash - * algorithms are as listed in the enum `s2n_psk_hmac` above. - * - * @param psk A pointer to the external PSK object to be updated with the PSK hash algorithm. - * @param hmac The PSK hash algorithm being set. - */ -S2N_API int s2n_psk_set_hmac(struct s2n_psk *psk, s2n_psk_hmac hmac); - -/** - * Appends a PSK object to the list of PSKs supported by the s2n connection. - * If a PSK with a duplicate identity is found, an error is returned and the PSK is not added to the list. - * Note that a copy of `psk` is stored on the connection. The user is still responsible for freeing the - * memory associated with `psk`. - * - * @param conn A pointer to the s2n_connection object that contains the list of PSKs supported. - * @param psk A pointer to the `s2n_psk` object to be appended to the list of PSKs on the s2n connection. - */ -S2N_API int s2n_connection_append_psk(struct s2n_connection *conn, struct s2n_psk *psk); - -/** - * The list of PSK modes supported by s2n-tls for TLS versions >= TLS1.3. - * Currently s2n-tls supports two modes - `S2N_PSK_MODE_RESUMPTION`, which represents the PSKs established - * using the previous connection via session resumption, and `S2N_PSK_MODE_EXTERNAL`, which represents PSKs - * established out-of-band/externally using a secure mutually agreed upon mechanism. - */ -typedef enum { - S2N_PSK_MODE_RESUMPTION, - S2N_PSK_MODE_EXTERNAL -} s2n_psk_mode; - -/** - * Sets the PSK mode on the s2n config object. - * The supported PSK modes are listed in the enum `s2n_psk_mode` above. - * - * @param config A pointer to the s2n_config object being updated. - * @param mode The PSK mode to be set. - */ -S2N_API int s2n_config_set_psk_mode(struct s2n_config *config, s2n_psk_mode mode); - -/** - * Sets the PSK mode on the s2n connection object. - * The supported PSK modes are listed in the enum `s2n_psk_mode` above. - * This API overrides the PSK mode set on config for this connection. - * - * @param conn A pointer to the s2n_connection object being updated. - * @param mode The PSK mode to be set. - */ -S2N_API int s2n_connection_set_psk_mode(struct s2n_connection *conn, s2n_psk_mode mode); - -/** - * Gets the negotiated PSK identity length from the s2n connection object. The negotiated PSK - * refers to the chosen PSK by the server to be used for the connection. - * - * This API can be used to determine if the negotiated PSK exists. If negotiated PSK exists a - * call to this API returns a value greater than zero. If the negotiated PSK does not exist, the - * value `0` is returned. - * - * @param conn A pointer to the s2n_connection object that successfully negotiated a PSK connection. - * @param identity_length The length of the negotiated PSK identity. - */ -S2N_API int s2n_connection_get_negotiated_psk_identity_length(struct s2n_connection *conn, uint16_t *identity_length); - -/** - * Gets the negotiated PSK identity from the s2n connection object. - * If the negotiated PSK does not exist, the PSK identity will not be obtained and no error will be returned. - * Prior to this API call, use `s2n_connection_get_negotiated_psk_identity_length` to determine if a - * negotiated PSK exists or not. - * - * # Safety - * - * The negotiated PSK identity will be copied into the identity buffer on success. - * Therefore, the identity buffer must have enough memory to fit the identity length. - * - * @param conn A pointer to the s2n_connection object. - * @param identity The negotiated PSK identity obtained from the s2n_connection object. - * @param max_identity_length The maximum length for the PSK identity. If the negotiated psk_identity length is - * greater than this `max_identity_length` value an error will be returned. - */ -S2N_API int s2n_connection_get_negotiated_psk_identity(struct s2n_connection *conn, uint8_t *identity, uint16_t max_identity_length); - -struct s2n_offered_psk; - -/** - * Creates a new s2n offered PSK object. - * An offered PSK object represents a single PSK sent by the client. - * - * # Safety - * - * Use `s2n_offered_psk_free` to free the memory allocated to the s2n offered PSK object created by this API. - * - * @returns struct s2n_offered_psk* Returns a pointer to the newly created offered PSK object. - */ -S2N_API struct s2n_offered_psk *s2n_offered_psk_new(void); - -/** - * Frees the memory associated with the `s2n_offered_psk` object. - * - * @param psk A pointer to the `s2n_offered_psk` object to be freed. - */ -S2N_API int s2n_offered_psk_free(struct s2n_offered_psk **psk); - -/** - * Gets the PSK identity and PSK identity length for a given offered PSK object. - * - * @param psk A pointer to the offered PSK object being read. - * @param identity The PSK identity being obtained. - * @param size The length of the PSK identity being obtained. - */ -S2N_API int s2n_offered_psk_get_identity(struct s2n_offered_psk *psk, uint8_t **identity, uint16_t *size); - -struct s2n_offered_psk_list; - -/** - * Checks whether the offered PSK list has an offered psk object next in line in the list. - * An offered PSK list contains all the PSKs offered by the client for the server to select. - * - * # Safety - * - * This API returns a pointer to the s2n-tls internal memory with limited lifetime. - * After the completion of `s2n_psk_selection_callback` this pointer is invalid. - * - * @param psk_list A pointer to the offered PSK list being read. - * @returns bool A boolean value representing whether an offered psk object is present next in line in the offered PSK list. - */ -S2N_API bool s2n_offered_psk_list_has_next(struct s2n_offered_psk_list *psk_list); - -/** - * Obtains the next offered PSK object from the list of offered PSKs. Use `s2n_offered_psk_list_has_next` - * prior to this API call to ensure we have not reached the end of the list. - * - * @param psk_list A pointer to the offered PSK list being read. - * @param psk A pointer to the next offered PSK object being obtained. - */ -S2N_API int s2n_offered_psk_list_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk); - -/** - * Returns the offered PSK list to its original read state. - * - * When `s2n_offered_psk_list_reread` is called, `s2n_offered_psk_list_next` will return the first PSK - * in the offered PSK list. - * - * @param psk_list A pointer to the offered PSK list being reread. - */ -S2N_API int s2n_offered_psk_list_reread(struct s2n_offered_psk_list *psk_list); - -/** - * Chooses a PSK from the offered PSK list to be used for the connection. - * This API matches the PSK identity received from the client against the server's known PSK identities - * list, in order to choose the PSK to be used for the connection. If the PSK identity sent from the client - * is NULL, no PSK is chosen for the connection. If the client offered PSK identity has no matching PSK identity - * with the server, an error will be returned. Use this API along with the `s2n_psk_selection_callback` callback - * to select a PSK identity. - * - * @param psk_list A pointer to the server's known PSK list used to compare for a matching PSK with the client. - * @param psk A pointer to the client's PSK object used to compare with the server's known PSK identities. - */ -S2N_API int s2n_offered_psk_list_choose_psk(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk); - -/** - * Callback function to select a PSK from a list of offered PSKs. - * Use this callback to implement custom PSK selection logic. The s2n-tls default PSK selection logic - * chooses the first matching PSK from the list of offered PSKs sent by the client. - * - * # Safety - * - * `context` is a void pointer and the caller is responsible for ensuring it is cast to the correct type. - * After the completion of this callback, the pointer to `psk_list` is invalid. - * - * @param conn A pointer to the s2n_connection object. - * @param context A pointer to a context for the caller to pass state to the callback, if needed. - * @param psk_list A pointer to the offered PSK list being read. - */ -typedef int (*s2n_psk_selection_callback)(struct s2n_connection *conn, void *context, - struct s2n_offered_psk_list *psk_list); - -/** - * Sets the callback to select the matching PSK. - * If this callback is not set s2n-tls uses a default PSK selection logic that selects the first matching - * server PSK. - * - * @param config A pointer to the s2n_config object. - * @param cb The function that should be called when the callback is triggered. - * @param context A pointer to a context for the caller to pass state to the callback, if needed. - */ -S2N_API int s2n_config_set_psk_selection_callback(struct s2n_config *config, s2n_psk_selection_callback cb, void *context); - -/** - * Get the number of bytes the connection has received. - * - * @param conn A pointer to the connection - * @returns return the number of bytes received by s2n-tls "on the wire" - */ -S2N_API extern uint64_t s2n_connection_get_wire_bytes_in(struct s2n_connection *conn); - -/** - * Get the number of bytes the connection has transmitted out. - * - * @param conn A pointer to the connection - * @returns return the number of bytes transmitted out by s2n-tls "on the wire" - */ -S2N_API extern uint64_t s2n_connection_get_wire_bytes_out(struct s2n_connection *conn); - -/** - * Access the protocol version supported by the client. - * - * @note The return value corresponds to the macros defined as S2N_SSLv2, - * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. - * - * @param conn A pointer to the connection - * @returns returns the highest protocol version supported by the client - */ -S2N_API extern int s2n_connection_get_client_protocol_version(struct s2n_connection *conn); - -/** - * Access the protocol version supported by the server. - * - * @note The return value corresponds to the macros defined as S2N_SSLv2, - * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. - * - * @param conn A pointer to the connection - * @returns Returns the highest protocol version supported by the server - */ -S2N_API extern int s2n_connection_get_server_protocol_version(struct s2n_connection *conn); - -/** - * Access the protocol version selected for the connection. - * - * @note The return value corresponds to the macros defined as S2N_SSLv2, - * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. - * - * @param conn A pointer to the connection - * @returns The protocol version actually negotiated by the handshake - */ -S2N_API extern int s2n_connection_get_actual_protocol_version(struct s2n_connection *conn); - -/** - * Access the client hello protocol version for the connection. - * - * @note The return value corresponds to the macros defined as S2N_SSLv2, - * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. - * - * @param conn A pointer to the connection - * @returns The protocol version used to send the initial client hello message. - */ -S2N_API extern int s2n_connection_get_client_hello_version(struct s2n_connection *conn); - -/** - * Access the protocol version from the header of the first record that contained the ClientHello message. - * - * @note This field has been deprecated and should not be confused with the client hello - * version. It is often set very low, usually to TLS1.0 for compatibility reasons, - * and should never be set higher than TLS1.2. Therefore this method should only be used - * for logging or fingerprinting. - * - * @param conn A pointer to the client hello struct - * @param out The protocol version in the record header containing the Client Hello. - */ -S2N_API extern int s2n_client_hello_get_legacy_record_version(struct s2n_client_hello *ch, uint8_t *out); - -/** - * Check if Client Auth was used for a connection. - * - * @param conn A pointer to the connection - * @returns 1 if the handshake completed and Client Auth was negotiated during then - * handshake. - */ -S2N_API extern int s2n_connection_client_cert_used(struct s2n_connection *conn); - -/** - * A function that provides a human readable string of the cipher suite that was chosen - * for a connection. - * - * @warning The string "TLS_NULL_WITH_NULL_NULL" is returned before the TLS handshake has been performed. - * This does not mean that the ciphersuite "TLS_NULL_WITH_NULL_NULL" will be used by the connection, - * it is merely being used as a placeholder. - * - * @note This function is only accurate after the TLS handshake. - * - * @param conn A pointer to the connection - * @returns A string indicating the cipher suite negotiated by s2n in OpenSSL format. - */ -S2N_API extern const char *s2n_connection_get_cipher(struct s2n_connection *conn); - -/** - * A metric to determine whether or not the server found a certificate that matched - * the client's SNI extension. - * - * S2N_SNI_NONE: Client did not send the SNI extension. - * S2N_SNI_EXACT_MATCH: Server had a certificate that matched the client's SNI extension. - * S2N_SNI_WILDCARD_MATCH: Server had a certificate with a domain name containing a wildcard character - * that could be matched to the client's SNI extension. - * S2N_SNI_NO_MATCH: Server did not have a certificate that could be matched to the client's - * SNI extension. - */ -typedef enum { - S2N_SNI_NONE = 1, - S2N_SNI_EXACT_MATCH, - S2N_SNI_WILDCARD_MATCH, - S2N_SNI_NO_MATCH, -} s2n_cert_sni_match; - -/** - * A function that provides insight into whether or not the server was able to send a certificate that - * partially or completely matched the client's SNI extension. - * - * @note This function can be used as a metric in a failed connection as long as the failure - * occurs after certificate selection. - * - * @param conn A pointer to the connection - * @param cert_match An enum indicating whether or not the server found a certificate - * that matched the client's SNI extension. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API extern int s2n_connection_get_certificate_match(struct s2n_connection *conn, s2n_cert_sni_match *match_status); - -/** - * Provides access to the TLS master secret. - * - * This is a dangerous method and should not be used unless absolutely necessary. - * Mishandling the master secret can compromise both the current connection - * and any past or future connections that use the same master secret due to - * session resumption. - * - * This method is only supported for older TLS versions, and will report an S2N_ERR_INVALID_STATE - * usage error if called for a TLS1.3 connection. TLS1.3 includes a new key schedule - * that derives independent secrets from the master secret for specific purposes, - * such as separate traffic, session ticket, and exporter secrets. Using the master - * secret directly circumvents that security feature, reducing the security of - * the protocol. - * - * If you need cryptographic material tied to the current TLS session, consider - * `s2n_connection_tls_exporter` instead. Although s2n_connection_tls_exporter - * currently only supports TLS1.3, there is also an RFC that describes exporters - * for older TLS versions: https://datatracker.ietf.org/doc/html/rfc5705 - * Using the master secret as-is or defining your own exporter is dangerous. - * - * @param conn A pointer to the connection. - * @param secret_bytes Memory to copy the master secret into. The secret - * is always 48 bytes long. - * @param max_size The size of the memory available at `secret_bytes`. Must be - * at least 48 bytes. - * @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. `secret_bytes` - * will be set on success. - */ -S2N_API extern int s2n_connection_get_master_secret(const struct s2n_connection *conn, - uint8_t *secret_bytes, size_t max_size); - -/** - * Provides access to the TLS-Exporter functionality. - * - * See https://datatracker.ietf.org/doc/html/rfc5705 and https://www.rfc-editor.org/rfc/rfc8446. - * - * @note This is currently only available with TLS 1.3 connections which have finished a handshake. - * - * @param conn A pointer to the connection - * @returns A POSIX error signal. If an error was returned, the value contained in `output` should be considered invalid. - */ -S2N_API extern int s2n_connection_tls_exporter(struct s2n_connection *conn, - const uint8_t *label, uint32_t label_length, const uint8_t *context, uint32_t context_length, - uint8_t *output, uint32_t output_length); - -/** - * Returns the IANA value for the connection's negotiated cipher suite. - * - * The value is returned in the form of `first,second`, in order to closely match - * the values defined in the [IANA Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4). - * For example if the connection's negotiated cipher suite is `TLS_AES_128_GCM_SHA256`, - * which is registered as `0x13,0x01`, then `first = 0x13` and `second = 0x01`. - * - * This method will only succeed after the cipher suite has been negotiated with the peer. - * - * @param conn A pointer to the connection being read - * @param first A pointer to a single byte, which will be updated with the first byte in the registered IANA value. - * @param second A pointer to a single byte, which will be updated with the second byte in the registered IANA value. - * @returns A POSIX error signal. If an error was returned, the values contained in `first` and `second` should be considered invalid. - */ -S2N_API extern int s2n_connection_get_cipher_iana_value(struct s2n_connection *conn, uint8_t *first, uint8_t *second); - -/** - * Function to check if the cipher used by current connection is supported by the current - * cipher preferences. - * @param conn A pointer to the s2n connection - * @param version A string representing the security policy to check against. - * @returns 1 if the connection satisfies the cipher suite. 0 if the connection does not satisfy the cipher suite. -1 if there is an error. - */ -S2N_API extern int s2n_connection_is_valid_for_cipher_preferences(struct s2n_connection *conn, const char *version); - -/** - * Function to get the human readable elliptic curve name for the connection. - * - * @deprecated Use `s2n_connection_get_key_exchange_group` instead - * - * @param conn A pointer to the s2n connection - * @returns A string indicating the elliptic curve used during ECDHE key exchange. The string "NONE" is returned if no curve was used. - */ -S2N_API extern const char *s2n_connection_get_curve(struct s2n_connection *conn); - -/** - * Function to get the human readable KEM name for the connection. - * - * @deprecated This function was previously used to retrieve the negotiated PQ group in TLS 1.2. - * PQ key exchange in TLS1.2 was experimental and is now deprecated. Use s2n_connection_get_kem_group_name() - * to retrieve the PQ TLS 1.3 Group name. - * - * @param conn A pointer to the s2n connection - * @returns A human readable string for the KEM group. If there is no KEM configured returns "NONE" - */ -S2N_API extern const char *s2n_connection_get_kem_name(struct s2n_connection *conn); - -/** - * Function to get the human readable KEM group name for the connection. - * - * @note PQ key exchange will not occur if the connection is < TLS1.3 or the configured security - * policy has no KEM groups on it. It also will not occur if the peer does not support PQ key exchange. - * In these instances this function will return "NONE". - * - * @param conn A pointer to the s2n connection - * @returns A human readable string for the KEM group. Returns "NONE" if no PQ key exchange occurred. - */ -S2N_API extern const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn); - -/** - * Function to get the human readable key exchange group name for the connection, for example: - * `secp521r1` or `SecP256r1MLKEM768`. If an EC curve or KEM was not negotiated, S2N_FAILURE will be - * returned. - * - * @note This function replaces `s2n_connection_get_curve` and `s2n_connection_get_kem_group_name`, returning - * the named group regardless if a hybrid PQ group was negotiated or not. - * - * @param conn A pointer to the s2n connection - * @param group_name A pointer that will be set to point to a const char* containing the group name - * @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. `group_name` will be set on success. - */ -S2N_API extern int s2n_connection_get_key_exchange_group(struct s2n_connection *conn, const char **group_name); - -/** - * Function to get the alert that caused a connection to close. s2n-tls considers all - * TLS alerts fatal and shuts down a connection whenever one is received. - * - * @param conn A pointer to the s2n connection - * @returns The TLS alert code that caused a connection to be shut down - */ -S2N_API extern int s2n_connection_get_alert(struct s2n_connection *conn); - -/** - * Function to return the last TLS handshake type that was processed. The returned format is a human readable string. - * - * @param conn A pointer to the s2n connection - * @returns A human-readable handshake type name, e.g. "NEGOTIATED|FULL_HANDSHAKE|PERFECT_FORWARD_SECRECY" - */ -S2N_API extern const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn); - -/** - * Function to return the last TLS message that was processed. The returned format is a human readable string. - * @param conn A pointer to the s2n connection - * @returns The last message name in the TLS state machine, e.g. "SERVER_HELLO", "APPLICATION_DATA". - */ -S2N_API extern const char *s2n_connection_get_last_message_name(struct s2n_connection *conn); - -/** - * Opaque async private key operation handle - */ -struct s2n_async_pkey_op; - -/** - * Sets whether or not a connection should enforce strict signature validation during the - * `s2n_async_pkey_op_apply` call. - * - * `mode` can take the following values: - * - `S2N_ASYNC_PKEY_VALIDATION_FAST` - default behavior: s2n-tls will perform only the minimum validation required for safe use of the asyn pkey operation. - * - `S2N_ASYNC_PKEY_VALIDATION_STRICT` - in addition to the previous checks, s2n-tls will also ensure that the signature created as a result of the async private key sign operation matches the public key on the connection. - */ -typedef enum { - S2N_ASYNC_PKEY_VALIDATION_FAST, - S2N_ASYNC_PKEY_VALIDATION_STRICT -} s2n_async_pkey_validation_mode; - -/** - * The type of private key operation - */ -typedef enum { - S2N_ASYNC_DECRYPT, - S2N_ASYNC_SIGN -} s2n_async_pkey_op_type; - -/** - * Callback function for handling private key operations - * - * Invoked every time an operation requiring the private key is encountered - * during the handshake. - * - * # Safety - * * `op` is owned by the application and MUST be freed. - * - * @param conn Connection which triggered the callback - * @param op An opaque object representing the private key operation - */ -typedef int (*s2n_async_pkey_fn)(struct s2n_connection *conn, struct s2n_async_pkey_op *op); - -/** - * Sets up the callback to invoke when private key operations occur. - * - * @param config Config to set the callback - * @param fn The function that should be called for each private key operation - */ -S2N_API extern int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn); - -/** - * Performs a private key operation using the given private key. - * - * # Safety - * * Can only be called once. Any subsequent calls will produce a `S2N_ERR_T_USAGE` error. - * * Safe to call from inside s2n_async_pkey_fn - * * Safe to call from a different thread, as long as no other thread is operating on `op`. - * - * @param op An opaque object representing the private key operation - * @param key The private key used for the operation. It can be extracted from - * `conn` through the `s2n_connection_get_selected_cert` and `s2n_cert_chain_and_key_get_private_key` calls - */ -S2N_API extern int s2n_async_pkey_op_perform(struct s2n_async_pkey_op *op, s2n_cert_private_key *key); - -/** - * Finalizes a private key operation and unblocks the connection. - * - * # Safety - * * `conn` must match the connection that originally triggered the callback. - * * Must be called after the operation is performed. - * * Can only be called once. Any subsequent calls will produce a `S2N_ERR_T_USAGE` error. - * * Safe to call from inside s2n_async_pkey_fn - * * Safe to call from a different thread, as long as no other thread is operating on `op`. - * - * @param op An opaque object representing the private key operation - * @param conn The connection associated with the operation that should be unblocked - */ -S2N_API extern int s2n_async_pkey_op_apply(struct s2n_async_pkey_op *op, struct s2n_connection *conn); - -/** - * Frees the opaque structure representing a private key operation. - * - * # Safety - * * MUST be called for every operation passed to s2n_async_pkey_fn - * * Safe to call before or after the connection that created the operation is freed - * - * @param op An opaque object representing the private key operation - */ -S2N_API extern int s2n_async_pkey_op_free(struct s2n_async_pkey_op *op); - -/** - * Configures whether or not s2n-tls will perform potentially expensive validation of - * the results of a private key operation. - * - * @param config Config to set the validation mode for - * @param mode What level of validation to perform - */ -S2N_API extern int s2n_config_set_async_pkey_validation_mode(struct s2n_config *config, s2n_async_pkey_validation_mode mode); - -/** - * Returns the type of the private key operation. - * - * @param op An opaque object representing the private key operation - * @param type A pointer to be set to the type - */ -S2N_API extern int s2n_async_pkey_op_get_op_type(struct s2n_async_pkey_op *op, s2n_async_pkey_op_type *type); - -/** - * Returns the size of the input to the private key operation. - * - * @param op An opaque object representing the private key operation - * @param data_len A pointer to be set to the size - */ -S2N_API extern int s2n_async_pkey_op_get_input_size(struct s2n_async_pkey_op *op, uint32_t *data_len); - -/** - * Returns the input to the private key operation. - * - * When signing, the input is the digest to sign. - * When decrypting, the input is the data to decrypt. - * - * When signing with ML-DSA, the input is the "external mu" pre-hash value described in - * https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-09.html#appendix-D - * - * # Safety - * * `data` must be sufficiently large to contain the input. - * `s2n_async_pkey_op_get_input_size` can be called to determine how much memory is required. - * * s2n-tls does not take ownership of `data`. - * The application still owns the memory and must free it if necessary. - * - * @param op An opaque object representing the private key operation - * @param data A pointer to a buffer to copy the input into - * @param data_len The maximum size of the `data` buffer - */ -S2N_API extern int s2n_async_pkey_op_get_input(struct s2n_async_pkey_op *op, uint8_t *data, uint32_t data_len); - -/** - * Sets the output of the private key operation. - * - * # Safety - * * s2n-tls does not take ownership of `data`. - * The application still owns the memory and must free it if necessary. - * - * @param op An opaque object representing the private key operation - * @param data A pointer to a buffer containing the output - * @param data_len The size of the `data` buffer - */ -S2N_API extern int s2n_async_pkey_op_set_output(struct s2n_async_pkey_op *op, const uint8_t *data, uint32_t data_len); - -/** - * Callback function for handling key log events - * - * THIS SHOULD BE USED FOR DEBUGGING PURPOSES ONLY! - * - * Each log line is formatted with the - * [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) - * without a newline. - * - * # Safety - * - * * `ctx` MUST be cast into the same type of pointer that was originally created - * * `logline` bytes MUST be copied or discarded before this function returns - * - * @param ctx Context for the callback - * @param conn Connection for which the log line is being emitted - * @param logline Pointer to the log line data - * @param len Length of the log line data - */ -typedef int (*s2n_key_log_fn)(void *ctx, struct s2n_connection *conn, uint8_t *logline, size_t len); - -/** - * Sets a key logging callback on the provided config - * - * THIS SHOULD BE USED FOR DEBUGGING PURPOSES ONLY! - * - * Setting this function enables configurations to emit secrets in the - * [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) - * - * # Safety - * - * * `callback` MUST cast `ctx` into the same type of pointer that was originally created - * * `ctx` MUST live for at least as long as it is set on the config - * - * @param config Config to set the callback - * @param callback The function that should be called for each secret log entry - * @param ctx The context to be passed when the callback is called - */ -S2N_API extern int s2n_config_set_key_log_cb(struct s2n_config *config, s2n_key_log_fn callback, void *ctx); - -/** - * s2n_config_enable_cert_req_dss_legacy_compat adds a dss cert type in the server certificate request when being called. - * It only sends the dss cert type in the cert request but does not succeed the handshake if a dss cert is received. - * Please DO NOT call this api unless you know you actually need legacy DSS certificate type compatibility - * @param config Config to enable legacy DSS certificates for - */ -S2N_API extern int s2n_config_enable_cert_req_dss_legacy_compat(struct s2n_config *config); - -/** - * Sets the maximum bytes of early data the server will accept. - * - * The default maximum is 0. If the maximum is 0, the server rejects all early data requests. - * The config maximum can be overridden by the connection maximum or the maximum on an external pre-shared key. - * - * @param config A pointer to the config - * @param max_early_data_size The maximum early data that the server will accept - * @returns A POSIX error signal. If successful, the maximum early data size was updated. - */ -S2N_API int s2n_config_set_server_max_early_data_size(struct s2n_config *config, uint32_t max_early_data_size); - -/** - * Sets the maximum bytes of early data the server will accept. - * - * The default maximum is 0. If the maximum is 0, the server rejects all early data requests. - * The connection maximum can be overridden by the maximum on an external pre-shared key. - * - * @param conn A pointer to the connection - * @param max_early_data_size The maximum early data the server will accept - * @returns A POSIX error signal. If successful, the maximum early data size was updated. - */ -S2N_API int s2n_connection_set_server_max_early_data_size(struct s2n_connection *conn, uint32_t max_early_data_size); - -/** - * Sets the user context associated with early data on a server. - * - * This context is passed to the `s2n_early_data_cb` callback to help decide whether to accept or reject early data. - * - * Unlike most contexts, the early data context is a byte buffer instead of a void pointer. - * This is because we need to serialize the context into session tickets. - * - * This API is intended for use with session resumption, and will not affect pre-shared keys. - * - * @param conn A pointer to the connection - * @param context A pointer to the user context data. This data will be copied. - * @param context_size The size of the data to read from the `context` pointer. - * @returns A POSIX error signal. If successful, the context was updated. - */ -S2N_API int s2n_connection_set_server_early_data_context(struct s2n_connection *conn, const uint8_t *context, uint16_t context_size); - -/** - * Configures a particular pre-shared key to allow early data. - * - * `max_early_data_size` must be set to the maximum early data accepted by the server. - * - * In order to use early data, the cipher suite set on the pre-shared key must match the cipher suite - * ultimately negotiated by the TLS handshake. Additionally, the cipher suite must have the same - * hmac algorithm as the pre-shared key. - * - * @param psk A pointer to the pre-shared key, created with `s2n_external_psk_new`. - * @param max_early_data_size The maximum early data that can be sent or received using this key. - * @param cipher_suite_first_byte The first byte in the registered IANA value of the associated cipher suite. - * @param cipher_suite_second_byte The second byte in the registered IANA value of the associated cipher suite. - * @returns A POSIX error signal. If successful, `psk` was updated. - */ -S2N_API int s2n_psk_configure_early_data(struct s2n_psk *psk, uint32_t max_early_data_size, - uint8_t cipher_suite_first_byte, uint8_t cipher_suite_second_byte); - -/** - * Sets the optional `application_protocol` associated with the given pre-shared key. - * - * In order to use early data, the `application_protocol` set on the pre-shared key must match - * the `application_protocol` ultimately negotiated by the TLS handshake. - * - * @param psk A pointer to the pre-shared key, created with `s2n_external_psk_new`. - * @param application_protocol A pointer to the associated application protocol data. This data will be copied. - * @param size The size of the data to read from the `application_protocol` pointer. - * @returns A POSIX error signal. If successful, the application protocol was set. - */ -S2N_API int s2n_psk_set_application_protocol(struct s2n_psk *psk, const uint8_t *application_protocol, uint8_t size); - -/** - * Sets the optional user early data context associated with the given pre-shared key. - * - * The early data context is passed to the `s2n_early_data_cb` callback to help decide whether - * to accept or reject early data. - * - * @param psk A pointer to the pre-shared key, created with `s2n_external_psk_new`. - * @param context A pointer to the associated user context data. This data will be copied. - * @param size The size of the data to read from the `context` pointer. - * @returns A POSIX error signal. If successful, the context was set. - */ -S2N_API int s2n_psk_set_early_data_context(struct s2n_psk *psk, const uint8_t *context, uint16_t size); - -/** - * The status of early data on a connection. - * - * S2N_EARLY_DATA_STATUS_OK: Early data is in progress. - * S2N_EARLY_DATA_STATUS_NOT_REQUESTED: The client did not request early data, so none was sent or received. - * S2N_EARLY_DATA_STATUS_REJECTED: The client requested early data, but the server rejected the request. - * Early data may have been sent, but was not received. - * S2N_EARLY_DATA_STATUS_END: All early data was successfully sent and received. - */ -typedef enum { - S2N_EARLY_DATA_STATUS_OK, - S2N_EARLY_DATA_STATUS_NOT_REQUESTED, - S2N_EARLY_DATA_STATUS_REJECTED, - S2N_EARLY_DATA_STATUS_END, -} s2n_early_data_status_t; - -/** - * Reports the current state of early data for a connection. - * - * See `s2n_early_data_status_t` for all possible states. - * - * @param conn A pointer to the connection - * @param status A pointer which will be set to the current early data status - * @returns A POSIX error signal. - */ -S2N_API int s2n_connection_get_early_data_status(struct s2n_connection *conn, s2n_early_data_status_t *status); - -/** - * Reports the remaining size of the early data allowed by a connection. - * - * If early data was rejected or not requested, the remaining early data size is 0. - * Otherwise, the remaining early data size is the maximum early data allowed by the connection, - * minus the early data sent or received so far. - * - * @param conn A pointer to the connection - * @param allowed_early_data_size A pointer which will be set to the remaining early data currently allowed by `conn` - * @returns A POSIX error signal. - */ -S2N_API int s2n_connection_get_remaining_early_data_size(struct s2n_connection *conn, uint32_t *allowed_early_data_size); - -/** - * Reports the maximum size of the early data allowed by a connection. - * - * This is the maximum amount of early data that can ever be sent and received for a connection. - * It is not affected by the actual status of the early data, so can be non-zero even if early data - * is rejected or not requested. - * - * @param conn A pointer to the connection - * @param max_early_data_size A pointer which will be set to the maximum early data allowed by `conn` - * @returns A POSIX error signal. - */ -S2N_API int s2n_connection_get_max_early_data_size(struct s2n_connection *conn, uint32_t *max_early_data_size); - -/** - * Called by the client to begin negotiation and send early data. - * - * See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch14-early-data.md - * for usage and examples. DO NOT USE unless you have considered the security issues and - * implemented mitigation for anti-replay attacks. - * - * @param conn A pointer to the connection - * @param data A pointer to the early data to be sent - * @param data_len The size of the early data to send - * @param data_sent A pointer which will be set to the size of the early data sent - * @param blocked A pointer which will be set to the blocked status, as in `s2n_negotiate`. - * @returns A POSIX error signal. The error should be handled as in `s2n_negotiate`. - */ -S2N_API int s2n_send_early_data(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len, - ssize_t *data_sent, s2n_blocked_status *blocked); - -/** - * Called by the server to begin negotiation and accept any early data the client sends. - * - * See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch14-early-data.md - * for usage and examples. DO NOT USE unless you have considered the security issues and - * implemented mitigation for anti-replay attacks. - * - * @param conn A pointer to the connection - * @param data A pointer to a buffer to store the early data received - * @param max_data_len The size of the early data buffer - * @param data_received A pointer which will be set to the size of the early data received - * @param blocked A pointer which will be set to the blocked status, as in `s2n_negotiate`. - * @returns A POSIX error signal. The error should be handled as in `s2n_negotiate`. - */ -S2N_API int s2n_recv_early_data(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, - ssize_t *data_received, s2n_blocked_status *blocked); - -struct s2n_offered_early_data; - -/** - * A callback which can be implemented to accept or reject early data. - * - * This callback is triggered only after the server has determined early data is otherwise acceptable according - * to the TLS early data specification. Implementations therefore only need to cover application-specific checks, - * not the standard TLS early data validation. - * - * This callback can be synchronous or asynchronous. For asynchronous behavior, return success without - * calling `s2n_offered_early_data_reject` or `s2n_offered_early_data_accept`. `early_data` will - * still be a valid reference, and the connection will block until `s2n_offered_early_data_reject` or - * `s2n_offered_early_data_accept` is called. - * - * @param conn A pointer to the connection - * @param early_data A pointer which can be used to access information about the proposed early data - * and then accept or reject it. - * @returns A POSIX error signal. If unsuccessful, the connection will be closed with an error. - */ -typedef int (*s2n_early_data_cb)(struct s2n_connection *conn, struct s2n_offered_early_data *early_data); - -/** - * Set a callback to accept or reject early data. - * - * @param config A pointer to the connection config - * @param cb A pointer to the implementation of the callback. - * @returns A POSIX error signal. If successful, the callback was set. - */ -S2N_API int s2n_config_set_early_data_cb(struct s2n_config *config, s2n_early_data_cb cb); - -/** - * Get the length of the early data context set by the user. - * - * @param early_data A pointer to the early data information - * @param context_len The length of the user context - * @returns A POSIX error signal. - */ -S2N_API int s2n_offered_early_data_get_context_length(struct s2n_offered_early_data *early_data, uint16_t *context_len); - -/** - * Get the early data context set by the user. - * - * @param early_data A pointer to the early data information - * @param context A byte buffer to copy the user context into - * @param max_len The size of `context`. Must be >= to the result of `s2n_offered_early_data_get_context_length`. - * @returns A POSIX error signal. - */ -S2N_API int s2n_offered_early_data_get_context(struct s2n_offered_early_data *early_data, uint8_t *context, uint16_t max_len); - -/** - * Reject early data offered by the client. - * - * @param early_data A pointer to the early data information - * @returns A POSIX error signal. If success, the client's early data will be rejected. - */ -S2N_API int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_data); - -/** - * Accept early data offered by the client. - * - * @param early_data A pointer to the early data information - * @returns A POSIX error signal. If success, the client's early data will be accepted. - */ -S2N_API int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data); - -/** - * Retrieves the list of supported groups configured by the security policy associated with `config`. - * - * The retrieved list of groups will contain all of the supported groups for a security policy that are compatible - * with the build of s2n-tls. For instance, PQ kem groups that are not supported by the linked libcrypto will not - * be written. Otherwise, all of the supported groups configured for the security policy will be written. This API - * can be used with the s2n_client_hello_get_supported_groups() API as a means of comparing compatibility between - * a client and server. - * - * IANA values for each of the supported groups are written to the provided `groups` array, and `groups_count` is - * set to the number of written supported groups. - * - * `groups_count_max` should be set to the maximum capacity of the `groups` array. If `groups_count_max` is less - * than the number of supported groups configured by the security policy, this function will error. - * - * Note that this API retrieves only the groups from a security policy that are available to negotiate via the - * supported groups extension, and does not return TLS 1.2 PQ kem groups that are negotiated in the supported PQ - * kem parameters extension. - * - * @param config A pointer to the s2n_config object from which the supported groups will be retrieved. - * @param groups The array to populate with the supported groups. - * @param groups_count_max The maximum number of supported groups that can fit in the `groups` array. - * @param groups_count Set to the number of supported groups written to `groups`. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, - uint16_t *groups_count); - -/* Indicates which serialized connection version will be provided. The default value is - * S2N_SERIALIZED_CONN_NONE, which indicates the feature is off. - */ -typedef enum { - S2N_SERIALIZED_CONN_NONE = 0, - S2N_SERIALIZED_CONN_V1 = 1 -} s2n_serialization_version; - -/** - * Set what version to use when serializing connections - * - * A version is required to serialize connections. Versioning ensures that all features negotiated - * during the handshake will be available wherever the connection is deserialized. Applications may - * need to update this version to pick up new features, since versioning may disable newer TLS - * features to ensure compatibility. - * - * @param config A pointer to the config object. - * @param version The requested version. - * @returns S2N_SUCCESS on success, S2N_FAILURE on error. - */ -S2N_API int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version); - -/** - * Retrieves the length of the serialized connection from `s2n_connection_serialize()`. Should be - * used to allocate enough memory for the serialized connection buffer. - * - * @note The size of the serialized connection changes based on parameters negotiated in the TLS - * handshake. Do not expect the size to always remain the same. - * - * @param conn A pointer to the connection object. - * @param length Output parameter where the length will be written. - * @returns S2N_SUCCESS on success, S2N_FAILURE on error. - */ -S2N_API int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length); - -/** - * Serializes the s2n_connection into the provided buffer. - * - * This API takes an established s2n-tls connection object and "serializes" it - * into a transferable object to be sent off-box or to another process. This transferable object can - * then be "deserialized" using the `s2n_connection_deserialize` method to instantiate an s2n-tls - * connection object that can talk to the original peer with the same encryption keys. - * - * @warning This feature is dangerous because it provides cryptographic material from a TLS session - * in plaintext. The serialized blob also does not include a MAC or signature, so a modified blob - * will be deserialized and used as-is. Users MUST both encrypt and MAC the serialized connection to - * provide secrecy and integrity before storing or sending it off-box. - * - * @note You MUST have used `s2n_config_set_serialization_version()` to set a version on the - * s2n_config object associated with this connection before this connection began its TLS handshake. - * @note Call `s2n_connection_serialization_length` to retrieve the amount of memory needed for the - * buffer parameter. - * @note This API will error if the handshake is not yet complete. Additionally it will error if there - * is still application data in the IO buffers given that this data does not get serialized by s2n-tls. - * You can use `s2n_send` to drain the send buffer and `s2n_peek` + `s2n_recv` to drain the read buffer. - * Note that s2n-tls will buffer record fragments until the complete record is available. `s2n_peek` - * will _not_ report buffered record fragments. To avoid buffered record fragments, applications should - * continue to call s2n_recv until a non-blocked status is returned, after which point s2n_peek and s2n_recv - * can be used to drain the read buffer. - * @warning Due to the above read/buffering interaction, this API must not be used - * with recv_buffering (s2n_connection_set_recv_buffering), because there is no - * way to ensure that an s2n-tls connection hasn't buffered any record fragments. - * @note Serialization is unsupported for TLS 1.0 & SSLv3 connections. See: https://github.com/aws/s2n-tls/issues/5538. - * - * @param conn A pointer to the connection object. - * @param buffer A pointer to the buffer where the serialized connection will be written. - * @param buffer_length Maximum amount of data that can be written to the buffer param. - * @returns S2N_SUCCESS on success, S2N_FAILURE on error. - */ -S2N_API int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); - -/** - * Deserializes the provided buffer into the `s2n_connection` parameter. - * - * @warning s2n-tls does not check the integrity of the provided buffer. The serialized blob does - * not include a MAC or signature, so a modified buffer will be deserialized and used as-is. A - * corrupted buffer will cause a connection failure when attempting to resume sending or receiving - * encrypted data. To avoid this, callers MUST MAC and encrypt the serialized connection before - * storing or sending it off-box. Encrypt-then-MAC using a key not stored alongside the blob is - * the recommended approach. - * - * @warning Only a minimal amount of information about the original TLS connection is serialized. - * Therefore, after deserialization, the connection will behave like a new `s2n_connection` from the - * `s2n_connection_new()` call, except that it can read/write encrypted data from a peer. Any desired - * config-level or connection-level configuration will need to be re-applied to the deserialized connection. - * For this same reason none of the connection getters will return useful information about the - * original connection after deserialization. Any information about the original connection needs to - * be retrieved before serialization. - * - * @param conn A pointer to the connection object. Should be a new s2n_connection object. - * @param buffer A pointer to the buffer where the serialized connection will be read from. - * @param buffer_length Maximum amount of data that can be read from the buffer parameter. - * @returns S2N_SUCCESS on success, S2N_FAILURE on error. - */ -S2N_API int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); - -/* Load all acceptable certificate authorities from the currently configured trust store. - * - * The loaded certificate authorities will be advertised during the handshake. - * This can help your peer select a certificate if they have multiple certificate - * chains available. - * - * For now, s2n-tls only supports advertising certificate authorities to support - * client auth, so only servers will send the list of certificate authorities. - * - * To avoid configuration mistakes, certificate authorities cannot be loaded from - * a trust store that includes the default system certificates. That means that - * s2n_config_new_minimal or s2n_config_wipe_trust_store should be used. - * - * s2n-tls currently limits the total certificate authorities size to 10k bytes. - * This method will fail if the certificate authorities retrieved from the trust - * store exceed that limit. - * - * @param config A pointer to the s2n_config object. - * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. - */ -S2N_API int s2n_config_set_cert_authorities_from_trust_store(struct s2n_config *config); - -#ifdef __cplusplus -} -#endif +/* + * 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. + */ + +/** + * @file s2n.h + * s2n-tls is a C99 implementation of the TLS/SSL protocols that is designed to + * be simple, small, fast, and with security as a priority.
It is released and + * licensed under the Apache License 2.0. + */ + +#pragma once + +#ifndef S2N_API + /** + * Marks a function as belonging to the public s2n API. + */ + #define S2N_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#else +#include +#endif + +/** + * Function return code + */ +#define S2N_SUCCESS 0 +/** + * Function return code + */ +#define S2N_FAILURE -1 + +/** + * Callback return code + */ +#define S2N_CALLBACK_BLOCKED -2 + +/** + * s2n minimum supported TLS record major version + */ +#define S2N_MINIMUM_SUPPORTED_TLS_RECORD_MAJOR_VERSION 2 + +/** + * s2n maximum supported TLS record major version + */ +#define S2N_MAXIMUM_SUPPORTED_TLS_RECORD_MAJOR_VERSION 3 + +/** + * s2n SSL 2.0 Version Constant + */ +#define S2N_SSLv2 20 + +/** + * s2n SSL 3.0 Version Constant + */ +#define S2N_SSLv3 30 + +/** + * s2n TLS 1.0 Version Constant + */ +#define S2N_TLS10 31 + +/** + * s2n TLS 1.1 Version Constant + */ +#define S2N_TLS11 32 + +/** + * s2n TLS 1.2 Version Constant + */ +#define S2N_TLS12 33 + +/** + * s2n TLS 1.3 Version Constant + */ +#define S2N_TLS13 34 + +/** + * s2n Unknown TLS Version + */ +#define S2N_UNKNOWN_PROTOCOL_VERSION 0 + +/** + * s2n-tls functions that return 'int' return 0 to indicate success and -1 to indicate + * failure. + * + * s2n-tls functions that return pointer types return NULL in the case of + * failure. + * + * When an s2n-tls function returns a failure, s2n_errno will be set to a value + * corresponding to the error. This error value can be translated into a string + * explaining the error in English by calling s2n_strerror(s2n_errno, "EN"). + * A string containing human readable error name; can be generated with `s2n_strerror_name`. + * A string containing internal debug information, including filename and line number, can be generated with `s2n_strerror_debug`. + * A string containing only the filename and line number can be generated with `s2n_strerror_source`. + * This string is useful to include when reporting issues to the s2n-tls development team. + * + * @warning To avoid possible confusion, s2n_errno should be cleared after processing an error: `s2n_errno = S2N_ERR_T_OK` + */ +extern __thread int s2n_errno; + +/** + * This function can be used instead of trying to resolve `s2n_errno` directly + * in runtimes where thread-local variables may not be easily accessible. + * + * @returns The address of the thread-local `s2n_errno` variable + */ +S2N_API extern int *s2n_errno_location(void); + +/** + * Used to help applications determine why an s2n-tls function failed. + * + * This enum is optimized for use in C switch statements. Each value in the enum represents + * an error "category". + * + * s2n-tls organizes errors into different "types" to allow applications to handle error + * values without catching all possibilities. Applications using non-blocking I/O should check + * the error type to determine if the I/O operation failed because it would block or for some other + * error. To retrieve the type for a given error use `s2n_error_get_type()`. Applications should + * perform any error handling logic using these high level types. + * + * See the [Error Handling](https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch03-error-handling.md) section for how the errors should be interpreted. + */ +typedef enum { + /** No error */ + S2N_ERR_T_OK = 0, + /** Underlying I/O operation failed, check system errno */ + S2N_ERR_T_IO, + /** EOF */ + S2N_ERR_T_CLOSED, + /** Underlying I/O operation would block */ + S2N_ERR_T_BLOCKED, + /** Incoming Alert */ + S2N_ERR_T_ALERT, + /** Failure in some part of the TLS protocol. Ex: CBC verification failure */ + S2N_ERR_T_PROTO, + /** Error internal to s2n-tls. A precondition could have failed. */ + S2N_ERR_T_INTERNAL, + /** User input error. Ex: Providing an invalid cipher preference version */ + S2N_ERR_T_USAGE +} s2n_error_type; + +/** + * Gets the category of error from an error. + * + * s2n-tls organizes errors into different "types" to allow applications to do logic on error values without catching all possibilities. + * Applications using non-blocking I/O should check error type to determine if the I/O operation failed because + * it would block or for some other error. + * + * @param error The error from s2n. Usually this is `s2n_errno`. + * @returns An s2n_error_type + */ +S2N_API extern int s2n_error_get_type(int error); + +/** + * An opaque configuration object, used by clients and servers for holding cryptographic certificates, keys and preferences. + */ +struct s2n_config; + +/** + * An opaque connection. Used to track each s2n connection. + */ +struct s2n_connection; + +/** + * Prevents S2N from calling `OPENSSL_init_crypto`/`OPENSSL_cleanup`/`EVP_cleanup` on OpenSSL versions + * prior to 1.1.x. This allows applications or languages that also init OpenSSL to interoperate + * with S2N. + * + * @warning This function must be called BEFORE s2n_init() to have any effect. It will return an error + * if s2n is already initialized. + * + * @note If you disable this and are using a version of OpenSSL/libcrypto < 1.1.x, you will + * be responsible for library init and cleanup (specifically `OPENSSL_add_all_algorithms()` + * or `OPENSSL_init_crypto()`, and EVP_* APIs will not be usable unless the library is initialized. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_crypto_disable_init(void); + +/** + * Prevents S2N from installing an atexit handler, which allows safe shutdown of S2N from within a + * re-entrant shared library + * + * @warning This function must be called BEFORE s2n_init() to have any effect. It will return an error + * if s2n is already initialized. + * + * @note When the atexit handler is disabled, callers are responsible for calling + * s2n_cleanup_final() to perform full library cleanup before process exit. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_disable_atexit(void); + +/** + * Fetches the OpenSSL version s2n-tls was compiled with. This can be used by applications to validate at runtime + * that the versions of s2n-tls and Openssl that they have loaded are correct. + * + * @returns the version number of OpenSSL that s2n-tls was compiled with + */ +S2N_API extern unsigned long s2n_get_openssl_version(void); + +/** + * Initializes the s2n-tls library and should be called once in your application, before any other s2n-tls + * functions are called. Failure to call s2n_init() will result in errors from other s2n-tls functions. + * + * @warning This function is not thread safe and should only be called once. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_init(void); + +/** + * @deprecated This function is a no-op. It previously cleaned up thread-local + * DRBG state, but the custom DRBG has been removed. Retained for API + * compatibility. Use s2n_cleanup_final() for full library cleanup. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cleanup(void); + +/* + * Performs a complete deinitialization and cleanup of the s2n-tls library. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cleanup_final(void); + +typedef enum { + S2N_FIPS_MODE_DISABLED = 0, + S2N_FIPS_MODE_ENABLED, +} s2n_fips_mode; + +/** + * Determines whether s2n-tls is operating in FIPS mode. + * + * s2n-tls enters FIPS mode on initialization when built with a version of AWS-LC that supports + * FIPS (https://github.com/aws/aws-lc/blob/main/crypto/fipsmodule/FIPS.md). FIPS mode controls + * some internal configuration related to FIPS support, like which random number generator is used. + * + * FIPS mode does not enforce the use of FIPS-approved cryptography. Applications attempting to use + * only FIPS-approved cryptography should also ensure that s2n-tls is configured to use a security + * policy that only supports FIPS-approved cryptography. + * + * @param fips_mode Set to the FIPS mode of s2n-tls. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_get_fips_mode(s2n_fips_mode *fips_mode); + +/** + * Creates a new s2n_config object. This object can (and should) be associated with many connection + * objects. + * + * The returned config will be initialized with default system certificates in its trust store. + * + * The returned config should be freed with `s2n_config_free()` after it's no longer in use by any + * connection. + * + * @returns A new configuration object suitable for configuring connections and associating certs + * and keys. + */ +S2N_API extern struct s2n_config *s2n_config_new(void); + +/** + * Creates a new s2n_config object with minimal default options. + * + * This function has better performance than `s2n_config_new()` because it does not load default + * system certificates into the trust store by default. To add system certificates to this config, + * call `s2n_config_load_system_certs()`. + * + * The returned config should be freed with `s2n_config_free()` after it's no longer in use by any + * connection. + * + * @returns A new configuration object suitable for configuring connections and associating certs + * and keys. + */ +S2N_API extern struct s2n_config *s2n_config_new_minimal(void); + +/** + * Frees the memory associated with an `s2n_config` object. + * + * @param config The configuration object being freed + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_free(struct s2n_config *config); + +/** + * Frees the DH params associated with an `s2n_config` object. + * + * @param config The configuration object with DH params being freed + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_free_dhparams(struct s2n_config *config); + +/** + * Frees the certificate chain and key associated with an `s2n_config` object. + * + * @param config The configuration object with DH params being freed + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_free_cert_chain_and_key(struct s2n_config *config); + +/** + * Callback function type used to get the system time. + * + * @param void* A pointer to arbitrary data for use within the callback + * @param uint64_t* A pointer that the callback will set to the time in nanoseconds + * The function should return 0 on success and -1 on failure. + */ +typedef int (*s2n_clock_time_nanoseconds)(void *, uint64_t *); + +/** + * Cache callback function that allows the caller to retrieve SSL session data + * from a cache. + * + * The callback function takes six arguments: + * a pointer to the s2n_connection object, + * a pointer to arbitrary data for use within the callback, + * a pointer to a key which can be used to retrieve the cached entry, + * a 64 bit unsigned integer specifying the size of this key, + * a pointer to a memory location where the value should be stored, + * and a pointer to a 64 bit unsigned integer specifying the size of this value. + * + * Initially *value_size will be set to the amount of space allocated for the value, + * the callback should set *value_size to the actual size of the data returned. + * If there is insufficient space, -1 should be returned. + * If the cache is not ready to provide data for the request, + * S2N_CALLBACK_BLOCKED should be returned. + * + * This will cause s2n_negotiate() to return S2N_BLOCKED_ON_APPLICATION_INPUT. + */ +typedef int (*s2n_cache_retrieve_callback)(struct s2n_connection *conn, void *, const void *key, uint64_t key_size, void *value, uint64_t *value_size); + +/** + * Cache callback function that allows the caller to store SSL session data in a + * cache. + * + * The callback function takes seven arguments: + * a pointer to the s2n_connection object, + * a pointer to arbitrary data for use within the callback, + * a 64-bit unsigned integer specifying the number of seconds the session data may be stored for, + * a pointer to a key which can be used to retrieve the cached entry, + * a 64 bit unsigned integer specifying the size of this key, + * a pointer to a value which should be stored, + * and a 64 bit unsigned integer specified the size of this value. + */ +typedef int (*s2n_cache_store_callback)(struct s2n_connection *conn, void *, uint64_t ttl_in_seconds, const void *key, uint64_t key_size, const void *value, uint64_t value_size); + +/** +* Cache callback function that allows the caller to set a callback function +* that will be used to delete SSL session data from a cache. +* +* The callback function takes four arguments: +* a pointer to s2n_connection object, +* a pointer to arbitrary data for use within the callback, +* a pointer to a key which can be used to delete the cached entry, +* and a 64 bit unsigned integer specifying the size of this key. +*/ +typedef int (*s2n_cache_delete_callback)(struct s2n_connection *conn, void *, const void *key, uint64_t key_size); + +/** + * Allows the caller to set a callback function that will be used to get the + * system time. The time returned should be the number of nanoseconds since the + * Unix epoch (Midnight, January 1st, 1970). + * + * s2n-tls uses this clock for timestamps. + * + * @param config The configuration object being updated + * @param clock_fn The wall clock time callback function + * @param ctx An opaque pointer that the callback will be invoked with + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx); + +/** + * Allows the caller to set a callback function that will be used to get + * monotonic time. The monotonic time is the time since an arbitrary, unspecified + * point. Unlike wall clock time, it MUST never move backwards. + * + * s2n-tls uses this clock for timers. + * + * @param config The configuration object being updated + * @param clock_fn The monotonic time callback function + * @param ctx An opaque pointer that the callback will be invoked with + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx); + +/** + * Translates an s2n_error code to a human readable string explaining the error. + * + * @param error The error code to explain. Usually this is s2n_errno + * @param lang The language to explain the error code. Pass "EN" or NULL for English. + * @returns The error string + */ +S2N_API extern const char *s2n_strerror(int error, const char *lang); + +/** + * Translates an s2n_error code to a human readable string containing internal debug + * information, including file name and line number. This function is useful when + * reporting issues to the s2n-tls development team. + * + * @param error The error code to explain. Usually this is s2n_errno + * @param lang The language to explain the error code. Pass "EN" or NULL for English. + * @returns The error string + */ +S2N_API extern const char *s2n_strerror_debug(int error, const char *lang); + +/** + * Translates an s2n_error code to a human readable string. + * + * @param error The error code to explain. Usually this is s2n_errno + * @returns The error string + */ +S2N_API extern const char *s2n_strerror_name(int error); + +/** + * Translates an s2n_error code to a filename and line number. + * + * @param error The error code to explain. Usually this is s2n_errno. + * @returns The error string. + */ +S2N_API extern const char *s2n_strerror_source(int error); + +/** + * Opaque stack trace structure. + */ +struct s2n_stacktrace; + +/** + * Checks if s2n stack trace captures are enabled. + * + * @returns True if stack traces are enabled. False if they are disabled. + */ +S2N_API extern bool s2n_stack_traces_enabled(void); + +/** + * Configures the s2n stack trace captures option. + * + * @param newval Boolean to determine if stack traces should be enabled. True to enable them. False to disable them. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_stack_traces_enabled_set(bool newval); + +/** + * Calculates the s2n stack trace. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_calculate_stacktrace(void); + +/** + * Prints the s2n stack trace to a file. The file descriptor is expected to be + * open and ready for writing. + * + * @param fptr A pointer to the file s2n-tls should write the stack trace to. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_print_stacktrace(FILE *fptr); + +/** + * Clean up the memory used to contain the stack trace. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_free_stacktrace(void); + +/** + * Export the s2n_stacktrace. + * + * @param trace A pointer to the s2n_stacktrace to fill. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_get_stacktrace(struct s2n_stacktrace *trace); + +/** + * Allows the caller to set a callback function that will be used to store SSL + * session data in a cache. + * + * @param config The configuration object being updated + * @param cache_store_callback The cache store callback function. + * @param data An opaque context pointer that the callback will be invoked with. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_cache_store_callback(struct s2n_config *config, s2n_cache_store_callback cache_store_callback, void *data); + +/** + * Allows the caller to set a callback function that will be used to retrieve SSL + * session data from a cache. + * + * @param config The configuration object being updated + * @param cache_retrieve_callback The cache retrieve callback function. + * @param data An opaque context pointer that the callback will be invoked with. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_cache_retrieve_callback(struct s2n_config *config, s2n_cache_retrieve_callback cache_retrieve_callback, void *data); + +/** + * Allows the caller to set a callback function that will be used to delete SSL + * session data from a cache. + * + * @param config The configuration object being updated + * @param cache_delete_callback The cache delete callback function. + * @param data An opaque context pointer that the callback will be invoked with. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_cache_delete_callback(struct s2n_config *config, s2n_cache_delete_callback cache_delete_callback, void *data); + +/** + * Called when `s2n_init` is executed. + */ +typedef int (*s2n_mem_init_callback)(void); + +/** + * Will be called when `s2n_cleanup` is executed. + */ +typedef int (*s2n_mem_cleanup_callback)(void); + +/** + * A function that can allocate at least `requested` bytes of memory. + * + * It stores the location of that memory in **\*ptr** and the size of the allocated + * data in **\*allocated**. The function may choose to allocate more memory + * than was requested. s2n-tls will consider all allocated memory available for + * use, and will attempt to free all allocated memory when able. + */ +typedef int (*s2n_mem_malloc_callback)(void **ptr, uint32_t requested, uint32_t *allocated); + +/** + * Frees memory allocated by s2n_mem_malloc_callback. + */ +typedef int (*s2n_mem_free_callback)(void *ptr, uint32_t size); + +/** + * Allows the caller to override s2n-tls's internal memory handling functions. + * + * @warning This function must be called before s2n_init(). + * + * @param mem_init_callback The s2n_mem_init_callback + * @param mem_cleanup_callback The s2n_mem_cleanup_callback + * @param mem_malloc_callback The s2n_mem_malloc_callback + * @param mem_free_callback The s2n_mem_free_callback + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, + s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback); + +/** + * @deprecated No longer used. See `s2n_rand_set_callbacks`. + */ +typedef int (*s2n_rand_init_callback)(void); + +/** + * @deprecated No longer used. See `s2n_rand_set_callbacks`. + */ +typedef int (*s2n_rand_cleanup_callback)(void); + +/** + * @deprecated No longer used. See `s2n_rand_set_callbacks`. + */ +typedef int (*s2n_rand_seed_callback)(void *data, uint32_t size); + +/** + * @deprecated No longer used. See `s2n_rand_set_callbacks`. + */ +typedef int (*s2n_rand_mix_callback)(void *data, uint32_t size); + +/** + * Allows the caller to override s2n-tls's entropy functions. + * + * @deprecated Custom random callbacks are no longer supported. Randomness is + * now delegated directly to libcrypto or /dev/urandom. This function is a + * no-op kept for backwards compatibility. + * + * @param rand_init_callback The s2n_rand_init_callback + * @param rand_cleanup_callback The s2n_rand_cleanup_callback + * @param rand_seed_callback The s2n_rand_seed_callback + * @param rand_mix_callback The s2n_rand_mix_callback + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback, s2n_rand_cleanup_callback rand_cleanup_callback, + s2n_rand_seed_callback rand_seed_callback, s2n_rand_mix_callback rand_mix_callback); + +/** + * TLS extensions supported by s2n-tls + */ +typedef enum { + S2N_EXTENSION_SERVER_NAME = 0, + S2N_EXTENSION_MAX_FRAG_LEN = 1, + S2N_EXTENSION_OCSP_STAPLING = 5, + S2N_EXTENSION_SUPPORTED_GROUPS = 10, + S2N_EXTENSION_EC_POINT_FORMATS = 11, + S2N_EXTENSION_SIGNATURE_ALGORITHMS = 13, + S2N_EXTENSION_ALPN = 16, + S2N_EXTENSION_CERTIFICATE_TRANSPARENCY = 18, + S2N_EXTENSION_SUPPORTED_VERSIONS = 43, + S2N_EXTENSION_RENEGOTIATION_INFO = 65281, +} s2n_tls_extension_type; + +/** + * MFL configurations from https://datatracker.ietf.org/doc/html/rfc6066#section-4. + */ +typedef enum { + S2N_TLS_MAX_FRAG_LEN_512 = 1, + S2N_TLS_MAX_FRAG_LEN_1024 = 2, + S2N_TLS_MAX_FRAG_LEN_2048 = 3, + S2N_TLS_MAX_FRAG_LEN_4096 = 4, +} s2n_max_frag_len; + +/** + * Opaque certificate type. + */ +struct s2n_cert; + +/** + * Opaque certificate chain and key type. + */ +struct s2n_cert_chain_and_key; + +/** + * Opaque key type. + */ +struct s2n_pkey; + +/** + * Opaque public key type. + */ +typedef struct s2n_pkey s2n_cert_public_key; + +/** + * Opaque private key type. + */ +typedef struct s2n_pkey s2n_cert_private_key; + +/** + * Creates a new s2n_cert_chain_and_key object. This object can be associated + * with many config objects. It is used to represent a certificate and key pair. + * + * @returns A new object used to represent a certificate-chain/key pair + */ +S2N_API extern struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void); + +/** + * Associates a certificate chain and private key with an `s2n_cert_chain_and_key` object. + * + * `cert_chain_pem` should be a PEM encoded certificate chain, with the first + * certificate in the chain being your leaf certificate. `private_key_pem` + * should be a PEM encoded private key corresponding to the leaf certificate. + * + * @note Prefer using s2n_cert_chain_and_key_load_pem_bytes. + * + * @param chain_and_key The certificate chain and private key handle + * @param chain_pem A byte array of a PEM encoded certificate chain. + * @param private_key_pem A byte array of a PEM encoded key. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem); + +/** + * Associates a certificate chain and private key with an `s2n_cert_chain_and_key` object. + * + * `cert_chain_pem` should be a PEM encoded certificate chain, with the first + * certificate in the chain being your leaf certificate. `private_key_pem` + * should be a PEM encoded private key corresponding to the leaf certificate. + * + * @param chain_and_key The certificate chain and private key handle + * @param chain_pem A byte array of a PEM encoded certificate chain. + * @param chain_pem_len Size of `chain_pem` + * @param private_key_pem A byte array of a PEM encoded key. + * @param private_key_pem_len Size of `private_key_pem` + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_load_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len, uint8_t *private_key_pem, uint32_t private_key_pem_len); + +/** + * Associates a public certificate chain with a `s2n_cert_chain_and_key` object. It does + * NOT set a private key, so the connection will need to be configured to + * [offload private key operations](https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch12-private-key-ops.md). + * + * @param chain_and_key The certificate chain and private key handle + * @param chain_pem A byte array of a PEM encoded certificate chain. + * @param chain_pem_len Size of `chain_pem` + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_load_public_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len); + +/** + * Frees the memory associated with an `s2n_cert_chain_and_key` object. + * + * @param cert_and_key The certificate chain and private key handle + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key); + +/** + * Adds a context to the `s2n_cert_chain_and_key` object. + * + * @param cert_and_key The certificate chain and private key handle + * @param ctx An opaque pointer to user supplied data. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx); + +/** + * Get the user supplied context from the `s2n_cert_chain_and_key` object. + * + * @param cert_and_key The certificate chain and private key handle + * @returns The user supplied pointer from s2n_cert_chain_and_key_set_ctx() + */ +S2N_API extern void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key); + +/** + * Get the private key from the `s2n_cert_chain_and_key` object. + * + * @param cert_and_key The certificate chain and private key handle + * @returns A pointer to the `s2n_cert_private_key` + */ +S2N_API extern s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *cert_and_key); + +/** + * Set the raw OCSP stapling data for a certificate chain. + * + * @param chain_and_key The certificate chain handle + * @param data A pointer to the raw OCSP stapling data bytes. The data will be copied. + * @param length The length of the data bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); + +/** + * Set the signed certificate timestamp (SCT) for a certificate chain. + * This is used for Certificate Transparency. + * + * @param chain_and_key The certificate chain handle + * @param data A pointer to the SCT data. The data will be copied. + * @param length The length of the data bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length); + +/** + * A callback function that is invoked if s2n-tls cannot resolve a conflict between + * two certificates with the same domain name. This function is invoked while certificates + * are added to an `s2n_config`. + * + * Currently, the only builtin resolution for domain name conflicts is certificate type(RSA, + * ECDSA, etc). The callback should return a pointer to the `s2n_cert_chain_and_key` that + * should be used for dns name `name`. + * + * If NULL is returned, the first certificate will be used. Typically an application + * will use properties like trust and expiry to implement tiebreaking. + */ +typedef struct s2n_cert_chain_and_key *(*s2n_cert_tiebreak_callback)(struct s2n_cert_chain_and_key *cert1, struct s2n_cert_chain_and_key *cert2, uint8_t *name, uint32_t name_len); + +/** + * Sets the `s2n_cert_tiebreak_callback` for resolving domain name conflicts. + * If no callback is set, the first certificate added for a domain name will always be preferred. + * + * @param config The configuration object being updated + * @param cert_tiebreak_cb The pointer to the certificate tiebreak function + * + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb); + +/** + * Associates a certificate chain and private key with an `s2n_config` object. + * Using this API, only one cert chain of each type (like ECDSA or RSA) may be associated with a config. + * `cert_chain_pem` should be a PEM encoded certificate chain, with the first certificate + * in the chain being your server's certificate. `private_key_pem` should be a + * PEM encoded private key corresponding to the server certificate. + * + * @deprecated Use s2n_config_add_cert_chain_and_key_to_store instead. + * + * @param config The configuration object being updated + * @param cert_chain_pem A byte array of a PEM encoded certificate chain. + * @param private_key_pem A byte array of a PEM encoded key. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem); + +/** + * The preferred method of associating a certificate chain and private key pair with an `s2n_config` object. + * This method may be called multiple times to support multiple key types (RSA, RSA-PSS, ECDSA) and multiple + * domains. On the server side, the certificate selected will be based on the incoming SNI value and the + * client's capabilities (supported ciphers). + * + * In the case of no certificate matching the client's SNI extension or if no SNI extension was sent by + * the client, the certificate from the `first` call to `s2n_config_add_cert_chain_and_key_to_store()` + * will be selected. Use `s2n_config_set_cert_chain_and_key_defaults()` to set different defaults. + * + * @warning It is not recommended to free or modify the `cert_key_pair` as any subsequent changes will be + * reflected in the config. + * + * @param config The configuration object being updated + * @param cert_key_pair The certificate chain and private key handle + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair); + +/** + * Explicitly sets certificate chain and private key pairs to be used as defaults for each auth + * method (key type). A "default" certificate is used when there is not an SNI match with any other + * configured certificate. + * + * Only one certificate can be set as the default per auth method (one RSA default, one ECDSA default, + * etc.). All previous default certificates will be cleared and re-set when this API is called. + * + * This API is called for a specific `s2n_config` object. s2n-tls will attempt to automatically choose + * default certificates for each auth method (key type) based on the order that `s2n_cert_chain_and_key` + * are added to the `s2n_config` using one of the APIs listed above. + * `s2n_config_set_cert_chain_and_key_defaults` can be called at any time; s2n-tls will clear defaults + * and no longer attempt to automatically choose any default certificates. + * + * @param config The configuration object being updated + * @param cert_key_pairs An array of certificate chain and private key handles + * @param num_cert_key_pairs The amount of handles in cert_key_pairs + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, + struct s2n_cert_chain_and_key **cert_key_pairs, uint32_t num_cert_key_pairs); + +/** + * Adds to the trust store from a CA file or directory containing trusted certificates. + * + * When configs are created with `s2n_config_new()`, the trust store is initialized with default + * system certificates. To completely override these certificates, call + * `s2n_config_wipe_trust_store()` before calling this function. + * + * @note The trust store will be initialized with the common locations for the host + * operating system by default. + * + * @warning This API uses the PEM parsing implementation from the linked libcrypto. This + * implementation will typically make a best-effort attempt to parse all of the certificates in the + * provided file or directory. This permissive approach may silently ignore malformed certificates, + * leading to possible connection failures if a certificate was expected to exist in the trust + * store but was skipped while parsing. As such, this API should only be used on PEMs that are + * known to be well-formed and parsable with the linked libcrypto, such as the system trust store. + * For all other PEMs, `s2n_config_add_pem_to_trust_store()` should be used instead, which parses + * more strictly. + * + * @param config The configuration object being updated + * @param ca_pem_filename A string for the file path of the CA PEM file. + * @param ca_dir A string for the directory of the CA PEM files. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_verification_ca_location(struct s2n_config *config, const char *ca_pem_filename, const char *ca_dir); + +/** + * Adds a PEM to the trust store. This will allocate memory, and load `pem` into the trust store. + * + * When configs are created with `s2n_config_new()`, the trust store is initialized with default + * system certificates. To completely override these certificates, call + * `s2n_config_wipe_trust_store()` before calling this function. + * + * @note This API uses the s2n-tls PEM parsing implementation, which is more strict than typical + * libcrypto implementations such as OpenSSL. An error is returned if any unexpected data is + * encountered while parsing `pem`. This allows applications to be made aware of any malformed + * certificates rather than attempt to negotiate with a partial trust store. However, some PEMs may + * need to be loaded that are not under control of the application, such as system trust stores. In + * this case, `s2n_config_set_verification_ca_location()` may be used, which performs more widely + * compatible and permissive parsing from the linked libcrypto. + * + * @param config The configuration object being updated + * @param pem The string value of the PEM certificate. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_add_pem_to_trust_store(struct s2n_config *config, const char *pem); + +/** + * Clears the trust store of all certificates. + * + * When configs are created with `s2n_config_new()`, the trust store is initialized with default + * system certificates. To completely override these certificates, call this function before + * functions like `s2n_config_set_verification_ca_location()` or + * `s2n_config_add_pem_to_trust_store()`. + * + * @param config The configuration object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_wipe_trust_store(struct s2n_config *config); + +/** + * Loads default system certificates into the trust store. + * + * `s2n_config_new_minimal()` doesn't load default system certificates into the config's trust + * store by default. If `config` was created with `s2n_config_new_minimal`, this function can be + * used to load system certificates into the trust store. + * + * @note This API will error if called on a config that has already loaded system certificates + * into its trust store, which includes all configs created with `s2n_config_new()`. + * + * @param config The configuration object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_load_system_certs(struct s2n_config *config); + +typedef enum { + S2N_VERIFY_AFTER_SIGN_DISABLED, + S2N_VERIFY_AFTER_SIGN_ENABLED +} s2n_verify_after_sign; + +/** + * Toggle whether generated signatures are verified before being sent. + * + * Although signatures produced by the underlying libcrypto should always be valid, + * hardware faults, bugs in the signing implementation, or other uncommon factors + * can cause unexpected mistakes in the final signatures. Because these mistakes + * can leak information about the private key, applications with low trust in their + * hardware or libcrypto may want to verify signatures before sending them. + * + * However, this feature will significantly impact handshake latency. + * Additionally, most libcrypto implementations already check for common errors in signatures. + */ +S2N_API extern int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after_sign mode); + +/** + * Set a custom send buffer size. + * + * This buffer is used to stage records for sending. By default, + * enough memory is allocated to hold a single record of the maximum + * size configured for the connection. With the default fragment size, + * that is about 8K bytes. + * + * Less memory can be allocated for the send buffer, but this will result in + * smaller, more fragmented records and increased overhead. While the absolute + * minimum size required is 1034 bytes, at least 2K bytes is recommended for + * reasonable record sizes. + * + * More memory can be allocated for the send buffer. This will result in s2n-tls + * buffering multiple records before sending them, reducing system write calls. + * At least 17K bytes is recommended for this use case, or at least 35K bytes + * if larger fragment sizes are used via `s2n_connection_prefer_throughput()`. + * + * @param config The configuration object being updated + * @param size The desired custom buffer size. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_send_buffer_size(struct s2n_config *config, uint32_t size); + +/** + * Enable or disable receiving of multiple TLS records in a single s2n_recv call + * + * By default, s2n-tls returns from s2n_recv() after reading a single TLS record. + * Enabling receiving of multiple records will instead cause s2n_recv() to attempt + * to read until the application-provided output buffer is full. This may be more + * efficient, especially if larger receive buffers are used. + * + * @note If this option is enabled with blocking IO, the call to s2n_recv() will + * not return until either the application-provided output buffer is full or the + * peer closes the connection. This may lead to unintentionally long waits if the + * peer does not send enough data. + * + * @param config The configuration object being updated + * @param enabled Set to `true` if multiple record receive is to be enabled; `false` to disable. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled); + +/** + * A callback function invoked (usually multiple times) during X.509 validation for each + * name encountered in the leaf certificate. + * + * Return 1 to trust that hostname or 0 to not trust the hostname. + * + * If this function returns 1, then the certificate is considered trusted and that portion + * of the X.509 validation will succeed. + * + * If no hostname results in a 1 being returned, the certificate will be untrusted and the + * validation will terminate immediately. + * + * Data is a opaque user context set in s2n_config_set_verify_host_callback() or s2n_connection_set_verify_host_callback(). + */ +typedef uint8_t (*s2n_verify_host_fn)(const char *host_name, size_t host_name_len, void *data); + +/** + * Sets the callback to use for verifying that a hostname from an X.509 certificate is trusted. + * + * The default behavior is to require that the hostname match the server name set with s2n_set_server_name(). + * This will likely lead to all client certificates being rejected, so the callback will need to be overridden when using + * client authentication. + * + * This change will be inherited by s2n_connections using this config. If a separate callback for different connections + * using the same config is desired, see s2n_connection_set_verify_host_callback(). + * + * @param config The configuration object being updated + * @param data A user supplied opaque context to pass back to the callback + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_verify_host_callback(struct s2n_config *config, s2n_verify_host_fn, void *data); + +/** + * Toggles whether or not to validate stapled OCSP responses. + * + * 1 means OCSP responses will be validated when they are encountered, while 0 means this step will + * be skipped. + * + * The default value is 1 if the underlying libCrypto implementation supports OCSP. + * + * @param config The configuration object being updated + * @param check_ocsp The desired OCSP response check configuration + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_check_stapled_ocsp_response(struct s2n_config *config, uint8_t check_ocsp); + +/** + * Disables timestamp validation for received certificates. + * + * By default, s2n-tls checks the notBefore and notAfter fields on the certificates it receives + * during the handshake. If the current date is not within the range of these fields for any + * certificate in the chain of trust, `s2n_negotiate()` will error. This validation is in + * accordance with RFC 5280, section 6.1.3 a.2: + * https://datatracker.ietf.org/doc/html/rfc5280#section-6.1.3. + * + * This API will disable this timestamp validation, permitting negotiation with peers that send + * expired certificates, or certificates that are not yet considered valid. + * + * @warning Applications calling this API should seriously consider the security implications of + * disabling this validation. The validity period of a certificate corresponds to the range of time + * in which the CA is guaranteed to maintain information regarding the certificate's revocation + * status. As such, it may not be possible to obtain accurate revocation information for + * certificates with invalid timestamps. Applications disabling this validation MUST implement + * some external method for limiting certificate lifetime. + * + * @param config The associated connection config. + * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. + */ +S2N_API extern int s2n_config_disable_x509_time_verification(struct s2n_config *config); + +/* Disable TLS intent verification for received certificates. + * + * By default, s2n-tls will verify that received certificates set Key Usage / Extended Key Usage + * fields that are consistent with the current TLS context (e.g. checking that serverAuth is set + * when verifying server certificates as a client, or checking that clientAuth is set when + * verifying client certificates as a server). This verification ensures that received certificates + * are being used for their intended purpose as specified by the issuer. + * + * This verification may be incompatible with some PKIs where intent is improperly specified. + * `s2n_config_disable_x509_intent_verification()` may be called in this case to disable the + * verification. This verification should only be disabled if it is known that all received + * certificates will be issued from a CA that intended for the certificates to be used in the given + * TLS context, despite what's indicated in the Key Usage / Extended Key Usage extensions. + * + * @note If a received certificate doesn't contain a Key Usage / Extended Key Usage extension, it's + * assumed that the issuer permits the certificate to be used for any purpose. s2n-tls will + * only reject a certificate due to invalid intent if the issuer explicitly indicates a + * purpose that is invalid for the TLS context in which it is received. + * + * @param config The associated connection config. + * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. + */ +S2N_API extern int s2n_config_disable_x509_intent_verification(struct s2n_config *config); + +/** + * Turns off all X.509 validation during the negotiation phase of the connection. This should only + * be used for testing or debugging purposes. + * + * @param config The configuration object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_disable_x509_verification(struct s2n_config *config); + +/** + * Sets the maximum allowed depth of a cert chain used for X509 validation. The default value is + * 7. If this limit is exceeded, validation will fail if s2n_config_disable_x509_verification() + * has not been called. 0 is an illegal value and will return an error. + * 1 means only a root certificate will be used. + * + * @param config The configuration object being updated + * @param max_depth The number of allowed certificates in the certificate chain + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_max_cert_chain_depth(struct s2n_config *config, uint16_t max_depth); + +/** + * Associates a set of Diffie-Hellman parameters with an `s2n_config` object. + * @note `dhparams_pem` should be PEM encoded DH parameters. + * + * @param config The configuration object being updated + * @param dhparams_pem A string containing the PEM encoded DH parameters. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem); + +/** + * Sets the security policy that includes the cipher/kem/signature/ecc preferences and + * protocol version. + * + * See the [USAGE-GUIDE.md](https://github.com/aws/s2n-tls/blob/main/docs/usage-guide) for how to use security policies. + */ +S2N_API extern int s2n_config_set_cipher_preferences(struct s2n_config *config, const char *version); + +/** + * Appends the provided application protocol to the preference list + * + * The data provided in `protocol` parameter will be copied into an internal buffer + * + * @param config The configuration object being updated + * @param protocol A pointer to a byte array value + * @param protocol_len The length of bytes that should be read from `protocol`. Note: this value cannot be 0, otherwise an error will be returned. + */ +S2N_API extern int s2n_config_append_protocol_preference(struct s2n_config *config, const uint8_t *protocol, uint8_t protocol_len); + +/** + * Sets the application protocol preferences on an `s2n_config` object. + * `protocols` is a list in order of preference, with most preferred protocol first, and of + * length `protocol_count`. + * + * When acting as an `S2N_CLIENT` the protocol list is included in the Client Hello message + * as the ALPN extension. + * + * As an `S2N_SERVER`, the list is used to negotiate a mutual application protocol with the + * client. After the negotiation for the connection has completed, the agreed upon protocol + * can be retrieved with s2n_get_application_protocol() + * + * @param config The configuration object being updated + * @param protocols The list of preferred protocols, in order of preference + * @param protocol_count The size of the protocols list + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_protocol_preferences(struct s2n_config *config, const char *const *protocols, int protocol_count); + +/** + * Enum used to define the type, if any, of certificate status request + * a connection should make during the handshake. The only supported status request type is + * OCSP, `S2N_STATUS_REQUEST_OCSP`. +*/ +typedef enum { + S2N_STATUS_REQUEST_NONE = 0, + S2N_STATUS_REQUEST_OCSP = 1 +} s2n_status_request_type; + +/** + * Sets up a connection to request the certificate status of a peer during an SSL handshake. If set + * to S2N_STATUS_REQUEST_NONE, no status request is made. + * + * @note SHA-1 is the only supported hash algorithm for the `certID` field. This is different + * from the hash algorithm used for the OCSP signature. See + * [RFC 6960](https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1) for more information. + * While unlikely to be the case, if support for a different hash algorithm is required, the + * s2n-tls validation can be disabled with `s2n_config_set_check_stapled_ocsp_response()` and the + * response can be retrieved for manual validation with `s2n_connection_get_ocsp_response()`. + * + * @param config The configuration object being updated + * @param type The desired request status type + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type); + +/** + * Enum to set Certificate Transparency Support level. + */ +typedef enum { + S2N_CT_SUPPORT_NONE = 0, + S2N_CT_SUPPORT_REQUEST = 1 +} s2n_ct_support_level; + +/** + * Set the Certificate Transparency Support level. + * + * @param config The configuration object being updated + * @param level The desired Certificate Transparency Support configuration + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_ct_support_level(struct s2n_config *config, s2n_ct_support_level level); + +/** + * Sets whether or not a connection should terminate on receiving a WARNING alert from its peer. + * + * `alert_behavior` can take the following values: + * - `S2N_ALERT_FAIL_ON_WARNINGS` default behavior: s2n-tls will terminate the connection if its peer sends a WARNING alert. + * - `S2N_ALERT_IGNORE_WARNINGS` - with the exception of `close_notify` s2n-tls will ignore all WARNING alerts and keep communicating with its peer. This setting is ignored in TLS1.3 + * + * @note TLS1.3 terminates a connection for all alerts except user_canceled. + * @warning S2N_ALERT_FAIL_ON_WARNINGS is the recommended behavior. Past TLS protocol vulnerabilities have involved downgrading alerts to warnings. + */ +typedef enum { + S2N_ALERT_FAIL_ON_WARNINGS = 0, + S2N_ALERT_IGNORE_WARNINGS = 1 +} s2n_alert_behavior; + +/** + * Sets the config's alert behavior based on the `s2n_alert_behavior` enum. + * + * @param config The configuration object being updated + * @param alert_behavior The desired alert behavior. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_behavior alert_behavior); + +/** + * Sets the extension data in the `s2n_config` object for the specified extension. + * This method will clear any existing data that is set. If the data and length + * parameters are set to NULL, no new data is set in the `s2n_config` object, + * effectively clearing existing data. + * + * @deprecated Use s2n_cert_chain_and_key_set_ocsp_data and s2n_cert_chain_and_key_set_sct_list instead. + * + * @param config The configuration object being updated + * @param type The extension type + * @param data Data for the extension + * @param length Length of the `data` buffer + */ +S2N_API extern int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_type type, const uint8_t *data, uint32_t length); + +/** + * Allows the caller to set a TLS Maximum Fragment Length extension that will be used + * to fragment outgoing messages. s2n-tls currently does not reject fragments larger + * than the configured maximum when in server mode. The TLS negotiated maximum fragment + * length overrides the preference set by the `s2n_connection_prefer_throughput` and + * `s2n_connection_prefer_low_latency`. + * + * @note Some TLS implementations do not respect their peer's max fragment length extension. + * + * @param config The configuration object being updated + * @param mfl_code The selected MFL size + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code); + +/** + * Allows the server to opt-in to accept client's TLS maximum fragment length extension + * requests. If this API is not called, and client requests the extension, server will ignore + * the request and continue TLS handshake with default maximum fragment length of 8k bytes + * + * @note Some TLS implementations do not respect their peer's max fragment length extension. + * + * @param config The configuration object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_accept_max_fragment_length(struct s2n_config *config); + +/** + * Sets the lifetime of the cached session state. The default value is 15 hours. + * + * @param config The configuration object being updated + * @param lifetime_in_secs The desired lifetime of the session state in seconds + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_session_state_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs); + +/** + * Enable or disable session resumption using session ticket. + * + * @param config The configuration object being updated + * @param enabled The configuration object being updated. Set to 1 to enable. Set to 0 to disable. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled); + +/** + * Enable or disable session caching. + * + * @note Session caching will not be turned on unless all three session cache callbacks are set + * prior to calling this function. + * + * @param config The configuration object being updated + * @param enabled The configuration object being updated. Set to 1 to enable. Set to 0 to disable. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_session_cache_onoff(struct s2n_config *config, uint8_t enabled); + +/** + * Sets how long a session ticket key will be in a state where it can be used for both encryption + * and decryption of tickets on the server side. + * + * @note The default value is 2 hours. + * @param config The configuration object being updated + * @param lifetime_in_secs The desired lifetime of decrypting and encrypting tickets in seconds + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs); + +/** + * Sets how long a session ticket key will be in a state where it can used just for decryption of + * already assigned tickets on the server side. Once decrypted, the session will resume and the + * server will issue a new session ticket encrypted using a key in encrypt-decrypt state. + * + * @note The default value is 13 hours. + * @param config The configuration object being updated + * @param lifetime_in_secs The desired lifetime of decrypting and encrypting tickets in seconds + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, uint64_t lifetime_in_secs); + +/** + * Adds session ticket key on the server side. It would be ideal to add new keys after every + * (encrypt_decrypt_key_lifetime_in_nanos/2) nanos because this will allow for gradual and + * linear transition of a key from encrypt-decrypt state to decrypt-only state. + * + * @param config The configuration object being updated + * @param name Name of the session ticket key that should be randomly generated to avoid collisions + * @param name_len Length of session ticket key name + * @param key Key used to perform encryption/decryption of session ticket + * @param key_len Length of the session ticket key + * @param intro_time_in_seconds_from_epoch Time at which the session ticket key is introduced. If this is 0, then intro_time_in_seconds_from_epoch is set to now. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_add_ticket_crypto_key(struct s2n_config *config, const uint8_t *name, uint32_t name_len, + uint8_t *key, uint32_t key_len, uint64_t intro_time_in_seconds_from_epoch); + +/** + * Requires that session tickets are only used when forward secrecy is possible. + * + * Restricts session resumption to TLS1.3, as the tickets used in TLS1.2 resumption are + * not forward secret. Clients should not expect to receive new session tickets and servers + * will not send new session tickets when TLS1.2 is negotiated and ticket forward secrecy is required. + * + * @note The default behavior is that forward secrecy is not required. + * + * @param config The config object being updated + * @param enabled Indicates if forward secrecy is required or not on tickets + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled); + +/** + * Sets user defined context on the `s2n_config` object. + * + * @param config The configuration object being updated + * @param ctx A pointer to the user defined ctx. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_ctx(struct s2n_config *config, void *ctx); + +/** + * Gets the user defined context from the `s2n_config` object. + * The context is set by calling s2n_config_set_ctx() + * + * @param config The configuration object being accessed + * @param ctx A pointer to the user defined ctx. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_get_ctx(struct s2n_config *config, void **ctx); + +/** + * Used to declare connections as server or client type, respectively. + */ +typedef enum { + S2N_SERVER, + S2N_CLIENT +} s2n_mode; + +/** + * Creates a new connection object. Each s2n-tls SSL/TLS connection uses + * one of these objects. These connection objects can be operated on by up + * to two threads at a time, one sender and one receiver, but neither sending + * nor receiving are atomic, so if these objects are being called by multiple + * sender or receiver threads, you must perform your own locking to ensure + * that only one sender or receiver is active at a time. + * + * The `mode` parameters specifies if the caller is a server, or is a client. + * Connections objects are re-usable across many connections, and should be + * re-used (to avoid deallocating and allocating memory). You should wipe + * connections immediately after use. + * + * @param mode The desired connection type + * @returns A s2n_connection handle + */ +S2N_API extern struct s2n_connection *s2n_connection_new(s2n_mode mode); + +/** + * Associates a configuration object with a connection. + * + * @param conn The connection object being associated + * @param config The configuration object being associated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *config); + +/** + * Sets user defined context in `s2n_connection` object. + * + * @param conn The connection object being updated + * @param ctx A pointer to the user defined context + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_ctx(struct s2n_connection *conn, void *ctx); + +/** + * Gets user defined context from a `s2n_connection` object. + * + * @param conn The connection object that contains the desired context + */ +S2N_API extern void *s2n_connection_get_ctx(struct s2n_connection *conn); + +/** + * The callback function takes a s2n-tls connection as input, which receives the ClientHello + * and the context previously provided in `s2n_config_set_client_hello_cb`. The callback can + * access any ClientHello information from the connection and use the `s2n_connection_set_config` + * call to change the config of the connection. + */ +typedef int s2n_client_hello_fn(struct s2n_connection *conn, void *ctx); + +/** + * Client Hello callback modes + * - `S2N_CLIENT_HELLO_CB_BLOCKING` (default): + * - In this mode s2n-tls expects the callback to complete its work and return the appropriate response code before the handshake continues. If any of the connection properties were changed based on the server_name extension the callback must either return a value greater than 0 or invoke `s2n_connection_server_name_extension_used`, otherwise the callback returns 0 to continue the handshake. + * - `S2N_CLIENT_HELLO_CB_NONBLOCKING`: + * - In non-blocking mode, s2n-tls expects the callback to not complete its work. If the callback returns a response code of 0, s2n-tls will return `S2N_FAILURE` with `S2N_ERR_T_BLOCKED` error type and `s2n_blocked_status` set to `S2N_BLOCKED_ON_APPLICATION_INPUT`. The handshake is paused and further calls to `s2n_negotiate` will continue to return the same error until `s2n_client_hello_cb_done` is invoked for the `s2n_connection` to resume the handshake. If any of the connection properties were changed on the basis of the server_name extension then `s2n_connection_server_name_extension_used` must be invoked before marking the callback done. + */ +typedef enum { + S2N_CLIENT_HELLO_CB_BLOCKING, + S2N_CLIENT_HELLO_CB_NONBLOCKING +} s2n_client_hello_cb_mode; + +/** + * Allows the caller to set a callback function that will be called after ClientHello was parsed. + * + * @param config The configuration object being updated + * @param client_hello_callback The client hello callback function + * @param ctx A pointer to a user defined context that the Client Hello callback will be invoked with. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_client_hello_cb(struct s2n_config *config, s2n_client_hello_fn client_hello_callback, void *ctx); + +/** + * Sets the callback execution mode. + * + * See s2n_client_hello_cb_mode for each mode's behavior. + * + * @param config The configuration object being updated + * @param cb_mode The desired callback mode + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_client_hello_cb_mode(struct s2n_config *config, s2n_client_hello_cb_mode cb_mode); + +/** + * Marks the non-blocking callback as complete. Can be invoked from within the callback when + * operating in non-blocking mode to continue the handshake. + * + * @param conn The connection object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_client_hello_cb_done(struct s2n_connection *conn); + +/** + * Must be invoked if any of the connection properties were changed on the basis of the server_name + * extension. This must be invoked before marking the Client Hello callback done. + * + * @param conn The connection object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_server_name_extension_used(struct s2n_connection *conn); + +/** + * Opaque client hello handle + */ +struct s2n_client_hello; + +/** + * Get the Client Hello from a s2n_connection. + * + * Earliest point during the handshake when this structure is available for use is in the + * client_hello_callback (see s2n_config_set_client_hello_cb()). + * + * @param conn The connection object containing the client hello + * @returns A handle to the s2n_client_hello structure holding the client hello message sent by the client during the handshake. NULL is returned if a Client Hello has not yet been received and parsed. + */ +S2N_API extern struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn); + +/** + * Creates an s2n_client_hello from bytes representing a ClientHello message. + * + * The input bytes should include the message header (message type and length), + * but not the record header. + * + * Unlike s2n_connection_get_client_hello, the s2n_client_hello returned by this + * method is owned by the application and must be freed with s2n_client_hello_free. + * + * This method does not support SSLv2 ClientHellos. + * + * @param bytes The raw bytes representing the ClientHello. + * @param size The size of raw_message. + * @returns A new s2n_client_hello on success, or NULL on failure. + */ +S2N_API extern struct s2n_client_hello *s2n_client_hello_parse_message(const uint8_t *bytes, uint32_t size); + +/** + * Frees an s2n_client_hello structure. + * + * This method should be called to free s2n_client_hellos returned by + * s2n_client_hello_parse_message. It will error if passed an s2n_client_hello + * returned by s2n_connection_get_client_hello and owned by the connection. + * + * @param ch The structure to be freed. + * @returns S2N_SUCCESS on success, S2N_FAILURE on failure. + */ +S2N_API extern int s2n_client_hello_free(struct s2n_client_hello **ch); + +/** + * Function to determine the size of the raw Client Hello buffer. + * + * Can be used to determine the necessary size of the `out` buffer for + * s2n_client_hello_get_raw_message() + * + * @param ch The Client Hello handle + * @returns The size of the ClientHello message received by the server + */ +S2N_API extern ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client_hello *ch); + +/** + * Copies `max_length` bytes of the ClientHello message into the `out` buffer. + * The ClientHello instrumented using this function will have the Random bytes + * zero-ed out. Use s2n_client_hello_get_random to access the random bytes. + * + * Note: SSLv2 ClientHello messages follow a different structure than more modern + * ClientHello messages. See [RFC5246](https://tools.ietf.org/html/rfc5246#appendix-E.2). + * In addition, due to how s2n-tls parses SSLv2 ClientHellos, the raw message is + * missing the first three bytes (the msg_type and version) and instead begins with + * the cipher_specs. To determine whether a ClientHello is an SSLv2 ClientHello, + * you will need to use s2n_connection_get_client_hello_version(). To get the + * protocol version advertised in the SSLv2 ClientHello (which may be higher + * than SSLv2), you will need to use s2n_connection_get_client_protocol_version(). + * + * @param ch The Client Hello handle + * @param out The destination buffer for the raw Client Hello + * @param max_length The size of out in bytes + * @returns The number of copied bytes + */ +S2N_API extern ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); + +/** + * Function to determine the size of the Client Hello cipher suites. + * This can be used to allocate the `out` buffer for s2n_client_hello_get_cipher_suites(). + * + * @param ch The Client Hello handle + * @returns the number of bytes the cipher_suites takes on the ClientHello message received by the server + */ +S2N_API extern ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch); + +/** + * Copies into the `out` buffer `max_length` bytes of the cipher_suites on the ClientHello. + * + * Note: SSLv2 ClientHello cipher suites follow a different structure than modern + * ClientHello messages. See [RFC5246](https://tools.ietf.org/html/rfc5246#appendix-E.2). + * To determine whether a ClientHello is an SSLv2 ClientHello, + * you will need to use s2n_connection_get_client_hello_version(). + * + * @param ch The Client Hello handle + * @param out The destination buffer for the raw Client Hello cipher suites + * @param max_length The size of out in bytes + * @returns The number of copied bytes + */ +S2N_API extern ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); + +/** + * Function to determine the size of the Client Hello extensions. + * This can be used to allocate the `out` buffer for s2n_client_hello_get_extensions(). + * + * @param ch The Client Hello handle + * @returns the number of bytes the extensions take in the ClientHello message received by the server + */ +S2N_API extern ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch); + +/** + * Copies into the `out` buffer `max_length` bytes of the extensions in the ClientHello. + * + * @param ch The Client Hello handle + * @param out The destination buffer for the raw Client Hello extensions + * @param max_length The size of out in bytes + * @returns The number of copied bytes + */ +S2N_API extern ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); + +/** + * Query the ClientHello message received by the server. Use this function to allocate the `out` buffer for + * other client hello extension functions. + * + * @param ch A pointer to the Client Hello + * @param extension_type Indicates the desired extension + * @returns The number of bytes the given extension type takes + */ +S2N_API extern ssize_t s2n_client_hello_get_extension_length(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type); + +/** + * Copies into the `out` buffer `max_length` bytes of a given extension type on the ClientHello + * + * `ch` is a pointer to the `s2n_client_hello` of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). + * + * @param ch A pointer to the Client Hello + * @param extension_type Indicates the desired extension + * @param out A pointer to the buffer that s2n will write the client session id to. This buffer MUST be the size of `max_length` + * @param max_length The size of `out`. + * @returns The number of copied bytes + */ +S2N_API extern ssize_t s2n_client_hello_get_extension_by_id(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type, uint8_t *out, uint32_t max_length); + +/** + * Used to check if a particular extension exists in the client hello. + * + * `ch` is a pointer to the `s2n_client_hello` of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). + * + * @param ch A pointer to the client hello object + * @param extension_iana The iana value of the extension + * @param exists A pointer that will be set to whether or not the extension exists + */ +S2N_API extern int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extension_iana, bool *exists); + +/** + * Get the the ClientHello session id length in bytes + * + * `ch` is a pointer to the `s2n_client_hello` of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). + * + * @param ch A pointer to the Client Hello + * @param out_length An out pointer. s2n will set it's value to the size of the session_id in bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_client_hello_get_session_id_length(struct s2n_client_hello *ch, uint32_t *out_length); + +/** + * Copies up to `max_length` bytes of the ClientHello session_id into the `out` buffer and stores the number of copied bytes in `out_length`. + * + * Retrieve the session id as sent by the client in the ClientHello message. The session id on the `s2n_connection` may change later + * when the server sends the ServerHello; see `s2n_connection_get_session_id` for how to get the final session id used for future session resumption. + * + * Use s2n_client_hello_get_session_id_length() to get the the ClientHello session id length in bytes. `ch` is a pointer to the `s2n_client_hello` + * of the `s2n_connection` which can be obtained using s2n_connection_get_client_hello(). + * + * @param ch A pointer to the Client Hello + * @param out A pointer to the buffer that s2n will write the client session id to. This buffer MUST be the size of `max_length` + * @param out_length An out pointer. s2n will set it's value to the size of the session_id in bytes. + * @param max_length The size of `out`. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length); + +/** + * Get the length of the compression methods list sent in the Client Hello. + * + * @param ch A pointer to the Client Hello + * @param out_length An out pointer. Will be set to the length of the compression methods list in bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_client_hello_get_compression_methods_length(struct s2n_client_hello *ch, uint32_t *out_length); + +/** + * Retrieves the list of compression methods sent in the Client Hello. + * + * Use `s2n_client_hello_get_compression_methods_length()` + * to retrieve how much memory should be allocated for the buffer in advance. + * + * @note Compression methods were removed in TLS1.3 and therefore the only valid value in this list is the + * "null" compression method when TLS1.3 is negotiated. + * + * @note s2n-tls has never supported compression methods in any TLS version and therefore a + * compression method will never be negotiated or used. + * + * @param ch A pointer to the Client Hello + * @param list A pointer to some memory that s2n will write the compression methods to. This memory MUST be the size of `list_length` + * @param list_length The size of `list`. + * @param out_length An out pointer. s2n will set its value to the size of the compression methods list in bytes. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_client_hello_get_compression_methods(struct s2n_client_hello *ch, uint8_t *list, uint32_t list_length, uint32_t *out_length); + +/** + * Access the Client Hello protocol version + * + * @note This field is a legacy field in TLS1.3 and is no longer used to negotiate the + * protocol version of the connection. It will be set to TLS1.2 even if TLS1.3 is negotiated. + * Therefore this method should only be used for logging or fingerprinting. + * + * @param ch A pointer to the client hello struct + * @param out The protocol version in the client hello. + */ +S2N_API extern int s2n_client_hello_get_legacy_protocol_version(struct s2n_client_hello *ch, uint8_t *out); + +/** + * Retrieves the client random value from the Client Hello. + * + * The client random is a 32-byte value sent by the client in the ClientHello message. + * It is used in the TLS handshake for key derivation and to prevent replay attacks. + * + * Copies up to `max_length` bytes of the client random into the `out` buffer. The number + * of bytes copied will be the minimum of `max_length` and S2N_TLS_RANDOM_DATA_LEN (32 bytes). + * + * @param ch A pointer to the client hello struct + * @param out A pointer to a buffer where the client random will be copied. + * @param max_length The maximum number of bytes to copy into the `out` buffer. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_client_hello_get_random(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length); + +/** + * Retrieves the supported groups received from the client in the supported groups extension. + * + * IANA values for each of the received supported groups are written to the provided `groups` + * array, and `groups_count` is set to the number of received supported groups. + * + * `groups_count_max` should be set to the maximum capacity of the `groups` array. If + * `groups_count_max` is less than the number of received supported groups, this function will + * error. To determine how large `groups` should be in advance, use + * `s2n_client_hello_get_extension_length()` with the S2N_EXTENSION_SUPPORTED_GROUPS extension + * type, and divide the value by 2. + * + * If no supported groups extension was received from the peer, or the received supported groups + * extension is malformed, this function will error. + * + * @param ch A pointer to the ClientHello. Can be retrieved from a connection via + * `s2n_connection_get_client_hello()`. + * @param groups The array to populate with the received supported groups. + * @param groups_count_max The maximum number of supported groups that can fit in the `groups` array. + * @param groups_count Returns the number of received supported groups. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups, + uint16_t groups_count_max, uint16_t *groups_count); + +/** + * Gets the length of the first server name in a Client Hello. + * + * @param ch A pointer to the ClientHello + * @param length A pointer which will be populated with the length of the server name + */ +S2N_API extern int s2n_client_hello_get_server_name_length(struct s2n_client_hello *ch, uint16_t *length); + +/** + * Gets the first server name in a Client Hello. + * + * Use `s2n_client_hello_get_server_name_length()` to get the amount of memory needed for the buffer. + * + * @param ch A pointer to the ClientHello + * @param server_name A pointer to the memory which will be populated with the server name + * @param length The maximum amount of data that can be written to `server_name` + * @param out_length A pointer which will be populated with the size of the server name + */ +S2N_API extern int s2n_client_hello_get_server_name(struct s2n_client_hello *ch, uint8_t *server_name, uint16_t length, uint16_t *out_length); + +/** + * Sets the file descriptor for a s2n connection. + * + * @warning If the read end of the pipe is closed unexpectedly, writing to the pipe will raise a SIGPIPE signal. + * **s2n-tls does NOT handle SIGPIPE.** A SIGPIPE signal will cause the process to terminate unless it is handled + * or ignored by the application. + * @note This file-descriptor should be active and connected + * @param conn A pointer to the s2n connection + * @param fd The new file descriptor + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_fd(struct s2n_connection *conn, int fd); + +/** + * Sets the file descriptor for the read channel of an s2n connection. + * + * @warning If the read end of the pipe is closed unexpectedly, writing to the pipe will raise a SIGPIPE signal. + * **s2n-tls does NOT handle SIGPIPE.** A SIGPIPE signal will cause the process to terminate unless it is handled + * or ignored by the application. + * @note This file-descriptor should be active and connected + * @param conn A pointer to the s2n connection + * @param readfd The new read file descriptor + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_read_fd(struct s2n_connection *conn, int readfd); + +/** + * Sets the assigned file descriptor for the write channel of an s2n connection. + * + * @note This file-descriptor should be active and connected + * @param conn A pointer to the s2n connection + * @param writefd The new write file descriptor + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_write_fd(struct s2n_connection *conn, int writefd); + +/** + * Gets the assigned file descriptor for the read channel of an s2n connection. + * + * @param conn A pointer to the s2n connection + * @param readfd pointer to place the used file descriptor. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_get_read_fd(struct s2n_connection *conn, int *readfd); + +/** + * Gets the assigned file descriptor for the write channel of an s2n connection. + * + * @param conn A pointer to the s2n connection + * @param writefd pointer to place the used file descriptor. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_get_write_fd(struct s2n_connection *conn, int *writefd); + +/** + * Indicates to s2n that the connection is using corked IO. + * + * @warning This API should only be used when using managed send IO. + * + * @param conn The connection object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_use_corked_io(struct s2n_connection *conn); + +/** + * Function pointer for a user provided recv callback. + */ +typedef int s2n_recv_fn(void *io_context, uint8_t *buf, uint32_t len); + +/** + * Function pointer for a user provided send callback. + */ +typedef int s2n_send_fn(void *io_context, const uint8_t *buf, uint32_t len); + +/** + * Set a context containing anything needed in the recv callback function (for example, + * a file descriptor), the buffer holding data to be sent or received, and the length of the buffer. + * + * @note The `io_context` passed to the callbacks may be set separately using `s2n_connection_set_recv_ctx` and `s2n_connection_set_send_ctx`. + * + * @param conn The connection object being updated + * @param ctx A user provided context that the callback will be invoked with + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_recv_ctx(struct s2n_connection *conn, void *ctx); + +/** + * Set a context containing anything needed in the send callback function (for example, + * a file descriptor), the buffer holding data to be sent or received, and the length of the buffer. + * + * @note The `io_context` passed to the callbacks may be set separately using `s2n_connection_set_recv_ctx` and `s2n_connection_set_send_ctx`. + * + * @param conn The connection object being updated + * @param ctx A user provided context that the callback will be invoked with + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_send_ctx(struct s2n_connection *conn, void *ctx); + +/** + * Configure a connection to use a recv callback to receive data. + * + * @note This callback may be blocking or nonblocking. + * @note The callback may receive less than the requested length. The function should return the number + * of bytes received, or set errno and return an error code < 0. + * + * @param conn The connection object being updated + * @param recv A recv callback function pointer + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_recv_cb(struct s2n_connection *conn, s2n_recv_fn recv); + +/** + * Configure a connection to use a send callback to send data. + * + * @note This callback may be blocking or nonblocking. + * @note The callback may send less than the requested length. The function should return the + * number of bytes sent or set errno and return an error code < 0. + * + * @param conn The connection object being updated + * @param send A send callback function pointer + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_send_cb(struct s2n_connection *conn, s2n_send_fn send); + +/** + * Change the behavior of s2n-tls when sending data to prefer high throughput. + * + * Connections preferring throughput will use + * large record sizes that minimize overhead. + * + * @param conn The connection object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_prefer_throughput(struct s2n_connection *conn); + +/** + * Change the behavior of s2n-tls when sending data to prefer low latency. + * + * Connections preferring low latency will be encrypted + * using small record sizes that can be decrypted sooner by the recipient. + * + * @param conn The connection object being updated + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_prefer_low_latency(struct s2n_connection *conn); + +/** + * Configure the connection to reduce potentially expensive calls to recv. + * + * If this setting is disabled, s2n-tls will call read twice for every TLS record, + * which can be expensive but ensures that s2n-tls will always attempt to read the + * exact number of bytes it requires. If this setting is enabled, s2n-tls will + * instead reduce the number of calls to read by attempting to read as much data + * as possible in each read call, storing the extra in the existing IO buffers. + * This may cause it to request more data than will ever actually be available. + * + * There is no additional memory cost of enabling this setting. It reuses the + * existing IO buffers. + * + * This setting is disabled by default. Depending on how your application detects + * data available for reading, buffering reads may break your event loop. + * In particular, note that: + * + * 1. File descriptor reads or calls to your custom s2n_recv_cb may request more + * data than is available. Reads must return partial data when available rather + * than blocking until all requested data is available. + * + * 2. s2n_negotiate may read and buffer application data records. + * You must call s2n_recv at least once after negotiation to ensure that you + * handle any buffered data. + * + * 3. s2n_recv may read and buffer more records than it parses and decrypts. + * You must call s2n_recv until it reports S2N_ERR_T_BLOCKED, rather than just + * until it reports S2N_SUCCESS. + * + * 4. s2n_peek reports available decrypted data. It does not report any data + * buffered by this feature. However, s2n_peek_buffered does report data + * buffered by this feature. + * + * 5. s2n_connection_release_buffers will not release the input buffer if it + * contains buffered data. + * + * For example: if your event loop uses `poll`, you will receive a POLLIN event + * for your read file descriptor when new data is available. When you call s2n_recv + * to read that data, s2n-tls reads one or more TLS records from the file descriptor. + * If you stop calling s2n_recv before it reports S2N_ERR_T_BLOCKED, some of those + * records may remain in s2n-tls's read buffer. If you read part of a record, + * s2n_peek will report the remainder of that record as available. But if you don't + * read any of a record, it remains encrypted and is not reported by s2n_peek, but + * is still reported by s2n_peek_buffered. And because the data is buffered in s2n-tls + * instead of in the file descriptor, another call to `poll` will NOT report any + * more data available. Your application may hang waiting for more data. + * + * @warning This feature cannot be enabled for a connection that will enable kTLS for receiving. + * + * @warning This feature may work with blocking IO, if used carefully. Your blocking + * IO must support partial reads (so MSG_WAITALL cannot be used). You will either + * need to know exactly how much data your peer is sending, or will need to use + * `s2n_peek` and `s2n_peek_buffered` rather than relying on S2N_ERR_T_BLOCKED + * as noted in #3 above. + * + * @param conn The connection object being updated + * @param enabled Set to `true` to enable, `false` to disable. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled); + +/** + * Reports how many bytes of unprocessed TLS records are buffered after the current + * record. + * + * This is only useful when `s2n_connection_set_recv_buffering` is enabled, and + * will return 0 otherwise. + * + * `s2n_peek_buffered` is not a replacement for `s2n_peek`. While `s2n_peek` reports + * application data that is ready for the application to read with no additional + * processing, `s2n_peek_buffered` reports raw TLS records that still need to be + * parsed and likely decrypted. Those records may contain application data, but + * they may also only contain TLS control messages. + * + * When receive buffering is enabled, it is useful to imagine that an s2n-tls + * connection behaves as if it has two buffers, one for the "current record", + * and one for "additional data". + * ```text + * c -> ciphertext + * p -> plaintext + * + * |-----current record-----|-------additional------------| + * case 1 - |ccccccccccccc + * case 2 - |pppppppppppppppppppppppp| + * case 3 - |pppppppppppppppppppppppp|ccccccccccccccccccc + * ``` + * In case 1, we received a record fragment. Records can only be decrypted + * once the full record is available. So "current record" just stores the record + * fragment (ciphertext). There are currently no APIs to determine if there + * is a record fragment stored. https://github.com/aws/s2n-tls/issues/5863. `s2n_peek_buffered` + * and `s2n_peek` will both return 0 in this case. + * + * In case 2, we received a full record, which is decrypted and stored in current + * record. `s2n_peek` will return the count of plaintext bytes + * yet to be read from "current record", and `s2n_peek_buffered` will return 0 + * + * In case 3, additional bytes (another record, complete or incomplete) were + * also read off the wire. In this case `s2n_peek_buffered` will return the length + * of ciphertext bytes buffered in "additional". This is the only case in which + * `s2n_peek_buffered` will return a non-zero result. + * + * @param conn A pointer to the s2n_connection object + * @returns The number of buffered encrypted bytes + */ +S2N_API extern uint32_t s2n_peek_buffered(struct s2n_connection *conn); + +/** + * Configure the connection to free IO buffers when they are not currently in use. + * + * This configuration can be used to minimize connection memory footprint size, at the cost + * of more calls to alloc and free. Some of these costs can be mitigated by configuring s2n-tls + * to use an allocator that includes thread-local caches or lock-free allocation patterns. + * + * @param conn The connection object being update + * @param enabled Set to `true` if dynamic buffers are enabled; `false` if disabled + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_dynamic_buffers(struct s2n_connection *conn, bool enabled); + +/** + * Changes the behavior of s2n-tls when sending data to initially prefer records + * small enough to fit in single ethernet frames. + * + * When dynamic record sizing is active, the connection sends records small enough + * to fit in a single standard 1500 byte ethernet frame. Otherwise, the connection + * chooses record sizes according to the configured maximum fragment length. + * + * Dynamic record sizing is active for the first resize_threshold bytes of a connection, + * and is reactivated whenever timeout_threshold seconds pass without sending data. + * + * @param conn The connection object being updated + * @param resize_threshold The number of bytes to send before changing the record size. Maximum 8MiB. + * @param timeout_threshold Reset record size back to a single segment after threshold seconds of inactivity + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_dynamic_record_threshold(struct s2n_connection *conn, uint32_t resize_threshold, uint16_t timeout_threshold); + +/** + * Sets the callback to use for verifying that a hostname from an X.509 certificate is trusted. + * + * The default behavior is to require that the hostname match the server name set with s2n_set_server_name(). This will + * likely lead to all client certificates being rejected, so the callback will need to be overridden when using client authentication. + * + * If a single callback for different connections using the same config is desired, see s2n_config_set_verify_host_callback(). + * + * @param conn A pointer to a s2n_connection object + * @param host_fn A pointer to a callback function that s2n will invoke in order to verify the hostname of an X.509 certificate + * @param data Opaque pointer to data that the verify host function will be invoked with + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_verify_host_callback(struct s2n_connection *conn, s2n_verify_host_fn host_fn, void *data); + +/** + * Used to opt-out of s2n-tls's built-in 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. + * + * 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 should call s2n_connection_get_delay() and pause + * activity on the connection for the specified number of nanoseconds before calling + * close() or shutdown(). + */ +typedef enum { + S2N_BUILT_IN_BLINDING, + S2N_SELF_SERVICE_BLINDING +} s2n_blinding; + +/** + * Used to configure s2n-tls to either use built-in blinding (set blinding to S2N_BUILT_IN_BLINDING) or + * self-service blinding (set blinding to S2N_SELF_SERVICE_BLINDING). + * + * @param conn The connection object being updated + * @param blinding The desired blinding mode for the connection + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blinding); + +/** + * Query the connection object for the configured blinding delay. + * @param conn The connection object being updated + * @returns the number of nanoseconds an application using self-service blinding should pause before calling close() or shutdown(). + */ +S2N_API extern uint64_t s2n_connection_get_delay(struct s2n_connection *conn); + +/** + * Configures the maximum blinding delay enforced after errors. + * + * Blinding protects your application from timing side channel attacks like Lucky13. While s2n-tls + * implements other, more specific mitigations for known timing side channels, blinding is important + * as a defense against currently unknown or unreported timing attacks. + * + * Setting a maximum delay lower than the recommended default (30s) will make timing attacks against + * your application easier. The lower you set the delay, the fewer requests and less total time an + * attacker will require to execute an attack. If you must lower the delay for reasons such as client + * timeouts, then you should choose the highest value practically possible to limit your risk. + * + * If you lower the blinding delay, you should also consider implementing monitoring and filtering + * to detect and reject suspicious traffic that could be gathering timing information from a potential + * side channel. Timing attacks usually involve repeatedly triggering TLS errors. + * + * @warning Do NOT set a lower blinding delay unless you understand the risks and have other + * mitigations for timing side channels in place. + * + * @note This delay needs to be set lower than any timeouts, such as your TCP socket timeout. + * + * @param config The config object being updated. + * @param seconds The maximum number of seconds that s2n-tls will delay for in the event of a + * sensitive error. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds); + +/** + * Sets the cipher preference override for the s2n_connection. Calling this function is not necessary + * unless you want to set the cipher preferences on the connection to something different than what is in the s2n_config. + * + * @param conn The connection object being updated + * @param version The human readable string representation of the security policy version. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_cipher_preferences(struct s2n_connection *conn, const char *version); + +/** + * Used to indicate the type of key update that is being requested. For further + * information refer to `s2n_connection_request_key_update`. +*/ +typedef enum { + S2N_KEY_UPDATE_NOT_REQUESTED = 0, + S2N_KEY_UPDATE_REQUESTED +} s2n_peer_key_update; + +/** + * Signals the connection to do a key_update at the next possible opportunity. Note that the resulting key update message + * will not be sent until `s2n_send` is called. + * + * @param conn The connection object to trigger the key update on. + * @param peer_request Indicates if a key update should also be requested + * of the peer. When set to `S2N_KEY_UPDATE_NOT_REQUESTED`, then only the sending + * key of `conn` will be updated. If set to `S2N_KEY_UPDATE_REQUESTED`, then + * the sending key of conn will be updated AND the peer will be requested to + * update their sending key. Note that s2n-tls currently only supports + * `peer_request` being set to `S2N_KEY_UPDATE_NOT_REQUESTED` and will return + * S2N_FAILURE if any other value is used. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure +*/ +S2N_API extern int s2n_connection_request_key_update(struct s2n_connection *conn, s2n_peer_key_update peer_request); +/** + * Appends the provided application protocol to the preference list + * + * The data provided in `protocol` parameter will be copied into an internal buffer + * + * @param conn The connection object being updated + * @param protocol A pointer to a slice of bytes + * @param protocol_len The length of bytes that should be read from `protocol`. Note: this value cannot be 0, otherwise an error will be returned. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_append_protocol_preference(struct s2n_connection *conn, const uint8_t *protocol, uint8_t protocol_len); + +/** + * Sets the protocol preference override for the s2n_connection. Calling this function is not necessary unless you want + * to set the protocol preferences on the connection to something different than what is in the s2n_config. + * + * @param conn The connection object being updated + * @param protocols A pointer to an array of protocol strings + * @param protocol_count The number of protocols contained in protocols + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_protocol_preferences(struct s2n_connection *conn, const char *const *protocols, int protocol_count); + +/** + * Sets the server name for the connection. + * + * The provided server name will be sent by the client to the server in the + * server_name ClientHello extension. It may be desirable for clients + * to provide this information to facilitate secure connections to + * servers that host multiple 'virtual' servers at a single underlying + * network address. + * + * s2n-tls does not place any restrictions on the provided server name. However, + * other TLS implementations might. Specifically, the TLS specification for the + * server_name extension requires that it be an ASCII-encoded DNS name without a + * trailing dot, and explicitly forbids literal IPv4 or IPv6 addresses. + * + * @param conn The connection object being queried + * @param server_name A pointer to a string containing the desired server name + * @warning `server_name` must be a NULL terminated string. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_set_server_name(struct s2n_connection *conn, const char *server_name); + +/** + * Query the connection for the selected server name. + * + * This can be used by a server to determine which server name the client is using. This function returns the first ServerName entry + * in the ServerNameList sent by the client. Subsequent entries are not returned. + * + * @param conn The connection object being queried + * @returns The server name associated with a connection, or NULL if none is found. + */ +S2N_API extern const char *s2n_get_server_name(struct s2n_connection *conn); + +/** + * Query the connection for the selected application protocol. + * + * @param conn The connection object being queried + * @returns The negotiated application protocol for a `s2n_connection`. In the event of no protocol being negotiated, NULL is returned. + */ +S2N_API extern const char *s2n_get_application_protocol(struct s2n_connection *conn); + +/** + * Query the connection for a buffer containing the OCSP response. + * + * @param conn The connection object being queried + * @param length A pointer that is set to the certificate transparency response buffer's size + * @returns A pointer to the OCSP response sent by a server during the handshake. If no status response is received, NULL is returned. + */ +S2N_API extern const uint8_t *s2n_connection_get_ocsp_response(struct s2n_connection *conn, uint32_t *length); + +/** + * Query the connection for a buffer containing the Certificate Transparency response. + * + * @param conn The connection object being queried + * @param length A pointer that is set to the certificate transparency response buffer's size + * @returns A pointer to the certificate transparency response buffer. + */ +S2N_API extern const uint8_t *s2n_connection_get_sct_list(struct s2n_connection *conn, uint32_t *length); + +/** + * Used in non-blocking mode to indicate in which direction s2n-tls became blocked on I/O before it + * returned control to the caller. This allows an application to avoid retrying s2n-tls operations + * until I/O is possible in that direction. + */ +typedef enum { + S2N_NOT_BLOCKED = 0, + S2N_BLOCKED_ON_READ, + S2N_BLOCKED_ON_WRITE, + S2N_BLOCKED_ON_APPLICATION_INPUT, + S2N_BLOCKED_ON_EARLY_DATA, +} s2n_blocked_status; + +/** + * Performs the initial "handshake" phase of a TLS connection and must be called before any s2n_recv() or s2n_send() calls. + * + * @note When using client authentication with TLS1.3, s2n_negotiate() will report a successful + * handshake to clients before the server validates the client certificate. If the server then + * rejects the client certificate, the client may later receive an alert while calling s2n_recv, + * potentially after already having sent application data with s2n_send. + * + * See the following example for guidance on calling `s2n_negotiate()`: + * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_negotiate.c + * + * @param conn A pointer to the s2n_connection object + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns S2N_SUCCESS if the handshake completed. S2N_FAILURE if the handshake encountered an error or is blocked. + */ +S2N_API extern int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked); + +/** + * Writes and encrypts `size` of `buf` data to the associated connection. s2n_send() will return the number of bytes + * written, and may indicate a partial write. + * + * @note Partial writes are possible not just for non-blocking I/O, but also for connections aborted while active. + * @note Unlike OpenSSL, repeated calls to s2n_send() should not duplicate the original parameters, but should + * update `buf` and `size` per the indication of size written. + * + * See the following example for guidance on calling `s2n_send()`: + * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_send.c + * + * @param conn A pointer to the s2n_connection object + * @param buf A pointer to a buffer that s2n will write data from + * @param size The size of buf + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. + */ +S2N_API extern ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked); + +/** + * Works in the same way as s2n_sendv_with_offset() but with the `offs` parameter implicitly assumed to be 0. + * Therefore in the partial write case, the caller would have to make sure that the `bufs` and `count` fields are modified in a way that takes + * the partial writes into account. + * + * @param conn A pointer to the s2n_connection object + * @param bufs A pointer to a vector of buffers that s2n will write data from. + * @param count The number of buffers in `bufs` + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. + */ +S2N_API extern ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked); + +/** + * Works in the same way as s2n_send() except that it accepts vectorized buffers. Will return the number of bytes written, and may indicate a partial write. Partial writes are possible not just for non-blocking I/O, but also for connections aborted while active. + * + * @note Partial writes are possible not just for non-blocking I/O, but also for connections aborted while active. + * + * @note Unlike OpenSSL, repeated calls to s2n_sendv_with_offset() should not duplicate the original parameters, but should update `bufs` and `count` per the indication of size written. + * + * See the following example for guidance on calling `s2n_sendv_with_offset()`: + * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_send.c + * + * @param conn A pointer to the s2n_connection object + * @param bufs A pointer to a vector of buffers that s2n will write data from. + * @param count The number of buffers in `bufs` + * @param offs The write cursor offset. This should be updated as data is written. See the example code. + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns The number of bytes written on success, which may indicate a partial write. S2N_FAILURE on failure. + */ +S2N_API extern ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, ssize_t offs, s2n_blocked_status *blocked); + +/** + * Decrypts and reads **size* to `buf` data from the associated + * connection. + * + * @note Unlike OpenSSL, repeated calls to `s2n_recv` should not duplicate the original parameters, but should update `buf` and `size` per the indication of size read. + * + * See the following example for guidance on calling `s2n_recv()`: + * https://github.com/aws/s2n-tls/blob/main/docs/examples/s2n_recv.c + * + * @param conn A pointer to the s2n_connection object + * @param buf A pointer to a buffer that s2n will place read data into. + * @param size Size of `buf` + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns The number of bytes read on success. 0 if the connection was shutdown by the peer. S2N_FAILURE on failure. + */ +S2N_API extern ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked); + +/** + * Allows users of s2n-tls to peek inside the data buffer of an s2n-tls connection to see if there more data to be read without actually reading it. + * + * This is useful when using select() on the underlying s2n-tls file descriptor with a message based application layer protocol. As a single call + * to s2n_recv may read all data off the underlying file descriptor, select() will be unable to tell you there if there is more application data + * ready for processing already loaded into the s2n-tls buffer. + * + * @note can then be used to determine if s2n_recv() needs to be called before more data comes in on the raw fd + * @param conn A pointer to the s2n_connection object + * @returns The number of bytes that can be read from the connection + */ +S2N_API extern uint32_t s2n_peek(struct s2n_connection *conn); + +/** + * Wipes and releases buffers and memory allocated during the TLS handshake. + * + * @note This function should be called after the handshake is successfully negotiated and logging or recording of handshake data is complete. + * + * @param conn A pointer to the s2n_connection object + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_free_handshake(struct s2n_connection *conn); + +/** + * Wipes and free the `in` and `out` buffers associated with a connection. + * + * @note This function may be called when a connection is + * in keep-alive or idle state to reduce memory overhead of long lived connections. + * + * @param conn A pointer to the s2n_connection object + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_release_buffers(struct s2n_connection *conn); + +/** + * Wipes an existing connection and allows it to be reused. Erases all data associated with a connection including + * pending reads. + * + * @note This function should be called after all I/O is completed and s2n_shutdown has been called. + * @note Reusing the same connection handle(s) is more performant than repeatedly calling s2n_connection_new() and s2n_connection_free(). + * + * @param conn A pointer to the s2n_connection object + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_wipe(struct s2n_connection *conn); + +/** + * Frees the memory associated with an s2n_connection + * handle. The handle is considered invalid after `s2n_connection_free` is used. + * s2n_connection_wipe() does not need to be called prior to this function. `s2n_connection_free` performs its own wipe + * of sensitive data. + * + * @param conn A pointer to the s2n_connection object + * @returns 0 on success. -1 on failure + */ +S2N_API extern int s2n_connection_free(struct s2n_connection *conn); + +/** + * Attempts a closure at the TLS layer. Does not close the underlying transport. This call may block in either direction. + * + * Unlike other TLS implementations, `s2n_shutdown` attempts a graceful shutdown by default. It will not return with success unless a close_notify alert is successfully + * sent and received. As a result, `s2n_shutdown` may fail when interacting with a non-conformant TLS implementation. + * + * Once `s2n_shutdown` is complete: + * * The s2n_connection handle cannot be used for reading for writing. + * * The underlying transport can be closed. Most likely via `shutdown()` or `close()`. + * * The s2n_connection handle can be freed via s2n_connection_free() or reused via s2n_connection_wipe() + * + * @param conn A pointer to the s2n_connection object + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_shutdown(struct s2n_connection *conn, s2n_blocked_status *blocked); + +/** + * Attempts to close the write side of the TLS connection. + * + * TLS1.3 supports closing the write side of a TLS connection while leaving the read + * side unaffected. This feature is usually referred to as "half-close". We send + * a close_notify alert, but do not wait for the peer to respond. + * + * Like `s2n_shutdown()`, this method does not affect the underlying transport. + * + * `s2n_shutdown_send()` may still be called for earlier TLS versions, but most + * TLS implementations will react by immediately discarding any pending writes and + * closing the connection. + * + * Once `s2n_shutdown_send()` is complete: + * * The s2n_connection handle CANNOT be used for writing. + * * The s2n_connection handle CAN be used for reading. + * * The write side of the underlying transport can be closed. Most likely via `shutdown()`. + * + * The application should still call `s2n_shutdown()` or wait for `s2n_recv()` to + * return 0 to indicate end-of-data before cleaning up the connection or closing + * the read side of the underlying transport. + * + * @param conn A pointer to the s2n_connection object + * @param blocked A pointer which will be set to the blocked status if an `S2N_ERR_T_BLOCKED` error is returned. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_shutdown_send(struct s2n_connection *conn, s2n_blocked_status *blocked); + +/** + * Used to declare what type of client certificate authentication to use. + * + * A s2n_connection will enforce client certificate authentication (mTLS) differently based on + * the `s2n_cert_auth_type` and `s2n_mode` (client/server) of the connection, as described below. + * + * Server behavior: + * - None (default): Will not request client authentication. + * - Optional: Request the client's certificate and validate it. If no certificate is received then + * no validation is performed. + * - Required: Request the client's certificate and validate it. Abort the handshake if a client + * certificate is not received. + * + * Client behavior: + * - None: Abort the handshake if the server requests client authentication. + * - Optional (default): Sends the client certificate if the server requests client + * authentication. No certificate is sent if the application hasn't provided a certificate. + * - Required: Send the client certificate. Abort the handshake if the server doesn't request + * client authentication or if the application hasn't provided a certificate. + */ +typedef enum { + S2N_CERT_AUTH_NONE, + S2N_CERT_AUTH_REQUIRED, + S2N_CERT_AUTH_OPTIONAL +} s2n_cert_auth_type; + +/** + * Gets Client Certificate authentication method the s2n_config object is using. + * + * @param config A pointer to a s2n_config object + * @param client_auth_type A pointer to a client auth policy. This will be updated to the s2n_config value. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_get_client_auth_type(struct s2n_config *config, s2n_cert_auth_type *client_auth_type); + +/** + * Sets whether or not a Client Certificate should be required to complete the TLS Connection. + * + * If this is set to `S2N_CERT_AUTH_OPTIONAL` the server will request a client certificate but allow the client to not provide one. + * Rejecting a client certificate when using `S2N_CERT_AUTH_OPTIONAL` will terminate the handshake. + * + * @param config A pointer to a s2n_config object + * @param client_auth_type The client auth policy for the connection + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_client_auth_type(struct s2n_config *config, s2n_cert_auth_type client_auth_type); + +/** + * Gets Client Certificate authentication method the s2n_connection object is using. + * + * @param conn A pointer to the s2n_connection object + * @param client_auth_type A pointer to a client auth policy. This will be updated to the s2n_connection value. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_get_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type *client_auth_type); + +/** + * Sets whether or not a Client Certificate should be required to complete the TLS Connection. + * + * If this is set to `S2N_CERT_AUTH_OPTIONAL` the server will request a client certificate but allow the client to not provide one. + * Rejecting a client certificate when using `S2N_CERT_AUTH_OPTIONAL` will terminate the handshake. + * + * @param conn A pointer to the s2n_connection object + * @param client_auth_type The client auth policy for the connection + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type client_auth_type); + +/** + * Gets the raw certificate chain received from the client. + * + * The retrieved certificate chain has the format described by the TLS 1.2 RFC: + * https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.2. Each certificate is a DER-encoded ASN.1 X.509, + * prepended by a 3 byte network-endian length value. Note that this format is used regardless of the connection's + * protocol version. + * + * @warning The buffer pointed to by `cert_chain_out` shares its lifetime with the s2n_connection object. + * + * @param conn A pointer to the s2n_connection object + * @param cert_chain_out A pointer that's set to the client certificate chain. + * @param cert_chain_len A pointer that's set to the size of the `cert_chain_out` buffer. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t **der_cert_chain_out, uint32_t *cert_chain_len); + +/** + * Sets the initial number of session tickets to send after a >=TLS1.3 handshake. The default value is one ticket. + * + * @param config A pointer to the config object. + * @param num The number of session tickets that will be sent. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num); + +/** + * Increases the number of session tickets to send after a >=TLS1.3 handshake. + * + * @param conn A pointer to the connection object. + * @param num The number of additional session tickets to send. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num); + +/** + * Returns the number of session tickets issued by the server. + * + * In TLS1.3, this number can be up to the limit configured by s2n_config_set_initial_ticket_count + * and s2n_connection_add_new_tickets_to_send. In earlier versions of TLS, this number will be either 0 or 1. + * + * This method only works for server connections. + * + * @param conn A pointer to the connection object. + * @param num The number of additional session tickets sent. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num); + +/** + * Sets the keying material lifetime for >=TLS1.3 session tickets so that one session doesn't get re-used ad infinitum. + * The default value is one week. + * + * @param conn A pointer to the connection object. + * @param lifetime_in_secs Lifetime of keying material in seconds. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs); + +struct s2n_session_ticket; + +/** + * Callback function for receiving a session ticket. + * + * This function will be called each time a session ticket is received, which may be multiple times for TLS1.3. + * + * # Safety + * + * `ctx` is a void pointer and the caller is responsible for ensuring it is cast to the correct type. + * `ticket` is valid only within the scope of this callback. + * + * @param conn A pointer to the connection object. + * @param ctx Context for the session ticket callback function. + * @param ticket Pointer to the received session ticket object. + */ +typedef int (*s2n_session_ticket_fn)(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket); + +/** + * Sets a session ticket callback to be called when a client receives a new session ticket. + * + * # Safety + * + * `callback` MUST cast `ctx` into the same type of pointer that was originally created. + * `ctx` MUST be valid for the lifetime of the config, or until a different context is set. + * + * @param config A pointer to the config object. + * @param callback The function that should be called when the callback is triggered. + * @param ctx The context to be passed when the callback is called. + */ +S2N_API extern int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx); + +/** + * Gets the length of the session ticket from a session ticket object. + * + * @param ticket Pointer to the session ticket object. + * @param data_len Pointer to be set to the length of the session ticket on success. + */ +S2N_API extern int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len); + +/** + * Gets the session ticket data from a session ticket object. + * + * # Safety + * The entire session ticket will be copied into `data` on success. Therefore, `data` MUST have enough + * memory to store the session ticket data. + * + * @param ticket Pointer to the session ticket object. + * @param max_data_len Maximum length of data that can be written to the 'data' pointer. + * @param data Pointer to where the session ticket data will be stored. + */ +S2N_API extern int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data); + +/** + * Gets the lifetime in seconds of the session ticket from a session ticket object. + * + * @param ticket Pointer to the session ticket object. + * @param session_lifetime Pointer to a variable where the lifetime of the session ticket will be stored. + */ +S2N_API extern int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime); + +/** + * De-serializes the session state and updates the connection accordingly. + * + * If this method fails, the connection should not be affected: calling s2n_negotiate + * with the connection should simply result in a full handshake. + * + * @param conn A pointer to the s2n_connection object + * @param session A pointer to a buffer of size `length` + * @param length The size of the `session` buffer + * + * @returns The number of copied bytes + */ +S2N_API extern int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length); + +/** + * Serializes the session state from connection and copies into the `session` buffer and returns the number of copied bytes + * + * @note This function is not recommended for > TLS 1.2 because in TLS1.3 + * servers can send multiple session tickets and this function will only + * return the most recently received ticket. + * + * @param conn A pointer to the s2n_connection object + * @param session A pointer to a buffer of size `max_length` + * @param max_length The size of the `session` buffer + * + * @returns The number of copied bytes + */ +S2N_API extern int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length); + +/** + * Retrieves a hint from the server indicating how long this ticket's lifetime is. + * + * @note This function is not recommended for > TLS 1.2 because in TLS1.3 + * servers can send multiple session tickets and this function will only + * return the most recently received ticket lifetime hint. + * + * @param conn A pointer to the s2n_connection object + * + * @returns The session ticket lifetime hint in seconds from the server or -1 when session ticket was not used for resumption. + */ +S2N_API extern int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn); + +/** + * Use this to query the serialized session state size before copying it into a buffer. + * + * @param conn A pointer to the s2n_connection object + * + * @returns number of bytes needed to store serialized session state + */ +S2N_API extern int s2n_connection_get_session_length(struct s2n_connection *conn); + +/** + * Gets the latest session id's length from the connection. + * + * Use this to query the session id size before copying it into a buffer. + * + * @param conn A pointer to the s2n_connection object + * + * @returns The latest session id length from the connection. Session id length will be 0 for TLS versions >= TLS1.3 as stateful session resumption has not yet been implemented in TLS1.3. + */ +S2N_API extern int s2n_connection_get_session_id_length(struct s2n_connection *conn); + +/** +* Gets the latest session id from the connection, copies it into the `session_id` buffer, and returns the number of copied bytes. +* +* The session id may change between s2n receiving the ClientHello and sending the ServerHello, but this function will always describe the latest session id. +* +* See s2n_client_hello_get_session_id() to get the session id as it was sent by the client in the ClientHello message. + * + * @param conn A pointer to the s2n_connection object + * @param session_id A pointer to a buffer of size `max_length` + * @param max_length The size of the `session_id` buffer + * + * @returns The number of copied bytes. + */ +S2N_API extern int s2n_connection_get_session_id(struct s2n_connection *conn, uint8_t *session_id, size_t max_length); + +/** + * Check if the connection was resumed from an earlier handshake. + * + * @param conn A pointer to the s2n_connection object + * + * @returns returns 1 if the handshake was abbreviated, otherwise returns 0 + */ +S2N_API extern int s2n_connection_is_session_resumed(struct s2n_connection *conn); + +/** + * Check if the connection is OCSP stapled. + * + * @param conn A pointer to the s2n_connection object + * + * @returns 1 if OCSP response was sent (if connection is in S2N_SERVER mode) or received (if connection is in S2N_CLIENT mode) during handshake, otherwise it returns 0. + */ +S2N_API extern int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn); + +/** + * TLS Signature Algorithms - RFC 5246 7.4.1.4.1 + * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 + */ +typedef enum { + S2N_TLS_SIGNATURE_ANONYMOUS = 0, + S2N_TLS_SIGNATURE_RSA = 1, + S2N_TLS_SIGNATURE_ECDSA = 3, + S2N_TLS_SIGNATURE_MLDSA = 9, + + /* Use Private Range for RSA PSS since it's not defined there */ + S2N_TLS_SIGNATURE_RSA_PSS_RSAE = 224, + S2N_TLS_SIGNATURE_RSA_PSS_PSS +} s2n_tls_signature_algorithm; + +/** TLS Hash Algorithms - https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 + */ +typedef enum { + S2N_TLS_HASH_NONE = 0, + S2N_TLS_HASH_MD5 = 1, + S2N_TLS_HASH_SHA1 = 2, + S2N_TLS_HASH_SHA224 = 3, + S2N_TLS_HASH_SHA256 = 4, + S2N_TLS_HASH_SHA384 = 5, + S2N_TLS_HASH_SHA512 = 6, + + /* Use Private Range for MD5_SHA1 */ + S2N_TLS_HASH_MD5_SHA1 = 224 +} s2n_tls_hash_algorithm; + +/** + * Get the connection's selected signature algorithm. + * + * @param conn A pointer to the s2n_connection object + * @param chosen_alg A pointer to a s2n_tls_signature_algorithm object. This is an output parameter. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. + */ +S2N_API extern int s2n_connection_get_selected_signature_algorithm(struct s2n_connection *conn, s2n_tls_signature_algorithm *chosen_alg); + +/** + * Get the connection's selected digest algorithm. + * + * @param conn A pointer to the s2n_connection object + * @param chosen_alg A pointer to a s2n_tls_hash_algorithm object. This is an output parameter. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. + */ +S2N_API extern int s2n_connection_get_selected_digest_algorithm(struct s2n_connection *conn, s2n_tls_hash_algorithm *chosen_alg); + +/** + * Get the client certificate's signature algorithm. + * + * @param conn A pointer to the s2n_connection object + * @param chosen_alg A pointer to a s2n_tls_signature_algorithm object. This is an output parameter. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. + */ +S2N_API extern int s2n_connection_get_selected_client_cert_signature_algorithm(struct s2n_connection *conn, s2n_tls_signature_algorithm *chosen_alg); + +/** + * Get the client certificate's digest algorithm. + * + * @param conn A pointer to the s2n_connection object + * @param chosen_alg A pointer to a s2n_tls_hash_algorithm object. This is an output parameter. + * + * @returns S2N_SUCCESS on success. S2N_FAILURE if bad parameters are received. + */ +S2N_API extern int s2n_connection_get_selected_client_cert_digest_algorithm(struct s2n_connection *conn, s2n_tls_hash_algorithm *chosen_alg); + +/** + * Get the human readable signature scheme for the connection. + * + * This method will return: + * 1. The IANA "description" for the negotiated signature scheme. + * For example, rsa_pss_rsae_sha384 or ecdsa_secp256r1_sha256. + * 2. An unofficial description, if the server signature did not use an official + * IANA signature scheme. This description will take the form + * "legacy__". + * For example, legacy_rsa_sha224 or legacy_ecdsa_sha256. + * 3. "none", if the handshake did not include a server signature. + * This may happen if session resumption or RSA key exchange are used. + * + * If the connection has not yet performed a handshake, this method will error. + * + * A note on unofficial descriptions: If TLS1.2 or earlier is negotiated, + * an official IANA signature scheme may not be chosen. Before TLS1.3, a combination + * of "signature algorithm" and "hash algorithm" were used instead of signature schemes. + * Not all combinations were later assigned to official signature schemes. + * + * A note on ECDSA signature schemes: TLS1.3 and TLS1.2 ECDSA "signature schemes" + * share the same IANA value. However, this method assigns them different descriptions + * because the TLS1.3 versions (like ecdsa_secp256r1_sha256) imply specific curves, + * while the TLS1.2 versions (like legacy_ecdsa_sha256) do not. + * + * IANA signature schemes: + * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme + * IANA signature algorithms: + * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 + * IANA hash algorithms: + * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 + * + * @param conn A pointer to the s2n connection + * @param group_name A pointer that will be set to the signature scheme name. + * @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. + */ +S2N_API extern int s2n_connection_get_signature_scheme(struct s2n_connection *conn, const char **scheme_name); + +/** + * Get the certificate used during the TLS handshake + * + * - If `conn` is a server connection, the certificate selected will depend on the + * ServerName sent by the client and supported ciphers. + * - If `conn` is a client connection, the certificate sent in response to a CertificateRequest + * message is returned. Currently s2n-tls supports loading only one certificate in client mode. Note that + * not all TLS endpoints will request a certificate. + * + * @param conn A pointer to the s2n_connection object + * + * @returns NULL if the certificate selection phase of the handshake has not completed or if a certificate was not requested by the peer + */ +S2N_API extern struct s2n_cert_chain_and_key *s2n_connection_get_selected_cert(struct s2n_connection *conn); + +/** + * @param chain_and_key A pointer to the s2n_cert_chain_and_key object being read. + * @param cert_length This return value represents the length of the s2n certificate chain `chain_and_key`. + * @returns the length of the s2n certificate chain `chain_and_key`. + */ +S2N_API extern int s2n_cert_chain_get_length(const struct s2n_cert_chain_and_key *chain_and_key, uint32_t *cert_length); + +/** + * Returns the certificate `out_cert` present at the index `cert_idx` of the certificate chain `chain_and_key`. + * + * Note that the index of the leaf certificate is zero. If the certificate chain `chain_and_key` is NULL or the + * certificate index value is not in the acceptable range for the input certificate chain, an error is returned. + * + * # Safety + * + * There is no memory allocation required for `out_cert` buffer prior to calling the `s2n_cert_chain_get_cert` API. + * The `out_cert` will contain the pointer to the s2n_cert initialized within the input s2n_cert_chain_and_key `chain_and_key`. + * The pointer to the output s2n certificate `out_cert` is valid until `chain_and_key` is freed up. + * If a caller wishes to persist the `out_cert` beyond the lifetime of `chain_and_key`, the contents would need to be + * copied prior to freeing `chain_and_key`. + * + * @param chain_and_key A pointer to the s2n_cert_chain_and_key object being read. + * @param out_cert A pointer to the output s2n_cert `out_cert` present at the index `cert_idx` of the certificate chain `chain_and_key`. + * @param cert_idx The certificate index for the requested certificate within the s2n certificate chain. + */ +S2N_API extern int s2n_cert_chain_get_cert(const struct s2n_cert_chain_and_key *chain_and_key, struct s2n_cert **out_cert, const uint32_t cert_idx); + +/** + * Returns the s2n certificate in DER format along with its length. + * + * The API gets the s2n certificate `cert` in DER format. The certificate is returned in the `out_cert_der` buffer. + * Here, `cert_len` represents the length of the certificate. + * + * A caller can use certificate parsing tools such as the ones provided by OpenSSL to parse the DER encoded certificate chain returned. + * + * # Safety + * + * The memory for the `out_cert_der` buffer is allocated and owned by s2n-tls. + * Since the size of the certificate can potentially be very large, a pointer to internal connection data is returned instead of + * copying the contents into a caller-provided buffer. + * + * The pointer to the output buffer `out_cert_der` is valid only while the connection exists. + * The `s2n_connection_free` API frees the memory associated with the out_cert_der buffer and after the `s2n_connection_wipe` API is + * called the memory pointed by out_cert_der is invalid. + * + * If a caller wishes to persist the `out_cert_der` beyond the lifetime of the connection, the contents would need to be + * copied prior to the connection termination. + * + * @param cert A pointer to the s2n_cert object being read. + * @param out_cert_der A pointer to the output buffer which will hold the s2n certificate `cert` in DER format. + * @param cert_length This return value represents the length of the certificate. + */ +S2N_API extern int s2n_cert_get_der(const struct s2n_cert *cert, const uint8_t **out_cert_der, uint32_t *cert_length); + +/** + * Returns the validated peer certificate chain as a `s2n_cert_chain_and_key` opaque object. + * + * The `s2n_cert_chain_and_key` parameter must be allocated by the caller using the `s2n_cert_chain_and_key_new` API + * prior to this function call and must be empty. To free the memory associated with the `s2n_cert_chain_and_key` object use the + * `s2n_cert_chain_and_key_free` API. + * + * @param conn A pointer to the s2n_connection object being read. + * @param cert_chain The returned validated peer certificate chain `cert_chain` retrieved from the s2n connection. + */ +S2N_API extern int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain); + +/** + * Returns the length of the DER encoded extension value of the ASN.1 X.509 certificate extension. + * + * @param cert A pointer to the s2n_cert object being read. + * @param oid A null-terminated cstring that contains the OID of the X.509 certificate extension to be read. + * @param ext_value_len This return value contains the length of DER encoded extension value of the ASN.1 X.509 certificate extension. + */ +S2N_API extern int s2n_cert_get_x509_extension_value_length(struct s2n_cert *cert, const uint8_t *oid, uint32_t *ext_value_len); + +/** + * Returns the DER encoding of an ASN.1 X.509 certificate extension value, it's length and a boolean critical. + * + * @param cert A pointer to the s2n_cert object being read. + * @param oid A null-terminated cstring that contains the OID of the X.509 certificate extension to be read. + * @param ext_value A pointer to the output buffer which will hold the DER encoding of an ASN.1 X.509 certificate extension value returned. + * @param ext_value_len This value is both an input and output parameter and represents the length of the output buffer `ext_value`. + * When used as an input parameter, the caller must use this parameter to convey the maximum length of `ext_value`. + * When used as an output parameter, `ext_value_len` holds the actual length of the DER encoding of the ASN.1 X.509 certificate extension value returned. + * @param critical This return value contains the boolean value for `critical`. + */ +S2N_API extern int s2n_cert_get_x509_extension_value(struct s2n_cert *cert, const uint8_t *oid, uint8_t *ext_value, uint32_t *ext_value_len, bool *critical); + +/** + * Returns the UTF8 String length of the ASN.1 X.509 certificate extension data. + * + * @param extension_data A pointer to the DER encoded ASN.1 X.509 certificate extension value being read. + * @param extension_len represents the length of the input buffer `extension_data`. + * @param utf8_str_len This return value contains the UTF8 String length of the ASN.1 X.509 certificate extension data. + */ +S2N_API extern int s2n_cert_get_utf8_string_from_extension_data_length(const uint8_t *extension_data, uint32_t extension_len, uint32_t *utf8_str_len); + +/** + * Returns the UTF8 String representation of the DER encoded ASN.1 X.509 certificate extension data. + * + * @param extension_data A pointer to the DER encoded ASN.1 X.509 certificate extension value being read. + * @param extension_len represents the length of the input buffer `extension_data`. + * @param out_data A pointer to the output buffer which will hold the UTF8 String representation of the DER encoded ASN.1 X.509 + * certificate extension data returned. + * @param out_len This value is both an input and output parameter and represents the length of the output buffer `out_data`. + * When used as an input parameter, the caller must use this parameter to convey the maximum length of `out_data`. + * When used as an output parameter, `out_len` holds the actual length of UTF8 String returned. + */ +S2N_API extern int s2n_cert_get_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len); + +/** + * Pre-shared key (PSK) Hash Algorithm - RFC 8446 Section-2.2 + */ +typedef enum { + S2N_PSK_HMAC_SHA256, + S2N_PSK_HMAC_SHA384, +} s2n_psk_hmac; + +/** + * Opaque pre shared key handle + */ +struct s2n_psk; + +/** + * Creates a new s2n external pre-shared key (PSK) object with `S2N_PSK_HMAC_SHA256` as the default + * PSK hash algorithm. An external PSK is a key established outside of TLS using a secure mutually agreed upon mechanism. + * + * Use `s2n_psk_free` to free the memory allocated to the s2n external PSK object created by this API. + * + * @returns struct s2n_psk* Returns a pointer to the newly created external PSK object. + */ +S2N_API struct s2n_psk *s2n_external_psk_new(void); + +/** + * Frees the memory associated with the external PSK object. + * + * @param psk Pointer to the PSK object to be freed. + */ +S2N_API int s2n_psk_free(struct s2n_psk **psk); + +/** + * Sets the identity for a given external PSK object. + * The identity is a unique identifier for the pre-shared secret. + * It is a non-secret value represented by raw bytes. + * + * # Safety + * + * The identity is transmitted over the network unencrypted and is a non-secret value. + * Do not include confidential information in the identity. + * + * Note that the identity is copied into s2n-tls memory and the caller is responsible for + * freeing the memory associated with the identity input. + * + * @param psk A pointer to a PSK object to be updated with the identity. + * @param identity The identity in raw bytes format to be copied. + * @param identity_size The length of the PSK identity being set. + */ +S2N_API int s2n_psk_set_identity(struct s2n_psk *psk, const uint8_t *identity, uint16_t identity_size); + +/** + * Sets the out-of-band/externally provisioned secret for a given external PSK object. + * + * # Safety + * + * Note that the secret is copied into s2n-tls memory and the caller is responsible for + * freeing the memory associated with the `secret` input. + * + * Deriving a shared secret from a password or other low-entropy source + * is not secure and is subject to dictionary attacks. + * See https://tools.ietf.org/rfc/rfc8446#section-2.2 for more information. + * + * @param psk A pointer to a PSK object to be updated with the secret. + * @param secret The secret in raw bytes format to be copied. + * @param secret_size The length of the pre-shared secret being set. + */ +S2N_API int s2n_psk_set_secret(struct s2n_psk *psk, const uint8_t *secret, uint16_t secret_size); + +/** + * Sets the hash algorithm for a given external PSK object. The supported PSK hash + * algorithms are as listed in the enum `s2n_psk_hmac` above. + * + * @param psk A pointer to the external PSK object to be updated with the PSK hash algorithm. + * @param hmac The PSK hash algorithm being set. + */ +S2N_API int s2n_psk_set_hmac(struct s2n_psk *psk, s2n_psk_hmac hmac); + +/** + * Appends a PSK object to the list of PSKs supported by the s2n connection. + * If a PSK with a duplicate identity is found, an error is returned and the PSK is not added to the list. + * Note that a copy of `psk` is stored on the connection. The user is still responsible for freeing the + * memory associated with `psk`. + * + * @param conn A pointer to the s2n_connection object that contains the list of PSKs supported. + * @param psk A pointer to the `s2n_psk` object to be appended to the list of PSKs on the s2n connection. + */ +S2N_API int s2n_connection_append_psk(struct s2n_connection *conn, struct s2n_psk *psk); + +/** + * The list of PSK modes supported by s2n-tls for TLS versions >= TLS1.3. + * Currently s2n-tls supports two modes - `S2N_PSK_MODE_RESUMPTION`, which represents the PSKs established + * using the previous connection via session resumption, and `S2N_PSK_MODE_EXTERNAL`, which represents PSKs + * established out-of-band/externally using a secure mutually agreed upon mechanism. + */ +typedef enum { + S2N_PSK_MODE_RESUMPTION, + S2N_PSK_MODE_EXTERNAL +} s2n_psk_mode; + +/** + * Sets the PSK mode on the s2n config object. + * The supported PSK modes are listed in the enum `s2n_psk_mode` above. + * + * @param config A pointer to the s2n_config object being updated. + * @param mode The PSK mode to be set. + */ +S2N_API int s2n_config_set_psk_mode(struct s2n_config *config, s2n_psk_mode mode); + +/** + * Sets the PSK mode on the s2n connection object. + * The supported PSK modes are listed in the enum `s2n_psk_mode` above. + * This API overrides the PSK mode set on config for this connection. + * + * @param conn A pointer to the s2n_connection object being updated. + * @param mode The PSK mode to be set. + */ +S2N_API int s2n_connection_set_psk_mode(struct s2n_connection *conn, s2n_psk_mode mode); + +/** + * Gets the negotiated PSK identity length from the s2n connection object. The negotiated PSK + * refers to the chosen PSK by the server to be used for the connection. + * + * This API can be used to determine if the negotiated PSK exists. If negotiated PSK exists a + * call to this API returns a value greater than zero. If the negotiated PSK does not exist, the + * value `0` is returned. + * + * @param conn A pointer to the s2n_connection object that successfully negotiated a PSK connection. + * @param identity_length The length of the negotiated PSK identity. + */ +S2N_API int s2n_connection_get_negotiated_psk_identity_length(struct s2n_connection *conn, uint16_t *identity_length); + +/** + * Gets the negotiated PSK identity from the s2n connection object. + * If the negotiated PSK does not exist, the PSK identity will not be obtained and no error will be returned. + * Prior to this API call, use `s2n_connection_get_negotiated_psk_identity_length` to determine if a + * negotiated PSK exists or not. + * + * # Safety + * + * The negotiated PSK identity will be copied into the identity buffer on success. + * Therefore, the identity buffer must have enough memory to fit the identity length. + * + * @param conn A pointer to the s2n_connection object. + * @param identity The negotiated PSK identity obtained from the s2n_connection object. + * @param max_identity_length The maximum length for the PSK identity. If the negotiated psk_identity length is + * greater than this `max_identity_length` value an error will be returned. + */ +S2N_API int s2n_connection_get_negotiated_psk_identity(struct s2n_connection *conn, uint8_t *identity, uint16_t max_identity_length); + +struct s2n_offered_psk; + +/** + * Creates a new s2n offered PSK object. + * An offered PSK object represents a single PSK sent by the client. + * + * # Safety + * + * Use `s2n_offered_psk_free` to free the memory allocated to the s2n offered PSK object created by this API. + * + * @returns struct s2n_offered_psk* Returns a pointer to the newly created offered PSK object. + */ +S2N_API struct s2n_offered_psk *s2n_offered_psk_new(void); + +/** + * Frees the memory associated with the `s2n_offered_psk` object. + * + * @param psk A pointer to the `s2n_offered_psk` object to be freed. + */ +S2N_API int s2n_offered_psk_free(struct s2n_offered_psk **psk); + +/** + * Gets the PSK identity and PSK identity length for a given offered PSK object. + * + * @param psk A pointer to the offered PSK object being read. + * @param identity The PSK identity being obtained. + * @param size The length of the PSK identity being obtained. + */ +S2N_API int s2n_offered_psk_get_identity(struct s2n_offered_psk *psk, uint8_t **identity, uint16_t *size); + +struct s2n_offered_psk_list; + +/** + * Checks whether the offered PSK list has an offered psk object next in line in the list. + * An offered PSK list contains all the PSKs offered by the client for the server to select. + * + * # Safety + * + * This API returns a pointer to the s2n-tls internal memory with limited lifetime. + * After the completion of `s2n_psk_selection_callback` this pointer is invalid. + * + * @param psk_list A pointer to the offered PSK list being read. + * @returns bool A boolean value representing whether an offered psk object is present next in line in the offered PSK list. + */ +S2N_API bool s2n_offered_psk_list_has_next(struct s2n_offered_psk_list *psk_list); + +/** + * Obtains the next offered PSK object from the list of offered PSKs. Use `s2n_offered_psk_list_has_next` + * prior to this API call to ensure we have not reached the end of the list. + * + * @param psk_list A pointer to the offered PSK list being read. + * @param psk A pointer to the next offered PSK object being obtained. + */ +S2N_API int s2n_offered_psk_list_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk); + +/** + * Returns the offered PSK list to its original read state. + * + * When `s2n_offered_psk_list_reread` is called, `s2n_offered_psk_list_next` will return the first PSK + * in the offered PSK list. + * + * @param psk_list A pointer to the offered PSK list being reread. + */ +S2N_API int s2n_offered_psk_list_reread(struct s2n_offered_psk_list *psk_list); + +/** + * Chooses a PSK from the offered PSK list to be used for the connection. + * This API matches the PSK identity received from the client against the server's known PSK identities + * list, in order to choose the PSK to be used for the connection. If the PSK identity sent from the client + * is NULL, no PSK is chosen for the connection. If the client offered PSK identity has no matching PSK identity + * with the server, an error will be returned. Use this API along with the `s2n_psk_selection_callback` callback + * to select a PSK identity. + * + * @param psk_list A pointer to the server's known PSK list used to compare for a matching PSK with the client. + * @param psk A pointer to the client's PSK object used to compare with the server's known PSK identities. + */ +S2N_API int s2n_offered_psk_list_choose_psk(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk); + +/** + * Callback function to select a PSK from a list of offered PSKs. + * Use this callback to implement custom PSK selection logic. The s2n-tls default PSK selection logic + * chooses the first matching PSK from the list of offered PSKs sent by the client. + * + * # Safety + * + * `context` is a void pointer and the caller is responsible for ensuring it is cast to the correct type. + * After the completion of this callback, the pointer to `psk_list` is invalid. + * + * @param conn A pointer to the s2n_connection object. + * @param context A pointer to a context for the caller to pass state to the callback, if needed. + * @param psk_list A pointer to the offered PSK list being read. + */ +typedef int (*s2n_psk_selection_callback)(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_list); + +/** + * Sets the callback to select the matching PSK. + * If this callback is not set s2n-tls uses a default PSK selection logic that selects the first matching + * server PSK. + * + * @param config A pointer to the s2n_config object. + * @param cb The function that should be called when the callback is triggered. + * @param context A pointer to a context for the caller to pass state to the callback, if needed. + */ +S2N_API int s2n_config_set_psk_selection_callback(struct s2n_config *config, s2n_psk_selection_callback cb, void *context); + +/** + * Get the number of bytes the connection has received. + * + * @param conn A pointer to the connection + * @returns return the number of bytes received by s2n-tls "on the wire" + */ +S2N_API extern uint64_t s2n_connection_get_wire_bytes_in(struct s2n_connection *conn); + +/** + * Get the number of bytes the connection has transmitted out. + * + * @param conn A pointer to the connection + * @returns return the number of bytes transmitted out by s2n-tls "on the wire" + */ +S2N_API extern uint64_t s2n_connection_get_wire_bytes_out(struct s2n_connection *conn); + +/** + * Access the protocol version supported by the client. + * + * @note The return value corresponds to the macros defined as S2N_SSLv2, + * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. + * + * @param conn A pointer to the connection + * @returns returns the highest protocol version supported by the client + */ +S2N_API extern int s2n_connection_get_client_protocol_version(struct s2n_connection *conn); + +/** + * Access the protocol version supported by the server. + * + * @note The return value corresponds to the macros defined as S2N_SSLv2, + * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. + * + * @param conn A pointer to the connection + * @returns Returns the highest protocol version supported by the server + */ +S2N_API extern int s2n_connection_get_server_protocol_version(struct s2n_connection *conn); + +/** + * Access the protocol version selected for the connection. + * + * @note The return value corresponds to the macros defined as S2N_SSLv2, + * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. + * + * @param conn A pointer to the connection + * @returns The protocol version actually negotiated by the handshake + */ +S2N_API extern int s2n_connection_get_actual_protocol_version(struct s2n_connection *conn); + +/** + * Access the client hello protocol version for the connection. + * + * @note The return value corresponds to the macros defined as S2N_SSLv2, + * S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12, and S2N_TLS13. + * + * @param conn A pointer to the connection + * @returns The protocol version used to send the initial client hello message. + */ +S2N_API extern int s2n_connection_get_client_hello_version(struct s2n_connection *conn); + +/** + * Access the protocol version from the header of the first record that contained the ClientHello message. + * + * @note This field has been deprecated and should not be confused with the client hello + * version. It is often set very low, usually to TLS1.0 for compatibility reasons, + * and should never be set higher than TLS1.2. Therefore this method should only be used + * for logging or fingerprinting. + * + * @param conn A pointer to the client hello struct + * @param out The protocol version in the record header containing the Client Hello. + */ +S2N_API extern int s2n_client_hello_get_legacy_record_version(struct s2n_client_hello *ch, uint8_t *out); + +/** + * Check if Client Auth was used for a connection. + * + * @param conn A pointer to the connection + * @returns 1 if the handshake completed and Client Auth was negotiated during then + * handshake. + */ +S2N_API extern int s2n_connection_client_cert_used(struct s2n_connection *conn); + +/** + * A function that provides a human readable string of the cipher suite that was chosen + * for a connection. + * + * @warning The string "TLS_NULL_WITH_NULL_NULL" is returned before the TLS handshake has been performed. + * This does not mean that the ciphersuite "TLS_NULL_WITH_NULL_NULL" will be used by the connection, + * it is merely being used as a placeholder. + * + * @note This function is only accurate after the TLS handshake. + * + * @param conn A pointer to the connection + * @returns A string indicating the cipher suite negotiated by s2n in OpenSSL format. + */ +S2N_API extern const char *s2n_connection_get_cipher(struct s2n_connection *conn); + +/** + * A metric to determine whether or not the server found a certificate that matched + * the client's SNI extension. + * + * S2N_SNI_NONE: Client did not send the SNI extension. + * S2N_SNI_EXACT_MATCH: Server had a certificate that matched the client's SNI extension. + * S2N_SNI_WILDCARD_MATCH: Server had a certificate with a domain name containing a wildcard character + * that could be matched to the client's SNI extension. + * S2N_SNI_NO_MATCH: Server did not have a certificate that could be matched to the client's + * SNI extension. + */ +typedef enum { + S2N_SNI_NONE = 1, + S2N_SNI_EXACT_MATCH, + S2N_SNI_WILDCARD_MATCH, + S2N_SNI_NO_MATCH, +} s2n_cert_sni_match; + +/** + * A function that provides insight into whether or not the server was able to send a certificate that + * partially or completely matched the client's SNI extension. + * + * @note This function can be used as a metric in a failed connection as long as the failure + * occurs after certificate selection. + * + * @param conn A pointer to the connection + * @param cert_match An enum indicating whether or not the server found a certificate + * that matched the client's SNI extension. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_connection_get_certificate_match(struct s2n_connection *conn, s2n_cert_sni_match *match_status); + +/** + * Provides access to the TLS master secret. + * + * This is a dangerous method and should not be used unless absolutely necessary. + * Mishandling the master secret can compromise both the current connection + * and any past or future connections that use the same master secret due to + * session resumption. + * + * This method is only supported for older TLS versions, and will report an S2N_ERR_INVALID_STATE + * usage error if called for a TLS1.3 connection. TLS1.3 includes a new key schedule + * that derives independent secrets from the master secret for specific purposes, + * such as separate traffic, session ticket, and exporter secrets. Using the master + * secret directly circumvents that security feature, reducing the security of + * the protocol. + * + * If you need cryptographic material tied to the current TLS session, consider + * `s2n_connection_tls_exporter` instead. Although s2n_connection_tls_exporter + * currently only supports TLS1.3, there is also an RFC that describes exporters + * for older TLS versions: https://datatracker.ietf.org/doc/html/rfc5705 + * Using the master secret as-is or defining your own exporter is dangerous. + * + * @param conn A pointer to the connection. + * @param secret_bytes Memory to copy the master secret into. The secret + * is always 48 bytes long. + * @param max_size The size of the memory available at `secret_bytes`. Must be + * at least 48 bytes. + * @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. `secret_bytes` + * will be set on success. + */ +S2N_API extern int s2n_connection_get_master_secret(const struct s2n_connection *conn, + uint8_t *secret_bytes, size_t max_size); + +/** + * Provides access to the TLS-Exporter functionality. + * + * See https://datatracker.ietf.org/doc/html/rfc5705 and https://www.rfc-editor.org/rfc/rfc8446. + * + * @note This is currently only available with TLS 1.3 connections which have finished a handshake. + * + * @param conn A pointer to the connection + * @returns A POSIX error signal. If an error was returned, the value contained in `output` should be considered invalid. + */ +S2N_API extern int s2n_connection_tls_exporter(struct s2n_connection *conn, + const uint8_t *label, uint32_t label_length, const uint8_t *context, uint32_t context_length, + uint8_t *output, uint32_t output_length); + +/** + * Returns the IANA value for the connection's negotiated cipher suite. + * + * The value is returned in the form of `first,second`, in order to closely match + * the values defined in the [IANA Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4). + * For example if the connection's negotiated cipher suite is `TLS_AES_128_GCM_SHA256`, + * which is registered as `0x13,0x01`, then `first = 0x13` and `second = 0x01`. + * + * This method will only succeed after the cipher suite has been negotiated with the peer. + * + * @param conn A pointer to the connection being read + * @param first A pointer to a single byte, which will be updated with the first byte in the registered IANA value. + * @param second A pointer to a single byte, which will be updated with the second byte in the registered IANA value. + * @returns A POSIX error signal. If an error was returned, the values contained in `first` and `second` should be considered invalid. + */ +S2N_API extern int s2n_connection_get_cipher_iana_value(struct s2n_connection *conn, uint8_t *first, uint8_t *second); + +/** + * Function to check if the cipher used by current connection is supported by the current + * cipher preferences. + * @param conn A pointer to the s2n connection + * @param version A string representing the security policy to check against. + * @returns 1 if the connection satisfies the cipher suite. 0 if the connection does not satisfy the cipher suite. -1 if there is an error. + */ +S2N_API extern int s2n_connection_is_valid_for_cipher_preferences(struct s2n_connection *conn, const char *version); + +/** + * Function to get the human readable elliptic curve name for the connection. + * + * @deprecated Use `s2n_connection_get_key_exchange_group` instead + * + * @param conn A pointer to the s2n connection + * @returns A string indicating the elliptic curve used during ECDHE key exchange. The string "NONE" is returned if no curve was used. + */ +S2N_API extern const char *s2n_connection_get_curve(struct s2n_connection *conn); + +/** + * Function to get the human readable KEM name for the connection. + * + * @deprecated This function was previously used to retrieve the negotiated PQ group in TLS 1.2. + * PQ key exchange in TLS1.2 was experimental and is now deprecated. Use s2n_connection_get_kem_group_name() + * to retrieve the PQ TLS 1.3 Group name. + * + * @param conn A pointer to the s2n connection + * @returns A human readable string for the KEM group. If there is no KEM configured returns "NONE" + */ +S2N_API extern const char *s2n_connection_get_kem_name(struct s2n_connection *conn); + +/** + * Function to get the human readable KEM group name for the connection. + * + * @note PQ key exchange will not occur if the connection is < TLS1.3 or the configured security + * policy has no KEM groups on it. It also will not occur if the peer does not support PQ key exchange. + * In these instances this function will return "NONE". + * + * @param conn A pointer to the s2n connection + * @returns A human readable string for the KEM group. Returns "NONE" if no PQ key exchange occurred. + */ +S2N_API extern const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn); + +/** + * Function to get the human readable key exchange group name for the connection, for example: + * `secp521r1` or `SecP256r1MLKEM768`. If an EC curve or KEM was not negotiated, S2N_FAILURE will be + * returned. + * + * @note This function replaces `s2n_connection_get_curve` and `s2n_connection_get_kem_group_name`, returning + * the named group regardless if a hybrid PQ group was negotiated or not. + * + * @param conn A pointer to the s2n connection + * @param group_name A pointer that will be set to point to a const char* containing the group name + * @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. `group_name` will be set on success. + */ +S2N_API extern int s2n_connection_get_key_exchange_group(struct s2n_connection *conn, const char **group_name); + +/** + * Function to get the alert that caused a connection to close. s2n-tls considers all + * TLS alerts fatal and shuts down a connection whenever one is received. + * + * @param conn A pointer to the s2n connection + * @returns The TLS alert code that caused a connection to be shut down + */ +S2N_API extern int s2n_connection_get_alert(struct s2n_connection *conn); + +/** + * Function to return the last TLS handshake type that was processed. The returned format is a human readable string. + * + * @param conn A pointer to the s2n connection + * @returns A human-readable handshake type name, e.g. "NEGOTIATED|FULL_HANDSHAKE|PERFECT_FORWARD_SECRECY" + */ +S2N_API extern const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn); + +/** + * Function to return the last TLS message that was processed. The returned format is a human readable string. + * @param conn A pointer to the s2n connection + * @returns The last message name in the TLS state machine, e.g. "SERVER_HELLO", "APPLICATION_DATA". + */ +S2N_API extern const char *s2n_connection_get_last_message_name(struct s2n_connection *conn); + +/** + * Opaque async private key operation handle + */ +struct s2n_async_pkey_op; + +/** + * Sets whether or not a connection should enforce strict signature validation during the + * `s2n_async_pkey_op_apply` call. + * + * `mode` can take the following values: + * - `S2N_ASYNC_PKEY_VALIDATION_FAST` - default behavior: s2n-tls will perform only the minimum validation required for safe use of the asyn pkey operation. + * - `S2N_ASYNC_PKEY_VALIDATION_STRICT` - in addition to the previous checks, s2n-tls will also ensure that the signature created as a result of the async private key sign operation matches the public key on the connection. + */ +typedef enum { + S2N_ASYNC_PKEY_VALIDATION_FAST, + S2N_ASYNC_PKEY_VALIDATION_STRICT +} s2n_async_pkey_validation_mode; + +/** + * The type of private key operation + */ +typedef enum { + S2N_ASYNC_DECRYPT, + S2N_ASYNC_SIGN +} s2n_async_pkey_op_type; + +/** + * Callback function for handling private key operations + * + * Invoked every time an operation requiring the private key is encountered + * during the handshake. + * + * # Safety + * * `op` is owned by the application and MUST be freed. + * + * @param conn Connection which triggered the callback + * @param op An opaque object representing the private key operation + */ +typedef int (*s2n_async_pkey_fn)(struct s2n_connection *conn, struct s2n_async_pkey_op *op); + +/** + * Sets up the callback to invoke when private key operations occur. + * + * @param config Config to set the callback + * @param fn The function that should be called for each private key operation + */ +S2N_API extern int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn); + +/** + * Performs a private key operation using the given private key. + * + * # Safety + * * Can only be called once. Any subsequent calls will produce a `S2N_ERR_T_USAGE` error. + * * Safe to call from inside s2n_async_pkey_fn + * * Safe to call from a different thread, as long as no other thread is operating on `op`. + * + * @param op An opaque object representing the private key operation + * @param key The private key used for the operation. It can be extracted from + * `conn` through the `s2n_connection_get_selected_cert` and `s2n_cert_chain_and_key_get_private_key` calls + */ +S2N_API extern int s2n_async_pkey_op_perform(struct s2n_async_pkey_op *op, s2n_cert_private_key *key); + +/** + * Finalizes a private key operation and unblocks the connection. + * + * # Safety + * * `conn` must match the connection that originally triggered the callback. + * * Must be called after the operation is performed. + * * Can only be called once. Any subsequent calls will produce a `S2N_ERR_T_USAGE` error. + * * Safe to call from inside s2n_async_pkey_fn + * * Safe to call from a different thread, as long as no other thread is operating on `op`. + * + * @param op An opaque object representing the private key operation + * @param conn The connection associated with the operation that should be unblocked + */ +S2N_API extern int s2n_async_pkey_op_apply(struct s2n_async_pkey_op *op, struct s2n_connection *conn); + +/** + * Frees the opaque structure representing a private key operation. + * + * # Safety + * * MUST be called for every operation passed to s2n_async_pkey_fn + * * Safe to call before or after the connection that created the operation is freed + * + * @param op An opaque object representing the private key operation + */ +S2N_API extern int s2n_async_pkey_op_free(struct s2n_async_pkey_op *op); + +/** + * Configures whether or not s2n-tls will perform potentially expensive validation of + * the results of a private key operation. + * + * @param config Config to set the validation mode for + * @param mode What level of validation to perform + */ +S2N_API extern int s2n_config_set_async_pkey_validation_mode(struct s2n_config *config, s2n_async_pkey_validation_mode mode); + +/** + * Returns the type of the private key operation. + * + * @param op An opaque object representing the private key operation + * @param type A pointer to be set to the type + */ +S2N_API extern int s2n_async_pkey_op_get_op_type(struct s2n_async_pkey_op *op, s2n_async_pkey_op_type *type); + +/** + * Returns the size of the input to the private key operation. + * + * @param op An opaque object representing the private key operation + * @param data_len A pointer to be set to the size + */ +S2N_API extern int s2n_async_pkey_op_get_input_size(struct s2n_async_pkey_op *op, uint32_t *data_len); + +/** + * Returns the input to the private key operation. + * + * When signing, the input is the digest to sign. + * When decrypting, the input is the data to decrypt. + * + * When signing with ML-DSA, the input is the "external mu" pre-hash value described in + * https://www.ietf.org/archive/id/draft-ietf-lamps-dilithium-certificates-09.html#appendix-D + * + * # Safety + * * `data` must be sufficiently large to contain the input. + * `s2n_async_pkey_op_get_input_size` can be called to determine how much memory is required. + * * s2n-tls does not take ownership of `data`. + * The application still owns the memory and must free it if necessary. + * + * @param op An opaque object representing the private key operation + * @param data A pointer to a buffer to copy the input into + * @param data_len The maximum size of the `data` buffer + */ +S2N_API extern int s2n_async_pkey_op_get_input(struct s2n_async_pkey_op *op, uint8_t *data, uint32_t data_len); + +/** + * Sets the output of the private key operation. + * + * # Safety + * * s2n-tls does not take ownership of `data`. + * The application still owns the memory and must free it if necessary. + * + * @param op An opaque object representing the private key operation + * @param data A pointer to a buffer containing the output + * @param data_len The size of the `data` buffer + */ +S2N_API extern int s2n_async_pkey_op_set_output(struct s2n_async_pkey_op *op, const uint8_t *data, uint32_t data_len); + +/** + * Callback function for handling key log events + * + * THIS SHOULD BE USED FOR DEBUGGING PURPOSES ONLY! + * + * Each log line is formatted with the + * [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) + * without a newline. + * + * # Safety + * + * * `ctx` MUST be cast into the same type of pointer that was originally created + * * `logline` bytes MUST be copied or discarded before this function returns + * + * @param ctx Context for the callback + * @param conn Connection for which the log line is being emitted + * @param logline Pointer to the log line data + * @param len Length of the log line data + */ +typedef int (*s2n_key_log_fn)(void *ctx, struct s2n_connection *conn, uint8_t *logline, size_t len); + +/** + * Sets a key logging callback on the provided config + * + * THIS SHOULD BE USED FOR DEBUGGING PURPOSES ONLY! + * + * Setting this function enables configurations to emit secrets in the + * [NSS Key Log Format](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) + * + * # Safety + * + * * `callback` MUST cast `ctx` into the same type of pointer that was originally created + * * `ctx` MUST live for at least as long as it is set on the config + * + * @param config Config to set the callback + * @param callback The function that should be called for each secret log entry + * @param ctx The context to be passed when the callback is called + */ +S2N_API extern int s2n_config_set_key_log_cb(struct s2n_config *config, s2n_key_log_fn callback, void *ctx); + +/** + * s2n_config_enable_cert_req_dss_legacy_compat adds a dss cert type in the server certificate request when being called. + * It only sends the dss cert type in the cert request but does not succeed the handshake if a dss cert is received. + * Please DO NOT call this api unless you know you actually need legacy DSS certificate type compatibility + * @param config Config to enable legacy DSS certificates for + */ +S2N_API extern int s2n_config_enable_cert_req_dss_legacy_compat(struct s2n_config *config); + +/** + * Sets the maximum bytes of early data the server will accept. + * + * The default maximum is 0. If the maximum is 0, the server rejects all early data requests. + * The config maximum can be overridden by the connection maximum or the maximum on an external pre-shared key. + * + * @param config A pointer to the config + * @param max_early_data_size The maximum early data that the server will accept + * @returns A POSIX error signal. If successful, the maximum early data size was updated. + */ +S2N_API int s2n_config_set_server_max_early_data_size(struct s2n_config *config, uint32_t max_early_data_size); + +/** + * Sets the maximum bytes of early data the server will accept. + * + * The default maximum is 0. If the maximum is 0, the server rejects all early data requests. + * The connection maximum can be overridden by the maximum on an external pre-shared key. + * + * @param conn A pointer to the connection + * @param max_early_data_size The maximum early data the server will accept + * @returns A POSIX error signal. If successful, the maximum early data size was updated. + */ +S2N_API int s2n_connection_set_server_max_early_data_size(struct s2n_connection *conn, uint32_t max_early_data_size); + +/** + * Sets the user context associated with early data on a server. + * + * This context is passed to the `s2n_early_data_cb` callback to help decide whether to accept or reject early data. + * + * Unlike most contexts, the early data context is a byte buffer instead of a void pointer. + * This is because we need to serialize the context into session tickets. + * + * This API is intended for use with session resumption, and will not affect pre-shared keys. + * + * @param conn A pointer to the connection + * @param context A pointer to the user context data. This data will be copied. + * @param context_size The size of the data to read from the `context` pointer. + * @returns A POSIX error signal. If successful, the context was updated. + */ +S2N_API int s2n_connection_set_server_early_data_context(struct s2n_connection *conn, const uint8_t *context, uint16_t context_size); + +/** + * Configures a particular pre-shared key to allow early data. + * + * `max_early_data_size` must be set to the maximum early data accepted by the server. + * + * In order to use early data, the cipher suite set on the pre-shared key must match the cipher suite + * ultimately negotiated by the TLS handshake. Additionally, the cipher suite must have the same + * hmac algorithm as the pre-shared key. + * + * @param psk A pointer to the pre-shared key, created with `s2n_external_psk_new`. + * @param max_early_data_size The maximum early data that can be sent or received using this key. + * @param cipher_suite_first_byte The first byte in the registered IANA value of the associated cipher suite. + * @param cipher_suite_second_byte The second byte in the registered IANA value of the associated cipher suite. + * @returns A POSIX error signal. If successful, `psk` was updated. + */ +S2N_API int s2n_psk_configure_early_data(struct s2n_psk *psk, uint32_t max_early_data_size, + uint8_t cipher_suite_first_byte, uint8_t cipher_suite_second_byte); + +/** + * Sets the optional `application_protocol` associated with the given pre-shared key. + * + * In order to use early data, the `application_protocol` set on the pre-shared key must match + * the `application_protocol` ultimately negotiated by the TLS handshake. + * + * @param psk A pointer to the pre-shared key, created with `s2n_external_psk_new`. + * @param application_protocol A pointer to the associated application protocol data. This data will be copied. + * @param size The size of the data to read from the `application_protocol` pointer. + * @returns A POSIX error signal. If successful, the application protocol was set. + */ +S2N_API int s2n_psk_set_application_protocol(struct s2n_psk *psk, const uint8_t *application_protocol, uint8_t size); + +/** + * Sets the optional user early data context associated with the given pre-shared key. + * + * The early data context is passed to the `s2n_early_data_cb` callback to help decide whether + * to accept or reject early data. + * + * @param psk A pointer to the pre-shared key, created with `s2n_external_psk_new`. + * @param context A pointer to the associated user context data. This data will be copied. + * @param size The size of the data to read from the `context` pointer. + * @returns A POSIX error signal. If successful, the context was set. + */ +S2N_API int s2n_psk_set_early_data_context(struct s2n_psk *psk, const uint8_t *context, uint16_t size); + +/** + * The status of early data on a connection. + * + * S2N_EARLY_DATA_STATUS_OK: Early data is in progress. + * S2N_EARLY_DATA_STATUS_NOT_REQUESTED: The client did not request early data, so none was sent or received. + * S2N_EARLY_DATA_STATUS_REJECTED: The client requested early data, but the server rejected the request. + * Early data may have been sent, but was not received. + * S2N_EARLY_DATA_STATUS_END: All early data was successfully sent and received. + */ +typedef enum { + S2N_EARLY_DATA_STATUS_OK, + S2N_EARLY_DATA_STATUS_NOT_REQUESTED, + S2N_EARLY_DATA_STATUS_REJECTED, + S2N_EARLY_DATA_STATUS_END, +} s2n_early_data_status_t; + +/** + * Reports the current state of early data for a connection. + * + * See `s2n_early_data_status_t` for all possible states. + * + * @param conn A pointer to the connection + * @param status A pointer which will be set to the current early data status + * @returns A POSIX error signal. + */ +S2N_API int s2n_connection_get_early_data_status(struct s2n_connection *conn, s2n_early_data_status_t *status); + +/** + * Reports the remaining size of the early data allowed by a connection. + * + * If early data was rejected or not requested, the remaining early data size is 0. + * Otherwise, the remaining early data size is the maximum early data allowed by the connection, + * minus the early data sent or received so far. + * + * @param conn A pointer to the connection + * @param allowed_early_data_size A pointer which will be set to the remaining early data currently allowed by `conn` + * @returns A POSIX error signal. + */ +S2N_API int s2n_connection_get_remaining_early_data_size(struct s2n_connection *conn, uint32_t *allowed_early_data_size); + +/** + * Reports the maximum size of the early data allowed by a connection. + * + * This is the maximum amount of early data that can ever be sent and received for a connection. + * It is not affected by the actual status of the early data, so can be non-zero even if early data + * is rejected or not requested. + * + * @param conn A pointer to the connection + * @param max_early_data_size A pointer which will be set to the maximum early data allowed by `conn` + * @returns A POSIX error signal. + */ +S2N_API int s2n_connection_get_max_early_data_size(struct s2n_connection *conn, uint32_t *max_early_data_size); + +/** + * Called by the client to begin negotiation and send early data. + * + * See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch14-early-data.md + * for usage and examples. DO NOT USE unless you have considered the security issues and + * implemented mitigation for anti-replay attacks. + * + * @param conn A pointer to the connection + * @param data A pointer to the early data to be sent + * @param data_len The size of the early data to send + * @param data_sent A pointer which will be set to the size of the early data sent + * @param blocked A pointer which will be set to the blocked status, as in `s2n_negotiate`. + * @returns A POSIX error signal. The error should be handled as in `s2n_negotiate`. + */ +S2N_API int s2n_send_early_data(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len, + ssize_t *data_sent, s2n_blocked_status *blocked); + +/** + * Called by the server to begin negotiation and accept any early data the client sends. + * + * See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide/topics/ch14-early-data.md + * for usage and examples. DO NOT USE unless you have considered the security issues and + * implemented mitigation for anti-replay attacks. + * + * @param conn A pointer to the connection + * @param data A pointer to a buffer to store the early data received + * @param max_data_len The size of the early data buffer + * @param data_received A pointer which will be set to the size of the early data received + * @param blocked A pointer which will be set to the blocked status, as in `s2n_negotiate`. + * @returns A POSIX error signal. The error should be handled as in `s2n_negotiate`. + */ +S2N_API int s2n_recv_early_data(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, + ssize_t *data_received, s2n_blocked_status *blocked); + +struct s2n_offered_early_data; + +/** + * A callback which can be implemented to accept or reject early data. + * + * This callback is triggered only after the server has determined early data is otherwise acceptable according + * to the TLS early data specification. Implementations therefore only need to cover application-specific checks, + * not the standard TLS early data validation. + * + * This callback can be synchronous or asynchronous. For asynchronous behavior, return success without + * calling `s2n_offered_early_data_reject` or `s2n_offered_early_data_accept`. `early_data` will + * still be a valid reference, and the connection will block until `s2n_offered_early_data_reject` or + * `s2n_offered_early_data_accept` is called. + * + * @param conn A pointer to the connection + * @param early_data A pointer which can be used to access information about the proposed early data + * and then accept or reject it. + * @returns A POSIX error signal. If unsuccessful, the connection will be closed with an error. + */ +typedef int (*s2n_early_data_cb)(struct s2n_connection *conn, struct s2n_offered_early_data *early_data); + +/** + * Set a callback to accept or reject early data. + * + * @param config A pointer to the connection config + * @param cb A pointer to the implementation of the callback. + * @returns A POSIX error signal. If successful, the callback was set. + */ +S2N_API int s2n_config_set_early_data_cb(struct s2n_config *config, s2n_early_data_cb cb); + +/** + * Get the length of the early data context set by the user. + * + * @param early_data A pointer to the early data information + * @param context_len The length of the user context + * @returns A POSIX error signal. + */ +S2N_API int s2n_offered_early_data_get_context_length(struct s2n_offered_early_data *early_data, uint16_t *context_len); + +/** + * Get the early data context set by the user. + * + * @param early_data A pointer to the early data information + * @param context A byte buffer to copy the user context into + * @param max_len The size of `context`. Must be >= to the result of `s2n_offered_early_data_get_context_length`. + * @returns A POSIX error signal. + */ +S2N_API int s2n_offered_early_data_get_context(struct s2n_offered_early_data *early_data, uint8_t *context, uint16_t max_len); + +/** + * Reject early data offered by the client. + * + * @param early_data A pointer to the early data information + * @returns A POSIX error signal. If success, the client's early data will be rejected. + */ +S2N_API int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_data); + +/** + * Accept early data offered by the client. + * + * @param early_data A pointer to the early data information + * @returns A POSIX error signal. If success, the client's early data will be accepted. + */ +S2N_API int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data); + +/** + * Retrieves the list of supported groups configured by the security policy associated with `config`. + * + * The retrieved list of groups will contain all of the supported groups for a security policy that are compatible + * with the build of s2n-tls. For instance, PQ kem groups that are not supported by the linked libcrypto will not + * be written. Otherwise, all of the supported groups configured for the security policy will be written. This API + * can be used with the s2n_client_hello_get_supported_groups() API as a means of comparing compatibility between + * a client and server. + * + * IANA values for each of the supported groups are written to the provided `groups` array, and `groups_count` is + * set to the number of written supported groups. + * + * `groups_count_max` should be set to the maximum capacity of the `groups` array. If `groups_count_max` is less + * than the number of supported groups configured by the security policy, this function will error. + * + * Note that this API retrieves only the groups from a security policy that are available to negotiate via the + * supported groups extension, and does not return TLS 1.2 PQ kem groups that are negotiated in the supported PQ + * kem parameters extension. + * + * @param config A pointer to the s2n_config object from which the supported groups will be retrieved. + * @param groups The array to populate with the supported groups. + * @param groups_count_max The maximum number of supported groups that can fit in the `groups` array. + * @param groups_count Set to the number of supported groups written to `groups`. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, + uint16_t *groups_count); + +/* Indicates which serialized connection version will be provided. The default value is + * S2N_SERIALIZED_CONN_NONE, which indicates the feature is off. + */ +typedef enum { + S2N_SERIALIZED_CONN_NONE = 0, + S2N_SERIALIZED_CONN_V1 = 1 +} s2n_serialization_version; + +/** + * Set what version to use when serializing connections + * + * A version is required to serialize connections. Versioning ensures that all features negotiated + * during the handshake will be available wherever the connection is deserialized. Applications may + * need to update this version to pick up new features, since versioning may disable newer TLS + * features to ensure compatibility. + * + * @param config A pointer to the config object. + * @param version The requested version. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version); + +/** + * Retrieves the length of the serialized connection from `s2n_connection_serialize()`. Should be + * used to allocate enough memory for the serialized connection buffer. + * + * @note The size of the serialized connection changes based on parameters negotiated in the TLS + * handshake. Do not expect the size to always remain the same. + * + * @param conn A pointer to the connection object. + * @param length Output parameter where the length will be written. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length); + +/** + * Serializes the s2n_connection into the provided buffer. + * + * This API takes an established s2n-tls connection object and "serializes" it + * into a transferable object to be sent off-box or to another process. This transferable object can + * then be "deserialized" using the `s2n_connection_deserialize` method to instantiate an s2n-tls + * connection object that can talk to the original peer with the same encryption keys. + * + * @warning This feature is dangerous because it provides cryptographic material from a TLS session + * in plaintext. The serialized blob also does not include a MAC or signature, so a modified blob + * will be deserialized and used as-is. Users MUST both encrypt and MAC the serialized connection to + * provide secrecy and integrity before storing or sending it off-box. + * + * @note You MUST have used `s2n_config_set_serialization_version()` to set a version on the + * s2n_config object associated with this connection before this connection began its TLS handshake. + * @note Call `s2n_connection_serialization_length` to retrieve the amount of memory needed for the + * buffer parameter. + * @note This API will error if the handshake is not yet complete. Additionally it will error if there + * is still application data in the IO buffers given that this data does not get serialized by s2n-tls. + * You can use `s2n_send` to drain the send buffer and `s2n_peek` + `s2n_recv` to drain the read buffer. + * Note that s2n-tls will buffer record fragments until the complete record is available. `s2n_peek` + * will _not_ report buffered record fragments. To avoid buffered record fragments, applications should + * continue to call s2n_recv until a non-blocked status is returned, after which point s2n_peek and s2n_recv + * can be used to drain the read buffer. + * @warning Due to the above read/buffering interaction, this API must not be used + * with recv_buffering (s2n_connection_set_recv_buffering), because there is no + * way to ensure that an s2n-tls connection hasn't buffered any record fragments. + * @note Serialization is unsupported for TLS 1.0 & SSLv3 connections. See: https://github.com/aws/s2n-tls/issues/5538. + * + * @param conn A pointer to the connection object. + * @param buffer A pointer to the buffer where the serialized connection will be written. + * @param buffer_length Maximum amount of data that can be written to the buffer param. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); + +/** + * Deserializes the provided buffer into the `s2n_connection` parameter. + * + * @warning s2n-tls does not check the integrity of the provided buffer. The serialized blob does + * not include a MAC or signature, so a modified buffer will be deserialized and used as-is. A + * corrupted buffer will cause a connection failure when attempting to resume sending or receiving + * encrypted data. To avoid this, callers MUST MAC and encrypt the serialized connection before + * storing or sending it off-box. Encrypt-then-MAC using a key not stored alongside the blob is + * the recommended approach. + * + * @warning Only a minimal amount of information about the original TLS connection is serialized. + * Therefore, after deserialization, the connection will behave like a new `s2n_connection` from the + * `s2n_connection_new()` call, except that it can read/write encrypted data from a peer. Any desired + * config-level or connection-level configuration will need to be re-applied to the deserialized connection. + * For this same reason none of the connection getters will return useful information about the + * original connection after deserialization. Any information about the original connection needs to + * be retrieved before serialization. + * + * @param conn A pointer to the connection object. Should be a new s2n_connection object. + * @param buffer A pointer to the buffer where the serialized connection will be read from. + * @param buffer_length Maximum amount of data that can be read from the buffer parameter. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); + +/* Load all acceptable certificate authorities from the currently configured trust store. + * + * The loaded certificate authorities will be advertised during the handshake. + * This can help your peer select a certificate if they have multiple certificate + * chains available. + * + * For now, s2n-tls only supports advertising certificate authorities to support + * client auth, so only servers will send the list of certificate authorities. + * + * To avoid configuration mistakes, certificate authorities cannot be loaded from + * a trust store that includes the default system certificates. That means that + * s2n_config_new_minimal or s2n_config_wipe_trust_store should be used. + * + * s2n-tls currently limits the total certificate authorities size to 10k bytes. + * This method will fail if the certificate authorities retrieved from the trust + * store exceed that limit. + * + * @param config A pointer to the s2n_config object. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API int s2n_config_set_cert_authorities_from_trust_store(struct s2n_config *config); + +#ifdef __cplusplus +} +#endif diff --git a/bin/common.c b/bin/common.c index 259f33b1d4b..2eb00fd33e9 100644 --- a/bin/common.c +++ b/bin/common.c @@ -1,578 +1,578 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "common.h" - -#include -#include -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif -#if !defined(_MSC_VER) -#include -#endif -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" -uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - -uint8_t default_ticket_key[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, - 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, - 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, - 0xb3, 0xe5 }; - -struct session_cache_entry session_cache[256]; - -static char dhparams[] = - "-----BEGIN DH PARAMETERS-----\n" - "MIIBCAKCAQEAy1+hVWCfNQoPB+NA733IVOONl8fCumiz9zdRRu1hzVa2yvGseUSq\n" - "Bbn6k0FQ7yMED6w5XWQKDC0z2m0FI/BPE3AjUfuPzEYGqTDf9zQZ2Lz4oAN90Sud\n" - "luOoEhYR99cEbCn0T4eBvEf9IUtczXUZ/wj7gzGbGG07dLfT+CmCRJxCjhrosenJ\n" - "gzucyS7jt1bobgU66JKkgMNm7hJY4/nhR5LWTCzZyzYQh2HM2Vk4K5ZqILpj/n0S\n" - "5JYTQ2PVhxP+Uu8+hICs/8VvM72DznjPZzufADipjC7CsQ4S6x/ecZluFtbb+ZTv\n" - "HI5CnYmkAwJ6+FSWGaZQDi8bgerFk9RWwwIBAg==\n" - "-----END DH PARAMETERS-----\n"; - -/* - * Since this is a server, and the mechanism for hostname verification is not defined for this use-case, - * allow any hostname through. If you are writing something with mutual auth and you have a scheme for verifying - * the client (e.g. a reverse DNS lookup), you would plug that in here. - */ -static uint8_t unsafe_verify_host_fn(const char *host_name, size_t host_name_len, void *data) -{ - return 1; -} - -int write_array_to_file(const char *path, uint8_t *data, size_t length) -{ - GUARD_EXIT_NULL(path); - GUARD_EXIT_NULL(data); - - FILE *file = fopen(path, "wb"); - if (!file) { - return S2N_FAILURE; - } - - if (fwrite(data, sizeof(char), length, file) != length) { - fclose(file); - return S2N_FAILURE; - } - fclose(file); - - return S2N_SUCCESS; -} - -int get_file_size(const char *path, size_t *length) -{ - GUARD_EXIT_NULL(path); - GUARD_EXIT_NULL(length); - - FILE *file = fopen(path, "rb"); - if (!file) { - return S2N_FAILURE; - } - - if (fseek(file, 0, SEEK_END) != 0) { - fclose(file); - return S2N_FAILURE; - } - - long file_length = ftell(file); - if (file_length < 0) { - fclose(file); - return S2N_FAILURE; - } - - *length = file_length; - fclose(file); - return S2N_SUCCESS; -} - -int load_file_to_array(const char *path, uint8_t *data, size_t max_length) -{ - GUARD_EXIT_NULL(path); - GUARD_EXIT_NULL(data); - - size_t file_size = 0; - if (get_file_size(path, &file_size) < 0 || file_size > max_length) { - return S2N_FAILURE; - } - - FILE *file = fopen(path, "rb"); - if (!file) { - return S2N_FAILURE; - } - - if (fread(data, sizeof(char), file_size, file) < file_size) { - fclose(file); - return S2N_FAILURE; - } - - fclose(file); - return S2N_SUCCESS; -} - -char *load_file_to_cstring(const char *path) -{ - FILE *pem_file = fopen(path, "rb"); - if (!pem_file) { - fprintf(stderr, "Failed to open file %s: '%s'\n", path, strerror(errno)); - return NULL; - } - - /* Make sure we can fit the pem into the output buffer */ - if (fseek(pem_file, 0, SEEK_END) < 0) { - fprintf(stderr, "Failed calling fseek: '%s'\n", strerror(errno)); - fclose(pem_file); - return NULL; - } - - const ssize_t pem_file_size = ftell(pem_file); - if (pem_file_size < 0) { - fprintf(stderr, "Failed calling ftell: '%s'\n", strerror(errno)); - fclose(pem_file); - return NULL; - } - - rewind(pem_file); - - char *pem_out = malloc(pem_file_size + 1); - if (pem_out == NULL) { - fprintf(stderr, "Failed allocating memory\n"); - fclose(pem_file); - return NULL; - } - - if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < (size_t) pem_file_size) { - fprintf(stderr, "Failed reading file: '%s'\n", strerror(errno)); - free(pem_out); - fclose(pem_file); - return NULL; - } - - pem_out[pem_file_size] = '\0'; - fclose(pem_file); - - return pem_out; -} - -int key_log_callback(void *file, struct s2n_connection *conn, uint8_t *logline, size_t len) -{ - if (fwrite(logline, 1, len, (FILE *) file) != len) { - return S2N_FAILURE; - } - - if (fprintf((FILE *) file, "\n") < 0) { - return S2N_FAILURE; - } - - return fflush((FILE *) file); -} - -/* An inverse map from an ascii value to a hexadecimal nibble value - * accounts for all possible char values, where 255 is invalid value */ -static const uint8_t hex_inverse[256] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 -}; - -int s2n_str_hex_to_bytes(const unsigned char *hex, uint8_t *out_bytes, uint32_t max_out_bytes_len) -{ - GUARD_EXIT_NULL(hex); - GUARD_EXIT_NULL(out_bytes); - - uint32_t len_with_spaces = strlen((const char *) hex); - size_t i = 0, j = 0; - while (j < len_with_spaces) { - if (hex[j] == ' ') { - j++; - continue; - } - - uint8_t high_nibble = hex_inverse[hex[j]]; - if (high_nibble == 255) { - fprintf(stderr, "Invalid HEX encountered\n"); - return S2N_FAILURE; - } - - uint8_t low_nibble = hex_inverse[hex[j + 1]]; - if (low_nibble == 255) { - fprintf(stderr, "Invalid HEX encountered\n"); - return S2N_FAILURE; - } - - if (max_out_bytes_len < i) { - fprintf(stderr, "Insufficient memory for bytes buffer, try increasing the allocation size\n"); - return S2N_FAILURE; - } - out_bytes[i] = high_nibble << 4 | low_nibble; - - i++; - j += 2; - } - - return S2N_SUCCESS; -} - -static int s2n_get_psk_hmac_alg(s2n_psk_hmac *psk_hmac, char *hmac_str) -{ - GUARD_EXIT_NULL(psk_hmac); - GUARD_EXIT_NULL(hmac_str); - - if (strcmp(hmac_str, "SHA256") == 0) { - *psk_hmac = S2N_PSK_HMAC_SHA256; - } else if (strcmp(hmac_str, "SHA384") == 0) { - *psk_hmac = S2N_PSK_HMAC_SHA384; - } else { - return S2N_FAILURE; - } - return S2N_SUCCESS; -} - -static int s2n_setup_external_psk(struct s2n_psk **psk, char *params) -{ - GUARD_EXIT_NULL(psk); - GUARD_EXIT_NULL(params); - - /* duplicate params as strtok will modify the input string */ - char *params_dup = malloc(strlen(params) + 1); - GUARD_EXIT_NULL(params_dup); - strcpy(params_dup, params); - - size_t token_idx = 0; - for (char *token = strtok(params_dup, ","); token != NULL; token = strtok(NULL, ","), token_idx++) { - switch (token_idx) { - case 0: - GUARD_EXIT(s2n_psk_set_identity(*psk, (const uint8_t *) token, strlen(token)), - "Error setting psk identity\n"); - break; - case 1: { - uint32_t max_secret_len = strlen(token) / 2; - uint8_t *secret = malloc(max_secret_len); - GUARD_EXIT_NULL(secret); - GUARD_EXIT(s2n_str_hex_to_bytes((const unsigned char *) token, secret, max_secret_len), "Error converting hex-encoded psk secret to bytes\n"); - GUARD_EXIT(s2n_psk_set_secret(*psk, secret, max_secret_len), "Error setting psk secret\n"); - free(secret); - } break; - case 2: { - s2n_psk_hmac psk_hmac_alg = 0; - GUARD_EXIT(s2n_get_psk_hmac_alg(&psk_hmac_alg, token), "Invalid psk hmac algorithm\n"); - GUARD_EXIT(s2n_psk_set_hmac(*psk, psk_hmac_alg), "Error setting psk hmac algorithm\n"); - } break; - default: - break; - } - } - - free(params_dup); - return S2N_SUCCESS; -} - -int s2n_setup_external_psk_list(struct s2n_connection *conn, char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH], size_t psk_list_len) -{ - GUARD_EXIT_NULL(conn); - GUARD_EXIT_NULL(psk_optarg_list); - - for (size_t i = 0; i < psk_list_len; i++) { - struct s2n_psk *psk = s2n_external_psk_new(); - GUARD_EXIT_NULL(psk); - GUARD_EXIT(s2n_setup_external_psk(&psk, psk_optarg_list[i]), "Error setting external PSK parameters\n"); - GUARD_EXIT(s2n_connection_append_psk(conn, psk), "Error appending psk to the connection\n"); - GUARD_EXIT(s2n_psk_free(&psk), "Error freeing psk\n"); - } - return S2N_SUCCESS; -} - -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"); - - GUARD_EXIT(s2n_config_set_cipher_preferences(config, cipher_prefs), "Error setting cipher prefs"); - - GUARD_EXIT(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache), "Error setting cache store callback"); - - GUARD_EXIT(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache), "Error setting cache retrieve callback"); - - GUARD_EXIT(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache), "Error setting cache retrieve callback"); - - if (conn_settings.enable_mfl) { - GUARD_EXIT(s2n_config_accept_max_fragment_length(config), "Error enabling TLS maximum fragment length extension in server"); - } - - if (s2n_config_set_verify_host_callback(config, unsafe_verify_host_fn, NULL)) { - print_s2n_error("Failure to set hostname verification callback"); - exit(1); - } - - if (conn_settings.session_ticket) { - GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); - } - - if (conn_settings.session_cache) { - GUARD_EXIT(s2n_config_set_session_cache_onoff(config, 1), "Error enabling session cache using id"); - } - - if (conn_settings.session_ticket || conn_settings.session_cache) { - /* Key initialization */ - uint8_t *st_key = NULL; - uint32_t st_key_length = 0; - - if (session_ticket_key_file_path) { - int fd = open(session_ticket_key_file_path, O_RDONLY); - GUARD_EXIT(fd, "Error opening session ticket key file"); - - struct stat st; - GUARD_EXIT(fstat(fd, &st), "Error fstat-ing session ticket key file"); - - st_key = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - POSIX_ENSURE(st_key != MAP_FAILED, S2N_ERR_MMAP); - - st_key_length = st.st_size; - - close(fd); - } else { - st_key = default_ticket_key; - st_key_length = sizeof(default_ticket_key); - } - - if (s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), st_key, st_key_length, 0) != 0) { - fprintf(stderr, "Error adding ticket key: '%s'\n", s2n_strerror(s2n_errno, "EN")); - exit(1); - } - } - return 0; -} - -int s2n_setup_server_connection(struct s2n_connection *conn, int fd, struct s2n_config *config, struct conn_settings settings) -{ - if (settings.deserialize_in) { - GUARD_RETURN(s2n_connection_deserialize_in(conn, settings.deserialize_in), "Failed to deserialize file"); - } - - if (settings.self_service_blinding) { - s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING); - } - - if (settings.mutual_auth) { - GUARD_RETURN(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED), "Error setting client auth type"); - - if (settings.ca_dir || settings.ca_file) { - GUARD_RETURN(s2n_config_set_verification_ca_location(config, settings.ca_file, settings.ca_dir), "Error adding verify location"); - } - - if (settings.insecure) { - GUARD_RETURN(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); - } - } - - GUARD_RETURN(s2n_connection_set_config(conn, config), "Error setting configuration"); - - if (settings.prefer_throughput) { - GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); - } - - if (settings.prefer_low_latency) { - GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); - } - - GUARD_RETURN(s2n_connection_set_fd(conn, fd), "Error setting file descriptor"); - - if (settings.use_corked_io) { - GUARD_RETURN(s2n_connection_use_corked_io(conn), "Error setting corked io"); - } - - GUARD_RETURN( - s2n_setup_external_psk_list(conn, settings.psk_optarg_list, settings.psk_list_len), - "Error setting external psk list"); - - GUARD_RETURN(early_data_recv(conn), "Error receiving early data"); - return 0; -} - -int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) -{ - struct session_cache_entry *cache = ctx; - - POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); - POSIX_ENSURE_INCLUSIVE_RANGE(1, value_size, MAX_VAL_LEN); - - uint8_t idx = ((const uint8_t *) key)[0]; - - memmove(cache[idx].key, key, key_size); - memmove(cache[idx].value, value, value_size); - - cache[idx].key_len = key_size; - cache[idx].value_len = value_size; - - return 0; -} - -int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) -{ - struct session_cache_entry *cache = ctx; - - POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); - - uint8_t idx = ((const uint8_t *) key)[0]; - - POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(*value_size >= cache[idx].value_len, S2N_ERR_INVALID_ARGUMENT); - - *value_size = cache[idx].value_len; - memmove(value, cache[idx].value, cache[idx].value_len); - - for (uint64_t i = 0; i < key_size; i++) { - printf("%02x", ((const uint8_t *) key)[i]); - } - printf("\n"); - - return 0; -} - -int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) -{ - struct session_cache_entry *cache = ctx; - - POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); - - uint8_t idx = ((const uint8_t *) key)[0]; - - if (cache[idx].key_len != 0) { - POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); - } - - cache[idx].key_len = 0; - cache[idx].value_len = 0; - - return 0; -} - -uint8_t unsafe_verify_host(const char *host_name, size_t host_name_len, void *data) -{ - struct verify_data *verify_data = (struct verify_data *) data; - - if (host_name_len > 2 && host_name[0] == '*' && host_name[1] == '.') { - char *suffix = strstr(verify_data->trusted_host, "."); - return (uint8_t) (strcasecmp(suffix, host_name + 1) == 0); - } - - /* If we're connecting to localhost, accept any values that represent localhost */ - bool is_localhost = (strcasecmp(verify_data->trusted_host, "localhost") == 0); - is_localhost |= (strcasecmp(verify_data->trusted_host, "127.0.0.1") == 0); - if (is_localhost) { - bool match = (strcasecmp(host_name, "localhost") == 0); - match |= (strcasecmp(host_name, "127.0.0.1") == 0); - /* Some of our older test certificates use odd common names */ - match |= (strcasecmp(host_name, "s2nTestServer") == 0); - return (uint8_t) match; - } - - return (uint8_t) (strcasecmp(host_name, verify_data->trusted_host) == 0); -} - -int wait_for_shutdown(struct s2n_connection *conn, int fd) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - while (s2n_shutdown(conn, &blocked) != S2N_SUCCESS) { - int errno_val = errno; - switch (s2n_error_get_type(s2n_errno)) { - case S2N_ERR_T_BLOCKED: - GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for shutdown"); - break; - case S2N_ERR_T_CLOSED: - /* We can't control the behavior of our peer. If the peer indicates end-of-stream - * without sending a close_notify, don't treat it as an error, but print a warning. - * - * This is common in our integration tests both because OpenSSL s_server - * never sends a close_notify (see https://github.com/openssl/openssl/issues/1806) - * and because we tend to kill processes rather than waiting for a graceful shutdown. - */ - fprintf(stdout, "Connection closed by peer\n"); - return S2N_SUCCESS; - case S2N_ERR_T_IO: - /* Again, we can't control the behavior of our peer, so just print a warning. - * Killing a process can result in its peer receiving a ECONNRESET. - */ - if (errno_val == ECONNRESET) { - fprintf(stdout, "Connection reset by peer\n"); - return S2N_SUCCESS; - } - /* Otherwise, IO errors are fatal and should be investigated */ - fprintf(stderr, "Unexpected IO error during shutdown: %s\n", strerror(errno_val)); - return S2N_FAILURE; - default: - return S2N_FAILURE; - } - } - return S2N_SUCCESS; -} - -int s2n_connection_serialize_out(struct s2n_connection *conn, const char *file_path) -{ - uint32_t serialize_length = 0; - GUARD_RETURN(s2n_connection_serialization_length(conn, &serialize_length), "Failed to get serialized connection length"); - uint8_t *mem = malloc(serialize_length); - GUARD_RETURN_NULL(mem); - GUARD_RETURN(s2n_connection_serialize(conn, mem, serialize_length), "Failed to get serialized connection"); - GUARD_RETURN(write_array_to_file(file_path, mem, serialize_length), "Failed to write serialized connection to file"); - free(mem); - - return 0; -} - -int s2n_connection_deserialize_in(struct s2n_connection *conn, const char *file_path) -{ - size_t deserialize_length = 0; - GUARD_RETURN(get_file_size(file_path, &deserialize_length), "Failed to read deserialize-in file size"); - ENSURE_RETURN(deserialize_length <= UINT32_MAX, "deserialize-in file size is too large"); - uint8_t *mem = malloc(deserialize_length); - GUARD_RETURN_NULL(mem); - GUARD_RETURN(load_file_to_array(file_path, mem, deserialize_length), "Failed to read deserialize-in file"); - GUARD_RETURN(s2n_connection_deserialize(conn, mem, (uint32_t) deserialize_length), "Failed to deserialize connection"); - free(mem); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "common.h" + +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" +uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + +uint8_t default_ticket_key[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, + 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, + 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, + 0xb3, 0xe5 }; + +struct session_cache_entry session_cache[256]; + +static char dhparams[] = + "-----BEGIN DH PARAMETERS-----\n" + "MIIBCAKCAQEAy1+hVWCfNQoPB+NA733IVOONl8fCumiz9zdRRu1hzVa2yvGseUSq\n" + "Bbn6k0FQ7yMED6w5XWQKDC0z2m0FI/BPE3AjUfuPzEYGqTDf9zQZ2Lz4oAN90Sud\n" + "luOoEhYR99cEbCn0T4eBvEf9IUtczXUZ/wj7gzGbGG07dLfT+CmCRJxCjhrosenJ\n" + "gzucyS7jt1bobgU66JKkgMNm7hJY4/nhR5LWTCzZyzYQh2HM2Vk4K5ZqILpj/n0S\n" + "5JYTQ2PVhxP+Uu8+hICs/8VvM72DznjPZzufADipjC7CsQ4S6x/ecZluFtbb+ZTv\n" + "HI5CnYmkAwJ6+FSWGaZQDi8bgerFk9RWwwIBAg==\n" + "-----END DH PARAMETERS-----\n"; + +/* + * Since this is a server, and the mechanism for hostname verification is not defined for this use-case, + * allow any hostname through. If you are writing something with mutual auth and you have a scheme for verifying + * the client (e.g. a reverse DNS lookup), you would plug that in here. + */ +static uint8_t unsafe_verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +int write_array_to_file(const char *path, uint8_t *data, size_t length) +{ + GUARD_EXIT_NULL(path); + GUARD_EXIT_NULL(data); + + FILE *file = fopen(path, "wb"); + if (!file) { + return S2N_FAILURE; + } + + if (fwrite(data, sizeof(char), length, file) != length) { + fclose(file); + return S2N_FAILURE; + } + fclose(file); + + return S2N_SUCCESS; +} + +int get_file_size(const char *path, size_t *length) +{ + GUARD_EXIT_NULL(path); + GUARD_EXIT_NULL(length); + + FILE *file = fopen(path, "rb"); + if (!file) { + return S2N_FAILURE; + } + + if (fseek(file, 0, SEEK_END) != 0) { + fclose(file); + return S2N_FAILURE; + } + + long file_length = ftell(file); + if (file_length < 0) { + fclose(file); + return S2N_FAILURE; + } + + *length = file_length; + fclose(file); + return S2N_SUCCESS; +} + +int load_file_to_array(const char *path, uint8_t *data, size_t max_length) +{ + GUARD_EXIT_NULL(path); + GUARD_EXIT_NULL(data); + + size_t file_size = 0; + if (get_file_size(path, &file_size) < 0 || file_size > max_length) { + return S2N_FAILURE; + } + + FILE *file = fopen(path, "rb"); + if (!file) { + return S2N_FAILURE; + } + + if (fread(data, sizeof(char), file_size, file) < file_size) { + fclose(file); + return S2N_FAILURE; + } + + fclose(file); + return S2N_SUCCESS; +} + +char *load_file_to_cstring(const char *path) +{ + FILE *pem_file = fopen(path, "rb"); + if (!pem_file) { + fprintf(stderr, "Failed to open file %s: '%s'\n", path, strerror(errno)); + return NULL; + } + + /* Make sure we can fit the pem into the output buffer */ + if (fseek(pem_file, 0, SEEK_END) < 0) { + fprintf(stderr, "Failed calling fseek: '%s'\n", strerror(errno)); + fclose(pem_file); + return NULL; + } + + const ssize_t pem_file_size = ftell(pem_file); + if (pem_file_size < 0) { + fprintf(stderr, "Failed calling ftell: '%s'\n", strerror(errno)); + fclose(pem_file); + return NULL; + } + + rewind(pem_file); + + char *pem_out = malloc(pem_file_size + 1); + if (pem_out == NULL) { + fprintf(stderr, "Failed allocating memory\n"); + fclose(pem_file); + return NULL; + } + + if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < (size_t) pem_file_size) { + fprintf(stderr, "Failed reading file: '%s'\n", strerror(errno)); + free(pem_out); + fclose(pem_file); + return NULL; + } + + pem_out[pem_file_size] = '\0'; + fclose(pem_file); + + return pem_out; +} + +int key_log_callback(void *file, struct s2n_connection *conn, uint8_t *logline, size_t len) +{ + if (fwrite(logline, 1, len, (FILE *) file) != len) { + return S2N_FAILURE; + } + + if (fprintf((FILE *) file, "\n") < 0) { + return S2N_FAILURE; + } + + return fflush((FILE *) file); +} + +/* An inverse map from an ascii value to a hexadecimal nibble value + * accounts for all possible char values, where 255 is invalid value */ +static const uint8_t hex_inverse[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 +}; + +int s2n_str_hex_to_bytes(const unsigned char *hex, uint8_t *out_bytes, uint32_t max_out_bytes_len) +{ + GUARD_EXIT_NULL(hex); + GUARD_EXIT_NULL(out_bytes); + + uint32_t len_with_spaces = strlen((const char *) hex); + size_t i = 0, j = 0; + while (j < len_with_spaces) { + if (hex[j] == ' ') { + j++; + continue; + } + + uint8_t high_nibble = hex_inverse[hex[j]]; + if (high_nibble == 255) { + fprintf(stderr, "Invalid HEX encountered\n"); + return S2N_FAILURE; + } + + uint8_t low_nibble = hex_inverse[hex[j + 1]]; + if (low_nibble == 255) { + fprintf(stderr, "Invalid HEX encountered\n"); + return S2N_FAILURE; + } + + if (max_out_bytes_len < i) { + fprintf(stderr, "Insufficient memory for bytes buffer, try increasing the allocation size\n"); + return S2N_FAILURE; + } + out_bytes[i] = high_nibble << 4 | low_nibble; + + i++; + j += 2; + } + + return S2N_SUCCESS; +} + +static int s2n_get_psk_hmac_alg(s2n_psk_hmac *psk_hmac, char *hmac_str) +{ + GUARD_EXIT_NULL(psk_hmac); + GUARD_EXIT_NULL(hmac_str); + + if (strcmp(hmac_str, "SHA256") == 0) { + *psk_hmac = S2N_PSK_HMAC_SHA256; + } else if (strcmp(hmac_str, "SHA384") == 0) { + *psk_hmac = S2N_PSK_HMAC_SHA384; + } else { + return S2N_FAILURE; + } + return S2N_SUCCESS; +} + +static int s2n_setup_external_psk(struct s2n_psk **psk, char *params) +{ + GUARD_EXIT_NULL(psk); + GUARD_EXIT_NULL(params); + + /* duplicate params as strtok will modify the input string */ + char *params_dup = malloc(strlen(params) + 1); + GUARD_EXIT_NULL(params_dup); + strcpy(params_dup, params); + + size_t token_idx = 0; + for (char *token = strtok(params_dup, ","); token != NULL; token = strtok(NULL, ","), token_idx++) { + switch (token_idx) { + case 0: + GUARD_EXIT(s2n_psk_set_identity(*psk, (const uint8_t *) token, strlen(token)), + "Error setting psk identity\n"); + break; + case 1: { + uint32_t max_secret_len = strlen(token) / 2; + uint8_t *secret = malloc(max_secret_len); + GUARD_EXIT_NULL(secret); + GUARD_EXIT(s2n_str_hex_to_bytes((const unsigned char *) token, secret, max_secret_len), "Error converting hex-encoded psk secret to bytes\n"); + GUARD_EXIT(s2n_psk_set_secret(*psk, secret, max_secret_len), "Error setting psk secret\n"); + free(secret); + } break; + case 2: { + s2n_psk_hmac psk_hmac_alg = 0; + GUARD_EXIT(s2n_get_psk_hmac_alg(&psk_hmac_alg, token), "Invalid psk hmac algorithm\n"); + GUARD_EXIT(s2n_psk_set_hmac(*psk, psk_hmac_alg), "Error setting psk hmac algorithm\n"); + } break; + default: + break; + } + } + + free(params_dup); + return S2N_SUCCESS; +} + +int s2n_setup_external_psk_list(struct s2n_connection *conn, char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH], size_t psk_list_len) +{ + GUARD_EXIT_NULL(conn); + GUARD_EXIT_NULL(psk_optarg_list); + + for (size_t i = 0; i < psk_list_len; i++) { + struct s2n_psk *psk = s2n_external_psk_new(); + GUARD_EXIT_NULL(psk); + GUARD_EXIT(s2n_setup_external_psk(&psk, psk_optarg_list[i]), "Error setting external PSK parameters\n"); + GUARD_EXIT(s2n_connection_append_psk(conn, psk), "Error appending psk to the connection\n"); + GUARD_EXIT(s2n_psk_free(&psk), "Error freeing psk\n"); + } + return S2N_SUCCESS; +} + +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"); + + GUARD_EXIT(s2n_config_set_cipher_preferences(config, cipher_prefs), "Error setting cipher prefs"); + + GUARD_EXIT(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache), "Error setting cache store callback"); + + GUARD_EXIT(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache), "Error setting cache retrieve callback"); + + GUARD_EXIT(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache), "Error setting cache retrieve callback"); + + if (conn_settings.enable_mfl) { + GUARD_EXIT(s2n_config_accept_max_fragment_length(config), "Error enabling TLS maximum fragment length extension in server"); + } + + if (s2n_config_set_verify_host_callback(config, unsafe_verify_host_fn, NULL)) { + print_s2n_error("Failure to set hostname verification callback"); + exit(1); + } + + if (conn_settings.session_ticket) { + GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); + } + + if (conn_settings.session_cache) { + GUARD_EXIT(s2n_config_set_session_cache_onoff(config, 1), "Error enabling session cache using id"); + } + + if (conn_settings.session_ticket || conn_settings.session_cache) { + /* Key initialization */ + uint8_t *st_key = NULL; + uint32_t st_key_length = 0; + + if (session_ticket_key_file_path) { + int fd = open(session_ticket_key_file_path, O_RDONLY); + GUARD_EXIT(fd, "Error opening session ticket key file"); + + struct stat st; + GUARD_EXIT(fstat(fd, &st), "Error fstat-ing session ticket key file"); + + st_key = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + POSIX_ENSURE(st_key != MAP_FAILED, S2N_ERR_MMAP); + + st_key_length = st.st_size; + + close(fd); + } else { + st_key = default_ticket_key; + st_key_length = sizeof(default_ticket_key); + } + + if (s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), st_key, st_key_length, 0) != 0) { + fprintf(stderr, "Error adding ticket key: '%s'\n", s2n_strerror(s2n_errno, "EN")); + exit(1); + } + } + return 0; +} + +int s2n_setup_server_connection(struct s2n_connection *conn, int fd, struct s2n_config *config, struct conn_settings settings) +{ + if (settings.deserialize_in) { + GUARD_RETURN(s2n_connection_deserialize_in(conn, settings.deserialize_in), "Failed to deserialize file"); + } + + if (settings.self_service_blinding) { + s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING); + } + + if (settings.mutual_auth) { + GUARD_RETURN(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED), "Error setting client auth type"); + + if (settings.ca_dir || settings.ca_file) { + GUARD_RETURN(s2n_config_set_verification_ca_location(config, settings.ca_file, settings.ca_dir), "Error adding verify location"); + } + + if (settings.insecure) { + GUARD_RETURN(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); + } + } + + GUARD_RETURN(s2n_connection_set_config(conn, config), "Error setting configuration"); + + if (settings.prefer_throughput) { + GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); + } + + if (settings.prefer_low_latency) { + GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); + } + + GUARD_RETURN(s2n_connection_set_fd(conn, fd), "Error setting file descriptor"); + + if (settings.use_corked_io) { + GUARD_RETURN(s2n_connection_use_corked_io(conn), "Error setting corked io"); + } + + GUARD_RETURN( + s2n_setup_external_psk_list(conn, settings.psk_optarg_list, settings.psk_list_len), + "Error setting external psk list"); + + GUARD_RETURN(early_data_recv(conn), "Error receiving early data"); + return 0; +} + +int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) +{ + struct session_cache_entry *cache = ctx; + + POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); + POSIX_ENSURE_INCLUSIVE_RANGE(1, value_size, MAX_VAL_LEN); + + uint8_t idx = ((const uint8_t *) key)[0]; + + memmove(cache[idx].key, key, key_size); + memmove(cache[idx].value, value, value_size); + + cache[idx].key_len = key_size; + cache[idx].value_len = value_size; + + return 0; +} + +int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) +{ + struct session_cache_entry *cache = ctx; + + POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); + + uint8_t idx = ((const uint8_t *) key)[0]; + + POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(*value_size >= cache[idx].value_len, S2N_ERR_INVALID_ARGUMENT); + + *value_size = cache[idx].value_len; + memmove(value, cache[idx].value, cache[idx].value_len); + + for (uint64_t i = 0; i < key_size; i++) { + printf("%02x", ((const uint8_t *) key)[i]); + } + printf("\n"); + + return 0; +} + +int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) +{ + struct session_cache_entry *cache = ctx; + + POSIX_ENSURE_INCLUSIVE_RANGE(1, key_size, MAX_KEY_LEN); + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].key_len != 0) { + POSIX_ENSURE(cache[idx].key_len == key_size, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(memcmp(cache[idx].key, key, key_size) == 0, S2N_ERR_INVALID_ARGUMENT); + } + + cache[idx].key_len = 0; + cache[idx].value_len = 0; + + return 0; +} + +uint8_t unsafe_verify_host(const char *host_name, size_t host_name_len, void *data) +{ + struct verify_data *verify_data = (struct verify_data *) data; + + if (host_name_len > 2 && host_name[0] == '*' && host_name[1] == '.') { + char *suffix = strstr(verify_data->trusted_host, "."); + return (uint8_t) (strcasecmp(suffix, host_name + 1) == 0); + } + + /* If we're connecting to localhost, accept any values that represent localhost */ + bool is_localhost = (strcasecmp(verify_data->trusted_host, "localhost") == 0); + is_localhost |= (strcasecmp(verify_data->trusted_host, "127.0.0.1") == 0); + if (is_localhost) { + bool match = (strcasecmp(host_name, "localhost") == 0); + match |= (strcasecmp(host_name, "127.0.0.1") == 0); + /* Some of our older test certificates use odd common names */ + match |= (strcasecmp(host_name, "s2nTestServer") == 0); + return (uint8_t) match; + } + + return (uint8_t) (strcasecmp(host_name, verify_data->trusted_host) == 0); +} + +int wait_for_shutdown(struct s2n_connection *conn, int fd) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (s2n_shutdown(conn, &blocked) != S2N_SUCCESS) { + int errno_val = errno; + switch (s2n_error_get_type(s2n_errno)) { + case S2N_ERR_T_BLOCKED: + GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for shutdown"); + break; + case S2N_ERR_T_CLOSED: + /* We can't control the behavior of our peer. If the peer indicates end-of-stream + * without sending a close_notify, don't treat it as an error, but print a warning. + * + * This is common in our integration tests both because OpenSSL s_server + * never sends a close_notify (see https://github.com/openssl/openssl/issues/1806) + * and because we tend to kill processes rather than waiting for a graceful shutdown. + */ + fprintf(stdout, "Connection closed by peer\n"); + return S2N_SUCCESS; + case S2N_ERR_T_IO: + /* Again, we can't control the behavior of our peer, so just print a warning. + * Killing a process can result in its peer receiving a ECONNRESET. + */ + if (errno_val == ECONNRESET) { + fprintf(stdout, "Connection reset by peer\n"); + return S2N_SUCCESS; + } + /* Otherwise, IO errors are fatal and should be investigated */ + fprintf(stderr, "Unexpected IO error during shutdown: %s\n", strerror(errno_val)); + return S2N_FAILURE; + default: + return S2N_FAILURE; + } + } + return S2N_SUCCESS; +} + +int s2n_connection_serialize_out(struct s2n_connection *conn, const char *file_path) +{ + uint32_t serialize_length = 0; + GUARD_RETURN(s2n_connection_serialization_length(conn, &serialize_length), "Failed to get serialized connection length"); + uint8_t *mem = malloc(serialize_length); + GUARD_RETURN_NULL(mem); + GUARD_RETURN(s2n_connection_serialize(conn, mem, serialize_length), "Failed to get serialized connection"); + GUARD_RETURN(write_array_to_file(file_path, mem, serialize_length), "Failed to write serialized connection to file"); + free(mem); + + return 0; +} + +int s2n_connection_deserialize_in(struct s2n_connection *conn, const char *file_path) +{ + size_t deserialize_length = 0; + GUARD_RETURN(get_file_size(file_path, &deserialize_length), "Failed to read deserialize-in file size"); + ENSURE_RETURN(deserialize_length <= UINT32_MAX, "deserialize-in file size is too large"); + uint8_t *mem = malloc(deserialize_length); + GUARD_RETURN_NULL(mem); + GUARD_RETURN(load_file_to_array(file_path, mem, deserialize_length), "Failed to read deserialize-in file"); + GUARD_RETURN(s2n_connection_deserialize(conn, mem, (uint32_t) deserialize_length), "Failed to deserialize connection"); + free(mem); + + return 0; +} diff --git a/bin/echo.c b/bin/echo.c index 961de036c57..39fc84b4e1f 100644 --- a/bin/echo.c +++ b/bin/echo.c @@ -1,482 +1,482 @@ -/* - * 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 -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "api/unstable/fingerprint.h" -#include "api/unstable/renegotiate.h" -#include "common.h" -#include "crypto/s2n_pkey.h" -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" - -#define STDIO_BUFSIZE 10240 - -const char *sig_alg_strs[] = { - [S2N_TLS_SIGNATURE_ANONYMOUS] = "None", - [S2N_TLS_SIGNATURE_RSA] = "RSA", - [S2N_TLS_SIGNATURE_ECDSA] = "ECDSA", - [S2N_TLS_SIGNATURE_RSA_PSS_RSAE] = "RSA-PSS-RSAE", - [S2N_TLS_SIGNATURE_RSA_PSS_PSS] = "RSA-PSS-PSS", - [S2N_TLS_SIGNATURE_MLDSA] = "MLDSA", -}; - -const char *sig_hash_strs[] = { - [S2N_TLS_HASH_NONE] = "None", - [S2N_TLS_HASH_MD5] = "MD5", - [S2N_TLS_HASH_SHA1] = "SHA1", - [S2N_TLS_HASH_SHA224] = "SHA224", - [S2N_TLS_HASH_SHA256] = "SHA256", - [S2N_TLS_HASH_SHA384] = "SHA384", - [S2N_TLS_HASH_SHA512] = "SHA512", - [S2N_TLS_HASH_MD5_SHA1] = "MD5_SHA1", -}; - -/* Careful: don't change this without updating the integration tests that check for it! - * Negative cases may stop working but not fail when this is updated. - */ -const char *pq_enabled_note = "PQ key exchange enabled"; - -void print_s2n_error(const char *app_error) -{ - fprintf(stderr, "[%d] %s: '%s' : '%s'\n", getpid(), app_error, s2n_strerror(s2n_errno, "EN"), - s2n_strerror_debug(s2n_errno, "EN")); -} - -/* Poll the given file descriptor for an event determined by the blocked status */ -int wait_for_event(int fd, s2n_blocked_status blocked) -{ - struct pollfd reader = { .fd = fd, .events = 0 }; - - switch (blocked) { - case S2N_NOT_BLOCKED: - return S2N_SUCCESS; - case S2N_BLOCKED_ON_READ: - reader.events |= POLLIN; - break; - case S2N_BLOCKED_ON_WRITE: - reader.events |= POLLOUT; - break; - case S2N_BLOCKED_ON_EARLY_DATA: - case S2N_BLOCKED_ON_APPLICATION_INPUT: - /* This case is not encountered by the s2nc/s2nd applications, - * but is detected for completeness */ - return S2N_SUCCESS; - } - - if (poll(&reader, 1, -1) < 0) { - fprintf(stderr, "Failed to poll connection: %s\n", strerror(errno)); - S2N_ERROR_PRESERVE_ERRNO(); - } - - return S2N_SUCCESS; -} - -int early_data_recv(struct s2n_connection *conn) -{ - uint32_t max_early_data_size = 0; - GUARD_RETURN(s2n_connection_get_max_early_data_size(conn, &max_early_data_size), "Error getting max early data size"); - if (max_early_data_size == 0) { - return S2N_SUCCESS; - } - - ssize_t total_data_recv = 0; - ssize_t data_recv = 0; - bool server_success = 0; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint8_t *early_data_received = (uint8_t *) malloc(max_early_data_size); - GUARD_EXIT_NULL(early_data_received); - - do { - server_success = (s2n_recv_early_data(conn, early_data_received + total_data_recv, - max_early_data_size - total_data_recv, &data_recv, &blocked) - >= S2N_SUCCESS); - total_data_recv += data_recv; - } while (!server_success); - - if (total_data_recv > 0) { - fprintf(stdout, "Early Data received: "); - for (ssize_t i = 0; i < total_data_recv; i++) { - fprintf(stdout, "%c", early_data_received[i]); - } - fprintf(stdout, "\n"); - } - - free(early_data_received); - - return S2N_SUCCESS; -} - -int early_data_send(struct s2n_connection *conn, uint8_t *data, uint32_t len) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - ssize_t total_data_sent = 0; - ssize_t data_sent = 0; - bool client_success = 0; - do { - client_success = (s2n_send_early_data(conn, data + total_data_sent, - len - total_data_sent, &data_sent, &blocked) - >= S2N_SUCCESS); - total_data_sent += data_sent; - } while (total_data_sent < len && !client_success); - - return S2N_SUCCESS; -} - -int print_connection_info(struct s2n_connection *conn) -{ - int client_hello_version = 0; - int client_protocol_version = 0; - int server_protocol_version = 0; - int actual_protocol_version = 0; - - if ((client_hello_version = s2n_connection_get_client_hello_version(conn)) < 0) { - fprintf(stderr, "Could not get client hello version\n"); - POSIX_BAIL(S2N_ERR_CLIENT_HELLO_VERSION); - } - if ((client_protocol_version = s2n_connection_get_client_protocol_version(conn)) < 0) { - fprintf(stderr, "Could not get client protocol version\n"); - POSIX_BAIL(S2N_ERR_CLIENT_PROTOCOL_VERSION); - } - if ((server_protocol_version = s2n_connection_get_server_protocol_version(conn)) < 0) { - fprintf(stderr, "Could not get server protocol version\n"); - POSIX_BAIL(S2N_ERR_SERVER_PROTOCOL_VERSION); - } - if ((actual_protocol_version = s2n_connection_get_actual_protocol_version(conn)) < 0) { - fprintf(stderr, "Could not get actual protocol version\n"); - POSIX_BAIL(S2N_ERR_ACTUAL_PROTOCOL_VERSION); - } - printf("CONNECTED:\n"); - printf("Handshake: %s\n", s2n_connection_get_handshake_type_name(conn)); - printf("Client hello version: %d\n", client_hello_version); - printf("Client protocol version: %d\n", client_protocol_version); - printf("Server protocol version: %d\n", server_protocol_version); - printf("Actual protocol version: %d\n", actual_protocol_version); - - if (s2n_get_server_name(conn)) { - printf("Server name: %s\n", s2n_get_server_name(conn)); - } - - if (s2n_get_application_protocol(conn)) { - printf("Application protocol: %s\n", s2n_get_application_protocol(conn)); - } - - const char *kem = s2n_connection_get_kem_name(conn); - if (strcmp(kem, "NONE") != 0) { - printf("Legacy TLS1.2 KEM: %s (%s, but the use of PQ key exchange with " - "TLS1.2 was experimental and is neither stable nor on a path to " - "standardization. The TLS1.3 version should be used instead: " - "https://aws.github.io/s2n-tls/usage-guide/ch15-post-quantum.html)\n", - kem, pq_enabled_note); - } - - const char *kem_group = s2n_connection_get_kem_group_name(conn); - if (strcmp(kem_group, "NONE") != 0) { - printf("KEM Group: %s (%s)\n", kem_group, pq_enabled_note); - } else { - printf("Curve: %s\n", s2n_connection_get_curve(conn)); - } - - uint32_t length = 0; - const uint8_t *status = s2n_connection_get_ocsp_response(conn, &length); - if (status && length > 0) { - printf("OCSP response received, length %u\n", length); - } - - printf("Cipher negotiated: %s\n", s2n_connection_get_cipher(conn)); - - s2n_tls_signature_algorithm server_sig_alg = 0, client_sig_alg = 0; - s2n_tls_hash_algorithm server_sig_hash = 0, client_sig_hash = 0; - GUARD_EXIT(s2n_connection_get_selected_signature_algorithm(conn, &server_sig_alg), - "Error getting server signature algorithm"); - GUARD_EXIT(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &client_sig_alg), - "Error getting client signature algorithm"); - GUARD_EXIT(s2n_connection_get_selected_digest_algorithm(conn, &server_sig_hash), - "Error getting server signature hash algorithm"); - GUARD_EXIT(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &client_sig_hash), - "Error getting client signature hash algorithm"); - printf("Server signature negotiated: %s+%s\n", sig_alg_strs[server_sig_alg], sig_hash_strs[server_sig_hash]); - if (client_sig_alg != S2N_TLS_SIGNATURE_ANONYMOUS) { - printf("Client signature negotiated: %s+%s\n", sig_alg_strs[client_sig_alg], sig_hash_strs[client_sig_hash]); - } - - bool session_resumed = s2n_connection_is_session_resumed(conn); - if (session_resumed) { - printf("Resumed session\n"); - } - - uint16_t identity_length = 0; - GUARD_EXIT(s2n_connection_get_negotiated_psk_identity_length(conn, &identity_length), "Error getting negotiated psk identity length from the connection\n"); - if (identity_length != 0 && !session_resumed) { - uint8_t *identity = (uint8_t *) malloc(identity_length); - GUARD_EXIT_NULL(identity); - GUARD_EXIT(s2n_connection_get_negotiated_psk_identity(conn, identity, identity_length), "Error getting negotiated psk identity from the connection\n"); - printf("Negotiated PSK identity: %.*s\n", identity_length, identity); - free(identity); - } - - s2n_early_data_status_t early_data_status = (s2n_early_data_status_t) 0; - GUARD_EXIT(s2n_connection_get_early_data_status(conn, &early_data_status), "Error getting early data status"); - const char *status_str = NULL; - switch (early_data_status) { - case S2N_EARLY_DATA_STATUS_OK: - status_str = "IN PROGRESS"; - break; - case S2N_EARLY_DATA_STATUS_NOT_REQUESTED: - status_str = "NOT REQUESTED"; - break; - case S2N_EARLY_DATA_STATUS_REJECTED: - status_str = "REJECTED"; - break; - case S2N_EARLY_DATA_STATUS_END: - status_str = "ACCEPTED"; - break; - } - GUARD_EXIT_NULL(status_str); - printf("Early Data status: %s\n", status_str); - - struct s2n_client_hello *ch = s2n_connection_get_client_hello(conn); - if (ch && client_hello_version > S2N_SSLv2) { - uint8_t ja3[16] = { 0 }; - uint32_t ja3_size = 0, str_size = 0; - GUARD_EXIT(s2n_client_hello_get_fingerprint_hash(ch, S2N_FINGERPRINT_JA3, - sizeof(ja3), ja3, &ja3_size, &str_size), - "Error calculating JA3"); - printf("JA3: "); - for (size_t i = 0; i < ja3_size; i++) { - printf("%02x", ja3[i]); - } - printf("\n"); - } - - printf("Wire bytes in: %" PRIu64 "\n", s2n_connection_get_wire_bytes_in(conn)); - printf("Wire bytes out: %" PRIu64 "\n", s2n_connection_get_wire_bytes_out(conn)); - - return 0; -} - -int negotiate(struct s2n_connection *conn, int fd) -{ - s2n_blocked_status blocked; - while (s2n_negotiate(conn, &blocked) != 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")); - if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { - fprintf(stderr, "Alert: %d\n", - s2n_connection_get_alert(conn)); - } - S2N_ERROR_PRESERVE_ERRNO(); - } - - if (wait_for_event(fd, blocked) != S2N_SUCCESS) { - S2N_ERROR_PRESERVE_ERRNO(); - } - } - - print_connection_info(conn); - - printf("s2n is ready\n"); - return 0; -} - -int renegotiate(struct s2n_connection *conn, int fd) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint8_t buffer[STDIO_BUFSIZE] = { 0 }; - ssize_t data_read = 0; - - GUARD_RETURN(s2n_renegotiate_wipe(conn), "Unable to prepare connection for renegotiate"); - GUARD_RETURN(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); - - fprintf(stdout, "RENEGOTIATE\n"); - fflush(stdout); - - while (s2n_renegotiate(conn, buffer, sizeof(buffer), &data_read, &blocked) != S2N_SUCCESS) { - uint8_t *data_ptr = buffer; - while (data_read > 0) { - ssize_t data_written = write(STDOUT_FILENO, data_ptr, data_read); - GUARD_RETURN(data_written, "Error writing to stdout\n"); - data_read -= data_written; - data_ptr += data_written; - } - - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Failed to renegotiate: '%s'. %s\n", s2n_strerror(s2n_errno, NULL), - s2n_strerror_debug(s2n_errno, NULL)); - if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { - fprintf(stderr, "Alert: %d\n", s2n_connection_get_alert(conn)); - } - return S2N_FAILURE; - } - - GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for renegotiate"); - } - - print_connection_info(conn); - printf("s2n is ready, again\n"); - return S2N_SUCCESS; -} - -void send_data(struct s2n_connection *conn, int sockfd, const char *data, uint64_t len, s2n_blocked_status *blocked) -{ - uint64_t bytes_remaining = len; - const char *data_ptr = data; - do { - ssize_t send_len = MIN(bytes_remaining, SSIZE_MAX); - ssize_t bytes_written = s2n_send(conn, data_ptr, send_len, blocked); - if (bytes_written < 0) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Error writing to connection: '%s'\n", - s2n_strerror(s2n_errno, "EN")); - exit(1); - } - - GUARD_EXIT(wait_for_event(sockfd, *blocked), "Unable to send data"); - continue; - } - - bytes_remaining -= bytes_written; - data_ptr += bytes_written; - - } while (bytes_remaining > 0); -} - -int echo(struct s2n_connection *conn, int sockfd, bool *stop_echo) -{ - struct pollfd readers[2]; - - readers[0].fd = sockfd; - readers[0].events = POLLIN; - readers[1].fd = STDIN_FILENO; - readers[1].events = POLLIN; - - /* Reset errno so that we can't inherit the errno == EINTR exit condition. */ - errno = 0; - - /* Act as a simple proxy between stdin and the SSL connection */ - int p = 0; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - do { - /* echo will send and receive Application Data back and forth between - * client and server, until stop_echo is true or stdin EOF is reached. */ - while (!(*stop_echo) && (p = poll(readers, 2, -1)) > 0) { - char buffer[STDIO_BUFSIZE]; - ssize_t bytes_read = 0; - - if (readers[0].revents & POLLIN) { - s2n_errno = S2N_ERR_T_OK; - bytes_read = s2n_recv(conn, buffer, STDIO_BUFSIZE, &blocked); - if (bytes_read == 0) { - return 0; - } - if (bytes_read < 0) { - switch (s2n_error_get_type(s2n_errno)) { - case S2N_ERR_T_BLOCKED: - /* Wait until poll tells us data is ready */ - continue; - case S2N_ERR_T_ALERT: - fprintf(stderr, "Received alert: %d\n", s2n_connection_get_alert(conn)); - break; - default: - fprintf(stderr, "Error reading from connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); - break; - } - exit(1); - } - - char *buf_ptr = buffer; - do { - ssize_t bytes_written = write(STDOUT_FILENO, buf_ptr, bytes_read); - if (bytes_written < 0) { - fprintf(stderr, "Error writing to stdout\n"); - exit(1); - } - - bytes_read -= bytes_written; - buf_ptr += bytes_written; - } while (bytes_read > 0); - } - - if (readers[1].revents & POLLIN) { - size_t bytes_available = 0; - - if (ioctl(STDIN_FILENO, FIONREAD, &bytes_available) < 0) { - bytes_available = 1; - } - - do { - /* We can only read as much data as we have space for. So it may - * take a couple loops to empty stdin. */ - size_t bytes_to_read = bytes_available; - if (bytes_available > sizeof(buffer)) { - bytes_to_read = sizeof(buffer); - } - - bytes_read = read(STDIN_FILENO, buffer, bytes_to_read); - if (bytes_read < 0 && errno != EINTR) { - fprintf(stderr, "Error reading from stdin\n"); - exit(1); - } - if (bytes_read == 0) { - fprintf(stderr, "Exiting on stdin EOF\n"); - return 0; - } - bytes_available -= bytes_read; - - /* We may not be able to write all the data we read in one shot, so - * keep sending until we have cleared our buffer. */ - send_data(conn, sockfd, buffer, bytes_read, &blocked); - - } while (bytes_available || blocked); - } - - if (readers[1].revents & POLLHUP) { - /* The stdin pipe hanged up, and we've handled all read from it above */ - return 0; - } - - if (readers[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { - fprintf(stderr, "Error polling from socket: err=%d hup=%d nval=%d\n", - (readers[0].revents & POLLERR) ? 1 : 0, - (readers[0].revents & POLLHUP) ? 1 : 0, - (readers[0].revents & POLLNVAL) ? 1 : 0); - POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); - } - - if (readers[1].revents & (POLLERR | POLLNVAL)) { - fprintf(stderr, "Error polling from socket: err=%d nval=%d\n", - (readers[1].revents & POLLERR) ? 1 : 0, - (readers[1].revents & POLLNVAL) ? 1 : 0); - POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); - } - } - } while (p < 0 && errno == EINTR); - - return 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 +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "api/unstable/fingerprint.h" +#include "api/unstable/renegotiate.h" +#include "common.h" +#include "crypto/s2n_pkey.h" +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" + +#define STDIO_BUFSIZE 10240 + +const char *sig_alg_strs[] = { + [S2N_TLS_SIGNATURE_ANONYMOUS] = "None", + [S2N_TLS_SIGNATURE_RSA] = "RSA", + [S2N_TLS_SIGNATURE_ECDSA] = "ECDSA", + [S2N_TLS_SIGNATURE_RSA_PSS_RSAE] = "RSA-PSS-RSAE", + [S2N_TLS_SIGNATURE_RSA_PSS_PSS] = "RSA-PSS-PSS", + [S2N_TLS_SIGNATURE_MLDSA] = "MLDSA", +}; + +const char *sig_hash_strs[] = { + [S2N_TLS_HASH_NONE] = "None", + [S2N_TLS_HASH_MD5] = "MD5", + [S2N_TLS_HASH_SHA1] = "SHA1", + [S2N_TLS_HASH_SHA224] = "SHA224", + [S2N_TLS_HASH_SHA256] = "SHA256", + [S2N_TLS_HASH_SHA384] = "SHA384", + [S2N_TLS_HASH_SHA512] = "SHA512", + [S2N_TLS_HASH_MD5_SHA1] = "MD5_SHA1", +}; + +/* Careful: don't change this without updating the integration tests that check for it! + * Negative cases may stop working but not fail when this is updated. + */ +const char *pq_enabled_note = "PQ key exchange enabled"; + +void print_s2n_error(const char *app_error) +{ + fprintf(stderr, "[%d] %s: '%s' : '%s'\n", getpid(), app_error, s2n_strerror(s2n_errno, "EN"), + s2n_strerror_debug(s2n_errno, "EN")); +} + +/* Poll the given file descriptor for an event determined by the blocked status */ +int wait_for_event(int fd, s2n_blocked_status blocked) +{ + struct pollfd reader = { .fd = fd, .events = 0 }; + + switch (blocked) { + case S2N_NOT_BLOCKED: + return S2N_SUCCESS; + case S2N_BLOCKED_ON_READ: + reader.events |= POLLIN; + break; + case S2N_BLOCKED_ON_WRITE: + reader.events |= POLLOUT; + break; + case S2N_BLOCKED_ON_EARLY_DATA: + case S2N_BLOCKED_ON_APPLICATION_INPUT: + /* This case is not encountered by the s2nc/s2nd applications, + * but is detected for completeness */ + return S2N_SUCCESS; + } + + if (poll(&reader, 1, -1) < 0) { + fprintf(stderr, "Failed to poll connection: %s\n", strerror(errno)); + S2N_ERROR_PRESERVE_ERRNO(); + } + + return S2N_SUCCESS; +} + +int early_data_recv(struct s2n_connection *conn) +{ + uint32_t max_early_data_size = 0; + GUARD_RETURN(s2n_connection_get_max_early_data_size(conn, &max_early_data_size), "Error getting max early data size"); + if (max_early_data_size == 0) { + return S2N_SUCCESS; + } + + ssize_t total_data_recv = 0; + ssize_t data_recv = 0; + bool server_success = 0; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t *early_data_received = (uint8_t *) malloc(max_early_data_size); + GUARD_EXIT_NULL(early_data_received); + + do { + server_success = (s2n_recv_early_data(conn, early_data_received + total_data_recv, + max_early_data_size - total_data_recv, &data_recv, &blocked) + >= S2N_SUCCESS); + total_data_recv += data_recv; + } while (!server_success); + + if (total_data_recv > 0) { + fprintf(stdout, "Early Data received: "); + for (ssize_t i = 0; i < total_data_recv; i++) { + fprintf(stdout, "%c", early_data_received[i]); + } + fprintf(stdout, "\n"); + } + + free(early_data_received); + + return S2N_SUCCESS; +} + +int early_data_send(struct s2n_connection *conn, uint8_t *data, uint32_t len) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t total_data_sent = 0; + ssize_t data_sent = 0; + bool client_success = 0; + do { + client_success = (s2n_send_early_data(conn, data + total_data_sent, + len - total_data_sent, &data_sent, &blocked) + >= S2N_SUCCESS); + total_data_sent += data_sent; + } while (total_data_sent < len && !client_success); + + return S2N_SUCCESS; +} + +int print_connection_info(struct s2n_connection *conn) +{ + int client_hello_version = 0; + int client_protocol_version = 0; + int server_protocol_version = 0; + int actual_protocol_version = 0; + + if ((client_hello_version = s2n_connection_get_client_hello_version(conn)) < 0) { + fprintf(stderr, "Could not get client hello version\n"); + POSIX_BAIL(S2N_ERR_CLIENT_HELLO_VERSION); + } + if ((client_protocol_version = s2n_connection_get_client_protocol_version(conn)) < 0) { + fprintf(stderr, "Could not get client protocol version\n"); + POSIX_BAIL(S2N_ERR_CLIENT_PROTOCOL_VERSION); + } + if ((server_protocol_version = s2n_connection_get_server_protocol_version(conn)) < 0) { + fprintf(stderr, "Could not get server protocol version\n"); + POSIX_BAIL(S2N_ERR_SERVER_PROTOCOL_VERSION); + } + if ((actual_protocol_version = s2n_connection_get_actual_protocol_version(conn)) < 0) { + fprintf(stderr, "Could not get actual protocol version\n"); + POSIX_BAIL(S2N_ERR_ACTUAL_PROTOCOL_VERSION); + } + printf("CONNECTED:\n"); + printf("Handshake: %s\n", s2n_connection_get_handshake_type_name(conn)); + printf("Client hello version: %d\n", client_hello_version); + printf("Client protocol version: %d\n", client_protocol_version); + printf("Server protocol version: %d\n", server_protocol_version); + printf("Actual protocol version: %d\n", actual_protocol_version); + + if (s2n_get_server_name(conn)) { + printf("Server name: %s\n", s2n_get_server_name(conn)); + } + + if (s2n_get_application_protocol(conn)) { + printf("Application protocol: %s\n", s2n_get_application_protocol(conn)); + } + + const char *kem = s2n_connection_get_kem_name(conn); + if (strcmp(kem, "NONE") != 0) { + printf("Legacy TLS1.2 KEM: %s (%s, but the use of PQ key exchange with " + "TLS1.2 was experimental and is neither stable nor on a path to " + "standardization. The TLS1.3 version should be used instead: " + "https://aws.github.io/s2n-tls/usage-guide/ch15-post-quantum.html)\n", + kem, pq_enabled_note); + } + + const char *kem_group = s2n_connection_get_kem_group_name(conn); + if (strcmp(kem_group, "NONE") != 0) { + printf("KEM Group: %s (%s)\n", kem_group, pq_enabled_note); + } else { + printf("Curve: %s\n", s2n_connection_get_curve(conn)); + } + + uint32_t length = 0; + const uint8_t *status = s2n_connection_get_ocsp_response(conn, &length); + if (status && length > 0) { + printf("OCSP response received, length %u\n", length); + } + + printf("Cipher negotiated: %s\n", s2n_connection_get_cipher(conn)); + + s2n_tls_signature_algorithm server_sig_alg = 0, client_sig_alg = 0; + s2n_tls_hash_algorithm server_sig_hash = 0, client_sig_hash = 0; + GUARD_EXIT(s2n_connection_get_selected_signature_algorithm(conn, &server_sig_alg), + "Error getting server signature algorithm"); + GUARD_EXIT(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &client_sig_alg), + "Error getting client signature algorithm"); + GUARD_EXIT(s2n_connection_get_selected_digest_algorithm(conn, &server_sig_hash), + "Error getting server signature hash algorithm"); + GUARD_EXIT(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &client_sig_hash), + "Error getting client signature hash algorithm"); + printf("Server signature negotiated: %s+%s\n", sig_alg_strs[server_sig_alg], sig_hash_strs[server_sig_hash]); + if (client_sig_alg != S2N_TLS_SIGNATURE_ANONYMOUS) { + printf("Client signature negotiated: %s+%s\n", sig_alg_strs[client_sig_alg], sig_hash_strs[client_sig_hash]); + } + + bool session_resumed = s2n_connection_is_session_resumed(conn); + if (session_resumed) { + printf("Resumed session\n"); + } + + uint16_t identity_length = 0; + GUARD_EXIT(s2n_connection_get_negotiated_psk_identity_length(conn, &identity_length), "Error getting negotiated psk identity length from the connection\n"); + if (identity_length != 0 && !session_resumed) { + uint8_t *identity = (uint8_t *) malloc(identity_length); + GUARD_EXIT_NULL(identity); + GUARD_EXIT(s2n_connection_get_negotiated_psk_identity(conn, identity, identity_length), "Error getting negotiated psk identity from the connection\n"); + printf("Negotiated PSK identity: %.*s\n", identity_length, identity); + free(identity); + } + + s2n_early_data_status_t early_data_status = (s2n_early_data_status_t) 0; + GUARD_EXIT(s2n_connection_get_early_data_status(conn, &early_data_status), "Error getting early data status"); + const char *status_str = NULL; + switch (early_data_status) { + case S2N_EARLY_DATA_STATUS_OK: + status_str = "IN PROGRESS"; + break; + case S2N_EARLY_DATA_STATUS_NOT_REQUESTED: + status_str = "NOT REQUESTED"; + break; + case S2N_EARLY_DATA_STATUS_REJECTED: + status_str = "REJECTED"; + break; + case S2N_EARLY_DATA_STATUS_END: + status_str = "ACCEPTED"; + break; + } + GUARD_EXIT_NULL(status_str); + printf("Early Data status: %s\n", status_str); + + struct s2n_client_hello *ch = s2n_connection_get_client_hello(conn); + if (ch && client_hello_version > S2N_SSLv2) { + uint8_t ja3[16] = { 0 }; + uint32_t ja3_size = 0, str_size = 0; + GUARD_EXIT(s2n_client_hello_get_fingerprint_hash(ch, S2N_FINGERPRINT_JA3, + sizeof(ja3), ja3, &ja3_size, &str_size), + "Error calculating JA3"); + printf("JA3: "); + for (size_t i = 0; i < ja3_size; i++) { + printf("%02x", ja3[i]); + } + printf("\n"); + } + + printf("Wire bytes in: %" PRIu64 "\n", s2n_connection_get_wire_bytes_in(conn)); + printf("Wire bytes out: %" PRIu64 "\n", s2n_connection_get_wire_bytes_out(conn)); + + return 0; +} + +int negotiate(struct s2n_connection *conn, int fd) +{ + s2n_blocked_status blocked; + while (s2n_negotiate(conn, &blocked) != 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")); + if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { + fprintf(stderr, "Alert: %d\n", + s2n_connection_get_alert(conn)); + } + S2N_ERROR_PRESERVE_ERRNO(); + } + + if (wait_for_event(fd, blocked) != S2N_SUCCESS) { + S2N_ERROR_PRESERVE_ERRNO(); + } + } + + print_connection_info(conn); + + printf("s2n is ready\n"); + return 0; +} + +int renegotiate(struct s2n_connection *conn, int fd) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t buffer[STDIO_BUFSIZE] = { 0 }; + ssize_t data_read = 0; + + GUARD_RETURN(s2n_renegotiate_wipe(conn), "Unable to prepare connection for renegotiate"); + GUARD_RETURN(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); + + fprintf(stdout, "RENEGOTIATE\n"); + fflush(stdout); + + while (s2n_renegotiate(conn, buffer, sizeof(buffer), &data_read, &blocked) != S2N_SUCCESS) { + uint8_t *data_ptr = buffer; + while (data_read > 0) { + ssize_t data_written = write(STDOUT_FILENO, data_ptr, data_read); + GUARD_RETURN(data_written, "Error writing to stdout\n"); + data_read -= data_written; + data_ptr += data_written; + } + + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Failed to renegotiate: '%s'. %s\n", s2n_strerror(s2n_errno, NULL), + s2n_strerror_debug(s2n_errno, NULL)); + if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_ALERT) { + fprintf(stderr, "Alert: %d\n", s2n_connection_get_alert(conn)); + } + return S2N_FAILURE; + } + + GUARD_RETURN(wait_for_event(fd, blocked), "Error polling IO for renegotiate"); + } + + print_connection_info(conn); + printf("s2n is ready, again\n"); + return S2N_SUCCESS; +} + +void send_data(struct s2n_connection *conn, int sockfd, const char *data, uint64_t len, s2n_blocked_status *blocked) +{ + uint64_t bytes_remaining = len; + const char *data_ptr = data; + do { + ssize_t send_len = MIN(bytes_remaining, SSIZE_MAX); + ssize_t bytes_written = s2n_send(conn, data_ptr, send_len, blocked); + if (bytes_written < 0) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Error writing to connection: '%s'\n", + s2n_strerror(s2n_errno, "EN")); + exit(1); + } + + GUARD_EXIT(wait_for_event(sockfd, *blocked), "Unable to send data"); + continue; + } + + bytes_remaining -= bytes_written; + data_ptr += bytes_written; + + } while (bytes_remaining > 0); +} + +int echo(struct s2n_connection *conn, int sockfd, bool *stop_echo) +{ + struct pollfd readers[2]; + + readers[0].fd = sockfd; + readers[0].events = POLLIN; + readers[1].fd = STDIN_FILENO; + readers[1].events = POLLIN; + + /* Reset errno so that we can't inherit the errno == EINTR exit condition. */ + errno = 0; + + /* Act as a simple proxy between stdin and the SSL connection */ + int p = 0; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + do { + /* echo will send and receive Application Data back and forth between + * client and server, until stop_echo is true or stdin EOF is reached. */ + while (!(*stop_echo) && (p = poll(readers, 2, -1)) > 0) { + char buffer[STDIO_BUFSIZE]; + ssize_t bytes_read = 0; + + if (readers[0].revents & POLLIN) { + s2n_errno = S2N_ERR_T_OK; + bytes_read = s2n_recv(conn, buffer, STDIO_BUFSIZE, &blocked); + if (bytes_read == 0) { + return 0; + } + if (bytes_read < 0) { + switch (s2n_error_get_type(s2n_errno)) { + case S2N_ERR_T_BLOCKED: + /* Wait until poll tells us data is ready */ + continue; + case S2N_ERR_T_ALERT: + fprintf(stderr, "Received alert: %d\n", s2n_connection_get_alert(conn)); + break; + default: + fprintf(stderr, "Error reading from connection: '%s'\n", s2n_strerror(s2n_errno, "EN")); + break; + } + exit(1); + } + + char *buf_ptr = buffer; + do { + ssize_t bytes_written = write(STDOUT_FILENO, buf_ptr, bytes_read); + if (bytes_written < 0) { + fprintf(stderr, "Error writing to stdout\n"); + exit(1); + } + + bytes_read -= bytes_written; + buf_ptr += bytes_written; + } while (bytes_read > 0); + } + + if (readers[1].revents & POLLIN) { + size_t bytes_available = 0; + + if (ioctl(STDIN_FILENO, FIONREAD, &bytes_available) < 0) { + bytes_available = 1; + } + + do { + /* We can only read as much data as we have space for. So it may + * take a couple loops to empty stdin. */ + size_t bytes_to_read = bytes_available; + if (bytes_available > sizeof(buffer)) { + bytes_to_read = sizeof(buffer); + } + + bytes_read = read(STDIN_FILENO, buffer, bytes_to_read); + if (bytes_read < 0 && errno != EINTR) { + fprintf(stderr, "Error reading from stdin\n"); + exit(1); + } + if (bytes_read == 0) { + fprintf(stderr, "Exiting on stdin EOF\n"); + return 0; + } + bytes_available -= bytes_read; + + /* We may not be able to write all the data we read in one shot, so + * keep sending until we have cleared our buffer. */ + send_data(conn, sockfd, buffer, bytes_read, &blocked); + + } while (bytes_available || blocked); + } + + if (readers[1].revents & POLLHUP) { + /* The stdin pipe hanged up, and we've handled all read from it above */ + return 0; + } + + if (readers[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fprintf(stderr, "Error polling from socket: err=%d hup=%d nval=%d\n", + (readers[0].revents & POLLERR) ? 1 : 0, + (readers[0].revents & POLLHUP) ? 1 : 0, + (readers[0].revents & POLLNVAL) ? 1 : 0); + POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); + } + + if (readers[1].revents & (POLLERR | POLLNVAL)) { + fprintf(stderr, "Error polling from socket: err=%d nval=%d\n", + (readers[1].revents & POLLERR) ? 1 : 0, + (readers[1].revents & POLLNVAL) ? 1 : 0); + POSIX_BAIL(S2N_ERR_POLLING_FROM_SOCKET); + } + } + } while (p < 0 && errno == EINTR); + + return 0; +} diff --git a/bin/policy.c b/bin/policy.c index 6ea7d7b0368..9ea8ec77bf9 100644 --- a/bin/policy.c +++ b/bin/policy.c @@ -1,61 +1,61 @@ -/* - * 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 -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "tls/policy/s2n_policy_feature.h" -#include "tls/s2n_security_policies.h" - -static int usage() -{ - printf("policy \n" - "example: policy default_tls13\n\n"); - return 0; -} - -int main(int argc, char *const *argv) -{ - if (argc != 2) { - usage(); - exit(1); - } - - if (s2n_init() != S2N_SUCCESS) { - fprintf(stderr, "Error: Failed to initialize s2n\n"); - exit(1); - } - - const char *policy_name = argv[1]; - const struct s2n_security_policy *policy = NULL; - if (s2n_find_security_policy_from_version(policy_name, &policy) != S2N_SUCCESS) { - fprintf(stderr, "Error: Failed to find security policy\n"); - s2n_cleanup(); - exit(1); - } - - uint32_t output_size = 0; - if (s2n_security_policy_write_fd(policy, S2N_POLICY_FORMAT_DEBUG_V1, STDOUT_FILENO, &output_size) != S2N_SUCCESS) { - s2n_cleanup(); - exit(1); - } - - s2n_cleanup(); - return 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 +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "tls/policy/s2n_policy_feature.h" +#include "tls/s2n_security_policies.h" + +static int usage() +{ + printf("policy \n" + "example: policy default_tls13\n\n"); + return 0; +} + +int main(int argc, char *const *argv) +{ + if (argc != 2) { + usage(); + exit(1); + } + + if (s2n_init() != S2N_SUCCESS) { + fprintf(stderr, "Error: Failed to initialize s2n\n"); + exit(1); + } + + const char *policy_name = argv[1]; + const struct s2n_security_policy *policy = NULL; + if (s2n_find_security_policy_from_version(policy_name, &policy) != S2N_SUCCESS) { + fprintf(stderr, "Error: Failed to find security policy\n"); + s2n_cleanup(); + exit(1); + } + + uint32_t output_size = 0; + if (s2n_security_policy_write_fd(policy, S2N_POLICY_FORMAT_DEBUG_V1, STDOUT_FILENO, &output_size) != S2N_SUCCESS) { + s2n_cleanup(); + exit(1); + } + + s2n_cleanup(); + return 0; +} diff --git a/bin/s2nc.c b/bin/s2nc.c index c06e0a5ee84..78bc9641ed7 100644 --- a/bin/s2nc.c +++ b/bin/s2nc.c @@ -1,829 +1,829 @@ -#include "utils/s2n_prelude.h" -/* - * 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 -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif -#if !defined(_MSC_VER) -#include -#endif - -#ifndef S2N_INTERN_LIBCRYPTO - #include - #include -#endif - -#include "api/s2n.h" -#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" - -#define OPT_TICKET_IN 1000 -#define OPT_TICKET_OUT 1001 -#define OPT_SEND_FILE 1002 -#define OPT_RENEG 1003 -#define OPT_NPN 1004 -#define OPT_PREFER_LOW_LATENCY 1005 -#define OPT_PREFER_THROUGHPUT 1006 -#define OPT_BUFFERED_SEND 1007 -#define OPT_SERIALIZE_OUT 1008 -#define OPT_DESERIALIZE_IN 1009 - -/* This should match the final cert in the s2nd default_certificate_chain */ -const char default_trusted_cert[] = - "-----BEGIN CERTIFICATE-----" - "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" - "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" - "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" - "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" - "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" - "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" - "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" - "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" - "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" - "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" - "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" - "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" - "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" - "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" - "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" - "MZ8=" - "-----END CERTIFICATE-----"; - -/* - * s2nc is an example client that uses many s2n-tls APIs. - * It is intended for testing purposes only, and should not be used in production. - */ -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"); - fprintf(stderr, "\n Options:\n\n"); - fprintf(stderr, " -a [protocols]\n"); - fprintf(stderr, " --alpn [protocols]\n"); - fprintf(stderr, " Sets the application protocols supported by this client, as a comma separated list.\n"); - fprintf(stderr, " -c [version_string]\n"); - fprintf(stderr, " --ciphers [version_string]\n"); - fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); - fprintf(stderr, " --enter-fips-mode\n"); - fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); - fprintf(stderr, " -e,--echo\n"); - fprintf(stderr, " Listen to stdin after TLS Connection is established and echo it to the Server\n"); - fprintf(stderr, " --send-file [file path]\n"); - fprintf(stderr, " Sends the contents of the provided file to the server after connecting.\n"); - fprintf(stderr, " -h,--help\n"); - fprintf(stderr, " Display this message and quit.\n"); - fprintf(stderr, " -n [server name]\n"); - fprintf(stderr, " --name [server name]\n"); - fprintf(stderr, " Sets the SNI server name header for this client. If not specified, the host value is used.\n"); - fprintf(stderr, " -s,--status\n"); - fprintf(stderr, " Request the OCSP status of the remote server certificate\n"); - fprintf(stderr, " -m,--mfl\n"); - fprintf(stderr, " Request maximum fragment length from: 512, 1024, 2048, 4096\n"); - fprintf(stderr, " -f,--ca-file [file path]\n"); - fprintf(stderr, " Location of trust store CA file (PEM format). If neither -f or -d are specified. System defaults will be used.\n"); - fprintf(stderr, " -d,--ca-dir [directory path]\n"); - fprintf(stderr, " Directory containing hashed trusted certs. If neither -f or -d are specified. System defaults will be used.\n"); - fprintf(stderr, " -i,--insecure\n"); - fprintf(stderr, " Turns off certification validation altogether.\n"); - fprintf(stderr, " -l,--cert [file path]\n"); - fprintf(stderr, " Path to a PEM encoded certificate. Optional. Will only be used for client auth\n"); - fprintf(stderr, " -k,--key [file path]\n"); - fprintf(stderr, " Path to a PEM encoded private key that matches cert. Will only be used for client auth\n"); - fprintf(stderr, " -r,--reconnect\n"); - fprintf(stderr, " Drop and re-make the connection using Session ticket. If session ticket is disabled, then re-make the connection using Session-ID \n"); - fprintf(stderr, " -T,--no-session-ticket \n"); - fprintf(stderr, " Disable session ticket for resumption.\n"); - fprintf(stderr, " --ticket-out [file path]\n"); - fprintf(stderr, " Path to a file where the session ticket can be stored.\n"); - fprintf(stderr, " --ticket-in [file path]\n"); - fprintf(stderr, " Path to session ticket file to resume connection.\n"); - fprintf(stderr, " -D,--dynamic\n"); - fprintf(stderr, " Set dynamic record resize threshold\n"); - fprintf(stderr, " -t,--timeout\n"); - fprintf(stderr, " Set dynamic record timeout threshold\n"); - fprintf(stderr, " -C,--corked-io\n"); - fprintf(stderr, " Turn on corked io\n"); - fprintf(stderr, " -B,--non-blocking\n"); - fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); - fprintf(stderr, " -L --key-log \n"); - fprintf(stderr, " Enable NSS key logging into the provided path\n"); - fprintf(stderr, " -P --psk \n" - " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" - " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" - " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); - fprintf(stderr, " -E ,--early-data \n"); - fprintf(stderr, " Sends data in file path as early data to the server. Early data will only be sent if s2nc receives a session ticket and resumes a session.\n"); - fprintf(stderr, " --renegotiation [accept|reject]\n" - " accept: Accept all server requests for a new handshake\n" - " reject: Reject all server requests for a new handshake\n"); - fprintf(stderr, " --npn \n"); - fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); - fprintf(stderr, "\n"); - fprintf(stderr, " --buffered-send \n"); - fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); - fprintf(stderr, " --prefer-low-latency\n"); - fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); - fprintf(stderr, " --prefer-throughput\n"); - fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); - /* clang-format on */ - exit(1); -} - -size_t session_state_length = 0; -uint8_t *session_state = NULL; -static int test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - GUARD_EXIT_NULL(conn); - GUARD_EXIT_NULL(ticket); - - GUARD_EXIT(s2n_session_ticket_get_data_len(ticket, &session_state_length), "Error getting ticket length "); - session_state = realloc(session_state, session_state_length); - if (session_state == NULL) { - print_s2n_error("Error getting new session state"); - exit(1); - } - GUARD_EXIT(s2n_session_ticket_get_data(ticket, session_state_length, session_state), "Error getting ticket data"); - - bool *session_ticket_recv = (bool *) ctx; - *session_ticket_recv = 1; - - return S2N_SUCCESS; -} - -struct reneg_req_ctx { - bool do_renegotiate; - s2n_renegotiate_response response; -}; - -static int reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) -{ - GUARD_EXIT_NULL(conn); - GUARD_EXIT_NULL(context); - GUARD_EXIT_NULL(response); - struct reneg_req_ctx *reneg_ctx = (struct reneg_req_ctx *) context; - - *response = reneg_ctx->response; - if (*response == S2N_RENEGOTIATE_ACCEPT) { - reneg_ctx->do_renegotiate = true; - } - return S2N_SUCCESS; -} - -static void setup_s2n_config(struct s2n_config *config, const char *cipher_prefs, s2n_status_request_type type, - struct verify_data *unsafe_verify_data, const char *host, const char *alpn_protocols, uint16_t mfl_value) -{ - if (config == NULL) { - print_s2n_error("Error getting new config"); - 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."); - - if (s2n_config_set_verify_host_callback(config, unsafe_verify_host, unsafe_verify_data) < 0) { - print_s2n_error("Error setting host name verification function."); - } - - if (type == S2N_STATUS_REQUEST_OCSP) { - if (s2n_config_set_check_stapled_ocsp_response(config, 1)) { - print_s2n_error("OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); - } - } - - unsafe_verify_data->trusted_host = host; - - if (alpn_protocols) { - /* Count the number of commas, this tells us how many protocols there - are in the list */ - const char *ptr = alpn_protocols; - int protocol_count = 1; - while (*ptr) { - if (*ptr == ',') { - protocol_count++; - } - ptr++; - } - - char **protocols = malloc(sizeof(char *) * protocol_count); - if (!protocols) { - fprintf(stderr, "Error allocating memory\n"); - exit(1); - } - - const char *next = alpn_protocols; - int idx = 0; - int length = 0; - ptr = alpn_protocols; - while (*ptr) { - if (*ptr == ',') { - protocols[idx] = malloc(length + 1); - if (!protocols[idx]) { - fprintf(stderr, "Error allocating memory\n"); - exit(1); - } - memmove(protocols[idx], next, length); - protocols[idx][length] = '\0'; - length = 0; - idx++; - ptr++; - next = ptr; - } else { - length++; - ptr++; - } - } - if (ptr != next) { - protocols[idx] = malloc(length + 1); - if (!protocols[idx]) { - fprintf(stderr, "Error allocating memory\n"); - exit(1); - } - memmove(protocols[idx], next, length); - protocols[idx][length] = '\0'; - } - - GUARD_EXIT(s2n_config_set_protocol_preferences(config, (const char *const *) protocols, protocol_count), "Failed to set protocol preferences"); - - while (protocol_count) { - protocol_count--; - free(protocols[protocol_count]); - } - free(protocols); - } - - uint8_t mfl_code = 0; - if (mfl_value > 0) { - switch (mfl_value) { - case 512: - mfl_code = S2N_TLS_MAX_FRAG_LEN_512; - break; - case 1024: - mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; - break; - case 2048: - mfl_code = S2N_TLS_MAX_FRAG_LEN_2048; - break; - case 4096: - mfl_code = S2N_TLS_MAX_FRAG_LEN_4096; - break; - default: - fprintf(stderr, "Invalid maximum fragment length value\n"); - exit(1); - } - } - - GUARD_EXIT(s2n_config_send_max_fragment_length(config, mfl_code), "Error setting maximum fragment length"); -} - -int main(int argc, char *const *argv) -{ - struct addrinfo hints, *ai_list = NULL, *ai = NULL; - int r = 0, sockfd = 0; - bool session_ticket_recv = 0; - /* Optional args */ - const char *alpn_protocols = NULL; - const char *server_name = NULL; - const char *ca_file = NULL; - const char *ca_dir = NULL; - const char *client_cert = NULL; - const char *client_key = NULL; - bool client_cert_input = false; - bool client_key_input = false; - const char *ticket_out = NULL; - char *ticket_in = NULL; - const char *serialize_out = NULL; - const char *deserialize_in = NULL; - uint16_t mfl_value = 0; - uint8_t insecure = 0; - int reconnect = 0; - uint8_t session_ticket = 1; - s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; - uint32_t dyn_rec_threshold = 0; - uint8_t dyn_rec_timeout = 0; - /* required args */ - const char *cipher_prefs = "default"; - int fips_mode = 0; - const char *host = NULL; - struct verify_data unsafe_verify_data; - const char *port = "443"; - bool echo_input = false; - const char *send_file = NULL; - int use_corked_io = 0; - uint8_t non_blocking = 0; - const char *key_log_path = NULL; - FILE *key_log_file = NULL; - char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH]; - size_t psk_list_len = 0; - char *early_data = NULL; - bool setup_reneg_cb = false; - struct reneg_req_ctx reneg_ctx = { 0 }; - bool npn = false; - uint32_t send_buffer_size = 0; - bool prefer_low_latency = false; - bool prefer_throughput = false; - - static struct option long_options[] = { - { "alpn", required_argument, 0, 'a' }, - { "ciphers", required_argument, 0, 'c' }, - { "enter-fips-mode", no_argument, NULL, 'F' }, - { "echo", no_argument, 0, 'e' }, - { "send-file", required_argument, 0, OPT_SEND_FILE }, - { "help", no_argument, 0, 'h' }, - { "name", required_argument, 0, 'n' }, - { "status", no_argument, 0, 's' }, - { "mfl", required_argument, 0, 'm' }, - { "ca-file", required_argument, 0, 'f' }, - { "ca-dir", required_argument, 0, 'd' }, - { "cert", required_argument, 0, 'l' }, - { "key", required_argument, 0, 'k' }, - { "insecure", no_argument, 0, 'i' }, - { "reconnect", no_argument, 0, 'r' }, - { "ticket-out", required_argument, 0, OPT_TICKET_OUT }, - { "ticket-in", required_argument, 0, OPT_TICKET_IN }, - { "no-session-ticket", no_argument, 0, 'T' }, - { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, - { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, - { "dynamic", required_argument, 0, 'D' }, - { "timeout", required_argument, 0, 't' }, - { "corked-io", no_argument, 0, 'C' }, - { "tls13", no_argument, 0, '3' }, - { "non-blocking", no_argument, 0, 'B' }, - { "key-log", required_argument, 0, 'L' }, - { "psk", required_argument, 0, 'P' }, - { "early-data", required_argument, 0, 'E' }, - { "renegotiation", required_argument, 0, OPT_RENEG }, - { "npn", no_argument, 0, OPT_NPN }, - { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, - { "prefer-low-latency", no_argument, NULL, OPT_PREFER_LOW_LATENCY }, - { "prefer-throughput", no_argument, NULL, OPT_PREFER_THROUGHPUT }, - { 0 }, - }; - - while (1) { - int option_index = 0; - int c = getopt_long(argc, argv, "a:c:ehn:m:sf:d:l:k:D:t:irTCBL:P:E:", long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'a': - alpn_protocols = optarg; - break; - case 'C': - use_corked_io = 1; - break; - case 'c': - cipher_prefs = optarg; - break; - case 'F': - fips_mode = 1; - break; - case 'e': - echo_input = true; - break; - case OPT_SEND_FILE: - send_file = load_file_to_cstring(optarg); - break; - case 'h': - usage(); - break; - case 'n': - server_name = optarg; - break; - case 's': - type = S2N_STATUS_REQUEST_OCSP; - break; - case 'm': - mfl_value = (uint16_t) atoi(optarg); - break; - case 'f': - ca_file = optarg; - break; - case 'd': - ca_dir = optarg; - break; - case 'l': - client_cert = load_file_to_cstring(optarg); - client_cert_input = true; - break; - case 'k': - client_key = load_file_to_cstring(optarg); - client_key_input = true; - break; - case 'i': - insecure = 1; - break; - case 'r': - reconnect = 5; - break; - case OPT_TICKET_OUT: - ticket_out = optarg; - break; - case OPT_TICKET_IN: - ticket_in = optarg; - break; - /* The serialize_out and deserialize_in options are not documented - * in the usage section as they are not intended to work correctly - * using s2nc by itself. s2nc and s2nd are processes which close - * their TCP connection upon exit. This will cause an error if one - * peer serializes and exits and the other doesn't, as serialization - * depends on a continuous TCP connection with the peer. Therefore, our - * only usage of this feature is in our integ test framework, - * which serializes and deserializes both client and server at the - * same time. Do not expect these options to work when using s2nc alone. - */ - case OPT_SERIALIZE_OUT: - serialize_out = optarg; - break; - case OPT_DESERIALIZE_IN: - deserialize_in = optarg; - break; - case 'T': - session_ticket = 0; - break; - case 't': - dyn_rec_timeout = (uint8_t) MIN(255, atoi(optarg)); - break; - case 'D': - errno = 0; - dyn_rec_threshold = strtoul(optarg, 0, 10); - if (errno == ERANGE) { - dyn_rec_threshold = 0; - } - break; - case '3': - /* Do nothing -- this argument is deprecated. */ - break; - case 'B': - non_blocking = 1; - break; - case 'L': - key_log_path = optarg; - break; - case 'P': - if (psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { - fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); - exit(1); - } - psk_optarg_list[psk_list_len++] = optarg; - break; - case 'E': - early_data = load_file_to_cstring(optarg); - GUARD_EXIT_NULL(early_data); - break; - case OPT_RENEG: - setup_reneg_cb = true; - if (strcmp(optarg, "accept") == 0) { - reneg_ctx.response = S2N_RENEGOTIATE_ACCEPT; - } else if (strcmp(optarg, "reject") == 0) { - reneg_ctx.response = S2N_RENEGOTIATE_REJECT; - } else { - fprintf(stderr, "Unrecognized option: %s\n", optarg); - exit(1); - } - break; - case OPT_NPN: - npn = true; - break; - case OPT_BUFFERED_SEND: { - intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); - if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { - fprintf(stderr, " must be a positive 32 bit value\n"); - exit(1); - } - send_buffer_size = (uint32_t) send_buffer_size_scanned_value; - break; - } - case OPT_PREFER_LOW_LATENCY: - prefer_low_latency = true; - break; - case OPT_PREFER_THROUGHPUT: - prefer_throughput = true; - break; - case '?': - default: - usage(); - break; - } - } - - if (optind < argc) { - host = argv[optind++]; - } - - /* cppcheck-suppress duplicateCondition */ - if (optind < argc) { - port = argv[optind++]; - } - - if (!host) { - usage(); - } - - if (!server_name) { - server_name = host; - } - - memset(&hints, 0, sizeof(hints)); - - 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); - } - - if (prefer_low_latency && prefer_throughput) { - fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); - exit(1); - } - - GUARD_EXIT(s2n_init(), "Error running s2n_init()"); - printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); - - if (fips_mode) { - s2n_fips_mode mode = 0; - GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); - if (mode != S2N_FIPS_MODE_ENABLED) { - fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); - exit(1); - } - printf("s2nc entered FIPS mode\n"); - } - - if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { - fprintf(stderr, "error: %s\n", gai_strerror(r)); - exit(1); - } - - do { - int connected = 0; - 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 = 1; - /* connect() succeeded */ - break; - } - - if (connected == 0) { - fprintf(stderr, "Failed to connect to %s:%s\n", host, port); - exit(1); - } - - if (non_blocking) { - int flags = fcntl(sockfd, F_GETFL, 0); - if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { - fprintf(stderr, "fcntl error: %s\n", strerror(errno)); - exit(1); - } - } - - struct s2n_config *config = s2n_config_new(); - setup_s2n_config(config, cipher_prefs, type, &unsafe_verify_data, host, alpn_protocols, mfl_value); - - if (send_buffer_size != 0) { - GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size"); - } - - if (client_cert_input != client_key_input) { - print_s2n_error("Client cert/key pair must be given."); - } - - if (client_cert_input) { - struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); - GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, client_cert, client_key), "Error getting certificate/key"); - GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); - } - - GUARD_EXIT(s2n_config_add_pem_to_trust_store(config, default_trusted_cert), - "Error adding default cert to trust store."); - if (ca_file || ca_dir) { - GUARD_EXIT(s2n_config_wipe_trust_store(config), "Error wiping trust store"); - if (s2n_config_set_verification_ca_location(config, ca_file, ca_dir) < 0) { - print_s2n_error("Error setting CA file for trust store."); - } - } else if (insecure) { - GUARD_EXIT(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); - } - - if (session_ticket) { - GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); - GUARD_EXIT(s2n_config_set_session_ticket_cb(config, test_session_ticket_cb, &session_ticket_recv), "Error setting session ticket callback"); - session_ticket_recv = 0; - } - - if (key_log_path) { - key_log_file = fopen(key_log_path, "a"); - GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); - GUARD_EXIT( - s2n_config_set_key_log_cb( - config, - key_log_callback, - (void *) key_log_file), - "Failed to set key log callback"); - } - - if (setup_reneg_cb) { - GUARD_EXIT(s2n_config_set_renegotiate_request_cb(config, reneg_req_cb, &reneg_ctx), - "Error setting renegotiation request callback"); - } - - if (npn) { - GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); - } - - if (serialize_out) { - GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), - "Error setting serialized version"); - } - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - - if (conn == NULL) { - print_s2n_error("Error getting new connection"); - exit(1); - } - - if (deserialize_in) { - GUARD_EXIT(s2n_connection_deserialize_in(conn, deserialize_in), "Failed to deserialize file"); - } - - GUARD_EXIT(s2n_connection_set_config(conn, config), "Error setting configuration"); - - GUARD_EXIT(s2n_set_server_name(conn, server_name), "Error setting server name"); - - GUARD_EXIT(s2n_connection_set_fd(conn, sockfd), "Error setting file descriptor"); - - GUARD_EXIT(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); - - if (use_corked_io) { - GUARD_EXIT(s2n_connection_use_corked_io(conn), "Error setting corked io"); - } - - /* Read in session ticket from previous session */ - if (ticket_in) { - GUARD_EXIT(get_file_size(ticket_in, &session_state_length), "Failed to read ticket-in file"); - free(session_state); - session_state = calloc(session_state_length, sizeof(uint8_t)); - GUARD_EXIT_NULL(session_state); - GUARD_EXIT(load_file_to_array(ticket_in, session_state, session_state_length), "Failed to read ticket-in file"); - } - - /* Update session state in connection if exists */ - if (session_state_length > 0) { - GUARD_EXIT(s2n_connection_set_session(conn, session_state, session_state_length), "Error setting session state in connection"); - } - - GUARD_EXIT(s2n_setup_external_psk_list(conn, psk_optarg_list, psk_list_len), "Error setting external psk list"); - - if (prefer_throughput) { - GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); - } - - if (prefer_low_latency) { - GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); - } - - if (early_data) { - if (!session_ticket) { - print_s2n_error("Early data can only be used with session tickets."); - exit(1); - } - /* Send early data if we have a received a session ticket from the server */ - if (session_state_length) { - uint32_t early_data_length = strlen(early_data); - GUARD_EXIT(early_data_send(conn, (uint8_t *) early_data, early_data_length), "Error sending early data"); - } - } - - if (!deserialize_in && negotiate(conn, sockfd) != 0) { - /* Error is printed in negotiate */ - S2N_ERROR_PRESERVE_ERRNO(); - } - - printf("Connected to %s:%s\n", host, port); - - /* Save session state from connection if reconnect is enabled. */ - if (reconnect > 0 || ticket_out) { - if (conn->actual_protocol_version >= S2N_TLS13) { - if (!session_ticket) { - print_s2n_error("s2nc can only reconnect in TLS1.3 with session tickets."); - exit(1); - } - GUARD_EXIT(echo(conn, sockfd, &session_ticket_recv), "Error calling echo"); - } else { - if (!session_ticket && s2n_connection_get_session_id_length(conn) <= 0) { - print_s2n_error("Endpoint sent empty session id so cannot resume session"); - exit(1); - } - free(session_state); - session_state_length = s2n_connection_get_session_length(conn); - session_state = calloc(session_state_length, sizeof(uint8_t)); - GUARD_EXIT_NULL(session_state); - if (s2n_connection_get_session(conn, session_state, session_state_length) != session_state_length) { - print_s2n_error("Error getting serialized session state"); - exit(1); - } - } - if (ticket_out) { - GUARD_EXIT(write_array_to_file(ticket_out, session_state, session_state_length), "Failed to write to ticket-out file"); - } - } - - if (dyn_rec_threshold > 0 && dyn_rec_timeout > 0) { - s2n_connection_set_dynamic_record_threshold(conn, dyn_rec_threshold, dyn_rec_timeout); - } - - GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); - - if (send_file != NULL) { - printf("Sending file contents:\n%s\n", send_file); - - unsigned long send_file_len = strlen(send_file); - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - send_data(conn, sockfd, send_file, send_file_len, &blocked); - } - - while (echo_input) { - fflush(stdout); - fflush(stderr); - echo(conn, sockfd, &reneg_ctx.do_renegotiate); - - if (!reneg_ctx.do_renegotiate) { - break; - } - - reneg_ctx.do_renegotiate = false; - GUARD_EXIT(renegotiate(conn, sockfd), "Renegotiation failed"); - } - - if (serialize_out) { - GUARD_EXIT(s2n_connection_serialize_out(conn, serialize_out), "Error serializing connection"); - } else { - GUARD_EXIT(wait_for_shutdown(conn, sockfd), "Error closing connection"); - } - - GUARD_EXIT(s2n_connection_free(conn), "Error freeing connection"); - - GUARD_EXIT(s2n_config_free(config), "Error freeing configuration"); - - close(sockfd); - reconnect--; - - } while (reconnect >= 0); - - if (key_log_file) { - fclose(key_log_file); - } - - GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); - - free(early_data); - free(session_state); - freeaddrinfo(ai_list); - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif + +#ifndef S2N_INTERN_LIBCRYPTO + #include + #include +#endif + +#include "api/s2n.h" +#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" + +#define OPT_TICKET_IN 1000 +#define OPT_TICKET_OUT 1001 +#define OPT_SEND_FILE 1002 +#define OPT_RENEG 1003 +#define OPT_NPN 1004 +#define OPT_PREFER_LOW_LATENCY 1005 +#define OPT_PREFER_THROUGHPUT 1006 +#define OPT_BUFFERED_SEND 1007 +#define OPT_SERIALIZE_OUT 1008 +#define OPT_DESERIALIZE_IN 1009 + +/* This should match the final cert in the s2nd default_certificate_chain */ +const char default_trusted_cert[] = + "-----BEGIN CERTIFICATE-----" + "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" + "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" + "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" + "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" + "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" + "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" + "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" + "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" + "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" + "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" + "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" + "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" + "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" + "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" + "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" + "MZ8=" + "-----END CERTIFICATE-----"; + +/* + * s2nc is an example client that uses many s2n-tls APIs. + * It is intended for testing purposes only, and should not be used in production. + */ +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"); + fprintf(stderr, "\n Options:\n\n"); + fprintf(stderr, " -a [protocols]\n"); + fprintf(stderr, " --alpn [protocols]\n"); + fprintf(stderr, " Sets the application protocols supported by this client, as a comma separated list.\n"); + fprintf(stderr, " -c [version_string]\n"); + fprintf(stderr, " --ciphers [version_string]\n"); + fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); + fprintf(stderr, " --enter-fips-mode\n"); + fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); + fprintf(stderr, " -e,--echo\n"); + fprintf(stderr, " Listen to stdin after TLS Connection is established and echo it to the Server\n"); + fprintf(stderr, " --send-file [file path]\n"); + fprintf(stderr, " Sends the contents of the provided file to the server after connecting.\n"); + fprintf(stderr, " -h,--help\n"); + fprintf(stderr, " Display this message and quit.\n"); + fprintf(stderr, " -n [server name]\n"); + fprintf(stderr, " --name [server name]\n"); + fprintf(stderr, " Sets the SNI server name header for this client. If not specified, the host value is used.\n"); + fprintf(stderr, " -s,--status\n"); + fprintf(stderr, " Request the OCSP status of the remote server certificate\n"); + fprintf(stderr, " -m,--mfl\n"); + fprintf(stderr, " Request maximum fragment length from: 512, 1024, 2048, 4096\n"); + fprintf(stderr, " -f,--ca-file [file path]\n"); + fprintf(stderr, " Location of trust store CA file (PEM format). If neither -f or -d are specified. System defaults will be used.\n"); + fprintf(stderr, " -d,--ca-dir [directory path]\n"); + fprintf(stderr, " Directory containing hashed trusted certs. If neither -f or -d are specified. System defaults will be used.\n"); + fprintf(stderr, " -i,--insecure\n"); + fprintf(stderr, " Turns off certification validation altogether.\n"); + fprintf(stderr, " -l,--cert [file path]\n"); + fprintf(stderr, " Path to a PEM encoded certificate. Optional. Will only be used for client auth\n"); + fprintf(stderr, " -k,--key [file path]\n"); + fprintf(stderr, " Path to a PEM encoded private key that matches cert. Will only be used for client auth\n"); + fprintf(stderr, " -r,--reconnect\n"); + fprintf(stderr, " Drop and re-make the connection using Session ticket. If session ticket is disabled, then re-make the connection using Session-ID \n"); + fprintf(stderr, " -T,--no-session-ticket \n"); + fprintf(stderr, " Disable session ticket for resumption.\n"); + fprintf(stderr, " --ticket-out [file path]\n"); + fprintf(stderr, " Path to a file where the session ticket can be stored.\n"); + fprintf(stderr, " --ticket-in [file path]\n"); + fprintf(stderr, " Path to session ticket file to resume connection.\n"); + fprintf(stderr, " -D,--dynamic\n"); + fprintf(stderr, " Set dynamic record resize threshold\n"); + fprintf(stderr, " -t,--timeout\n"); + fprintf(stderr, " Set dynamic record timeout threshold\n"); + fprintf(stderr, " -C,--corked-io\n"); + fprintf(stderr, " Turn on corked io\n"); + fprintf(stderr, " -B,--non-blocking\n"); + fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); + fprintf(stderr, " -L --key-log \n"); + fprintf(stderr, " Enable NSS key logging into the provided path\n"); + fprintf(stderr, " -P --psk \n" + " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" + " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" + " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); + fprintf(stderr, " -E ,--early-data \n"); + fprintf(stderr, " Sends data in file path as early data to the server. Early data will only be sent if s2nc receives a session ticket and resumes a session.\n"); + fprintf(stderr, " --renegotiation [accept|reject]\n" + " accept: Accept all server requests for a new handshake\n" + " reject: Reject all server requests for a new handshake\n"); + fprintf(stderr, " --npn \n"); + fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); + fprintf(stderr, "\n"); + fprintf(stderr, " --buffered-send \n"); + fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); + fprintf(stderr, " --prefer-low-latency\n"); + fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); + fprintf(stderr, " --prefer-throughput\n"); + fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); + /* clang-format on */ + exit(1); +} + +size_t session_state_length = 0; +uint8_t *session_state = NULL; +static int test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + GUARD_EXIT_NULL(conn); + GUARD_EXIT_NULL(ticket); + + GUARD_EXIT(s2n_session_ticket_get_data_len(ticket, &session_state_length), "Error getting ticket length "); + session_state = realloc(session_state, session_state_length); + if (session_state == NULL) { + print_s2n_error("Error getting new session state"); + exit(1); + } + GUARD_EXIT(s2n_session_ticket_get_data(ticket, session_state_length, session_state), "Error getting ticket data"); + + bool *session_ticket_recv = (bool *) ctx; + *session_ticket_recv = 1; + + return S2N_SUCCESS; +} + +struct reneg_req_ctx { + bool do_renegotiate; + s2n_renegotiate_response response; +}; + +static int reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) +{ + GUARD_EXIT_NULL(conn); + GUARD_EXIT_NULL(context); + GUARD_EXIT_NULL(response); + struct reneg_req_ctx *reneg_ctx = (struct reneg_req_ctx *) context; + + *response = reneg_ctx->response; + if (*response == S2N_RENEGOTIATE_ACCEPT) { + reneg_ctx->do_renegotiate = true; + } + return S2N_SUCCESS; +} + +static void setup_s2n_config(struct s2n_config *config, const char *cipher_prefs, s2n_status_request_type type, + struct verify_data *unsafe_verify_data, const char *host, const char *alpn_protocols, uint16_t mfl_value) +{ + if (config == NULL) { + print_s2n_error("Error getting new config"); + 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."); + + if (s2n_config_set_verify_host_callback(config, unsafe_verify_host, unsafe_verify_data) < 0) { + print_s2n_error("Error setting host name verification function."); + } + + if (type == S2N_STATUS_REQUEST_OCSP) { + if (s2n_config_set_check_stapled_ocsp_response(config, 1)) { + print_s2n_error("OCSP validation is not supported by the linked libCrypto implementation. It cannot be set."); + } + } + + unsafe_verify_data->trusted_host = host; + + if (alpn_protocols) { + /* Count the number of commas, this tells us how many protocols there + are in the list */ + const char *ptr = alpn_protocols; + int protocol_count = 1; + while (*ptr) { + if (*ptr == ',') { + protocol_count++; + } + ptr++; + } + + char **protocols = malloc(sizeof(char *) * protocol_count); + if (!protocols) { + fprintf(stderr, "Error allocating memory\n"); + exit(1); + } + + const char *next = alpn_protocols; + int idx = 0; + int length = 0; + ptr = alpn_protocols; + while (*ptr) { + if (*ptr == ',') { + protocols[idx] = malloc(length + 1); + if (!protocols[idx]) { + fprintf(stderr, "Error allocating memory\n"); + exit(1); + } + memmove(protocols[idx], next, length); + protocols[idx][length] = '\0'; + length = 0; + idx++; + ptr++; + next = ptr; + } else { + length++; + ptr++; + } + } + if (ptr != next) { + protocols[idx] = malloc(length + 1); + if (!protocols[idx]) { + fprintf(stderr, "Error allocating memory\n"); + exit(1); + } + memmove(protocols[idx], next, length); + protocols[idx][length] = '\0'; + } + + GUARD_EXIT(s2n_config_set_protocol_preferences(config, (const char *const *) protocols, protocol_count), "Failed to set protocol preferences"); + + while (protocol_count) { + protocol_count--; + free(protocols[protocol_count]); + } + free(protocols); + } + + uint8_t mfl_code = 0; + if (mfl_value > 0) { + switch (mfl_value) { + case 512: + mfl_code = S2N_TLS_MAX_FRAG_LEN_512; + break; + case 1024: + mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + break; + case 2048: + mfl_code = S2N_TLS_MAX_FRAG_LEN_2048; + break; + case 4096: + mfl_code = S2N_TLS_MAX_FRAG_LEN_4096; + break; + default: + fprintf(stderr, "Invalid maximum fragment length value\n"); + exit(1); + } + } + + GUARD_EXIT(s2n_config_send_max_fragment_length(config, mfl_code), "Error setting maximum fragment length"); +} + +int main(int argc, char *const *argv) +{ + struct addrinfo hints, *ai_list = NULL, *ai = NULL; + int r = 0, sockfd = 0; + bool session_ticket_recv = 0; + /* Optional args */ + const char *alpn_protocols = NULL; + const char *server_name = NULL; + const char *ca_file = NULL; + const char *ca_dir = NULL; + const char *client_cert = NULL; + const char *client_key = NULL; + bool client_cert_input = false; + bool client_key_input = false; + const char *ticket_out = NULL; + char *ticket_in = NULL; + const char *serialize_out = NULL; + const char *deserialize_in = NULL; + uint16_t mfl_value = 0; + uint8_t insecure = 0; + int reconnect = 0; + uint8_t session_ticket = 1; + s2n_status_request_type type = S2N_STATUS_REQUEST_NONE; + uint32_t dyn_rec_threshold = 0; + uint8_t dyn_rec_timeout = 0; + /* required args */ + const char *cipher_prefs = "default"; + int fips_mode = 0; + const char *host = NULL; + struct verify_data unsafe_verify_data; + const char *port = "443"; + bool echo_input = false; + const char *send_file = NULL; + int use_corked_io = 0; + uint8_t non_blocking = 0; + const char *key_log_path = NULL; + FILE *key_log_file = NULL; + char *psk_optarg_list[S2N_MAX_PSK_LIST_LENGTH]; + size_t psk_list_len = 0; + char *early_data = NULL; + bool setup_reneg_cb = false; + struct reneg_req_ctx reneg_ctx = { 0 }; + bool npn = false; + uint32_t send_buffer_size = 0; + bool prefer_low_latency = false; + bool prefer_throughput = false; + + static struct option long_options[] = { + { "alpn", required_argument, 0, 'a' }, + { "ciphers", required_argument, 0, 'c' }, + { "enter-fips-mode", no_argument, NULL, 'F' }, + { "echo", no_argument, 0, 'e' }, + { "send-file", required_argument, 0, OPT_SEND_FILE }, + { "help", no_argument, 0, 'h' }, + { "name", required_argument, 0, 'n' }, + { "status", no_argument, 0, 's' }, + { "mfl", required_argument, 0, 'm' }, + { "ca-file", required_argument, 0, 'f' }, + { "ca-dir", required_argument, 0, 'd' }, + { "cert", required_argument, 0, 'l' }, + { "key", required_argument, 0, 'k' }, + { "insecure", no_argument, 0, 'i' }, + { "reconnect", no_argument, 0, 'r' }, + { "ticket-out", required_argument, 0, OPT_TICKET_OUT }, + { "ticket-in", required_argument, 0, OPT_TICKET_IN }, + { "no-session-ticket", no_argument, 0, 'T' }, + { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, + { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, + { "dynamic", required_argument, 0, 'D' }, + { "timeout", required_argument, 0, 't' }, + { "corked-io", no_argument, 0, 'C' }, + { "tls13", no_argument, 0, '3' }, + { "non-blocking", no_argument, 0, 'B' }, + { "key-log", required_argument, 0, 'L' }, + { "psk", required_argument, 0, 'P' }, + { "early-data", required_argument, 0, 'E' }, + { "renegotiation", required_argument, 0, OPT_RENEG }, + { "npn", no_argument, 0, OPT_NPN }, + { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, + { "prefer-low-latency", no_argument, NULL, OPT_PREFER_LOW_LATENCY }, + { "prefer-throughput", no_argument, NULL, OPT_PREFER_THROUGHPUT }, + { 0 }, + }; + + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "a:c:ehn:m:sf:d:l:k:D:t:irTCBL:P:E:", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'a': + alpn_protocols = optarg; + break; + case 'C': + use_corked_io = 1; + break; + case 'c': + cipher_prefs = optarg; + break; + case 'F': + fips_mode = 1; + break; + case 'e': + echo_input = true; + break; + case OPT_SEND_FILE: + send_file = load_file_to_cstring(optarg); + break; + case 'h': + usage(); + break; + case 'n': + server_name = optarg; + break; + case 's': + type = S2N_STATUS_REQUEST_OCSP; + break; + case 'm': + mfl_value = (uint16_t) atoi(optarg); + break; + case 'f': + ca_file = optarg; + break; + case 'd': + ca_dir = optarg; + break; + case 'l': + client_cert = load_file_to_cstring(optarg); + client_cert_input = true; + break; + case 'k': + client_key = load_file_to_cstring(optarg); + client_key_input = true; + break; + case 'i': + insecure = 1; + break; + case 'r': + reconnect = 5; + break; + case OPT_TICKET_OUT: + ticket_out = optarg; + break; + case OPT_TICKET_IN: + ticket_in = optarg; + break; + /* The serialize_out and deserialize_in options are not documented + * in the usage section as they are not intended to work correctly + * using s2nc by itself. s2nc and s2nd are processes which close + * their TCP connection upon exit. This will cause an error if one + * peer serializes and exits and the other doesn't, as serialization + * depends on a continuous TCP connection with the peer. Therefore, our + * only usage of this feature is in our integ test framework, + * which serializes and deserializes both client and server at the + * same time. Do not expect these options to work when using s2nc alone. + */ + case OPT_SERIALIZE_OUT: + serialize_out = optarg; + break; + case OPT_DESERIALIZE_IN: + deserialize_in = optarg; + break; + case 'T': + session_ticket = 0; + break; + case 't': + dyn_rec_timeout = (uint8_t) MIN(255, atoi(optarg)); + break; + case 'D': + errno = 0; + dyn_rec_threshold = strtoul(optarg, 0, 10); + if (errno == ERANGE) { + dyn_rec_threshold = 0; + } + break; + case '3': + /* Do nothing -- this argument is deprecated. */ + break; + case 'B': + non_blocking = 1; + break; + case 'L': + key_log_path = optarg; + break; + case 'P': + if (psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { + fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); + exit(1); + } + psk_optarg_list[psk_list_len++] = optarg; + break; + case 'E': + early_data = load_file_to_cstring(optarg); + GUARD_EXIT_NULL(early_data); + break; + case OPT_RENEG: + setup_reneg_cb = true; + if (strcmp(optarg, "accept") == 0) { + reneg_ctx.response = S2N_RENEGOTIATE_ACCEPT; + } else if (strcmp(optarg, "reject") == 0) { + reneg_ctx.response = S2N_RENEGOTIATE_REJECT; + } else { + fprintf(stderr, "Unrecognized option: %s\n", optarg); + exit(1); + } + break; + case OPT_NPN: + npn = true; + break; + case OPT_BUFFERED_SEND: { + intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); + if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { + fprintf(stderr, " must be a positive 32 bit value\n"); + exit(1); + } + send_buffer_size = (uint32_t) send_buffer_size_scanned_value; + break; + } + case OPT_PREFER_LOW_LATENCY: + prefer_low_latency = true; + break; + case OPT_PREFER_THROUGHPUT: + prefer_throughput = true; + break; + case '?': + default: + usage(); + break; + } + } + + if (optind < argc) { + host = argv[optind++]; + } + + /* cppcheck-suppress duplicateCondition */ + if (optind < argc) { + port = argv[optind++]; + } + + if (!host) { + usage(); + } + + if (!server_name) { + server_name = host; + } + + memset(&hints, 0, sizeof(hints)); + + 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); + } + + if (prefer_low_latency && prefer_throughput) { + fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); + exit(1); + } + + GUARD_EXIT(s2n_init(), "Error running s2n_init()"); + printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); + + if (fips_mode) { + s2n_fips_mode mode = 0; + GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); + if (mode != S2N_FIPS_MODE_ENABLED) { + fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); + exit(1); + } + printf("s2nc entered FIPS mode\n"); + } + + if ((r = getaddrinfo(host, port, &hints, &ai_list)) != 0) { + fprintf(stderr, "error: %s\n", gai_strerror(r)); + exit(1); + } + + do { + int connected = 0; + 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 = 1; + /* connect() succeeded */ + break; + } + + if (connected == 0) { + fprintf(stderr, "Failed to connect to %s:%s\n", host, port); + exit(1); + } + + if (non_blocking) { + int flags = fcntl(sockfd, F_GETFL, 0); + if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { + fprintf(stderr, "fcntl error: %s\n", strerror(errno)); + exit(1); + } + } + + struct s2n_config *config = s2n_config_new(); + setup_s2n_config(config, cipher_prefs, type, &unsafe_verify_data, host, alpn_protocols, mfl_value); + + if (send_buffer_size != 0) { + GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size"); + } + + if (client_cert_input != client_key_input) { + print_s2n_error("Client cert/key pair must be given."); + } + + if (client_cert_input) { + struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); + GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, client_cert, client_key), "Error getting certificate/key"); + GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); + } + + GUARD_EXIT(s2n_config_add_pem_to_trust_store(config, default_trusted_cert), + "Error adding default cert to trust store."); + if (ca_file || ca_dir) { + GUARD_EXIT(s2n_config_wipe_trust_store(config), "Error wiping trust store"); + if (s2n_config_set_verification_ca_location(config, ca_file, ca_dir) < 0) { + print_s2n_error("Error setting CA file for trust store."); + } + } else if (insecure) { + GUARD_EXIT(s2n_config_disable_x509_verification(config), "Error disabling X.509 validation"); + } + + if (session_ticket) { + GUARD_EXIT(s2n_config_set_session_tickets_onoff(config, 1), "Error enabling session tickets"); + GUARD_EXIT(s2n_config_set_session_ticket_cb(config, test_session_ticket_cb, &session_ticket_recv), "Error setting session ticket callback"); + session_ticket_recv = 0; + } + + if (key_log_path) { + key_log_file = fopen(key_log_path, "a"); + GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); + GUARD_EXIT( + s2n_config_set_key_log_cb( + config, + key_log_callback, + (void *) key_log_file), + "Failed to set key log callback"); + } + + if (setup_reneg_cb) { + GUARD_EXIT(s2n_config_set_renegotiate_request_cb(config, reneg_req_cb, &reneg_ctx), + "Error setting renegotiation request callback"); + } + + if (npn) { + GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); + } + + if (serialize_out) { + GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), + "Error setting serialized version"); + } + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + if (conn == NULL) { + print_s2n_error("Error getting new connection"); + exit(1); + } + + if (deserialize_in) { + GUARD_EXIT(s2n_connection_deserialize_in(conn, deserialize_in), "Failed to deserialize file"); + } + + GUARD_EXIT(s2n_connection_set_config(conn, config), "Error setting configuration"); + + GUARD_EXIT(s2n_set_server_name(conn, server_name), "Error setting server name"); + + GUARD_EXIT(s2n_connection_set_fd(conn, sockfd), "Error setting file descriptor"); + + GUARD_EXIT(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL), "Error setting ClientAuth optional"); + + if (use_corked_io) { + GUARD_EXIT(s2n_connection_use_corked_io(conn), "Error setting corked io"); + } + + /* Read in session ticket from previous session */ + if (ticket_in) { + GUARD_EXIT(get_file_size(ticket_in, &session_state_length), "Failed to read ticket-in file"); + free(session_state); + session_state = calloc(session_state_length, sizeof(uint8_t)); + GUARD_EXIT_NULL(session_state); + GUARD_EXIT(load_file_to_array(ticket_in, session_state, session_state_length), "Failed to read ticket-in file"); + } + + /* Update session state in connection if exists */ + if (session_state_length > 0) { + GUARD_EXIT(s2n_connection_set_session(conn, session_state, session_state_length), "Error setting session state in connection"); + } + + GUARD_EXIT(s2n_setup_external_psk_list(conn, psk_optarg_list, psk_list_len), "Error setting external psk list"); + + if (prefer_throughput) { + GUARD_RETURN(s2n_connection_prefer_throughput(conn), "Error setting prefer throughput"); + } + + if (prefer_low_latency) { + GUARD_RETURN(s2n_connection_prefer_low_latency(conn), "Error setting prefer low latency"); + } + + if (early_data) { + if (!session_ticket) { + print_s2n_error("Early data can only be used with session tickets."); + exit(1); + } + /* Send early data if we have a received a session ticket from the server */ + if (session_state_length) { + uint32_t early_data_length = strlen(early_data); + GUARD_EXIT(early_data_send(conn, (uint8_t *) early_data, early_data_length), "Error sending early data"); + } + } + + if (!deserialize_in && negotiate(conn, sockfd) != 0) { + /* Error is printed in negotiate */ + S2N_ERROR_PRESERVE_ERRNO(); + } + + printf("Connected to %s:%s\n", host, port); + + /* Save session state from connection if reconnect is enabled. */ + if (reconnect > 0 || ticket_out) { + if (conn->actual_protocol_version >= S2N_TLS13) { + if (!session_ticket) { + print_s2n_error("s2nc can only reconnect in TLS1.3 with session tickets."); + exit(1); + } + GUARD_EXIT(echo(conn, sockfd, &session_ticket_recv), "Error calling echo"); + } else { + if (!session_ticket && s2n_connection_get_session_id_length(conn) <= 0) { + print_s2n_error("Endpoint sent empty session id so cannot resume session"); + exit(1); + } + free(session_state); + session_state_length = s2n_connection_get_session_length(conn); + session_state = calloc(session_state_length, sizeof(uint8_t)); + GUARD_EXIT_NULL(session_state); + if (s2n_connection_get_session(conn, session_state, session_state_length) != session_state_length) { + print_s2n_error("Error getting serialized session state"); + exit(1); + } + } + if (ticket_out) { + GUARD_EXIT(write_array_to_file(ticket_out, session_state, session_state_length), "Failed to write to ticket-out file"); + } + } + + if (dyn_rec_threshold > 0 && dyn_rec_timeout > 0) { + s2n_connection_set_dynamic_record_threshold(conn, dyn_rec_threshold, dyn_rec_timeout); + } + + GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); + + if (send_file != NULL) { + printf("Sending file contents:\n%s\n", send_file); + + unsigned long send_file_len = strlen(send_file); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + send_data(conn, sockfd, send_file, send_file_len, &blocked); + } + + while (echo_input) { + fflush(stdout); + fflush(stderr); + echo(conn, sockfd, &reneg_ctx.do_renegotiate); + + if (!reneg_ctx.do_renegotiate) { + break; + } + + reneg_ctx.do_renegotiate = false; + GUARD_EXIT(renegotiate(conn, sockfd), "Renegotiation failed"); + } + + if (serialize_out) { + GUARD_EXIT(s2n_connection_serialize_out(conn, serialize_out), "Error serializing connection"); + } else { + GUARD_EXIT(wait_for_shutdown(conn, sockfd), "Error closing connection"); + } + + GUARD_EXIT(s2n_connection_free(conn), "Error freeing connection"); + + GUARD_EXIT(s2n_config_free(config), "Error freeing configuration"); + + close(sockfd); + reconnect--; + + } while (reconnect >= 0); + + if (key_log_file) { + fclose(key_log_file); + } + + GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); + + free(early_data); + free(session_state); + freeaddrinfo(ai_list); + return 0; +} diff --git a/bin/s2nd.c b/bin/s2nd.c index d0c363d6965..c43654d3481 100644 --- a/bin/s2nd.c +++ b/bin/s2nd.c @@ -1,719 +1,719 @@ -#include "utils/s2n_prelude.h" -/* - * 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 -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif -#if !defined(_MSC_VER) -#include -#endif -#include -#if !defined(_MSC_VER) -#include -#endif - -#ifndef S2N_INTERN_LIBCRYPTO - #include - #include -#endif - -#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 - -/* - * s2nd is an example server that uses many s2n-tls APIs. - * It is intended for testing purposes only, and should not be used in production. - */ - -static char default_certificate_chain[] = - "-----BEGIN CERTIFICATE-----" - "MIIDHTCCAgWgAwIBAgIUPxywpg3/+VHmj8jJSvK62XC06zMwDQYJKoZIhvcNAQEL" - "BQAwHjEcMBoGA1UEAwwTczJuVGVzdEludGVybWVkaWF0ZTAgFw0yMDAxMjQwMTEw" - "MjFaGA8yMTE5MTIzMTAxMTAyMVowGDEWMBQGA1UEAwwNczJuVGVzdFNlcnZlcjCC" - "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJUbMpdROM6cjb8xgr5kgKHn" - "JVDfhbLg4pxBwWwlayb6/N60JLG9KzWAWhZBmz+Px6kr/1dL6+bL3mLuNBCQpYBS" - "Pee2n7KL9PvsMYZmnYFyn94bXbjBCRxGR+a9lcGHLlZ4C+rrLNi9pUwxf7VIRglR" - "zwHWAFg5xTX6lCmziNM4OMkq8lHkLopHDUg5yI4VTc3EEGqDIf3+0BheIHcUFbIW" - "kFOjRDdL3lMGKEj0+LErzzbhJczBlRMqSMiuYeaWgORLpRNtMeNmbR8oLJFchpF0" - "A9fIO2/Yg+nclcDDhsUBkkfcIKRySGDumKLuYM+hOHp5vQo8tcvyQ6s3U5YULQUC" - "AwEAAaNXMFUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUkVKVmfjICpx4fkvJO6YJ" - "mdoKz3owDgYDVR0PAQH/BAQDAgPoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMA0G" - "CSqGSIb3DQEBCwUAA4IBAQBXoWDI1Gi9snC4K6S7E0AoLmGEPUWzc4fd4Cbj9PRp" - "mSKpsJOYjmneIV34WqnvUXrBkkzblEb9RdszN96WuRAaZJQegRtKOWN5Iggd4sHM" - "8XEx/LeJHc08uSb2d/TnhhOPALoJl/w6M5e6yOezCEJorsOXuVBcbuEKfne7oMA1" - "GziFnVPtwiwXxsX16KilsQRylnK0bV/x1BOgYByCDcXorMndsAYjn4yG1D4l8TbC" - "kCtK1bafEVoASpOFQ8tSeOXBL7Fvw9mFFzs3/ajBTz2nBLDsnP8XH5C/vy8wNGSd" - "Tdcs7DRLYhNJxYopcMgCwyyCAtEFcHkovCSrJ6HUl/ko" - "-----END CERTIFICATE-----" - "-----BEGIN CERTIFICATE-----" - "MIIDCTCCAfGgAwIBAgIUfdybeOdDMd7cPXk6RTcEqeM3IEIwDQYJKoZIhvcNAQEL" - "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwOTUzWhgPMjEx" - "OTEyMzEwMTA5NTNaMB4xHDAaBgNVBAMME3MyblRlc3RJbnRlcm1lZGlhdGUwggEi" - "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2NsDkrZjYbyVeF1R9337y9OHM" - "C2xSRGB6SHrVG1bQZlPxI+E6DqDJcMB4tFLkA7AJxxRLxA7KvO9PzcHAlsqvYcMV" - "gOSAjUZ0Eiwwf6Rtgo2yByj2n1K5XDN3bpt1rROD0BIEnaU9GZd3U0QUYHBRfp0E" - "IdeWuRrlFbPpWXnBaQB/2jEfCuZzpPOiKMWt99GQ4bFBOSzpYdXLALGfb15Kr6RF" - "YoMlsyeijNeePxLeYgracu+vzJLvEzx1U7OGnlWz+VKBw/mz3gABqFfxurN5E8yb" - "4AWJ5kEUJobYcxwe+DoimPdPTWgByJlMpKjfIbnroz/oTZMiNfUCtKT3GTejAgMB" - "AAGjRTBDMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFEasSJIPBZTXyYjI" - "CN2m1Ttz3sUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAxveh" - "GKJPu7DXjoMePzlRGML2iIDT6MgKpsMnO5sNgUbJTFV3KeuASRm1SXVrVFHcQDov" - "l9P10ff0J9KOVrRCawMZZxjjtNAIrSW0G7fwmTgJMTuM5vaaGRjKy018LApcr//Q" - "Nwjh4sw9KOtNIE9krT06kli9zjsgr/EWwPCHSin8oONDgCNn1WgtrSMexsF1BSzU" - "OTq+nyn4nOPOEUthjmepG2eDkd17MNJ6GdKYnFRmC+ctSH028akERhz+EtavU4Cd" - "2eSFTKtbxOuZXyfsOwjhrufp/Ss9i57x3XotBNJ8Fv7VpxI19+Zag4DMGzd3Pisu" - "Q1VpfValnMGtVWPleg==" - "-----END CERTIFICATE-----" - "-----BEGIN CERTIFICATE-----" - "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" - "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" - "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" - "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" - "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" - "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" - "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" - "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" - "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" - "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" - "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" - "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" - "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" - "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" - "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" - "MZ8=" - "-----END CERTIFICATE-----"; - -static char default_private_key[] = - "-----BEGIN RSA PRIVATE KEY-----" - "MIIEogIBAAKCAQEAlRsyl1E4zpyNvzGCvmSAoeclUN+FsuDinEHBbCVrJvr83rQk" - "sb0rNYBaFkGbP4/HqSv/V0vr5sveYu40EJClgFI957afsov0++wxhmadgXKf3htd" - "uMEJHEZH5r2VwYcuVngL6uss2L2lTDF/tUhGCVHPAdYAWDnFNfqUKbOI0zg4ySry" - "UeQuikcNSDnIjhVNzcQQaoMh/f7QGF4gdxQVshaQU6NEN0veUwYoSPT4sSvPNuEl" - "zMGVEypIyK5h5paA5EulE20x42ZtHygskVyGkXQD18g7b9iD6dyVwMOGxQGSR9wg" - "pHJIYO6You5gz6E4enm9Cjy1y/JDqzdTlhQtBQIDAQABAoIBAGTaSJXg8jON4LJ5" - "op11DSx1U+An0B71zVEziMjFZnyvN2rLHia6dQdzEXwMVB3h+oKKp+M8DwvEyV7R" - "D5ZEwCzTc9vOwqXZ1JKxZ64oqlBsX4WzrOjSaH8fanK/uRN1g/ooqKb0+xh+7ddj" - "g6XyhKy5EPOE9Ca4rJOeMakjLmDuleQecT/DixYV6azhfaJoD70XZJWv3YzSpu/X" - "Ma+i3of0alsG/lROjNtEXE3nKzcTUgyAUoQeYRwCVpgssg/4VAUPJNDP4dVmxW8f" - "eNmjlTyXmR9S08SXkqmCHe2mBUsZY9nqcDE6ZWILZKFWIfZD9W+j2ce0FMvcc9kz" - "psxaUQECgYEAxqwsb5aQy6HBF54tdkHbUQEJMelSLNW0G1GUrcLB7eqL7qo3dUA8" - "8PDQ/dTwmmJ7aE0SK2xkQDVKXNbV4OvUNgP6tbzLWEbvmuFAEg5X1jFH2VSdwQhl" - "RDwTQw3wPZ5udy64L6gmsdDch+I7l1v4ex66RWFW+4WIs1altsLiJa0CgYEAwCGW" - "2cjtZ3kIzWgxf7DdnoUTwBM1ATBUYvx7uqVq+dbc/p8cSeMSPz3LUluaVJ2EOjEV" - "QWhx0Ih5qeitzReHRU0OHgxEgjbpJwhseD9O5POSd+fE3TtDQArOxyw4CIJKk4Z2" - "QmqzaO/LboN3Tp+/N9zfVoNZKHcCNra/uKNTH7kCgYA3QFazSdpG51s96D2Yb8RA" - "iNs3yD2UPnJyToPctxcbxWjZHPmDYDQShcZ5cSjgppbPcO+mp+RRfwCJRS4B+VPx" - "GbY1qKWcjU3BcvdQjjCbXuUuabvdnSocieCJe2zelhr+hj2u80KfnQhXufD8rRUz" - "mF4RQXrhREe6KFS5uQUPmQKBgE4rXFyvSyfWLqajxb/WDdT4/9gd+GrLZwn+/7go" - "pSWRLcjKo4/MOxhP4/FWI6xZifrDDYrXG7dkT1u5tzzCXd7sQtom05jHDoU7ACbM" - "WyT7lJQEUCxSeEIOI6MVcpbDq+PpySOsleIT7gjApEHw7LOlwZhJSHUWNmhcYhSV" - "HrTBAoGADAvBqV7JItjm2+qkXXEdPVzOunqjQdnXZjMAJ75PhHnLCCfnTRu53hT3" - "JxDETLLa/r42PlqGZ6bqSW+C+ObgYOvvySqvX8CE9o208ZwCLjHYxuYLH/86Lppr" - "ggF9KQ0xWz7Km3GXv5+bwM5bcgt1A/s6sZCimXuj3Fle3RqOTF0=" - "-----END RSA PRIVATE KEY-----"; - -#define OPT_BUFFERED_SEND 1000 -#define OPT_SERIALIZE_OUT 1001 -#define OPT_DESERIALIZE_IN 1002 - -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"); - fprintf(stderr, "\n Options:\n\n"); - fprintf(stderr, " -a [protocol]\n"); - fprintf(stderr, " --alpn [protocol]\n"); - fprintf(stderr, " Sets a single application protocol supported by this server.\n"); - fprintf(stderr, " -c [version_string]\n"); - fprintf(stderr, " --ciphers [version_string]\n"); - fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); - fprintf(stderr, " --enter-fips-mode\n"); - fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); - fprintf(stderr, " --cert\n"); - fprintf(stderr, " Path to a PEM encoded certificate [chain]. Option can be repeated to load multiple certs.\n"); - fprintf(stderr, " --key\n"); - fprintf(stderr, " Path to a PEM encoded private key that matches cert. Option can be repeated to load multiple certs.\n"); - fprintf(stderr, " -m\n"); - fprintf(stderr, " --mutualAuth\n"); - fprintf(stderr, " Request a Client Certificate. Any RSA Certificate will be accepted.\n"); - fprintf(stderr, " -n\n"); - fprintf(stderr, " --negotiate\n"); - fprintf(stderr, " Only perform tls handshake and then shutdown the connection\n"); - fprintf(stderr, " --parallelize\n"); - fprintf(stderr, " Create a new Connection handler thread for each new connection. Useful for tests with lots of connections.\n"); - fprintf(stderr, " Warning: this option isn't compatible with TLS Resumption, since each thread gets its own Session cache.\n"); - fprintf(stderr, " --prefer-low-latency\n"); - fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); - fprintf(stderr, " --prefer-throughput\n"); - fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); - fprintf(stderr, " --enable-mfl\n"); - fprintf(stderr, " Accept client's TLS maximum fragment length extension request\n"); - fprintf(stderr, " --ocsp\n"); - fprintf(stderr, " Path to a DER formatted OCSP response for stapling\n"); - fprintf(stderr, " -s\n"); - fprintf(stderr, " --self-service-blinding\n"); - fprintf(stderr, " Don't introduce 10-30 second delays on TLS Handshake errors. \n"); - fprintf(stderr, " Warning: this should only be used for testing since skipping blinding may allow timing side channels.\n"); - fprintf(stderr, " -t,--ca-file [file path]\n"); - fprintf(stderr, " Location of trust store CA file (PEM format). If neither -t or -d are specified. System defaults will be used."); - fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); - fprintf(stderr, " -d,--ca-dir [directory path]\n"); - fprintf(stderr, " Directory containing hashed trusted certs. If neither -t or -d are specified. System defaults will be used."); - fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); - fprintf(stderr, " -i,--insecure\n"); - fprintf(stderr, " Turns off certification validation altogether.\n"); - fprintf(stderr, " --stk-file\n"); - fprintf(stderr, " Location of key file used for encryption and decryption of session ticket.\n"); - fprintf(stderr, " -T,--no-session-ticket\n"); - fprintf(stderr, " Disable session ticket for resumption.\n"); - fprintf(stderr, " -C,--corked-io\n"); - fprintf(stderr, " Turn on corked io\n"); - fprintf(stderr, " --non-blocking\n"); - fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); - fprintf(stderr, " -w --https-server\n"); - fprintf(stderr, " Run s2nd in a simple https server mode.\n"); - fprintf(stderr, " -b --https-bench \n"); - fprintf(stderr, " Send number of bytes in https server mode to test throughput.\n"); - fprintf(stderr, " -L --key-log \n"); - fprintf(stderr, " Enable NSS key logging into the provided path\n"); - fprintf(stderr, " -P --psk \n" - " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" - " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" - " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); - fprintf(stderr, " -E, --max-early-data \n"); - fprintf(stderr, " Sets maximum early data allowed in session tickets. \n"); - fprintf(stderr, " -N --npn \n"); - fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); - fprintf(stderr, " -h,--help\n"); - fprintf(stderr, " Display this message and quit.\n"); - fprintf(stderr, " --buffered-send \n"); - fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); - fprintf(stderr, " -X, --max-conns \n"); - fprintf(stderr, " Sets the max number of connections s2nd will accept before shutting down.\n"); - /* clang-format on */ - exit(1); -} - -int handle_connection(int fd, struct s2n_config *config, struct conn_settings settings) -{ - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - if (!conn) { - print_s2n_error("Error getting new s2n connection"); - S2N_ERROR_PRESERVE_ERRNO(); - } - - GUARD_EXIT(s2n_setup_server_connection(conn, fd, config, settings), "Error setting up connection"); - - if (!settings.deserialize_in && negotiate(conn, fd) != S2N_SUCCESS) { - if (settings.mutual_auth) { - if (!s2n_connection_client_cert_used(conn)) { - print_s2n_error("Error: Mutual Auth was required, but not negotiated"); - } - } - - /* Error is printed in negotiate */ - S2N_ERROR_PRESERVE_ERRNO(); - } - - GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); - - if (settings.https_server) { - https(conn, settings.https_bench); - } else if (!settings.only_negotiate) { - bool stop_echo = false; - echo(conn, fd, &stop_echo); - } - - if (settings.serialize_out) { - GUARD_RETURN(s2n_connection_serialize_out(conn, settings.serialize_out), "Error serializing connection"); - } else { - GUARD_RETURN(wait_for_shutdown(conn, fd), "Error closing connection"); - } - - GUARD_RETURN(s2n_connection_wipe(conn), "Error wiping connection"); - - GUARD_RETURN(s2n_connection_free(conn), "Error freeing connection"); - - return 0; -} - -int main(int argc, char *const *argv) -{ - struct addrinfo hints, *ai = NULL; - int r = 0, sockfd = 0; - - /* required args */ - const char *host = NULL; - const char *port = NULL; - - const char *ocsp_response_file_path = NULL; - const char *session_ticket_key_file_path = NULL; - const char *cipher_prefs = "default"; - const char *alpn = NULL; - const char *key_log_path = NULL; - - /* The certificates provided by the user. If there are none provided, we will use the hardcoded default cert. - * The associated private key for each cert will be at the same index in private_keys. If the user mixes up the - * order of --cert --key for a given cert/key pair, s2n will fail to load the cert and s2nd will exit. - */ - int num_user_certificates = 0; - int num_user_private_keys = 0; - const char *certificates[MAX_CERTIFICATES] = { 0 }; - const char *private_keys[MAX_CERTIFICATES] = { 0 }; - - struct conn_settings conn_settings = { 0 }; - int fips_mode = 0; - int parallelize = 0; - int non_blocking = 0; - long int bytes = 0; - conn_settings.session_ticket = 1; - conn_settings.session_cache = 1; - conn_settings.max_conns = -1; - conn_settings.psk_list_len = 0; - int max_early_data = 0; - uint32_t send_buffer_size = 0; - bool npn = false; - - struct option long_options[] = { - { "ciphers", required_argument, NULL, 'c' }, - { "enable-mfl", no_argument, NULL, 'e' }, - { "enter-fips-mode", no_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "key", required_argument, NULL, 'k' }, - { "prefer-low-latency", no_argument, NULL, 'l' }, - { "mutualAuth", no_argument, NULL, 'm' }, - { "negotiate", no_argument, NULL, 'n' }, - { "ocsp", required_argument, NULL, 'o' }, - { "parallelize", no_argument, ¶llelize, 1 }, - { "prefer-throughput", no_argument, NULL, 'p' }, - { "cert", required_argument, NULL, 'r' }, - { "self-service-blinding", no_argument, NULL, 's' }, - { "ca-dir", required_argument, 0, 'd' }, - { "ca-file", required_argument, 0, 't' }, - { "insecure", no_argument, 0, 'i' }, - { "stk-file", required_argument, 0, 'a' }, - { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, - { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, - { "no-session-ticket", no_argument, 0, 'T' }, - { "corked-io", no_argument, 0, 'C' }, - { "max-conns", optional_argument, 0, 'X' }, - { "tls13", no_argument, 0, '3' }, - { "https-server", no_argument, 0, 'w' }, - { "https-bench", required_argument, 0, 'b' }, - { "alpn", required_argument, 0, 'A' }, - { "npn", no_argument, 0, 'N' }, - { "non-blocking", no_argument, 0, 'B' }, - { "key-log", required_argument, 0, 'L' }, - { "psk", required_argument, 0, 'P' }, - { "max-early-data", required_argument, 0, 'E' }, - { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, - /* Per getopt(3) the last element of the array has to be filled with all zeros */ - { 0 }, - }; - while (1) { - int option_index = 0; - int c = getopt_long(argc, argv, "c:hmnst:d:iTCX::wb:A:P:E:", long_options, &option_index); - if (c == -1) { - break; - } - - switch (c) { - case 0: - /* getopt_long() returns 0 if an option.flag is non-null (Eg "parallelize") */ - break; - case 'C': - conn_settings.use_corked_io = 1; - break; - case 'c': - cipher_prefs = optarg; - break; - case 'e': - conn_settings.enable_mfl = 1; - break; - case 'f': - fips_mode = 1; - break; - case 'h': - usage(); - break; - case 'k': - if (num_user_private_keys == MAX_CERTIFICATES) { - fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); - exit(1); - } - private_keys[num_user_private_keys] = load_file_to_cstring(optarg); - num_user_private_keys++; - break; - case 'l': - conn_settings.prefer_low_latency = 1; - break; - case 'm': - conn_settings.mutual_auth = 1; - break; - case 'n': - conn_settings.only_negotiate = 1; - break; - case 'o': - ocsp_response_file_path = optarg; - break; - case 'p': - conn_settings.prefer_throughput = 1; - break; - case 'r': - if (num_user_certificates == MAX_CERTIFICATES) { - fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); - exit(1); - } - certificates[num_user_certificates] = load_file_to_cstring(optarg); - num_user_certificates++; - break; - case 's': - conn_settings.self_service_blinding = 1; - break; - case 'd': - conn_settings.ca_dir = optarg; - break; - case 't': - conn_settings.ca_file = optarg; - break; - case 'i': - conn_settings.insecure = 1; - break; - case 'a': - session_ticket_key_file_path = optarg; - break; - case 'T': - conn_settings.session_ticket = 0; - break; - case '3': - /* Do nothing -- this argument is deprecated */ - break; - case 'X': - if (optarg == NULL) { - conn_settings.max_conns = 1; - } else { - conn_settings.max_conns = atoi(optarg); - } - break; - case 'w': - fprintf(stdout, "Running s2nd in simple https server mode\n"); - conn_settings.https_server = 1; - break; - case 'b': - bytes = strtoul(optarg, NULL, 10); - GUARD_EXIT(bytes, "https-bench bytes needs to be some positive long value."); - conn_settings.https_bench = bytes; - break; - case OPT_BUFFERED_SEND: { - intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); - if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { - fprintf(stderr, " must be a positive 32 bit value\n"); - exit(1); - } - send_buffer_size = (uint32_t) send_buffer_size_scanned_value; - break; - } - /* The serialize_out and deserialize_in options are not documented - * in the usage section as they are not intended to work correctly - * using s2nd by itself. s2nc and s2nd are processes which close - * their TCP connection upon exit. This will cause an error if one - * peer serializes and exits and the other doesn't, as serialization - * depends on a continuous TCP connection with the peer. Therefore, our - * only usage of this feature is in our integ test framework, - * which serializes and deserializes both client and server at the - * same time. Do not expect these options to work when using s2nd alone. - */ - case OPT_SERIALIZE_OUT: - conn_settings.serialize_out = optarg; - break; - case OPT_DESERIALIZE_IN: - conn_settings.deserialize_in = optarg; - break; - case 'A': - alpn = optarg; - break; - case 'B': - non_blocking = 1; - break; - case 'L': - key_log_path = optarg; - break; - case 'P': - if (conn_settings.psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { - fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); - exit(1); - } - conn_settings.psk_optarg_list[conn_settings.psk_list_len++] = optarg; - break; - case 'E': - max_early_data = atoi(optarg); - break; - case 'N': - npn = true; - break; - case '?': - default: - fprintf(stdout, "getopt_long returned: %d", c); - usage(); - break; - } - } - - if (conn_settings.prefer_throughput && conn_settings.prefer_low_latency) { - fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); - exit(1); - } - - if (optind < argc) { - host = argv[optind++]; - } - - /* cppcheck-suppress duplicateCondition */ - if (optind < argc) { - port = argv[optind++]; - } - - if (!host || !port) { - usage(); - } - - if (setvbuf(stdin, NULL, _IONBF, 0) < 0) { - fprintf(stderr, "Error disabling buffering for stdin\n"); - exit(1); - } - - if (setvbuf(stdout, NULL, _IONBF, 0) < 0) { - fprintf(stderr, "Error disabling buffering for stdout\n"); - exit(1); - } - - memset(&hints, 0, sizeof(hints)); - - 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); - } - - if ((r = getaddrinfo(host, port, &hints, &ai)) < 0) { - fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(r)); - exit(1); - } - - if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { - fprintf(stderr, "socket error: %s\n", strerror(errno)); - exit(1); - } - - r = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int)) < 0) { - fprintf(stderr, "setsockopt error: %s\n", strerror(errno)); - exit(1); - } - - if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { - fprintf(stderr, "bind error: %s\n", strerror(errno)); - exit(1); - } - - if (listen(sockfd, 1) == -1) { - fprintf(stderr, "listen error: %s\n", strerror(errno)); - exit(1); - } - - GUARD_EXIT(s2n_init(), "Error running s2n_init()"); - printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); - - if (fips_mode) { - s2n_fips_mode mode = 0; - GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); - if (mode != S2N_FIPS_MODE_ENABLED) { - fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); - exit(1); - } - printf("s2nd entered FIPS mode\n"); - } - - printf("Listening on %s:%s\n", host, port); - - struct s2n_config *config = s2n_config_new(); - if (!config) { - print_s2n_error("Error getting new s2n config"); - exit(1); - } - - if (num_user_certificates != num_user_private_keys) { - fprintf(stderr, "Mismatched certificate(%d) and private key(%d) count!\n", num_user_certificates, num_user_private_keys); - exit(1); - } - - int num_certificates = 0; - if (num_user_certificates == 0) { - certificates[0] = default_certificate_chain; - private_keys[0] = default_private_key; - num_certificates = 1; - } else { - num_certificates = num_user_certificates; - } - - for (int i = 0; i < num_certificates; i++) { - struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); - GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, certificates[i], private_keys[i]), "Error getting certificate/key"); - - if (ocsp_response_file_path) { - int fd = open(ocsp_response_file_path, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Error opening OCSP response file: '%s'\n", strerror(errno)); - exit(1); - } - - struct stat st = { 0 }; - if (fstat(fd, &st) < 0) { - fprintf(stderr, "Error fstat-ing OCSP response file: '%s'\n", strerror(errno)); - exit(1); - } - - uint8_t *ocsp_response = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_response, st.st_size) < 0) { - fprintf(stderr, "Error adding ocsp response: '%s'\n", s2n_strerror(s2n_errno, "EN")); - exit(1); - } - - close(fd); - } - - GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); - } - - s2n_set_common_server_config(max_early_data, config, conn_settings, cipher_prefs, session_ticket_key_file_path); - - if (parallelize) { - struct sigaction sa; - - sa.sa_handler = SIG_IGN; -#if defined(SA_NOCLDWAIT) - sa.sa_flags = SA_NOCLDWAIT; -#endif - sigemptyset(&sa.sa_mask); - sigaction(SIGCHLD, &sa, NULL); - } - - if (alpn) { - const char *protocols[] = { alpn }; - GUARD_EXIT(s2n_config_set_protocol_preferences(config, protocols, s2n_array_len(protocols)), "Failed to set alpn"); - } - - if (send_buffer_size != 0) { - GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size."); - } - - if (npn) { - GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); - } - - if (conn_settings.serialize_out) { - GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), - "Error setting serialized version"); - } - - FILE *key_log_file = NULL; - - if (key_log_path) { - key_log_file = fopen(key_log_path, "a"); - GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); - GUARD_EXIT( - s2n_config_set_key_log_cb( - config, - key_log_callback, - (void *) key_log_file), - "Failed to set key log callback"); - } - - int fd = 0; - while ((fd = accept(sockfd, ai->ai_addr, &ai->ai_addrlen)) > 0) { - if (non_blocking) { - int flags = fcntl(sockfd, F_GETFL, 0); - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { - fprintf(stderr, "fcntl error: %s\n", strerror(errno)); - exit(1); - } - } - - if (!parallelize) { - int rc = handle_connection(fd, config, conn_settings); - close(fd); - if (rc < 0) { - exit(rc); - } - - /* If max_conns was set, then exit after it is reached. Otherwise - * unlimited connections are allow, so ignore the variable. */ - if (conn_settings.max_conns > 0) { - if (conn_settings.max_conns-- == 1) { - GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); - exit(0); - } - } - } else { - /* Fork Process, one for the Acceptor (parent), and another for the Handler (child). */ - pid_t child_pid = fork(); - - if (child_pid == 0) { - /* This is the Child Handler Thread. We should handle the connection, then exit. */ - int rc = handle_connection(fd, config, conn_settings); - close(fd); - _exit(rc); - } else if (child_pid == -1) { - close(fd); - print_s2n_error("Error calling fork(). Acceptor unable to start handler."); - exit(1); - } else { - /* This is the parent Acceptor Thread, continue listening for new connections */ - close(fd); - continue; - } - } - } - - if (key_log_file) { - fclose(key_log_file); - } - - GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#include +#if !defined(_MSC_VER) +#include +#endif + +#ifndef S2N_INTERN_LIBCRYPTO + #include + #include +#endif + +#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 + +/* + * s2nd is an example server that uses many s2n-tls APIs. + * It is intended for testing purposes only, and should not be used in production. + */ + +static char default_certificate_chain[] = + "-----BEGIN CERTIFICATE-----" + "MIIDHTCCAgWgAwIBAgIUPxywpg3/+VHmj8jJSvK62XC06zMwDQYJKoZIhvcNAQEL" + "BQAwHjEcMBoGA1UEAwwTczJuVGVzdEludGVybWVkaWF0ZTAgFw0yMDAxMjQwMTEw" + "MjFaGA8yMTE5MTIzMTAxMTAyMVowGDEWMBQGA1UEAwwNczJuVGVzdFNlcnZlcjCC" + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJUbMpdROM6cjb8xgr5kgKHn" + "JVDfhbLg4pxBwWwlayb6/N60JLG9KzWAWhZBmz+Px6kr/1dL6+bL3mLuNBCQpYBS" + "Pee2n7KL9PvsMYZmnYFyn94bXbjBCRxGR+a9lcGHLlZ4C+rrLNi9pUwxf7VIRglR" + "zwHWAFg5xTX6lCmziNM4OMkq8lHkLopHDUg5yI4VTc3EEGqDIf3+0BheIHcUFbIW" + "kFOjRDdL3lMGKEj0+LErzzbhJczBlRMqSMiuYeaWgORLpRNtMeNmbR8oLJFchpF0" + "A9fIO2/Yg+nclcDDhsUBkkfcIKRySGDumKLuYM+hOHp5vQo8tcvyQ6s3U5YULQUC" + "AwEAAaNXMFUwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUkVKVmfjICpx4fkvJO6YJ" + "mdoKz3owDgYDVR0PAQH/BAQDAgPoMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMA0G" + "CSqGSIb3DQEBCwUAA4IBAQBXoWDI1Gi9snC4K6S7E0AoLmGEPUWzc4fd4Cbj9PRp" + "mSKpsJOYjmneIV34WqnvUXrBkkzblEb9RdszN96WuRAaZJQegRtKOWN5Iggd4sHM" + "8XEx/LeJHc08uSb2d/TnhhOPALoJl/w6M5e6yOezCEJorsOXuVBcbuEKfne7oMA1" + "GziFnVPtwiwXxsX16KilsQRylnK0bV/x1BOgYByCDcXorMndsAYjn4yG1D4l8TbC" + "kCtK1bafEVoASpOFQ8tSeOXBL7Fvw9mFFzs3/ajBTz2nBLDsnP8XH5C/vy8wNGSd" + "Tdcs7DRLYhNJxYopcMgCwyyCAtEFcHkovCSrJ6HUl/ko" + "-----END CERTIFICATE-----" + "-----BEGIN CERTIFICATE-----" + "MIIDCTCCAfGgAwIBAgIUfdybeOdDMd7cPXk6RTcEqeM3IEIwDQYJKoZIhvcNAQEL" + "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwOTUzWhgPMjEx" + "OTEyMzEwMTA5NTNaMB4xHDAaBgNVBAMME3MyblRlc3RJbnRlcm1lZGlhdGUwggEi" + "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2NsDkrZjYbyVeF1R9337y9OHM" + "C2xSRGB6SHrVG1bQZlPxI+E6DqDJcMB4tFLkA7AJxxRLxA7KvO9PzcHAlsqvYcMV" + "gOSAjUZ0Eiwwf6Rtgo2yByj2n1K5XDN3bpt1rROD0BIEnaU9GZd3U0QUYHBRfp0E" + "IdeWuRrlFbPpWXnBaQB/2jEfCuZzpPOiKMWt99GQ4bFBOSzpYdXLALGfb15Kr6RF" + "YoMlsyeijNeePxLeYgracu+vzJLvEzx1U7OGnlWz+VKBw/mz3gABqFfxurN5E8yb" + "4AWJ5kEUJobYcxwe+DoimPdPTWgByJlMpKjfIbnroz/oTZMiNfUCtKT3GTejAgMB" + "AAGjRTBDMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFEasSJIPBZTXyYjI" + "CN2m1Ttz3sUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEAxveh" + "GKJPu7DXjoMePzlRGML2iIDT6MgKpsMnO5sNgUbJTFV3KeuASRm1SXVrVFHcQDov" + "l9P10ff0J9KOVrRCawMZZxjjtNAIrSW0G7fwmTgJMTuM5vaaGRjKy018LApcr//Q" + "Nwjh4sw9KOtNIE9krT06kli9zjsgr/EWwPCHSin8oONDgCNn1WgtrSMexsF1BSzU" + "OTq+nyn4nOPOEUthjmepG2eDkd17MNJ6GdKYnFRmC+ctSH028akERhz+EtavU4Cd" + "2eSFTKtbxOuZXyfsOwjhrufp/Ss9i57x3XotBNJ8Fv7VpxI19+Zag4DMGzd3Pisu" + "Q1VpfValnMGtVWPleg==" + "-----END CERTIFICATE-----" + "-----BEGIN CERTIFICATE-----" + "MIIC/jCCAeagAwIBAgIUFFjxpSf0mUsrVbyLPQhccDYfixowDQYJKoZIhvcNAQEL" + "BQAwFjEUMBIGA1UEAwwLczJuVGVzdFJvb3QwIBcNMjAwMTI0MDEwODIyWhgPMjEx" + "OTEyMzEwMTA4MjJaMBYxFDASBgNVBAMMC3MyblRlc3RSb290MIIBIjANBgkqhkiG" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3AaOAlkcxJHryCI9SfwB9q4PA53hv5tz4ZL" + "be37b69v58mfP+D18cWIBHUmkmN6gWWoWZ/9hv75pxcNXW0zPn7+wOVvXLUjtmkq" + "1IGT/mykhasw00viaBFAuBHZ5iLwfc4/cjUFAPVCKLmfv5Xs7TJVzWA/0mR4r1h8" + "uFqqXczkVMklIbsOIrlZXz8ifQs3DpFA2FeoziEh+Pcb4c3QBPgCHFDEGyTSdqo9" + "+NbS+iRlw0T6tqUOpC0DdKXo/3mJNBmy4XPahTi9zgsu7b+UVqemL7eXXf/iSr5y" + "iwJKJjz+N/rLpcF1VJtF8q0fpHagzljQaN7/emjg7BplUUyLawIDAQABo0IwQDAP" + "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTDmXkyQEJ7ZciyE4KF7wAJKDxMfDAO" + "BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAFobyhsc7mYoGaA7N4Pp" + "it+MQZZNzWte5vWal/3/2V7ZGrJsgeCPwLblzzTmey85RilX6ovMQHEqT1vBFSHq" + "nntMZnHkEl2QLU8XopJWR4MXK7LzjjQYaXiZhGbJbtylVSfATAa/ZzdgjBx1C8aD" + "IM1+ELGCP/UHD0YEJkFoxSUwXGAXoV8I+cPDAWHC6VnC4mY8qubhx95FpX02ERnz" + "1Cw2YWtntyO8P52dEJD1+0EJjtVX4Bj5wwgJHHbDkPP1IzFrR/uBC2LCjtRY+UtZ" + "kfoDfWu2tslkLK7/LaC5qZyCPKnpPHLLz8gUWKlvbuejM99FTlBg/tcH+bv5x7WB" + "MZ8=" + "-----END CERTIFICATE-----"; + +static char default_private_key[] = + "-----BEGIN RSA PRIVATE KEY-----" + "MIIEogIBAAKCAQEAlRsyl1E4zpyNvzGCvmSAoeclUN+FsuDinEHBbCVrJvr83rQk" + "sb0rNYBaFkGbP4/HqSv/V0vr5sveYu40EJClgFI957afsov0++wxhmadgXKf3htd" + "uMEJHEZH5r2VwYcuVngL6uss2L2lTDF/tUhGCVHPAdYAWDnFNfqUKbOI0zg4ySry" + "UeQuikcNSDnIjhVNzcQQaoMh/f7QGF4gdxQVshaQU6NEN0veUwYoSPT4sSvPNuEl" + "zMGVEypIyK5h5paA5EulE20x42ZtHygskVyGkXQD18g7b9iD6dyVwMOGxQGSR9wg" + "pHJIYO6You5gz6E4enm9Cjy1y/JDqzdTlhQtBQIDAQABAoIBAGTaSJXg8jON4LJ5" + "op11DSx1U+An0B71zVEziMjFZnyvN2rLHia6dQdzEXwMVB3h+oKKp+M8DwvEyV7R" + "D5ZEwCzTc9vOwqXZ1JKxZ64oqlBsX4WzrOjSaH8fanK/uRN1g/ooqKb0+xh+7ddj" + "g6XyhKy5EPOE9Ca4rJOeMakjLmDuleQecT/DixYV6azhfaJoD70XZJWv3YzSpu/X" + "Ma+i3of0alsG/lROjNtEXE3nKzcTUgyAUoQeYRwCVpgssg/4VAUPJNDP4dVmxW8f" + "eNmjlTyXmR9S08SXkqmCHe2mBUsZY9nqcDE6ZWILZKFWIfZD9W+j2ce0FMvcc9kz" + "psxaUQECgYEAxqwsb5aQy6HBF54tdkHbUQEJMelSLNW0G1GUrcLB7eqL7qo3dUA8" + "8PDQ/dTwmmJ7aE0SK2xkQDVKXNbV4OvUNgP6tbzLWEbvmuFAEg5X1jFH2VSdwQhl" + "RDwTQw3wPZ5udy64L6gmsdDch+I7l1v4ex66RWFW+4WIs1altsLiJa0CgYEAwCGW" + "2cjtZ3kIzWgxf7DdnoUTwBM1ATBUYvx7uqVq+dbc/p8cSeMSPz3LUluaVJ2EOjEV" + "QWhx0Ih5qeitzReHRU0OHgxEgjbpJwhseD9O5POSd+fE3TtDQArOxyw4CIJKk4Z2" + "QmqzaO/LboN3Tp+/N9zfVoNZKHcCNra/uKNTH7kCgYA3QFazSdpG51s96D2Yb8RA" + "iNs3yD2UPnJyToPctxcbxWjZHPmDYDQShcZ5cSjgppbPcO+mp+RRfwCJRS4B+VPx" + "GbY1qKWcjU3BcvdQjjCbXuUuabvdnSocieCJe2zelhr+hj2u80KfnQhXufD8rRUz" + "mF4RQXrhREe6KFS5uQUPmQKBgE4rXFyvSyfWLqajxb/WDdT4/9gd+GrLZwn+/7go" + "pSWRLcjKo4/MOxhP4/FWI6xZifrDDYrXG7dkT1u5tzzCXd7sQtom05jHDoU7ACbM" + "WyT7lJQEUCxSeEIOI6MVcpbDq+PpySOsleIT7gjApEHw7LOlwZhJSHUWNmhcYhSV" + "HrTBAoGADAvBqV7JItjm2+qkXXEdPVzOunqjQdnXZjMAJ75PhHnLCCfnTRu53hT3" + "JxDETLLa/r42PlqGZ6bqSW+C+ObgYOvvySqvX8CE9o208ZwCLjHYxuYLH/86Lppr" + "ggF9KQ0xWz7Km3GXv5+bwM5bcgt1A/s6sZCimXuj3Fle3RqOTF0=" + "-----END RSA PRIVATE KEY-----"; + +#define OPT_BUFFERED_SEND 1000 +#define OPT_SERIALIZE_OUT 1001 +#define OPT_DESERIALIZE_IN 1002 + +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"); + fprintf(stderr, "\n Options:\n\n"); + fprintf(stderr, " -a [protocol]\n"); + fprintf(stderr, " --alpn [protocol]\n"); + fprintf(stderr, " Sets a single application protocol supported by this server.\n"); + fprintf(stderr, " -c [version_string]\n"); + fprintf(stderr, " --ciphers [version_string]\n"); + fprintf(stderr, " Set the cipher preference version string. Defaults to \"default\" \n"); + fprintf(stderr, " --enter-fips-mode\n"); + fprintf(stderr, " Enter libcrypto's FIPS mode. The linked version of OpenSSL must be built with the FIPS module.\n"); + fprintf(stderr, " --cert\n"); + fprintf(stderr, " Path to a PEM encoded certificate [chain]. Option can be repeated to load multiple certs.\n"); + fprintf(stderr, " --key\n"); + fprintf(stderr, " Path to a PEM encoded private key that matches cert. Option can be repeated to load multiple certs.\n"); + fprintf(stderr, " -m\n"); + fprintf(stderr, " --mutualAuth\n"); + fprintf(stderr, " Request a Client Certificate. Any RSA Certificate will be accepted.\n"); + fprintf(stderr, " -n\n"); + fprintf(stderr, " --negotiate\n"); + fprintf(stderr, " Only perform tls handshake and then shutdown the connection\n"); + fprintf(stderr, " --parallelize\n"); + fprintf(stderr, " Create a new Connection handler thread for each new connection. Useful for tests with lots of connections.\n"); + fprintf(stderr, " Warning: this option isn't compatible with TLS Resumption, since each thread gets its own Session cache.\n"); + fprintf(stderr, " --prefer-low-latency\n"); + fprintf(stderr, " Prefer low latency by clamping maximum outgoing record size at 1500.\n"); + fprintf(stderr, " --prefer-throughput\n"); + fprintf(stderr, " Prefer throughput by raising maximum outgoing record size to 16k\n"); + fprintf(stderr, " --enable-mfl\n"); + fprintf(stderr, " Accept client's TLS maximum fragment length extension request\n"); + fprintf(stderr, " --ocsp\n"); + fprintf(stderr, " Path to a DER formatted OCSP response for stapling\n"); + fprintf(stderr, " -s\n"); + fprintf(stderr, " --self-service-blinding\n"); + fprintf(stderr, " Don't introduce 10-30 second delays on TLS Handshake errors. \n"); + fprintf(stderr, " Warning: this should only be used for testing since skipping blinding may allow timing side channels.\n"); + fprintf(stderr, " -t,--ca-file [file path]\n"); + fprintf(stderr, " Location of trust store CA file (PEM format). If neither -t or -d are specified. System defaults will be used."); + fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); + fprintf(stderr, " -d,--ca-dir [directory path]\n"); + fprintf(stderr, " Directory containing hashed trusted certs. If neither -t or -d are specified. System defaults will be used."); + fprintf(stderr, " This option is only used if mutual auth is enabled.\n"); + fprintf(stderr, " -i,--insecure\n"); + fprintf(stderr, " Turns off certification validation altogether.\n"); + fprintf(stderr, " --stk-file\n"); + fprintf(stderr, " Location of key file used for encryption and decryption of session ticket.\n"); + fprintf(stderr, " -T,--no-session-ticket\n"); + fprintf(stderr, " Disable session ticket for resumption.\n"); + fprintf(stderr, " -C,--corked-io\n"); + fprintf(stderr, " Turn on corked io\n"); + fprintf(stderr, " --non-blocking\n"); + fprintf(stderr, " Set the non-blocking flag on the connection's socket.\n"); + fprintf(stderr, " -w --https-server\n"); + fprintf(stderr, " Run s2nd in a simple https server mode.\n"); + fprintf(stderr, " -b --https-bench \n"); + fprintf(stderr, " Send number of bytes in https server mode to test throughput.\n"); + fprintf(stderr, " -L --key-log \n"); + fprintf(stderr, " Enable NSS key logging into the provided path\n"); + fprintf(stderr, " -P --psk \n" + " A comma-separated list of psk parameters in this order: psk_identity, psk_secret and psk_hmac_alg.\n" + " Note that the maximum number of permitted psks is 10, the psk-secret is hex-encoded, and whitespace is not allowed before or after the commas.\n" + " Ex: --psk psk_id,psk_secret,SHA256 --psk shared_id,shared_secret,SHA384.\n"); + fprintf(stderr, " -E, --max-early-data \n"); + fprintf(stderr, " Sets maximum early data allowed in session tickets. \n"); + fprintf(stderr, " -N --npn \n"); + fprintf(stderr, " Indicates support for the NPN extension. The '--alpn' option MUST be used with this option to signal the protocols supported."); + fprintf(stderr, " -h,--help\n"); + fprintf(stderr, " Display this message and quit.\n"); + fprintf(stderr, " --buffered-send \n"); + fprintf(stderr, " Set s2n_send to buffer up to bytes before sending records over the wire.\n"); + fprintf(stderr, " -X, --max-conns \n"); + fprintf(stderr, " Sets the max number of connections s2nd will accept before shutting down.\n"); + /* clang-format on */ + exit(1); +} + +int handle_connection(int fd, struct s2n_config *config, struct conn_settings settings) +{ + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + if (!conn) { + print_s2n_error("Error getting new s2n connection"); + S2N_ERROR_PRESERVE_ERRNO(); + } + + GUARD_EXIT(s2n_setup_server_connection(conn, fd, config, settings), "Error setting up connection"); + + if (!settings.deserialize_in && negotiate(conn, fd) != S2N_SUCCESS) { + if (settings.mutual_auth) { + if (!s2n_connection_client_cert_used(conn)) { + print_s2n_error("Error: Mutual Auth was required, but not negotiated"); + } + } + + /* Error is printed in negotiate */ + S2N_ERROR_PRESERVE_ERRNO(); + } + + GUARD_EXIT(s2n_connection_free_handshake(conn), "Error freeing handshake memory after negotiation"); + + if (settings.https_server) { + https(conn, settings.https_bench); + } else if (!settings.only_negotiate) { + bool stop_echo = false; + echo(conn, fd, &stop_echo); + } + + if (settings.serialize_out) { + GUARD_RETURN(s2n_connection_serialize_out(conn, settings.serialize_out), "Error serializing connection"); + } else { + GUARD_RETURN(wait_for_shutdown(conn, fd), "Error closing connection"); + } + + GUARD_RETURN(s2n_connection_wipe(conn), "Error wiping connection"); + + GUARD_RETURN(s2n_connection_free(conn), "Error freeing connection"); + + return 0; +} + +int main(int argc, char *const *argv) +{ + struct addrinfo hints, *ai = NULL; + int r = 0, sockfd = 0; + + /* required args */ + const char *host = NULL; + const char *port = NULL; + + const char *ocsp_response_file_path = NULL; + const char *session_ticket_key_file_path = NULL; + const char *cipher_prefs = "default"; + const char *alpn = NULL; + const char *key_log_path = NULL; + + /* The certificates provided by the user. If there are none provided, we will use the hardcoded default cert. + * The associated private key for each cert will be at the same index in private_keys. If the user mixes up the + * order of --cert --key for a given cert/key pair, s2n will fail to load the cert and s2nd will exit. + */ + int num_user_certificates = 0; + int num_user_private_keys = 0; + const char *certificates[MAX_CERTIFICATES] = { 0 }; + const char *private_keys[MAX_CERTIFICATES] = { 0 }; + + struct conn_settings conn_settings = { 0 }; + int fips_mode = 0; + int parallelize = 0; + int non_blocking = 0; + long int bytes = 0; + conn_settings.session_ticket = 1; + conn_settings.session_cache = 1; + conn_settings.max_conns = -1; + conn_settings.psk_list_len = 0; + int max_early_data = 0; + uint32_t send_buffer_size = 0; + bool npn = false; + + struct option long_options[] = { + { "ciphers", required_argument, NULL, 'c' }, + { "enable-mfl", no_argument, NULL, 'e' }, + { "enter-fips-mode", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "key", required_argument, NULL, 'k' }, + { "prefer-low-latency", no_argument, NULL, 'l' }, + { "mutualAuth", no_argument, NULL, 'm' }, + { "negotiate", no_argument, NULL, 'n' }, + { "ocsp", required_argument, NULL, 'o' }, + { "parallelize", no_argument, ¶llelize, 1 }, + { "prefer-throughput", no_argument, NULL, 'p' }, + { "cert", required_argument, NULL, 'r' }, + { "self-service-blinding", no_argument, NULL, 's' }, + { "ca-dir", required_argument, 0, 'd' }, + { "ca-file", required_argument, 0, 't' }, + { "insecure", no_argument, 0, 'i' }, + { "stk-file", required_argument, 0, 'a' }, + { "serialize-out", required_argument, 0, OPT_SERIALIZE_OUT }, + { "deserialize-in", required_argument, 0, OPT_DESERIALIZE_IN }, + { "no-session-ticket", no_argument, 0, 'T' }, + { "corked-io", no_argument, 0, 'C' }, + { "max-conns", optional_argument, 0, 'X' }, + { "tls13", no_argument, 0, '3' }, + { "https-server", no_argument, 0, 'w' }, + { "https-bench", required_argument, 0, 'b' }, + { "alpn", required_argument, 0, 'A' }, + { "npn", no_argument, 0, 'N' }, + { "non-blocking", no_argument, 0, 'B' }, + { "key-log", required_argument, 0, 'L' }, + { "psk", required_argument, 0, 'P' }, + { "max-early-data", required_argument, 0, 'E' }, + { "buffered-send", required_argument, 0, OPT_BUFFERED_SEND }, + /* Per getopt(3) the last element of the array has to be filled with all zeros */ + { 0 }, + }; + while (1) { + int option_index = 0; + int c = getopt_long(argc, argv, "c:hmnst:d:iTCX::wb:A:P:E:", long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 0: + /* getopt_long() returns 0 if an option.flag is non-null (Eg "parallelize") */ + break; + case 'C': + conn_settings.use_corked_io = 1; + break; + case 'c': + cipher_prefs = optarg; + break; + case 'e': + conn_settings.enable_mfl = 1; + break; + case 'f': + fips_mode = 1; + break; + case 'h': + usage(); + break; + case 'k': + if (num_user_private_keys == MAX_CERTIFICATES) { + fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); + exit(1); + } + private_keys[num_user_private_keys] = load_file_to_cstring(optarg); + num_user_private_keys++; + break; + case 'l': + conn_settings.prefer_low_latency = 1; + break; + case 'm': + conn_settings.mutual_auth = 1; + break; + case 'n': + conn_settings.only_negotiate = 1; + break; + case 'o': + ocsp_response_file_path = optarg; + break; + case 'p': + conn_settings.prefer_throughput = 1; + break; + case 'r': + if (num_user_certificates == MAX_CERTIFICATES) { + fprintf(stderr, "Cannot support more than %d certificates!\n", MAX_CERTIFICATES); + exit(1); + } + certificates[num_user_certificates] = load_file_to_cstring(optarg); + num_user_certificates++; + break; + case 's': + conn_settings.self_service_blinding = 1; + break; + case 'd': + conn_settings.ca_dir = optarg; + break; + case 't': + conn_settings.ca_file = optarg; + break; + case 'i': + conn_settings.insecure = 1; + break; + case 'a': + session_ticket_key_file_path = optarg; + break; + case 'T': + conn_settings.session_ticket = 0; + break; + case '3': + /* Do nothing -- this argument is deprecated */ + break; + case 'X': + if (optarg == NULL) { + conn_settings.max_conns = 1; + } else { + conn_settings.max_conns = atoi(optarg); + } + break; + case 'w': + fprintf(stdout, "Running s2nd in simple https server mode\n"); + conn_settings.https_server = 1; + break; + case 'b': + bytes = strtoul(optarg, NULL, 10); + GUARD_EXIT(bytes, "https-bench bytes needs to be some positive long value."); + conn_settings.https_bench = bytes; + break; + case OPT_BUFFERED_SEND: { + intmax_t send_buffer_size_scanned_value = strtoimax(optarg, 0, 10); + if (send_buffer_size_scanned_value > UINT32_MAX || send_buffer_size_scanned_value < 0) { + fprintf(stderr, " must be a positive 32 bit value\n"); + exit(1); + } + send_buffer_size = (uint32_t) send_buffer_size_scanned_value; + break; + } + /* The serialize_out and deserialize_in options are not documented + * in the usage section as they are not intended to work correctly + * using s2nd by itself. s2nc and s2nd are processes which close + * their TCP connection upon exit. This will cause an error if one + * peer serializes and exits and the other doesn't, as serialization + * depends on a continuous TCP connection with the peer. Therefore, our + * only usage of this feature is in our integ test framework, + * which serializes and deserializes both client and server at the + * same time. Do not expect these options to work when using s2nd alone. + */ + case OPT_SERIALIZE_OUT: + conn_settings.serialize_out = optarg; + break; + case OPT_DESERIALIZE_IN: + conn_settings.deserialize_in = optarg; + break; + case 'A': + alpn = optarg; + break; + case 'B': + non_blocking = 1; + break; + case 'L': + key_log_path = optarg; + break; + case 'P': + if (conn_settings.psk_list_len >= S2N_MAX_PSK_LIST_LENGTH) { + fprintf(stderr, "Error setting psks, maximum number of psks permitted is 10.\n"); + exit(1); + } + conn_settings.psk_optarg_list[conn_settings.psk_list_len++] = optarg; + break; + case 'E': + max_early_data = atoi(optarg); + break; + case 'N': + npn = true; + break; + case '?': + default: + fprintf(stdout, "getopt_long returned: %d", c); + usage(); + break; + } + } + + if (conn_settings.prefer_throughput && conn_settings.prefer_low_latency) { + fprintf(stderr, "prefer-throughput and prefer-low-latency options are mutually exclusive\n"); + exit(1); + } + + if (optind < argc) { + host = argv[optind++]; + } + + /* cppcheck-suppress duplicateCondition */ + if (optind < argc) { + port = argv[optind++]; + } + + if (!host || !port) { + usage(); + } + + if (setvbuf(stdin, NULL, _IONBF, 0) < 0) { + fprintf(stderr, "Error disabling buffering for stdin\n"); + exit(1); + } + + if (setvbuf(stdout, NULL, _IONBF, 0) < 0) { + fprintf(stderr, "Error disabling buffering for stdout\n"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + + 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); + } + + if ((r = getaddrinfo(host, port, &hints, &ai)) < 0) { + fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(r)); + exit(1); + } + + if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { + fprintf(stderr, "socket error: %s\n", strerror(errno)); + exit(1); + } + + r = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(int)) < 0) { + fprintf(stderr, "setsockopt error: %s\n", strerror(errno)); + exit(1); + } + + if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { + fprintf(stderr, "bind error: %s\n", strerror(errno)); + exit(1); + } + + if (listen(sockfd, 1) == -1) { + fprintf(stderr, "listen error: %s\n", strerror(errno)); + exit(1); + } + + GUARD_EXIT(s2n_init(), "Error running s2n_init()"); + printf("libcrypto: %s\n", s2n_libcrypto_get_version_name()); + + if (fips_mode) { + s2n_fips_mode mode = 0; + GUARD_EXIT(s2n_get_fips_mode(&mode), "Unable to retrieve FIPS mode"); + if (mode != S2N_FIPS_MODE_ENABLED) { + fprintf(stderr, "FIPS mode not enabled: libcrypto does not support FIPS\n"); + exit(1); + } + printf("s2nd entered FIPS mode\n"); + } + + printf("Listening on %s:%s\n", host, port); + + struct s2n_config *config = s2n_config_new(); + if (!config) { + print_s2n_error("Error getting new s2n config"); + exit(1); + } + + if (num_user_certificates != num_user_private_keys) { + fprintf(stderr, "Mismatched certificate(%d) and private key(%d) count!\n", num_user_certificates, num_user_private_keys); + exit(1); + } + + int num_certificates = 0; + if (num_user_certificates == 0) { + certificates[0] = default_certificate_chain; + private_keys[0] = default_private_key; + num_certificates = 1; + } else { + num_certificates = num_user_certificates; + } + + for (int i = 0; i < num_certificates; i++) { + struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(); + GUARD_EXIT(s2n_cert_chain_and_key_load_pem(chain_and_key, certificates[i], private_keys[i]), "Error getting certificate/key"); + + if (ocsp_response_file_path) { + int fd = open(ocsp_response_file_path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error opening OCSP response file: '%s'\n", strerror(errno)); + exit(1); + } + + struct stat st = { 0 }; + if (fstat(fd, &st) < 0) { + fprintf(stderr, "Error fstat-ing OCSP response file: '%s'\n", strerror(errno)); + exit(1); + } + + uint8_t *ocsp_response = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_response, st.st_size) < 0) { + fprintf(stderr, "Error adding ocsp response: '%s'\n", s2n_strerror(s2n_errno, "EN")); + exit(1); + } + + close(fd); + } + + GUARD_EXIT(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key), "Error setting certificate/key"); + } + + s2n_set_common_server_config(max_early_data, config, conn_settings, cipher_prefs, session_ticket_key_file_path); + + if (parallelize) { + struct sigaction sa; + + sa.sa_handler = SIG_IGN; +#if defined(SA_NOCLDWAIT) + sa.sa_flags = SA_NOCLDWAIT; +#endif + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, NULL); + } + + if (alpn) { + const char *protocols[] = { alpn }; + GUARD_EXIT(s2n_config_set_protocol_preferences(config, protocols, s2n_array_len(protocols)), "Failed to set alpn"); + } + + if (send_buffer_size != 0) { + GUARD_EXIT(s2n_config_set_send_buffer_size(config, send_buffer_size), "Error setting send buffer size."); + } + + if (npn) { + GUARD_EXIT(s2n_config_set_npn(config, 1), "Error setting npn support"); + } + + if (conn_settings.serialize_out) { + GUARD_EXIT(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), + "Error setting serialized version"); + } + + FILE *key_log_file = NULL; + + if (key_log_path) { + key_log_file = fopen(key_log_path, "a"); + GUARD_EXIT(key_log_file == NULL ? S2N_FAILURE : S2N_SUCCESS, "Failed to open key log file"); + GUARD_EXIT( + s2n_config_set_key_log_cb( + config, + key_log_callback, + (void *) key_log_file), + "Failed to set key log callback"); + } + + int fd = 0; + while ((fd = accept(sockfd, ai->ai_addr, &ai->ai_addrlen)) > 0) { + if (non_blocking) { + int flags = fcntl(sockfd, F_GETFL, 0); + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + fprintf(stderr, "fcntl error: %s\n", strerror(errno)); + exit(1); + } + } + + if (!parallelize) { + int rc = handle_connection(fd, config, conn_settings); + close(fd); + if (rc < 0) { + exit(rc); + } + + /* If max_conns was set, then exit after it is reached. Otherwise + * unlimited connections are allow, so ignore the variable. */ + if (conn_settings.max_conns > 0) { + if (conn_settings.max_conns-- == 1) { + GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); + exit(0); + } + } + } else { + /* Fork Process, one for the Acceptor (parent), and another for the Handler (child). */ + pid_t child_pid = fork(); + + if (child_pid == 0) { + /* This is the Child Handler Thread. We should handle the connection, then exit. */ + int rc = handle_connection(fd, config, conn_settings); + close(fd); + _exit(rc); + } else if (child_pid == -1) { + close(fd); + print_s2n_error("Error calling fork(). Acceptor unable to start handler."); + exit(1); + } else { + /* This is the parent Acceptor Thread, continue listening for new connections */ + close(fd); + continue; + } + } + } + + if (key_log_file) { + fclose(key_log_file); + } + + GUARD_EXIT(s2n_cleanup(), "Error running s2n_cleanup()"); + + return 0; +} diff --git a/cmake/modules/Findcrypto.cmake b/cmake/modules/Findcrypto.cmake index 492084c14d8..1bdc7efca08 100644 --- a/cmake/modules/Findcrypto.cmake +++ b/cmake/modules/Findcrypto.cmake @@ -1,129 +1,129 @@ -# - Try to find LibCrypto include dirs and libraries -# -# Usage of this module as follows: -# -# find_package(crypto) -# -# Variables used by this module, they can change the default behaviour and need -# to be set before calling find_package: -# -# Variables defined by this module: -# -# crypto_FOUND System has libcrypto, include and library dirs found -# crypto_INCLUDE_DIR The crypto include directories. -# crypto_LIBRARY The crypto library, depending on the value of BUILD_SHARED_LIBS. -# crypto_SHARED_LIBRARY The path to libcrypto.so -# crypto_STATIC_LIBRARY The path to libcrypto.a - -if (TARGET crypto OR TARGET AWS::crypto) - if (TARGET crypto) - set(TARGET_NAME "crypto") - else() - set(TARGET_NAME "AWS::crypto") - endif() - - get_target_property(crypto_INCLUDE_DIR ${TARGET_NAME} INTERFACE_INCLUDE_DIRECTORIES) - message(STATUS "S2N found target: ${TARGET_NAME}") - message(STATUS "crypto Include Dir: ${crypto_INCLUDE_DIR}") - set(CRYPTO_FOUND true) - set(crypto_FOUND true) -else() - find_package(OpenSSL MODULE REQUIRED) - if(OPENSSL_FOUND) - if (TARGET OpenSSL::Crypto) - set(crypto_LIBRARY OpenSSL::Crypto) - get_target_property(crypto_INCLUDE_DIR OpenSSL::Crypto INTERFACE_INCLUDE_DIRECTORIES) - # If property wasn't set, fallback to the variable - if(NOT crypto_INCLUDE_DIR) - set(crypto_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) - endif() - else() - set(crypto_LIBRARY ${OPENSSL_CRYPTO_LIBRARY}) - set(crypto_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) - endif() - set(crypto_FOUND true) - set(CRYPTO_FOUND true) - endif() - - if(NOT crypto_FOUND) - find_path(crypto_INCLUDE_DIR - NAMES openssl/crypto.h - HINTS - "${CMAKE_PREFIX_PATH}" - "${CMAKE_INSTALL_PREFIX}" - PATH_SUFFIXES include - ) - - find_library(crypto_SHARED_LIBRARY - NAMES libcrypto.so libcrypto.dylib libcrypto.dll.a crypto.lib libcrypto.lib - HINTS - "${CMAKE_PREFIX_PATH}" - "${CMAKE_INSTALL_PREFIX}" - PATH_SUFFIXES build/crypto build lib64 lib - ) - - find_library(crypto_STATIC_LIBRARY - NAMES libcrypto.a libcrypto.lib crypto.lib - HINTS - "${CMAKE_PREFIX_PATH}" - "${CMAKE_INSTALL_PREFIX}" - PATH_SUFFIXES build/crypto build lib64 lib - ) - - if (NOT crypto_LIBRARY) - if (BUILD_SHARED_LIBS OR S2N_USE_CRYPTO_SHARED_LIBS) - if (crypto_SHARED_LIBRARY) - set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) - else() - set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) - endif() - else() - if (crypto_STATIC_LIBRARY) - set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) - else() - set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) - endif() - endif() - endif() - endif() - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(crypto DEFAULT_MSG - crypto_LIBRARY - crypto_INCLUDE_DIR - ) - - mark_as_advanced( - crypto_ROOT_DIR - crypto_INCLUDE_DIR - crypto_LIBRARY - crypto_SHARED_LIBRARY - crypto_STATIC_LIBRARY - ) - - if(CRYPTO_FOUND OR crypto_FOUND) - set(CRYPTO_FOUND true) - set(crypto_FOUND true) - - message(STATUS "LibCrypto Include Dir: ${crypto_INCLUDE_DIR}") - message(STATUS "LibCrypto Shared Lib: ${crypto_SHARED_LIBRARY}") - message(STATUS "LibCrypto Static Lib: ${crypto_STATIC_LIBRARY}") - - if (NOT TARGET crypto AND NOT TARGET AWS::crypto) - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads REQUIRED) - if (TARGET OpenSSL::Crypto) - add_library(AWS::crypto ALIAS OpenSSL::Crypto) - elseif(EXISTS "${crypto_LIBRARY}") - add_library(AWS::crypto UNKNOWN IMPORTED) - set_target_properties(AWS::crypto PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${crypto_INCLUDE_DIR}") - set_target_properties(AWS::crypto PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${crypto_LIBRARY}") - add_dependencies(AWS::crypto Threads::Threads) - endif() - endif() - endif() - -endif() +# - Try to find LibCrypto include dirs and libraries +# +# Usage of this module as follows: +# +# find_package(crypto) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# Variables defined by this module: +# +# crypto_FOUND System has libcrypto, include and library dirs found +# crypto_INCLUDE_DIR The crypto include directories. +# crypto_LIBRARY The crypto library, depending on the value of BUILD_SHARED_LIBS. +# crypto_SHARED_LIBRARY The path to libcrypto.so +# crypto_STATIC_LIBRARY The path to libcrypto.a + +if (TARGET crypto OR TARGET AWS::crypto) + if (TARGET crypto) + set(TARGET_NAME "crypto") + else() + set(TARGET_NAME "AWS::crypto") + endif() + + get_target_property(crypto_INCLUDE_DIR ${TARGET_NAME} INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "S2N found target: ${TARGET_NAME}") + message(STATUS "crypto Include Dir: ${crypto_INCLUDE_DIR}") + set(CRYPTO_FOUND true) + set(crypto_FOUND true) +else() + find_package(OpenSSL MODULE REQUIRED) + if(OPENSSL_FOUND) + if (TARGET OpenSSL::Crypto) + set(crypto_LIBRARY OpenSSL::Crypto) + get_target_property(crypto_INCLUDE_DIR OpenSSL::Crypto INTERFACE_INCLUDE_DIRECTORIES) + # If property wasn't set, fallback to the variable + if(NOT crypto_INCLUDE_DIR) + set(crypto_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) + endif() + else() + set(crypto_LIBRARY ${OPENSSL_CRYPTO_LIBRARY}) + set(crypto_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) + endif() + set(crypto_FOUND true) + set(CRYPTO_FOUND true) + endif() + + if(NOT crypto_FOUND) + find_path(crypto_INCLUDE_DIR + NAMES openssl/crypto.h + HINTS + "${CMAKE_PREFIX_PATH}" + "${CMAKE_INSTALL_PREFIX}" + PATH_SUFFIXES include + ) + + find_library(crypto_SHARED_LIBRARY + NAMES libcrypto.so libcrypto.dylib libcrypto.dll.a crypto.lib libcrypto.lib + HINTS + "${CMAKE_PREFIX_PATH}" + "${CMAKE_INSTALL_PREFIX}" + PATH_SUFFIXES build/crypto build lib64 lib + ) + + find_library(crypto_STATIC_LIBRARY + NAMES libcrypto.a libcrypto.lib crypto.lib + HINTS + "${CMAKE_PREFIX_PATH}" + "${CMAKE_INSTALL_PREFIX}" + PATH_SUFFIXES build/crypto build lib64 lib + ) + + if (NOT crypto_LIBRARY) + if (BUILD_SHARED_LIBS OR S2N_USE_CRYPTO_SHARED_LIBS) + if (crypto_SHARED_LIBRARY) + set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) + else() + set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) + endif() + else() + if (crypto_STATIC_LIBRARY) + set(crypto_LIBRARY ${crypto_STATIC_LIBRARY}) + else() + set(crypto_LIBRARY ${crypto_SHARED_LIBRARY}) + endif() + endif() + endif() + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(crypto DEFAULT_MSG + crypto_LIBRARY + crypto_INCLUDE_DIR + ) + + mark_as_advanced( + crypto_ROOT_DIR + crypto_INCLUDE_DIR + crypto_LIBRARY + crypto_SHARED_LIBRARY + crypto_STATIC_LIBRARY + ) + + if(CRYPTO_FOUND OR crypto_FOUND) + set(CRYPTO_FOUND true) + set(crypto_FOUND true) + + message(STATUS "LibCrypto Include Dir: ${crypto_INCLUDE_DIR}") + message(STATUS "LibCrypto Shared Lib: ${crypto_SHARED_LIBRARY}") + message(STATUS "LibCrypto Static Lib: ${crypto_STATIC_LIBRARY}") + + if (NOT TARGET crypto AND NOT TARGET AWS::crypto) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + if (TARGET OpenSSL::Crypto) + add_library(AWS::crypto ALIAS OpenSSL::Crypto) + elseif(EXISTS "${crypto_LIBRARY}") + add_library(AWS::crypto UNKNOWN IMPORTED) + set_target_properties(AWS::crypto PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${crypto_INCLUDE_DIR}") + set_target_properties(AWS::crypto PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${crypto_LIBRARY}") + add_dependencies(AWS::crypto Threads::Threads) + endif() + endif() + endif() + +endif() diff --git a/crypto/s2n_certificate.c b/crypto/s2n_certificate.c index 4679c6e7fbf..311cba71903 100644 --- a/crypto/s2n_certificate.c +++ b/crypto/s2n_certificate.c @@ -1,877 +1,877 @@ -/* - * 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. - */ - -#ifndef _GNU_SOURCE - #define _GNU_SOURCE -#endif - -#include "crypto/s2n_certificate.h" - -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "crypto/s2n_openssl_x509.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_connection.h" -#include "utils/s2n_array.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -int s2n_cert_set_cert_type(struct s2n_cert *cert, s2n_pkey_type pkey_type) -{ - POSIX_ENSURE_REF(cert); - cert->pkey_type = pkey_type; - POSIX_GUARD_RESULT(s2n_pkey_setup_for_type(&cert->public_key, pkey_type)); - return 0; -} - -int s2n_create_cert_chain_from_stuffer(struct s2n_cert_chain *cert_chain_out, struct s2n_stuffer *chain_in_stuffer) -{ - DEFER_CLEANUP(struct s2n_stuffer cert_out_stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); - - struct s2n_cert **insert = &cert_chain_out->head; - uint32_t chain_size = 0; - while (s2n_stuffer_has_pem_encapsulated_block(chain_in_stuffer)) { - int result = s2n_stuffer_certificate_from_pem(chain_in_stuffer, &cert_out_stuffer); - POSIX_ENSURE(result == S2N_SUCCESS, S2N_ERR_INVALID_PEM); - - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); - POSIX_GUARD(s2n_blob_zero(&mem)); - - struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; - POSIX_GUARD(s2n_alloc(&new_node->raw, s2n_stuffer_data_available(&cert_out_stuffer))); - POSIX_GUARD(s2n_stuffer_read(&cert_out_stuffer, &new_node->raw)); - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - /* Additional 3 bytes for the length field in the protocol */ - chain_size += new_node->raw.size + 3; - new_node->next = NULL; - *insert = new_node; - insert = &new_node->next; - }; - - POSIX_ENSURE(chain_size > 0, S2N_ERR_NO_CERTIFICATE_IN_PEM); - cert_chain_out->chain_size = chain_size; - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_cert_chain_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, struct s2n_stuffer *chain_in_stuffer) -{ - return s2n_create_cert_chain_from_stuffer(cert_and_key->cert_chain, chain_in_stuffer); -} - -int s2n_cert_chain_and_key_set_cert_chain_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *cert_chain_pem, uint32_t cert_chain_len) -{ - DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); - - POSIX_GUARD(s2n_stuffer_init_ro_from_string(&chain_in_stuffer, cert_chain_pem, cert_chain_len)); - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_cert_chain(struct s2n_cert_chain_and_key *cert_and_key, const char *cert_chain_pem) -{ - DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); - - /* Turn the chain into a stuffer */ - POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_private_key_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, - struct s2n_stuffer *key_in_stuffer, struct s2n_stuffer *key_out_stuffer) -{ - struct s2n_blob key_blob = { 0 }; - - POSIX_GUARD(s2n_pkey_zero_init(cert_and_key->private_key)); - - /* Convert pem to asn1 and asn1 to the private key. Handles both PKCS#1 and PKCS#8 formats */ - int type_hint = 0; - POSIX_GUARD(s2n_stuffer_private_key_from_pem(key_in_stuffer, key_out_stuffer, &type_hint)); - key_blob.size = s2n_stuffer_data_available(key_out_stuffer); - key_blob.data = s2n_stuffer_raw_read(key_out_stuffer, key_blob.size); - POSIX_ENSURE_REF(key_blob.data); - - POSIX_GUARD_RESULT(s2n_asn1der_to_private_key(cert_and_key->private_key, &key_blob, type_hint)); - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_private_key_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *private_key_pem, uint32_t private_key_len) -{ - DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); - - /* Put the private key pem in a stuffer */ - POSIX_GUARD(s2n_stuffer_init_ro_from_string(&key_in_stuffer, private_key_pem, private_key_len)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, private_key_len)); - - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_private_key(struct s2n_cert_chain_and_key *cert_and_key, const char *private_key_pem) -{ - POSIX_ENSURE_REF(private_key_pem); - - DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); - - /* Put the private key pem in a stuffer */ - POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); - - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_free(&chain_and_key->ocsp_status)); - if (data && length) { - POSIX_GUARD(s2n_alloc(&chain_and_key->ocsp_status, length)); - POSIX_CHECKED_MEMCPY(chain_and_key->ocsp_status.data, data, length); - } - return 0; -} - -int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_free(&chain_and_key->sct_list)); - if (data && length) { - POSIX_GUARD(s2n_alloc(&chain_and_key->sct_list, length)); - POSIX_CHECKED_MEMCPY(chain_and_key->sct_list.data, data, length); - } - return 0; -} - -struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) -{ - DEFER_CLEANUP(struct s2n_blob chain_and_key_mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); - PTR_GUARD_POSIX(s2n_blob_zero(&chain_and_key_mem)); - - DEFER_CLEANUP(struct s2n_blob cert_chain_mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain))); - PTR_GUARD_POSIX(s2n_blob_zero(&cert_chain_mem)); - - DEFER_CLEANUP(struct s2n_blob pkey_mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key))); - PTR_GUARD_POSIX(s2n_blob_zero(&pkey_mem)); - - DEFER_CLEANUP(struct s2n_array *cn_names = NULL, s2n_array_free_p); - cn_names = s2n_array_new(sizeof(struct s2n_blob)); - PTR_ENSURE_REF(cn_names); - - DEFER_CLEANUP(struct s2n_array *san_names = NULL, s2n_array_free_p); - san_names = s2n_array_new(sizeof(struct s2n_blob)); - PTR_ENSURE_REF(san_names); - - struct s2n_cert_chain_and_key *chain_and_key = (struct s2n_cert_chain_and_key *) (void *) chain_and_key_mem.data; - chain_and_key->cert_chain = (struct s2n_cert_chain *) (void *) cert_chain_mem.data; - chain_and_key->private_key = (s2n_cert_private_key *) (void *) pkey_mem.data; - chain_and_key->cn_names = cn_names; - chain_and_key->san_names = san_names; - - ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key_mem); - ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain_mem); - ZERO_TO_DISABLE_DEFER_CLEANUP(pkey_mem); - ZERO_TO_DISABLE_DEFER_CLEANUP(cn_names); - ZERO_TO_DISABLE_DEFER_CLEANUP(san_names); - return chain_and_key; -} - -DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); - -int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) -{ - POSIX_ENSURE_REF(chain_and_key->san_names); - POSIX_ENSURE_REF(x509_cert); - - DEFER_CLEANUP(GENERAL_NAMES *san_names = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL), GENERAL_NAMES_free_pointer); - if (san_names == NULL) { - /* No SAN extension */ - return 0; - } - - const int num_san_names = sk_GENERAL_NAME_num(san_names); - for (int i = 0; i < num_san_names; i++) { - GENERAL_NAME *san_name = sk_GENERAL_NAME_value(san_names, i); - if (!san_name) { - continue; - } - - if (san_name->type == GEN_DNS) { - /* Decoding isn't necessary here since a DNS SAN name is ASCII(type V_ASN1_IA5STRING) */ - unsigned char *san_str = san_name->d.dNSName->data; - const size_t san_str_len = san_name->d.dNSName->length; - struct s2n_blob *san_blob = NULL; - POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->san_names, (void **) &san_blob)); - if (!san_blob) { - POSIX_BAIL(S2N_ERR_NULL_SANS); - } - - if (s2n_alloc(san_blob, san_str_len)) { - S2N_ERROR_PRESERVE_ERRNO(); - } - - POSIX_CHECKED_MEMCPY(san_blob->data, san_str, san_str_len); - san_blob->size = san_str_len; - /* normalize san_blob to lowercase */ - POSIX_GUARD(s2n_blob_char_to_lower(san_blob)); - } - } - - return 0; -} - -/* Parse CN names from the Subject of the leaf certificate. Technically there can by multiple CNs - * in the Subject but practically very few certificates in the wild will have more than one CN. - * Since the data for this certificate is coming from the application and not from an untrusted - * source, we will try our best to parse all of the CNs. - * - * A recent CAB thread proposed removing support for multiple CNs: - * https://cabforum.org/pipermail/public/2016-April/007242.html - */ - -DEFINE_POINTER_CLEANUP_FUNC(unsigned char *, OPENSSL_free); - -int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) -{ - POSIX_ENSURE_REF(chain_and_key->cn_names); - POSIX_ENSURE_REF(x509_cert); - - X509_NAME *subject = X509_get_subject_name(x509_cert); - if (!subject) { - return 0; - } - - int lastpos = -1; - while ((lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, lastpos)) >= 0) { - X509_NAME_ENTRY *name_entry = X509_NAME_get_entry(subject, lastpos); - if (!name_entry) { - continue; - } - - ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(name_entry); - if (!asn1_str) { - continue; - } - - /* We need to try and decode the CN since it may be encoded as unicode with a - * direct ASCII equivalent. Any non ASCII bytes in the string will fail later when we - * actually compare hostnames. - * - * `ASN1_STRING_to_UTF8` allocates in both the success case and in the zero return case, but - * not in the failure case (negative return value). Therefore, we use `ZERO_TO_DISABLE_DEFER_CLEANUP` - * in the failure case to prevent double-freeing `utf8_str`. For the zero and success cases, `utf8_str` - * will be freed by the `DEFER_CLEANUP`. - */ - DEFER_CLEANUP(unsigned char *utf8_str, OPENSSL_free_pointer); - const int utf8_out_len = ASN1_STRING_to_UTF8(&utf8_str, asn1_str); - if (utf8_out_len < 0) { - /* On failure, ASN1_STRING_to_UTF8 does not allocate any memory */ - ZERO_TO_DISABLE_DEFER_CLEANUP(utf8_str); - continue; - } else if (utf8_out_len == 0) { - /* We still need to free memory for this case, so let the DEFER_CLEANUP free it - * see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 and - * https://security.archlinux.org/CVE-2017-7521 - */ - } else { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->cn_names, (void **) &cn_name)); - if (cn_name == NULL) { - POSIX_BAIL(S2N_ERR_NULL_CN_NAME); - } - - if (s2n_alloc(cn_name, utf8_out_len) < 0) { - S2N_ERROR_PRESERVE_ERRNO(); - } - POSIX_CHECKED_MEMCPY(cn_name->data, utf8_str, utf8_out_len); - cn_name->size = utf8_out_len; - /* normalize cn_name to lowercase */ - POSIX_GUARD(s2n_blob_char_to_lower(cn_name)); - } - } - - return 0; -} - -static int s2n_cert_chain_and_key_set_names(struct s2n_cert_chain_and_key *chain_and_key, X509 *cert) -{ - POSIX_GUARD(s2n_cert_chain_and_key_load_sans(chain_and_key, cert)); - /* For current use cases, we *could* avoid populating the common names if any sans were loaded in - * s2n_cert_chain_and_key_load_sans. Let's unconditionally populate this field to avoid surprises - * in the future. - */ - POSIX_GUARD(s2n_cert_chain_and_key_load_cns(chain_and_key, cert)); - return 0; -} - -int s2n_cert_chain_and_key_load(struct s2n_cert_chain_and_key *chain_and_key) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(chain_and_key->cert_chain); - POSIX_ENSURE_REF(chain_and_key->cert_chain->head); - POSIX_ENSURE_REF(chain_and_key->private_key); - struct s2n_cert *head = chain_and_key->cert_chain->head; - - DEFER_CLEANUP(X509 *leaf_cert = NULL, X509_free_pointer); - POSIX_GUARD_RESULT(s2n_openssl_x509_parse(&head->raw, &leaf_cert)); - POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(leaf_cert, &head->info)); - - /* Parse the leaf cert for the public key and certificate type */ - DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - POSIX_GUARD_RESULT(s2n_pkey_from_x509(leaf_cert, &public_key, &pkey_type)); - - POSIX_ENSURE(pkey_type != S2N_PKEY_TYPE_UNKNOWN, S2N_ERR_CERT_TYPE_UNSUPPORTED); - POSIX_GUARD(s2n_cert_set_cert_type(head, pkey_type)); - - /* Validate the leaf cert's public key matches the provided private key */ - if (s2n_pkey_check_key_exists(chain_and_key->private_key) == S2N_SUCCESS) { - POSIX_GUARD(s2n_pkey_match(&public_key, chain_and_key->private_key)); - } - - /* Populate name information from the SAN/CN for the leaf certificate */ - POSIX_GUARD(s2n_cert_chain_and_key_set_names(chain_and_key, leaf_cert)); - - /* populate libcrypto nid's required for cert restrictions */ - struct s2n_cert *current = head->next; - while (current != NULL) { - DEFER_CLEANUP(X509 *parsed_cert = NULL, X509_free_pointer); - POSIX_GUARD_RESULT(s2n_openssl_x509_parse(¤t->raw, &parsed_cert)); - POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(parsed_cert, ¤t->info)); - - current = current->next; - } - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem) -{ - POSIX_ENSURE_REF(chain_and_key); - - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, chain_pem)); - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); - - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_load_public_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len) -{ - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); - POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); - return S2N_SUCCESS; -} - -int s2n_cert_chain_and_key_load_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, - uint32_t chain_pem_len, uint8_t *private_key_pem, uint32_t private_key_pem_len) -{ - POSIX_ENSURE_REF(chain_and_key); - - POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); - POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_bytes(chain_and_key, private_key_pem, private_key_pem_len)); - - POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); - - return S2N_SUCCESS; -} - -S2N_CLEANUP_RESULT s2n_cert_chain_and_key_ptr_free(struct s2n_cert_chain_and_key **cert_and_key) -{ - RESULT_ENSURE_REF(cert_and_key); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(*cert_and_key)); - *cert_and_key = NULL; - return S2N_RESULT_OK; -} - -int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key) -{ - if (cert_and_key == NULL) { - return 0; - } - - /* Walk the chain and free the certs */ - if (cert_and_key->cert_chain) { - struct s2n_cert *node = cert_and_key->cert_chain->head; - while (node) { - /* Free the cert */ - POSIX_GUARD(s2n_free(&node->raw)); - /* update head so it won't point to freed memory */ - cert_and_key->cert_chain->head = node->next; - /* Free the node */ - POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); - node = cert_and_key->cert_chain->head; - } - - POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->cert_chain, sizeof(struct s2n_cert_chain))); - } - - if (cert_and_key->private_key) { - POSIX_GUARD(s2n_pkey_free(cert_and_key->private_key)); - POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->private_key, sizeof(s2n_cert_private_key))); - } - - uint32_t len = 0; - - if (cert_and_key->san_names) { - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->san_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *san_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->san_names, i, (void **) &san_name)); - POSIX_GUARD(s2n_free(san_name)); - } - POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->san_names)); - cert_and_key->san_names = NULL; - } - - if (cert_and_key->cn_names) { - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->cn_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->cn_names, i, (void **) &cn_name)); - POSIX_GUARD(s2n_free(cn_name)); - } - POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->cn_names)); - cert_and_key->cn_names = NULL; - } - - POSIX_GUARD(s2n_free(&cert_and_key->ocsp_status)); - POSIX_GUARD(s2n_free(&cert_and_key->sct_list)); - - POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key, sizeof(struct s2n_cert_chain_and_key))); - return 0; -} - -int s2n_cert_chain_free(struct s2n_cert_chain *cert_chain) -{ - /* Walk the chain and free the certs/nodes allocated prior to failure */ - if (cert_chain) { - struct s2n_cert *node = cert_chain->head; - while (node) { - /* Free the cert */ - POSIX_GUARD(s2n_free(&node->raw)); - /* update head so it won't point to freed memory */ - cert_chain->head = node->next; - /* Free the node */ - POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); - node = cert_chain->head; - } - } - - return S2N_SUCCESS; -} - -int s2n_send_cert_chain(struct s2n_connection *conn, struct s2n_stuffer *out, struct s2n_cert_chain_and_key *chain_and_key) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(chain_and_key); - struct s2n_cert_chain *chain = chain_and_key->cert_chain; - POSIX_ENSURE_REF(chain); - struct s2n_cert *cur_cert = chain->head; - POSIX_ENSURE_REF(cur_cert); - - struct s2n_stuffer_reservation cert_chain_size = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint24(out, &cert_chain_size)); - - /* Send certs and extensions (in TLS 1.3) */ - bool first_entry = true; - while (cur_cert) { - POSIX_ENSURE_REF(cur_cert); - POSIX_GUARD(s2n_stuffer_write_uint24(out, cur_cert->raw.size)); - POSIX_GUARD(s2n_stuffer_write_bytes(out, cur_cert->raw.data, cur_cert->raw.size)); - - /* According to https://tools.ietf.org/html/rfc8446#section-4.4.2, - * If an extension applies to the entire chain, it SHOULD be included in - * the first CertificateEntry. - * While the spec allow extensions to be included in other certificate - * entries, only the first matter to use here */ - if (conn->actual_protocol_version >= S2N_TLS13) { - if (first_entry) { - POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, out)); - first_entry = false; - } else { - POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, out)); - } - } - cur_cert = cur_cert->next; - } - - POSIX_GUARD(s2n_stuffer_write_vector_size(&cert_chain_size)); - - return 0; -} - -int s2n_send_empty_cert_chain(struct s2n_stuffer *out) -{ - POSIX_ENSURE_REF(out); - POSIX_GUARD(s2n_stuffer_write_uint24(out, 0)); - return 0; -} - -static int s2n_does_cert_san_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(dns_name); - - struct s2n_array *san_names = chain_and_key->san_names; - uint32_t len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(san_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *san_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(san_names, i, (void **) &san_name)); - POSIX_ENSURE_REF(san_name); - if ((dns_name->size == san_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) san_name->data, dns_name->size) == 0)) { - return 1; - } - } - - return 0; -} - -static int s2n_does_cert_cn_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(dns_name); - - struct s2n_array *cn_names = chain_and_key->cn_names; - uint32_t len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(cn_names, &len)); - for (uint32_t i = 0; i < len; i++) { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cn_names, i, (void **) &cn_name)); - POSIX_ENSURE_REF(cn_name); - if ((dns_name->size == cn_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) cn_name->data, dns_name->size) == 0)) { - return 1; - } - } - - return 0; -} - -int s2n_cert_chain_and_key_matches_dns_name(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) -{ - uint32_t len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(chain_and_key->san_names, &len)); - if (len > 0) { - if (s2n_does_cert_san_match_hostname(chain_and_key, dns_name)) { - return 1; - } - } else { - /* Per https://tools.ietf.org/html/rfc6125#section-6.4.4 we only will - * consider the CN for matching if no valid DNS entries are provided - * in a SAN. - */ - if (s2n_does_cert_cn_match_hostname(chain_and_key, dns_name)) { - return 1; - } - } - - return 0; -} - -int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx) -{ - cert_and_key->context = ctx; - return 0; -} - -void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key) -{ - return cert_and_key->context; -} - -s2n_pkey_type s2n_cert_chain_and_key_get_pkey_type(struct s2n_cert_chain_and_key *chain_and_key) -{ - if (chain_and_key == NULL - || chain_and_key->cert_chain == NULL - || chain_and_key->cert_chain->head == NULL) { - return S2N_PKEY_TYPE_UNKNOWN; - } - return chain_and_key->cert_chain->head->pkey_type; -} - -s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *chain_and_key) -{ - PTR_ENSURE_REF(chain_and_key); - return chain_and_key->private_key; -} - -int s2n_cert_chain_get_length(const struct s2n_cert_chain_and_key *chain_and_key, uint32_t *cert_length) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(cert_length); - - struct s2n_cert *head_cert = chain_and_key->cert_chain->head; - POSIX_ENSURE_REF(head_cert); - *cert_length = 1; - struct s2n_cert *next_cert = head_cert->next; - while (next_cert != NULL) { - *cert_length += 1; - next_cert = next_cert->next; - } - - return S2N_SUCCESS; -} - -int s2n_cert_chain_get_cert(const struct s2n_cert_chain_and_key *chain_and_key, struct s2n_cert **out_cert, - const uint32_t cert_idx) -{ - POSIX_ENSURE_REF(chain_and_key); - POSIX_ENSURE_REF(out_cert); - - struct s2n_cert *cur_cert = chain_and_key->cert_chain->head; - POSIX_ENSURE_REF(cur_cert); - uint32_t counter = 0; - - struct s2n_cert *next_cert = cur_cert->next; - - while ((next_cert != NULL) && (counter < cert_idx)) { - cur_cert = next_cert; - next_cert = next_cert->next; - counter++; - } - - POSIX_ENSURE(counter == cert_idx, S2N_ERR_NO_CERT_FOUND); - POSIX_ENSURE(cur_cert != NULL, S2N_ERR_NO_CERT_FOUND); - *out_cert = cur_cert; - - return S2N_SUCCESS; -} - -int s2n_cert_get_der(const struct s2n_cert *cert, const uint8_t **out_cert_der, uint32_t *cert_length) -{ - POSIX_ENSURE_REF(cert); - POSIX_ENSURE_REF(out_cert_der); - POSIX_ENSURE_REF(cert_length); - - *cert_length = cert->raw.size; - *out_cert_der = cert->raw.data; - - return S2N_SUCCESS; -} - -static int s2n_asn1_obj_free(ASN1_OBJECT **data) -{ - if (*data != NULL) { - ASN1_OBJECT_free(*data); - } - return S2N_SUCCESS; -} - -static int s2n_asn1_string_free(ASN1_STRING **data) -{ - if (*data != NULL) { - ASN1_STRING_free(*data); - } - return S2N_SUCCESS; -} - -static int s2n_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) -{ - DEFER_CLEANUP(ASN1_STRING *asn1_str = NULL, s2n_asn1_string_free); - /* Note that d2i_ASN1_UTF8STRING increments *der_in to the byte following the parsed data. - * Using a temporary variable is mandatory to prevent memory free-ing errors. - * Ref to the warning section here for more information: - * https://www.openssl.org/docs/man1.1.0/man3/d2i_ASN1_UTF8STRING.html. - */ - const uint8_t *asn1_str_data = extension_data; - asn1_str = d2i_ASN1_UTF8STRING(NULL, (const unsigned char **) (void *) &asn1_str_data, extension_len); - POSIX_ENSURE(asn1_str != NULL, S2N_ERR_INVALID_X509_EXTENSION_TYPE); - /* ASN1_STRING_type() returns the type of `asn1_str`, using standard constants such as V_ASN1_OCTET_STRING. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_type.html. - */ - int type = ASN1_STRING_type(asn1_str); - POSIX_ENSURE(type == V_ASN1_UTF8STRING, S2N_ERR_INVALID_X509_EXTENSION_TYPE); - - int len = ASN1_STRING_length(asn1_str); - POSIX_ENSURE_GTE(len, 0); - if (out_data != NULL) { - POSIX_ENSURE((int64_t) *out_len >= (int64_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); - /* ASN1_STRING_data() returns an internal pointer to the data. - * Since this is an internal pointer it should not be freed or modified in any way. - * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. - */ - unsigned char *internal_data = ASN1_STRING_data(asn1_str); - POSIX_ENSURE_REF(internal_data); - POSIX_CHECKED_MEMCPY(out_data, internal_data, len); - } - *out_len = len; - return S2N_SUCCESS; -} - -int s2n_cert_get_utf8_string_from_extension_data_length(const uint8_t *extension_data, uint32_t extension_len, uint32_t *utf8_str_len) -{ - POSIX_ENSURE_REF(extension_data); - POSIX_ENSURE_GT(extension_len, 0); - POSIX_ENSURE_REF(utf8_str_len); - - POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, NULL, utf8_str_len)); - - return S2N_SUCCESS; -} - -int s2n_cert_get_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) -{ - POSIX_ENSURE_REF(extension_data); - POSIX_ENSURE_GT(extension_len, 0); - POSIX_ENSURE_REF(out_data); - POSIX_ENSURE_REF(out_len); - - POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, out_data, out_len)); - - return S2N_SUCCESS; -} - -static int s2n_parse_x509_extension(struct s2n_cert *cert, const uint8_t *oid, - uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) -{ - POSIX_ENSURE_REF(cert->raw.data); - /* Obtain the openssl x509 cert from the ASN1 DER certificate input. - * Note that d2i_X509 increments *der_in to the byte following the parsed data. - * Using a temporary variable is mandatory to prevent memory free-ing errors. - * Ref to the warning section here for more information: - * https://www.openssl.org/docs/man1.1.0/man3/d2i_X509.html. - */ - uint8_t *der_in = cert->raw.data; - DEFER_CLEANUP(X509 *x509_cert = d2i_X509(NULL, (const unsigned char **) (void *) &der_in, cert->raw.size), - X509_free_pointer); - POSIX_ENSURE_REF(x509_cert); - - /* Retrieve the number of x509 extensions present in the certificate - * X509_get_ext_count returns the number of extensions in the x509 certificate. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext_count.html. - */ - int ext_count_value = X509_get_ext_count(x509_cert); - POSIX_ENSURE_GT(ext_count_value, 0); - size_t ext_count = (size_t) ext_count_value; - - /* OBJ_txt2obj() converts the input text string into an ASN1_OBJECT structure. - * If no_name is 0 then long names and short names will be interpreted as well as numerical forms. - * If no_name is 1 only the numerical form is acceptable. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html. - */ - DEFER_CLEANUP(ASN1_OBJECT *asn1_obj_in = OBJ_txt2obj((const char *) oid, 0), s2n_asn1_obj_free); - POSIX_ENSURE_REF(asn1_obj_in); - - for (size_t loc = 0; loc < ext_count; loc++) { - ASN1_OCTET_STRING *asn1_str = NULL; - bool match_found = false; - - /* Retrieve the x509 extension at location loc. - * X509_get_ext() retrieves extension loc from x. - * The index loc can take any value from 0 to X509_get_ext_count(x) - 1. - * The returned extension is an internal pointer which must not be freed up by the application. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html. - */ - X509_EXTENSION *x509_ext = X509_get_ext(x509_cert, loc); - POSIX_ENSURE_REF(x509_ext); - - /* Retrieve the extension object/OID/extnId. - * X509_EXTENSION_get_object() returns the extension type of `x509_ext` as an ASN1_OBJECT pointer. - * The returned pointer is an internal value which must not be freed up. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_object.html. - */ - ASN1_OBJECT *asn1_obj = X509_EXTENSION_get_object(x509_ext); - POSIX_ENSURE_REF(asn1_obj); - - /* OBJ_cmp() compares two ASN1_OBJECT objects. If the two are identical 0 is returned. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_cmp.html. - */ - match_found = (0 == OBJ_cmp(asn1_obj_in, asn1_obj)); - - /* If match found, retrieve the corresponding OID value for the x509 extension */ - if (match_found) { - /* X509_EXTENSION_get_data() returns the data of extension `x509_ext`. - * The returned pointer is an internal value which must not be freed up. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_data.html. - */ - asn1_str = X509_EXTENSION_get_data(x509_ext); - POSIX_ENSURE_REF(asn1_str); - /* ASN1_STRING_length() returns the length of the content of `asn1_str`. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_length.html. - */ - int len = ASN1_STRING_length(asn1_str); - if (ext_value != NULL) { - POSIX_ENSURE_GTE(len, 0); - POSIX_ENSURE(*ext_value_len >= (uint32_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); - /* ASN1_STRING_data() returns an internal pointer to the data. - * Since this is an internal pointer it should not be freed or modified in any way. - * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. - */ - unsigned char *internal_data = ASN1_STRING_data(asn1_str); - POSIX_ENSURE_REF(internal_data); - POSIX_CHECKED_MEMCPY(ext_value, internal_data, len); - } - if (critical != NULL) { - /* Retrieve the x509 extension's critical value. - * X509_EXTENSION_get_critical() returns the criticality of extension `x509_ext`, - * it returns 1 for critical and 0 for non-critical. - * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_critical.html. - */ - *critical = X509_EXTENSION_get_critical(x509_ext); - } - *ext_value_len = len; - return S2N_SUCCESS; - } - } - - POSIX_BAIL(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND); -} - -int s2n_cert_get_x509_extension_value_length(struct s2n_cert *cert, const uint8_t *oid, uint32_t *ext_value_len) -{ - POSIX_ENSURE_REF(cert); - POSIX_ENSURE_REF(oid); - POSIX_ENSURE_REF(ext_value_len); - - POSIX_GUARD(s2n_parse_x509_extension(cert, oid, NULL, ext_value_len, NULL)); - - return S2N_SUCCESS; -} - -int s2n_cert_get_x509_extension_value(struct s2n_cert *cert, const uint8_t *oid, - uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) -{ - POSIX_ENSURE_REF(cert); - POSIX_ENSURE_REF(oid); - POSIX_ENSURE_REF(ext_value); - POSIX_ENSURE_REF(ext_value_len); - POSIX_ENSURE_REF(critical); - - POSIX_GUARD(s2n_parse_x509_extension(cert, oid, ext_value, ext_value_len, critical)); - - return S2N_SUCCESS; -} +/* + * 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. + */ + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + +#include "crypto/s2n_certificate.h" + +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_openssl_x509.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +int s2n_cert_set_cert_type(struct s2n_cert *cert, s2n_pkey_type pkey_type) +{ + POSIX_ENSURE_REF(cert); + cert->pkey_type = pkey_type; + POSIX_GUARD_RESULT(s2n_pkey_setup_for_type(&cert->public_key, pkey_type)); + return 0; +} + +int s2n_create_cert_chain_from_stuffer(struct s2n_cert_chain *cert_chain_out, struct s2n_stuffer *chain_in_stuffer) +{ + DEFER_CLEANUP(struct s2n_stuffer cert_out_stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&cert_out_stuffer, 2048)); + + struct s2n_cert **insert = &cert_chain_out->head; + uint32_t chain_size = 0; + while (s2n_stuffer_has_pem_encapsulated_block(chain_in_stuffer)) { + int result = s2n_stuffer_certificate_from_pem(chain_in_stuffer, &cert_out_stuffer); + POSIX_ENSURE(result == S2N_SUCCESS, S2N_ERR_INVALID_PEM); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); + POSIX_GUARD(s2n_blob_zero(&mem)); + + struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; + POSIX_GUARD(s2n_alloc(&new_node->raw, s2n_stuffer_data_available(&cert_out_stuffer))); + POSIX_GUARD(s2n_stuffer_read(&cert_out_stuffer, &new_node->raw)); + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + /* Additional 3 bytes for the length field in the protocol */ + chain_size += new_node->raw.size + 3; + new_node->next = NULL; + *insert = new_node; + insert = &new_node->next; + }; + + POSIX_ENSURE(chain_size > 0, S2N_ERR_NO_CERTIFICATE_IN_PEM); + cert_chain_out->chain_size = chain_size; + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_cert_chain_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, struct s2n_stuffer *chain_in_stuffer) +{ + return s2n_create_cert_chain_from_stuffer(cert_and_key->cert_chain, chain_in_stuffer); +} + +int s2n_cert_chain_and_key_set_cert_chain_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *cert_chain_pem, uint32_t cert_chain_len) +{ + DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); + + POSIX_GUARD(s2n_stuffer_init_ro_from_string(&chain_in_stuffer, cert_chain_pem, cert_chain_len)); + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_cert_chain(struct s2n_cert_chain_and_key *cert_and_key, const char *cert_chain_pem) +{ + DEFER_CLEANUP(struct s2n_stuffer chain_in_stuffer = { 0 }, s2n_stuffer_free); + + /* Turn the chain into a stuffer */ + POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&chain_in_stuffer, cert_chain_pem)); + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_from_stuffer(cert_and_key, &chain_in_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_private_key_from_stuffer(struct s2n_cert_chain_and_key *cert_and_key, + struct s2n_stuffer *key_in_stuffer, struct s2n_stuffer *key_out_stuffer) +{ + struct s2n_blob key_blob = { 0 }; + + POSIX_GUARD(s2n_pkey_zero_init(cert_and_key->private_key)); + + /* Convert pem to asn1 and asn1 to the private key. Handles both PKCS#1 and PKCS#8 formats */ + int type_hint = 0; + POSIX_GUARD(s2n_stuffer_private_key_from_pem(key_in_stuffer, key_out_stuffer, &type_hint)); + key_blob.size = s2n_stuffer_data_available(key_out_stuffer); + key_blob.data = s2n_stuffer_raw_read(key_out_stuffer, key_blob.size); + POSIX_ENSURE_REF(key_blob.data); + + POSIX_GUARD_RESULT(s2n_asn1der_to_private_key(cert_and_key->private_key, &key_blob, type_hint)); + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_private_key_bytes(struct s2n_cert_chain_and_key *cert_and_key, uint8_t *private_key_pem, uint32_t private_key_len) +{ + DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); + + /* Put the private key pem in a stuffer */ + POSIX_GUARD(s2n_stuffer_init_ro_from_string(&key_in_stuffer, private_key_pem, private_key_len)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, private_key_len)); + + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_private_key(struct s2n_cert_chain_and_key *cert_and_key, const char *private_key_pem) +{ + POSIX_ENSURE_REF(private_key_pem); + + DEFER_CLEANUP(struct s2n_stuffer key_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_out_stuffer = { 0 }, s2n_stuffer_free); + + /* Put the private key pem in a stuffer */ + POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&key_in_stuffer, private_key_pem)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&key_out_stuffer, strlen(private_key_pem))); + + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_from_stuffer(cert_and_key, &key_in_stuffer, &key_out_stuffer)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_set_ocsp_data(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_free(&chain_and_key->ocsp_status)); + if (data && length) { + POSIX_GUARD(s2n_alloc(&chain_and_key->ocsp_status, length)); + POSIX_CHECKED_MEMCPY(chain_and_key->ocsp_status.data, data, length); + } + return 0; +} + +int s2n_cert_chain_and_key_set_sct_list(struct s2n_cert_chain_and_key *chain_and_key, const uint8_t *data, uint32_t length) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_free(&chain_and_key->sct_list)); + if (data && length) { + POSIX_GUARD(s2n_alloc(&chain_and_key->sct_list, length)); + POSIX_CHECKED_MEMCPY(chain_and_key->sct_list.data, data, length); + } + return 0; +} + +struct s2n_cert_chain_and_key *s2n_cert_chain_and_key_new(void) +{ + DEFER_CLEANUP(struct s2n_blob chain_and_key_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&chain_and_key_mem, sizeof(struct s2n_cert_chain_and_key))); + PTR_GUARD_POSIX(s2n_blob_zero(&chain_and_key_mem)); + + DEFER_CLEANUP(struct s2n_blob cert_chain_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&cert_chain_mem, sizeof(struct s2n_cert_chain))); + PTR_GUARD_POSIX(s2n_blob_zero(&cert_chain_mem)); + + DEFER_CLEANUP(struct s2n_blob pkey_mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&pkey_mem, sizeof(s2n_cert_private_key))); + PTR_GUARD_POSIX(s2n_blob_zero(&pkey_mem)); + + DEFER_CLEANUP(struct s2n_array *cn_names = NULL, s2n_array_free_p); + cn_names = s2n_array_new(sizeof(struct s2n_blob)); + PTR_ENSURE_REF(cn_names); + + DEFER_CLEANUP(struct s2n_array *san_names = NULL, s2n_array_free_p); + san_names = s2n_array_new(sizeof(struct s2n_blob)); + PTR_ENSURE_REF(san_names); + + struct s2n_cert_chain_and_key *chain_and_key = (struct s2n_cert_chain_and_key *) (void *) chain_and_key_mem.data; + chain_and_key->cert_chain = (struct s2n_cert_chain *) (void *) cert_chain_mem.data; + chain_and_key->private_key = (s2n_cert_private_key *) (void *) pkey_mem.data; + chain_and_key->cn_names = cn_names; + chain_and_key->san_names = san_names; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(pkey_mem); + ZERO_TO_DISABLE_DEFER_CLEANUP(cn_names); + ZERO_TO_DISABLE_DEFER_CLEANUP(san_names); + return chain_and_key; +} + +DEFINE_POINTER_CLEANUP_FUNC(GENERAL_NAMES *, GENERAL_NAMES_free); + +int s2n_cert_chain_and_key_load_sans(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) +{ + POSIX_ENSURE_REF(chain_and_key->san_names); + POSIX_ENSURE_REF(x509_cert); + + DEFER_CLEANUP(GENERAL_NAMES *san_names = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL), GENERAL_NAMES_free_pointer); + if (san_names == NULL) { + /* No SAN extension */ + return 0; + } + + const int num_san_names = sk_GENERAL_NAME_num(san_names); + for (int i = 0; i < num_san_names; i++) { + GENERAL_NAME *san_name = sk_GENERAL_NAME_value(san_names, i); + if (!san_name) { + continue; + } + + if (san_name->type == GEN_DNS) { + /* Decoding isn't necessary here since a DNS SAN name is ASCII(type V_ASN1_IA5STRING) */ + unsigned char *san_str = san_name->d.dNSName->data; + const size_t san_str_len = san_name->d.dNSName->length; + struct s2n_blob *san_blob = NULL; + POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->san_names, (void **) &san_blob)); + if (!san_blob) { + POSIX_BAIL(S2N_ERR_NULL_SANS); + } + + if (s2n_alloc(san_blob, san_str_len)) { + S2N_ERROR_PRESERVE_ERRNO(); + } + + POSIX_CHECKED_MEMCPY(san_blob->data, san_str, san_str_len); + san_blob->size = san_str_len; + /* normalize san_blob to lowercase */ + POSIX_GUARD(s2n_blob_char_to_lower(san_blob)); + } + } + + return 0; +} + +/* Parse CN names from the Subject of the leaf certificate. Technically there can by multiple CNs + * in the Subject but practically very few certificates in the wild will have more than one CN. + * Since the data for this certificate is coming from the application and not from an untrusted + * source, we will try our best to parse all of the CNs. + * + * A recent CAB thread proposed removing support for multiple CNs: + * https://cabforum.org/pipermail/public/2016-April/007242.html + */ + +DEFINE_POINTER_CLEANUP_FUNC(unsigned char *, OPENSSL_free); + +int s2n_cert_chain_and_key_load_cns(struct s2n_cert_chain_and_key *chain_and_key, X509 *x509_cert) +{ + POSIX_ENSURE_REF(chain_and_key->cn_names); + POSIX_ENSURE_REF(x509_cert); + + X509_NAME *subject = X509_get_subject_name(x509_cert); + if (!subject) { + return 0; + } + + int lastpos = -1; + while ((lastpos = X509_NAME_get_index_by_NID(subject, NID_commonName, lastpos)) >= 0) { + X509_NAME_ENTRY *name_entry = X509_NAME_get_entry(subject, lastpos); + if (!name_entry) { + continue; + } + + ASN1_STRING *asn1_str = X509_NAME_ENTRY_get_data(name_entry); + if (!asn1_str) { + continue; + } + + /* We need to try and decode the CN since it may be encoded as unicode with a + * direct ASCII equivalent. Any non ASCII bytes in the string will fail later when we + * actually compare hostnames. + * + * `ASN1_STRING_to_UTF8` allocates in both the success case and in the zero return case, but + * not in the failure case (negative return value). Therefore, we use `ZERO_TO_DISABLE_DEFER_CLEANUP` + * in the failure case to prevent double-freeing `utf8_str`. For the zero and success cases, `utf8_str` + * will be freed by the `DEFER_CLEANUP`. + */ + DEFER_CLEANUP(unsigned char *utf8_str, OPENSSL_free_pointer); + const int utf8_out_len = ASN1_STRING_to_UTF8(&utf8_str, asn1_str); + if (utf8_out_len < 0) { + /* On failure, ASN1_STRING_to_UTF8 does not allocate any memory */ + ZERO_TO_DISABLE_DEFER_CLEANUP(utf8_str); + continue; + } else if (utf8_out_len == 0) { + /* We still need to free memory for this case, so let the DEFER_CLEANUP free it + * see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7521 and + * https://security.archlinux.org/CVE-2017-7521 + */ + } else { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_pushback(chain_and_key->cn_names, (void **) &cn_name)); + if (cn_name == NULL) { + POSIX_BAIL(S2N_ERR_NULL_CN_NAME); + } + + if (s2n_alloc(cn_name, utf8_out_len) < 0) { + S2N_ERROR_PRESERVE_ERRNO(); + } + POSIX_CHECKED_MEMCPY(cn_name->data, utf8_str, utf8_out_len); + cn_name->size = utf8_out_len; + /* normalize cn_name to lowercase */ + POSIX_GUARD(s2n_blob_char_to_lower(cn_name)); + } + } + + return 0; +} + +static int s2n_cert_chain_and_key_set_names(struct s2n_cert_chain_and_key *chain_and_key, X509 *cert) +{ + POSIX_GUARD(s2n_cert_chain_and_key_load_sans(chain_and_key, cert)); + /* For current use cases, we *could* avoid populating the common names if any sans were loaded in + * s2n_cert_chain_and_key_load_sans. Let's unconditionally populate this field to avoid surprises + * in the future. + */ + POSIX_GUARD(s2n_cert_chain_and_key_load_cns(chain_and_key, cert)); + return 0; +} + +int s2n_cert_chain_and_key_load(struct s2n_cert_chain_and_key *chain_and_key) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(chain_and_key->cert_chain); + POSIX_ENSURE_REF(chain_and_key->cert_chain->head); + POSIX_ENSURE_REF(chain_and_key->private_key); + struct s2n_cert *head = chain_and_key->cert_chain->head; + + DEFER_CLEANUP(X509 *leaf_cert = NULL, X509_free_pointer); + POSIX_GUARD_RESULT(s2n_openssl_x509_parse(&head->raw, &leaf_cert)); + POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(leaf_cert, &head->info)); + + /* Parse the leaf cert for the public key and certificate type */ + DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + POSIX_GUARD_RESULT(s2n_pkey_from_x509(leaf_cert, &public_key, &pkey_type)); + + POSIX_ENSURE(pkey_type != S2N_PKEY_TYPE_UNKNOWN, S2N_ERR_CERT_TYPE_UNSUPPORTED); + POSIX_GUARD(s2n_cert_set_cert_type(head, pkey_type)); + + /* Validate the leaf cert's public key matches the provided private key */ + if (s2n_pkey_check_key_exists(chain_and_key->private_key) == S2N_SUCCESS) { + POSIX_GUARD(s2n_pkey_match(&public_key, chain_and_key->private_key)); + } + + /* Populate name information from the SAN/CN for the leaf certificate */ + POSIX_GUARD(s2n_cert_chain_and_key_set_names(chain_and_key, leaf_cert)); + + /* populate libcrypto nid's required for cert restrictions */ + struct s2n_cert *current = head->next; + while (current != NULL) { + DEFER_CLEANUP(X509 *parsed_cert = NULL, X509_free_pointer); + POSIX_GUARD_RESULT(s2n_openssl_x509_parse(¤t->raw, &parsed_cert)); + POSIX_GUARD_RESULT(s2n_openssl_x509_get_cert_info(parsed_cert, ¤t->info)); + + current = current->next; + } + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_load_pem(struct s2n_cert_chain_and_key *chain_and_key, const char *chain_pem, const char *private_key_pem) +{ + POSIX_ENSURE_REF(chain_and_key); + + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, chain_pem)); + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); + + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_load_public_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, uint32_t chain_pem_len) +{ + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); + POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); + return S2N_SUCCESS; +} + +int s2n_cert_chain_and_key_load_pem_bytes(struct s2n_cert_chain_and_key *chain_and_key, uint8_t *chain_pem, + uint32_t chain_pem_len, uint8_t *private_key_pem, uint32_t private_key_pem_len) +{ + POSIX_ENSURE_REF(chain_and_key); + + POSIX_GUARD(s2n_cert_chain_and_key_set_cert_chain_bytes(chain_and_key, chain_pem, chain_pem_len)); + POSIX_GUARD(s2n_cert_chain_and_key_set_private_key_bytes(chain_and_key, private_key_pem, private_key_pem_len)); + + POSIX_GUARD(s2n_cert_chain_and_key_load(chain_and_key)); + + return S2N_SUCCESS; +} + +S2N_CLEANUP_RESULT s2n_cert_chain_and_key_ptr_free(struct s2n_cert_chain_and_key **cert_and_key) +{ + RESULT_ENSURE_REF(cert_and_key); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(*cert_and_key)); + *cert_and_key = NULL; + return S2N_RESULT_OK; +} + +int s2n_cert_chain_and_key_free(struct s2n_cert_chain_and_key *cert_and_key) +{ + if (cert_and_key == NULL) { + return 0; + } + + /* Walk the chain and free the certs */ + if (cert_and_key->cert_chain) { + struct s2n_cert *node = cert_and_key->cert_chain->head; + while (node) { + /* Free the cert */ + POSIX_GUARD(s2n_free(&node->raw)); + /* update head so it won't point to freed memory */ + cert_and_key->cert_chain->head = node->next; + /* Free the node */ + POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); + node = cert_and_key->cert_chain->head; + } + + POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->cert_chain, sizeof(struct s2n_cert_chain))); + } + + if (cert_and_key->private_key) { + POSIX_GUARD(s2n_pkey_free(cert_and_key->private_key)); + POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key->private_key, sizeof(s2n_cert_private_key))); + } + + uint32_t len = 0; + + if (cert_and_key->san_names) { + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->san_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *san_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->san_names, i, (void **) &san_name)); + POSIX_GUARD(s2n_free(san_name)); + } + POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->san_names)); + cert_and_key->san_names = NULL; + } + + if (cert_and_key->cn_names) { + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_and_key->cn_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_and_key->cn_names, i, (void **) &cn_name)); + POSIX_GUARD(s2n_free(cn_name)); + } + POSIX_GUARD_RESULT(s2n_array_free(cert_and_key->cn_names)); + cert_and_key->cn_names = NULL; + } + + POSIX_GUARD(s2n_free(&cert_and_key->ocsp_status)); + POSIX_GUARD(s2n_free(&cert_and_key->sct_list)); + + POSIX_GUARD(s2n_free_object((uint8_t **) &cert_and_key, sizeof(struct s2n_cert_chain_and_key))); + return 0; +} + +int s2n_cert_chain_free(struct s2n_cert_chain *cert_chain) +{ + /* Walk the chain and free the certs/nodes allocated prior to failure */ + if (cert_chain) { + struct s2n_cert *node = cert_chain->head; + while (node) { + /* Free the cert */ + POSIX_GUARD(s2n_free(&node->raw)); + /* update head so it won't point to freed memory */ + cert_chain->head = node->next; + /* Free the node */ + POSIX_GUARD(s2n_free_object((uint8_t **) &node, sizeof(struct s2n_cert))); + node = cert_chain->head; + } + } + + return S2N_SUCCESS; +} + +int s2n_send_cert_chain(struct s2n_connection *conn, struct s2n_stuffer *out, struct s2n_cert_chain_and_key *chain_and_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(chain_and_key); + struct s2n_cert_chain *chain = chain_and_key->cert_chain; + POSIX_ENSURE_REF(chain); + struct s2n_cert *cur_cert = chain->head; + POSIX_ENSURE_REF(cur_cert); + + struct s2n_stuffer_reservation cert_chain_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint24(out, &cert_chain_size)); + + /* Send certs and extensions (in TLS 1.3) */ + bool first_entry = true; + while (cur_cert) { + POSIX_ENSURE_REF(cur_cert); + POSIX_GUARD(s2n_stuffer_write_uint24(out, cur_cert->raw.size)); + POSIX_GUARD(s2n_stuffer_write_bytes(out, cur_cert->raw.data, cur_cert->raw.size)); + + /* According to https://tools.ietf.org/html/rfc8446#section-4.4.2, + * If an extension applies to the entire chain, it SHOULD be included in + * the first CertificateEntry. + * While the spec allow extensions to be included in other certificate + * entries, only the first matter to use here */ + if (conn->actual_protocol_version >= S2N_TLS13) { + if (first_entry) { + POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, out)); + first_entry = false; + } else { + POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, out)); + } + } + cur_cert = cur_cert->next; + } + + POSIX_GUARD(s2n_stuffer_write_vector_size(&cert_chain_size)); + + return 0; +} + +int s2n_send_empty_cert_chain(struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(out); + POSIX_GUARD(s2n_stuffer_write_uint24(out, 0)); + return 0; +} + +static int s2n_does_cert_san_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(dns_name); + + struct s2n_array *san_names = chain_and_key->san_names; + uint32_t len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(san_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *san_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(san_names, i, (void **) &san_name)); + POSIX_ENSURE_REF(san_name); + if ((dns_name->size == san_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) san_name->data, dns_name->size) == 0)) { + return 1; + } + } + + return 0; +} + +static int s2n_does_cert_cn_match_hostname(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(dns_name); + + struct s2n_array *cn_names = chain_and_key->cn_names; + uint32_t len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(cn_names, &len)); + for (uint32_t i = 0; i < len; i++) { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cn_names, i, (void **) &cn_name)); + POSIX_ENSURE_REF(cn_name); + if ((dns_name->size == cn_name->size) && (strncasecmp((const char *) dns_name->data, (const char *) cn_name->data, dns_name->size) == 0)) { + return 1; + } + } + + return 0; +} + +int s2n_cert_chain_and_key_matches_dns_name(const struct s2n_cert_chain_and_key *chain_and_key, const struct s2n_blob *dns_name) +{ + uint32_t len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(chain_and_key->san_names, &len)); + if (len > 0) { + if (s2n_does_cert_san_match_hostname(chain_and_key, dns_name)) { + return 1; + } + } else { + /* Per https://tools.ietf.org/html/rfc6125#section-6.4.4 we only will + * consider the CN for matching if no valid DNS entries are provided + * in a SAN. + */ + if (s2n_does_cert_cn_match_hostname(chain_and_key, dns_name)) { + return 1; + } + } + + return 0; +} + +int s2n_cert_chain_and_key_set_ctx(struct s2n_cert_chain_and_key *cert_and_key, void *ctx) +{ + cert_and_key->context = ctx; + return 0; +} + +void *s2n_cert_chain_and_key_get_ctx(struct s2n_cert_chain_and_key *cert_and_key) +{ + return cert_and_key->context; +} + +s2n_pkey_type s2n_cert_chain_and_key_get_pkey_type(struct s2n_cert_chain_and_key *chain_and_key) +{ + if (chain_and_key == NULL + || chain_and_key->cert_chain == NULL + || chain_and_key->cert_chain->head == NULL) { + return S2N_PKEY_TYPE_UNKNOWN; + } + return chain_and_key->cert_chain->head->pkey_type; +} + +s2n_cert_private_key *s2n_cert_chain_and_key_get_private_key(struct s2n_cert_chain_and_key *chain_and_key) +{ + PTR_ENSURE_REF(chain_and_key); + return chain_and_key->private_key; +} + +int s2n_cert_chain_get_length(const struct s2n_cert_chain_and_key *chain_and_key, uint32_t *cert_length) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(cert_length); + + struct s2n_cert *head_cert = chain_and_key->cert_chain->head; + POSIX_ENSURE_REF(head_cert); + *cert_length = 1; + struct s2n_cert *next_cert = head_cert->next; + while (next_cert != NULL) { + *cert_length += 1; + next_cert = next_cert->next; + } + + return S2N_SUCCESS; +} + +int s2n_cert_chain_get_cert(const struct s2n_cert_chain_and_key *chain_and_key, struct s2n_cert **out_cert, + const uint32_t cert_idx) +{ + POSIX_ENSURE_REF(chain_and_key); + POSIX_ENSURE_REF(out_cert); + + struct s2n_cert *cur_cert = chain_and_key->cert_chain->head; + POSIX_ENSURE_REF(cur_cert); + uint32_t counter = 0; + + struct s2n_cert *next_cert = cur_cert->next; + + while ((next_cert != NULL) && (counter < cert_idx)) { + cur_cert = next_cert; + next_cert = next_cert->next; + counter++; + } + + POSIX_ENSURE(counter == cert_idx, S2N_ERR_NO_CERT_FOUND); + POSIX_ENSURE(cur_cert != NULL, S2N_ERR_NO_CERT_FOUND); + *out_cert = cur_cert; + + return S2N_SUCCESS; +} + +int s2n_cert_get_der(const struct s2n_cert *cert, const uint8_t **out_cert_der, uint32_t *cert_length) +{ + POSIX_ENSURE_REF(cert); + POSIX_ENSURE_REF(out_cert_der); + POSIX_ENSURE_REF(cert_length); + + *cert_length = cert->raw.size; + *out_cert_der = cert->raw.data; + + return S2N_SUCCESS; +} + +static int s2n_asn1_obj_free(ASN1_OBJECT **data) +{ + if (*data != NULL) { + ASN1_OBJECT_free(*data); + } + return S2N_SUCCESS; +} + +static int s2n_asn1_string_free(ASN1_STRING **data) +{ + if (*data != NULL) { + ASN1_STRING_free(*data); + } + return S2N_SUCCESS; +} + +static int s2n_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) +{ + DEFER_CLEANUP(ASN1_STRING *asn1_str = NULL, s2n_asn1_string_free); + /* Note that d2i_ASN1_UTF8STRING increments *der_in to the byte following the parsed data. + * Using a temporary variable is mandatory to prevent memory free-ing errors. + * Ref to the warning section here for more information: + * https://www.openssl.org/docs/man1.1.0/man3/d2i_ASN1_UTF8STRING.html. + */ + const uint8_t *asn1_str_data = extension_data; + asn1_str = d2i_ASN1_UTF8STRING(NULL, (const unsigned char **) (void *) &asn1_str_data, extension_len); + POSIX_ENSURE(asn1_str != NULL, S2N_ERR_INVALID_X509_EXTENSION_TYPE); + /* ASN1_STRING_type() returns the type of `asn1_str`, using standard constants such as V_ASN1_OCTET_STRING. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_type.html. + */ + int type = ASN1_STRING_type(asn1_str); + POSIX_ENSURE(type == V_ASN1_UTF8STRING, S2N_ERR_INVALID_X509_EXTENSION_TYPE); + + int len = ASN1_STRING_length(asn1_str); + POSIX_ENSURE_GTE(len, 0); + if (out_data != NULL) { + POSIX_ENSURE((int64_t) *out_len >= (int64_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); + /* ASN1_STRING_data() returns an internal pointer to the data. + * Since this is an internal pointer it should not be freed or modified in any way. + * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. + */ + unsigned char *internal_data = ASN1_STRING_data(asn1_str); + POSIX_ENSURE_REF(internal_data); + POSIX_CHECKED_MEMCPY(out_data, internal_data, len); + } + *out_len = len; + return S2N_SUCCESS; +} + +int s2n_cert_get_utf8_string_from_extension_data_length(const uint8_t *extension_data, uint32_t extension_len, uint32_t *utf8_str_len) +{ + POSIX_ENSURE_REF(extension_data); + POSIX_ENSURE_GT(extension_len, 0); + POSIX_ENSURE_REF(utf8_str_len); + + POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, NULL, utf8_str_len)); + + return S2N_SUCCESS; +} + +int s2n_cert_get_utf8_string_from_extension_data(const uint8_t *extension_data, uint32_t extension_len, uint8_t *out_data, uint32_t *out_len) +{ + POSIX_ENSURE_REF(extension_data); + POSIX_ENSURE_GT(extension_len, 0); + POSIX_ENSURE_REF(out_data); + POSIX_ENSURE_REF(out_len); + + POSIX_GUARD(s2n_utf8_string_from_extension_data(extension_data, extension_len, out_data, out_len)); + + return S2N_SUCCESS; +} + +static int s2n_parse_x509_extension(struct s2n_cert *cert, const uint8_t *oid, + uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) +{ + POSIX_ENSURE_REF(cert->raw.data); + /* Obtain the openssl x509 cert from the ASN1 DER certificate input. + * Note that d2i_X509 increments *der_in to the byte following the parsed data. + * Using a temporary variable is mandatory to prevent memory free-ing errors. + * Ref to the warning section here for more information: + * https://www.openssl.org/docs/man1.1.0/man3/d2i_X509.html. + */ + uint8_t *der_in = cert->raw.data; + DEFER_CLEANUP(X509 *x509_cert = d2i_X509(NULL, (const unsigned char **) (void *) &der_in, cert->raw.size), + X509_free_pointer); + POSIX_ENSURE_REF(x509_cert); + + /* Retrieve the number of x509 extensions present in the certificate + * X509_get_ext_count returns the number of extensions in the x509 certificate. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext_count.html. + */ + int ext_count_value = X509_get_ext_count(x509_cert); + POSIX_ENSURE_GT(ext_count_value, 0); + size_t ext_count = (size_t) ext_count_value; + + /* OBJ_txt2obj() converts the input text string into an ASN1_OBJECT structure. + * If no_name is 0 then long names and short names will be interpreted as well as numerical forms. + * If no_name is 1 only the numerical form is acceptable. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html. + */ + DEFER_CLEANUP(ASN1_OBJECT *asn1_obj_in = OBJ_txt2obj((const char *) oid, 0), s2n_asn1_obj_free); + POSIX_ENSURE_REF(asn1_obj_in); + + for (size_t loc = 0; loc < ext_count; loc++) { + ASN1_OCTET_STRING *asn1_str = NULL; + bool match_found = false; + + /* Retrieve the x509 extension at location loc. + * X509_get_ext() retrieves extension loc from x. + * The index loc can take any value from 0 to X509_get_ext_count(x) - 1. + * The returned extension is an internal pointer which must not be freed up by the application. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html. + */ + X509_EXTENSION *x509_ext = X509_get_ext(x509_cert, loc); + POSIX_ENSURE_REF(x509_ext); + + /* Retrieve the extension object/OID/extnId. + * X509_EXTENSION_get_object() returns the extension type of `x509_ext` as an ASN1_OBJECT pointer. + * The returned pointer is an internal value which must not be freed up. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_object.html. + */ + ASN1_OBJECT *asn1_obj = X509_EXTENSION_get_object(x509_ext); + POSIX_ENSURE_REF(asn1_obj); + + /* OBJ_cmp() compares two ASN1_OBJECT objects. If the two are identical 0 is returned. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/OBJ_cmp.html. + */ + match_found = (0 == OBJ_cmp(asn1_obj_in, asn1_obj)); + + /* If match found, retrieve the corresponding OID value for the x509 extension */ + if (match_found) { + /* X509_EXTENSION_get_data() returns the data of extension `x509_ext`. + * The returned pointer is an internal value which must not be freed up. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_data.html. + */ + asn1_str = X509_EXTENSION_get_data(x509_ext); + POSIX_ENSURE_REF(asn1_str); + /* ASN1_STRING_length() returns the length of the content of `asn1_str`. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/ASN1_STRING_length.html. + */ + int len = ASN1_STRING_length(asn1_str); + if (ext_value != NULL) { + POSIX_ENSURE_GTE(len, 0); + POSIX_ENSURE(*ext_value_len >= (uint32_t) len, S2N_ERR_INSUFFICIENT_MEM_SIZE); + /* ASN1_STRING_data() returns an internal pointer to the data. + * Since this is an internal pointer it should not be freed or modified in any way. + * Ref: https://www.openssl.org/docs/man1.0.2/man3/ASN1_STRING_data.html. + */ + unsigned char *internal_data = ASN1_STRING_data(asn1_str); + POSIX_ENSURE_REF(internal_data); + POSIX_CHECKED_MEMCPY(ext_value, internal_data, len); + } + if (critical != NULL) { + /* Retrieve the x509 extension's critical value. + * X509_EXTENSION_get_critical() returns the criticality of extension `x509_ext`, + * it returns 1 for critical and 0 for non-critical. + * Ref: https://www.openssl.org/docs/man1.1.0/man3/X509_EXTENSION_get_critical.html. + */ + *critical = X509_EXTENSION_get_critical(x509_ext); + } + *ext_value_len = len; + return S2N_SUCCESS; + } + } + + POSIX_BAIL(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND); +} + +int s2n_cert_get_x509_extension_value_length(struct s2n_cert *cert, const uint8_t *oid, uint32_t *ext_value_len) +{ + POSIX_ENSURE_REF(cert); + POSIX_ENSURE_REF(oid); + POSIX_ENSURE_REF(ext_value_len); + + POSIX_GUARD(s2n_parse_x509_extension(cert, oid, NULL, ext_value_len, NULL)); + + return S2N_SUCCESS; +} + +int s2n_cert_get_x509_extension_value(struct s2n_cert *cert, const uint8_t *oid, + uint8_t *ext_value, uint32_t *ext_value_len, bool *critical) +{ + POSIX_ENSURE_REF(cert); + POSIX_ENSURE_REF(oid); + POSIX_ENSURE_REF(ext_value); + POSIX_ENSURE_REF(ext_value_len); + POSIX_ENSURE_REF(critical); + + POSIX_GUARD(s2n_parse_x509_extension(cert, oid, ext_value, ext_value_len, critical)); + + return S2N_SUCCESS; +} diff --git a/crypto/s2n_dhe.c b/crypto/s2n_dhe.c index ba400890e8e..1395e8e5bb6 100644 --- a/crypto/s2n_dhe.c +++ b/crypto/s2n_dhe.c @@ -1,384 +1,384 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_dhe.h" - -#include -#include -#include -#include - -#include "crypto/s2n_openssl.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -#define S2N_MIN_DH_PRIME_SIZE_BYTES (2048 / 8) - -/* Caller is not responsible for freeing values returned by these accessors - * Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html - */ -static const BIGNUM *s2n_get_Ys_dh_param(struct s2n_dh_params *dh_params) -{ - const BIGNUM *Ys = NULL; - -/* DH made opaque in Openssl 1.1.0 */ -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - DH_get0_key(dh_params->dh, &Ys, NULL); -#else - Ys = dh_params->dh->pub_key; -#endif - - return Ys; -} - -static const BIGNUM *s2n_get_p_dh_param(struct s2n_dh_params *dh_params) -{ - const BIGNUM *p = NULL; -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - DH_get0_pqg(dh_params->dh, &p, NULL, NULL); -#else - p = dh_params->dh->p; -#endif - - return p; -} - -static const BIGNUM *s2n_get_g_dh_param(struct s2n_dh_params *dh_params) -{ - const BIGNUM *g = NULL; -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - DH_get0_pqg(dh_params->dh, NULL, NULL, &g); -#else - g = dh_params->dh->g; -#endif - - return g; -} - -static int s2n_check_p_g_dh_params(struct s2n_dh_params *dh_params) -{ - POSIX_ENSURE_REF(dh_params); - POSIX_ENSURE_REF(dh_params->dh); - - const BIGNUM *p = s2n_get_p_dh_param(dh_params); - const BIGNUM *g = s2n_get_g_dh_param(dh_params); - - POSIX_ENSURE_REF(g); - POSIX_ENSURE_REF(p); - - S2N_ERROR_IF(DH_size(dh_params->dh) < S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_PARAMS_CREATE); - S2N_ERROR_IF(BN_is_zero(g), S2N_ERR_DH_PARAMS_CREATE); - S2N_ERROR_IF(BN_is_zero(p), S2N_ERR_DH_PARAMS_CREATE); - - return S2N_SUCCESS; -} - -static int s2n_check_pub_key_dh_params(struct s2n_dh_params *dh_params) -{ - const BIGNUM *pub_key = s2n_get_Ys_dh_param(dh_params); - POSIX_ENSURE_REF(pub_key); - - /* - * https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5 - * - * The following algorithm MAY be used to validate a received public - * key y. - * - * 1. Verify that y lies within the interval [2,p-1]. - * If it does not, the key is invalid. - * - * This check is optional per the RFC, but applied here as - * defense-in-depth to reject degenerate public key values. - */ - S2N_ERROR_IF(BN_is_zero(pub_key), S2N_ERR_DH_PARAMS_CREATE); - S2N_ERROR_IF(BN_is_one(pub_key), S2N_ERR_DH_PARAMS_CREATE); - - const BIGNUM *p = s2n_get_p_dh_param(dh_params); - POSIX_ENSURE_REF(p); - - BIGNUM *p_minus_one = BN_dup(p); - POSIX_ENSURE_REF(p_minus_one); - if (!BN_sub_word(p_minus_one, 1)) { - BN_free(p_minus_one); - POSIX_BAIL(S2N_ERR_DH_PARAMS_CREATE); - } - int cmp = BN_cmp(pub_key, p_minus_one); - BN_free(p_minus_one); - S2N_ERROR_IF(cmp > 0, S2N_ERR_DH_PARAMS_CREATE); - - return S2N_SUCCESS; -} - -static int s2n_set_p_g_Ys_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *p, struct s2n_blob *g, - struct s2n_blob *Ys) -{ - POSIX_ENSURE(p->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); - POSIX_ENSURE(g->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); - POSIX_ENSURE(Ys->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); - BIGNUM *bn_p = BN_bin2bn((const unsigned char *) p->data, p->size, NULL); - BIGNUM *bn_g = BN_bin2bn((const unsigned char *) g->data, g->size, NULL); - BIGNUM *bn_Ys = BN_bin2bn((const unsigned char *) Ys->data, Ys->size, NULL); - -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - /* Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html: - * values that have been passed in should not be freed directly after this function has been called - */ - POSIX_GUARD_OSSL(DH_set0_pqg(dh_params->dh, bn_p, NULL, bn_g), S2N_ERR_DH_PARAMS_CREATE); - - /* Same as DH_set0_pqg */ - POSIX_GUARD_OSSL(DH_set0_key(dh_params->dh, bn_Ys, NULL), S2N_ERR_DH_PARAMS_CREATE); -#else - dh_params->dh->p = bn_p; - dh_params->dh->g = bn_g; - dh_params->dh->pub_key = bn_Ys; -#endif - - return S2N_SUCCESS; -} - -int s2n_check_all_dh_params(struct s2n_dh_params *dh_params) -{ - POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); - POSIX_GUARD(s2n_check_pub_key_dh_params(dh_params)); - - return S2N_SUCCESS; -} - -int s2n_pkcs3_to_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *pkcs3) -{ - POSIX_ENSURE_REF(dh_params); - POSIX_PRECONDITION(s2n_blob_validate(pkcs3)); - DEFER_CLEANUP(struct s2n_dh_params temp_dh_params = { 0 }, s2n_dh_params_free); - - uint8_t *original_ptr = pkcs3->data; - temp_dh_params.dh = d2i_DHparams(NULL, (const unsigned char **) (void *) &pkcs3->data, pkcs3->size); - - POSIX_GUARD(s2n_check_p_g_dh_params(&temp_dh_params)); - - if (pkcs3->data) { - POSIX_ENSURE_GTE(pkcs3->data, original_ptr); - POSIX_ENSURE((uint32_t) (pkcs3->data - original_ptr) == pkcs3->size, S2N_ERR_INVALID_PKCS3); - } - - pkcs3->data = original_ptr; - - /* Require at least 2048 bits for the DH size */ - POSIX_ENSURE(DH_size(temp_dh_params.dh) >= S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_TOO_SMALL); - - /* Check the generator and prime */ - POSIX_GUARD(s2n_dh_params_check(&temp_dh_params)); - - dh_params->dh = temp_dh_params.dh; - - ZERO_TO_DISABLE_DEFER_CLEANUP(temp_dh_params); - - return S2N_SUCCESS; -} - -int s2n_dh_p_g_Ys_to_dh_params(struct s2n_dh_params *server_dh_params, struct s2n_blob *p, struct s2n_blob *g, - struct s2n_blob *Ys) -{ - POSIX_ENSURE_REF(server_dh_params); - POSIX_PRECONDITION(s2n_blob_validate(p)); - POSIX_PRECONDITION(s2n_blob_validate(g)); - POSIX_PRECONDITION(s2n_blob_validate(Ys)); - - server_dh_params->dh = DH_new(); - POSIX_ENSURE(server_dh_params->dh != NULL, S2N_ERR_DH_PARAMS_CREATE); - - POSIX_GUARD(s2n_set_p_g_Ys_dh_params(server_dh_params, p, g, Ys)); - POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); - - return S2N_SUCCESS; -} - -int s2n_dh_params_to_p_g_Ys(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *out, struct s2n_blob *output) -{ - POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); - POSIX_PRECONDITION(s2n_stuffer_validate(out)); - POSIX_PRECONDITION(s2n_blob_validate(output)); - - const BIGNUM *bn_p = s2n_get_p_dh_param(server_dh_params); - const BIGNUM *bn_g = s2n_get_g_dh_param(server_dh_params); - const BIGNUM *bn_Ys = s2n_get_Ys_dh_param(server_dh_params); - - uint16_t p_size = BN_num_bytes(bn_p); - uint16_t g_size = BN_num_bytes(bn_g); - uint16_t Ys_size = BN_num_bytes(bn_Ys); - uint8_t *p = NULL; - uint8_t *g = NULL; - uint8_t *Ys = NULL; - - output->data = s2n_stuffer_raw_write(out, 0); - POSIX_ENSURE_REF(output->data); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, p_size)); - p = s2n_stuffer_raw_write(out, p_size); - POSIX_ENSURE_REF(p); - POSIX_ENSURE(BN_bn2bin(bn_p, p) == p_size, S2N_ERR_DH_SERIALIZING); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, g_size)); - g = s2n_stuffer_raw_write(out, g_size); - POSIX_ENSURE_REF(g); - POSIX_ENSURE(BN_bn2bin(bn_g, g) == g_size, S2N_ERR_DH_SERIALIZING); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, Ys_size)); - Ys = s2n_stuffer_raw_write(out, Ys_size); - POSIX_ENSURE_REF(Ys); - POSIX_ENSURE(BN_bn2bin(bn_Ys, Ys) == Ys_size, S2N_ERR_DH_SERIALIZING); - - output->size = p_size + 2 + g_size + 2 + Ys_size + 2; - - return S2N_SUCCESS; -} - -int s2n_dh_compute_shared_secret_as_client(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_out, - struct s2n_blob *shared_key) -{ - struct s2n_dh_params client_params = { 0 }; - uint8_t *client_pub_key = NULL; - uint16_t client_pub_key_size = 0; - int shared_key_size = 0; - - POSIX_GUARD(s2n_dh_params_check(server_dh_params)); - POSIX_GUARD(s2n_dh_params_copy(server_dh_params, &client_params)); - POSIX_GUARD(s2n_dh_generate_ephemeral_key(&client_params)); - POSIX_GUARD(s2n_alloc(shared_key, DH_size(server_dh_params->dh))); - - const BIGNUM *client_pub_key_bn = s2n_get_Ys_dh_param(&client_params); - POSIX_ENSURE_REF(client_pub_key_bn); - client_pub_key_size = BN_num_bytes(client_pub_key_bn); - POSIX_GUARD(s2n_stuffer_write_uint16(Yc_out, client_pub_key_size)); - client_pub_key = s2n_stuffer_raw_write(Yc_out, client_pub_key_size); - if (client_pub_key == NULL) { - POSIX_GUARD(s2n_free(shared_key)); - POSIX_GUARD(s2n_dh_params_free(&client_params)); - POSIX_BAIL(S2N_ERR_DH_WRITING_PUBLIC_KEY); - } - - if (BN_bn2bin(client_pub_key_bn, client_pub_key) != client_pub_key_size) { - POSIX_GUARD(s2n_free(shared_key)); - POSIX_GUARD(s2n_dh_params_free(&client_params)); - POSIX_BAIL(S2N_ERR_DH_COPYING_PUBLIC_KEY); - } - - /* server_dh_params already validated */ - const BIGNUM *server_pub_key_bn = s2n_get_Ys_dh_param(server_dh_params); - shared_key_size = DH_compute_key(shared_key->data, server_pub_key_bn, client_params.dh); - if (shared_key_size < 0) { - POSIX_GUARD(s2n_free(shared_key)); - POSIX_GUARD(s2n_dh_params_free(&client_params)); - POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); - } - - shared_key->size = shared_key_size; - - POSIX_GUARD(s2n_dh_params_free(&client_params)); - - return S2N_SUCCESS; -} - -int s2n_dh_compute_shared_secret_as_server(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_in, - struct s2n_blob *shared_key) -{ - uint16_t Yc_length = 0; - struct s2n_blob Yc = { 0 }; - int shared_key_size = 0; - BIGNUM *pub_key = NULL; - - POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); - int server_dh_params_size = DH_size(server_dh_params->dh); - POSIX_ENSURE(server_dh_params_size <= INT32_MAX, S2N_ERR_INTEGER_OVERFLOW); - - /* - * As defined in https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.2, - * the client's DH public value (Yc) is sent as a variable-length opaque value. - * Validate that Yc_length does not exceed the DH group size to prevent - * unnecessary computation and memory allocation on oversized keys. - * - * According to https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5, - * the valid range of Yc is [2, p-1]. When encoding a BIGNUM to bytes, - * leading zeros are often stripped, in which case Yc_length might be - * less than server_dh_params_size. - */ - POSIX_GUARD(s2n_stuffer_read_uint16(Yc_in, &Yc_length)); - POSIX_ENSURE(Yc_length > 0, S2N_ERR_DH_SHARED_SECRET); - POSIX_ENSURE((int) Yc_length <= server_dh_params_size, S2N_ERR_DH_SHARED_SECRET); - - Yc.size = Yc_length; - Yc.data = s2n_stuffer_raw_read(Yc_in, Yc.size); - POSIX_ENSURE_REF(Yc.data); - - pub_key = BN_bin2bn((const unsigned char *) Yc.data, Yc.size, NULL); - POSIX_ENSURE_REF(pub_key); - POSIX_GUARD(s2n_alloc(shared_key, server_dh_params_size)); - - shared_key_size = DH_compute_key(shared_key->data, pub_key, server_dh_params->dh); - if (shared_key_size <= 0) { - BN_free(pub_key); - POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); - } - - shared_key->size = shared_key_size; - - BN_free(pub_key); - - return S2N_SUCCESS; -} - -int s2n_dh_params_check(struct s2n_dh_params *dh_params) -{ - POSIX_ENSURE_REF(dh_params); - POSIX_ENSURE_REF(dh_params->dh); - int codes = 0; - - POSIX_GUARD_OSSL(DH_check(dh_params->dh, &codes), S2N_ERR_DH_PARAMETER_CHECK); - POSIX_ENSURE(codes == 0, S2N_ERR_DH_PARAMETER_CHECK); - - return S2N_SUCCESS; -} - -int s2n_dh_params_copy(struct s2n_dh_params *from, struct s2n_dh_params *to) -{ - POSIX_GUARD(s2n_check_p_g_dh_params(from)); - POSIX_ENSURE_REF(to); - - to->dh = DHparams_dup(from->dh); - POSIX_ENSURE(to->dh != NULL, S2N_ERR_DH_COPYING_PARAMETERS); - - return S2N_SUCCESS; -} - -int s2n_dh_generate_ephemeral_key(struct s2n_dh_params *dh_params) -{ - POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); - - POSIX_GUARD_OSSL(DH_generate_key(dh_params->dh), S2N_ERR_DH_GENERATING_PARAMETERS); - - return S2N_SUCCESS; -} - -int s2n_dh_params_free(struct s2n_dh_params *dh_params) -{ - POSIX_ENSURE_REF(dh_params); - DH_free(dh_params->dh); - dh_params->dh = NULL; - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_dhe.h" + +#include +#include +#include +#include + +#include "crypto/s2n_openssl.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +#define S2N_MIN_DH_PRIME_SIZE_BYTES (2048 / 8) + +/* Caller is not responsible for freeing values returned by these accessors + * Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html + */ +static const BIGNUM *s2n_get_Ys_dh_param(struct s2n_dh_params *dh_params) +{ + const BIGNUM *Ys = NULL; + +/* DH made opaque in Openssl 1.1.0 */ +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + DH_get0_key(dh_params->dh, &Ys, NULL); +#else + Ys = dh_params->dh->pub_key; +#endif + + return Ys; +} + +static const BIGNUM *s2n_get_p_dh_param(struct s2n_dh_params *dh_params) +{ + const BIGNUM *p = NULL; +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + DH_get0_pqg(dh_params->dh, &p, NULL, NULL); +#else + p = dh_params->dh->p; +#endif + + return p; +} + +static const BIGNUM *s2n_get_g_dh_param(struct s2n_dh_params *dh_params) +{ + const BIGNUM *g = NULL; +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + DH_get0_pqg(dh_params->dh, NULL, NULL, &g); +#else + g = dh_params->dh->g; +#endif + + return g; +} + +static int s2n_check_p_g_dh_params(struct s2n_dh_params *dh_params) +{ + POSIX_ENSURE_REF(dh_params); + POSIX_ENSURE_REF(dh_params->dh); + + const BIGNUM *p = s2n_get_p_dh_param(dh_params); + const BIGNUM *g = s2n_get_g_dh_param(dh_params); + + POSIX_ENSURE_REF(g); + POSIX_ENSURE_REF(p); + + S2N_ERROR_IF(DH_size(dh_params->dh) < S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_PARAMS_CREATE); + S2N_ERROR_IF(BN_is_zero(g), S2N_ERR_DH_PARAMS_CREATE); + S2N_ERROR_IF(BN_is_zero(p), S2N_ERR_DH_PARAMS_CREATE); + + return S2N_SUCCESS; +} + +static int s2n_check_pub_key_dh_params(struct s2n_dh_params *dh_params) +{ + const BIGNUM *pub_key = s2n_get_Ys_dh_param(dh_params); + POSIX_ENSURE_REF(pub_key); + + /* + * https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5 + * + * The following algorithm MAY be used to validate a received public + * key y. + * + * 1. Verify that y lies within the interval [2,p-1]. + * If it does not, the key is invalid. + * + * This check is optional per the RFC, but applied here as + * defense-in-depth to reject degenerate public key values. + */ + S2N_ERROR_IF(BN_is_zero(pub_key), S2N_ERR_DH_PARAMS_CREATE); + S2N_ERROR_IF(BN_is_one(pub_key), S2N_ERR_DH_PARAMS_CREATE); + + const BIGNUM *p = s2n_get_p_dh_param(dh_params); + POSIX_ENSURE_REF(p); + + BIGNUM *p_minus_one = BN_dup(p); + POSIX_ENSURE_REF(p_minus_one); + if (!BN_sub_word(p_minus_one, 1)) { + BN_free(p_minus_one); + POSIX_BAIL(S2N_ERR_DH_PARAMS_CREATE); + } + int cmp = BN_cmp(pub_key, p_minus_one); + BN_free(p_minus_one); + S2N_ERROR_IF(cmp > 0, S2N_ERR_DH_PARAMS_CREATE); + + return S2N_SUCCESS; +} + +static int s2n_set_p_g_Ys_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *p, struct s2n_blob *g, + struct s2n_blob *Ys) +{ + POSIX_ENSURE(p->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); + POSIX_ENSURE(g->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); + POSIX_ENSURE(Ys->size <= INT_MAX, S2N_ERR_INTEGER_OVERFLOW); + BIGNUM *bn_p = BN_bin2bn((const unsigned char *) p->data, p->size, NULL); + BIGNUM *bn_g = BN_bin2bn((const unsigned char *) g->data, g->size, NULL); + BIGNUM *bn_Ys = BN_bin2bn((const unsigned char *) Ys->data, Ys->size, NULL); + +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + /* Per https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html: + * values that have been passed in should not be freed directly after this function has been called + */ + POSIX_GUARD_OSSL(DH_set0_pqg(dh_params->dh, bn_p, NULL, bn_g), S2N_ERR_DH_PARAMS_CREATE); + + /* Same as DH_set0_pqg */ + POSIX_GUARD_OSSL(DH_set0_key(dh_params->dh, bn_Ys, NULL), S2N_ERR_DH_PARAMS_CREATE); +#else + dh_params->dh->p = bn_p; + dh_params->dh->g = bn_g; + dh_params->dh->pub_key = bn_Ys; +#endif + + return S2N_SUCCESS; +} + +int s2n_check_all_dh_params(struct s2n_dh_params *dh_params) +{ + POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); + POSIX_GUARD(s2n_check_pub_key_dh_params(dh_params)); + + return S2N_SUCCESS; +} + +int s2n_pkcs3_to_dh_params(struct s2n_dh_params *dh_params, struct s2n_blob *pkcs3) +{ + POSIX_ENSURE_REF(dh_params); + POSIX_PRECONDITION(s2n_blob_validate(pkcs3)); + DEFER_CLEANUP(struct s2n_dh_params temp_dh_params = { 0 }, s2n_dh_params_free); + + uint8_t *original_ptr = pkcs3->data; + temp_dh_params.dh = d2i_DHparams(NULL, (const unsigned char **) (void *) &pkcs3->data, pkcs3->size); + + POSIX_GUARD(s2n_check_p_g_dh_params(&temp_dh_params)); + + if (pkcs3->data) { + POSIX_ENSURE_GTE(pkcs3->data, original_ptr); + POSIX_ENSURE((uint32_t) (pkcs3->data - original_ptr) == pkcs3->size, S2N_ERR_INVALID_PKCS3); + } + + pkcs3->data = original_ptr; + + /* Require at least 2048 bits for the DH size */ + POSIX_ENSURE(DH_size(temp_dh_params.dh) >= S2N_MIN_DH_PRIME_SIZE_BYTES, S2N_ERR_DH_TOO_SMALL); + + /* Check the generator and prime */ + POSIX_GUARD(s2n_dh_params_check(&temp_dh_params)); + + dh_params->dh = temp_dh_params.dh; + + ZERO_TO_DISABLE_DEFER_CLEANUP(temp_dh_params); + + return S2N_SUCCESS; +} + +int s2n_dh_p_g_Ys_to_dh_params(struct s2n_dh_params *server_dh_params, struct s2n_blob *p, struct s2n_blob *g, + struct s2n_blob *Ys) +{ + POSIX_ENSURE_REF(server_dh_params); + POSIX_PRECONDITION(s2n_blob_validate(p)); + POSIX_PRECONDITION(s2n_blob_validate(g)); + POSIX_PRECONDITION(s2n_blob_validate(Ys)); + + server_dh_params->dh = DH_new(); + POSIX_ENSURE(server_dh_params->dh != NULL, S2N_ERR_DH_PARAMS_CREATE); + + POSIX_GUARD(s2n_set_p_g_Ys_dh_params(server_dh_params, p, g, Ys)); + POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); + + return S2N_SUCCESS; +} + +int s2n_dh_params_to_p_g_Ys(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *out, struct s2n_blob *output) +{ + POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); + POSIX_PRECONDITION(s2n_stuffer_validate(out)); + POSIX_PRECONDITION(s2n_blob_validate(output)); + + const BIGNUM *bn_p = s2n_get_p_dh_param(server_dh_params); + const BIGNUM *bn_g = s2n_get_g_dh_param(server_dh_params); + const BIGNUM *bn_Ys = s2n_get_Ys_dh_param(server_dh_params); + + uint16_t p_size = BN_num_bytes(bn_p); + uint16_t g_size = BN_num_bytes(bn_g); + uint16_t Ys_size = BN_num_bytes(bn_Ys); + uint8_t *p = NULL; + uint8_t *g = NULL; + uint8_t *Ys = NULL; + + output->data = s2n_stuffer_raw_write(out, 0); + POSIX_ENSURE_REF(output->data); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, p_size)); + p = s2n_stuffer_raw_write(out, p_size); + POSIX_ENSURE_REF(p); + POSIX_ENSURE(BN_bn2bin(bn_p, p) == p_size, S2N_ERR_DH_SERIALIZING); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, g_size)); + g = s2n_stuffer_raw_write(out, g_size); + POSIX_ENSURE_REF(g); + POSIX_ENSURE(BN_bn2bin(bn_g, g) == g_size, S2N_ERR_DH_SERIALIZING); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, Ys_size)); + Ys = s2n_stuffer_raw_write(out, Ys_size); + POSIX_ENSURE_REF(Ys); + POSIX_ENSURE(BN_bn2bin(bn_Ys, Ys) == Ys_size, S2N_ERR_DH_SERIALIZING); + + output->size = p_size + 2 + g_size + 2 + Ys_size + 2; + + return S2N_SUCCESS; +} + +int s2n_dh_compute_shared_secret_as_client(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_out, + struct s2n_blob *shared_key) +{ + struct s2n_dh_params client_params = { 0 }; + uint8_t *client_pub_key = NULL; + uint16_t client_pub_key_size = 0; + int shared_key_size = 0; + + POSIX_GUARD(s2n_dh_params_check(server_dh_params)); + POSIX_GUARD(s2n_dh_params_copy(server_dh_params, &client_params)); + POSIX_GUARD(s2n_dh_generate_ephemeral_key(&client_params)); + POSIX_GUARD(s2n_alloc(shared_key, DH_size(server_dh_params->dh))); + + const BIGNUM *client_pub_key_bn = s2n_get_Ys_dh_param(&client_params); + POSIX_ENSURE_REF(client_pub_key_bn); + client_pub_key_size = BN_num_bytes(client_pub_key_bn); + POSIX_GUARD(s2n_stuffer_write_uint16(Yc_out, client_pub_key_size)); + client_pub_key = s2n_stuffer_raw_write(Yc_out, client_pub_key_size); + if (client_pub_key == NULL) { + POSIX_GUARD(s2n_free(shared_key)); + POSIX_GUARD(s2n_dh_params_free(&client_params)); + POSIX_BAIL(S2N_ERR_DH_WRITING_PUBLIC_KEY); + } + + if (BN_bn2bin(client_pub_key_bn, client_pub_key) != client_pub_key_size) { + POSIX_GUARD(s2n_free(shared_key)); + POSIX_GUARD(s2n_dh_params_free(&client_params)); + POSIX_BAIL(S2N_ERR_DH_COPYING_PUBLIC_KEY); + } + + /* server_dh_params already validated */ + const BIGNUM *server_pub_key_bn = s2n_get_Ys_dh_param(server_dh_params); + shared_key_size = DH_compute_key(shared_key->data, server_pub_key_bn, client_params.dh); + if (shared_key_size < 0) { + POSIX_GUARD(s2n_free(shared_key)); + POSIX_GUARD(s2n_dh_params_free(&client_params)); + POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); + } + + shared_key->size = shared_key_size; + + POSIX_GUARD(s2n_dh_params_free(&client_params)); + + return S2N_SUCCESS; +} + +int s2n_dh_compute_shared_secret_as_server(struct s2n_dh_params *server_dh_params, struct s2n_stuffer *Yc_in, + struct s2n_blob *shared_key) +{ + uint16_t Yc_length = 0; + struct s2n_blob Yc = { 0 }; + int shared_key_size = 0; + BIGNUM *pub_key = NULL; + + POSIX_GUARD(s2n_check_all_dh_params(server_dh_params)); + int server_dh_params_size = DH_size(server_dh_params->dh); + POSIX_ENSURE(server_dh_params_size <= INT32_MAX, S2N_ERR_INTEGER_OVERFLOW); + + /* + * As defined in https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.2, + * the client's DH public value (Yc) is sent as a variable-length opaque value. + * Validate that Yc_length does not exceed the DH group size to prevent + * unnecessary computation and memory allocation on oversized keys. + * + * According to https://www.rfc-editor.org/rfc/rfc2631#section-2.1.5, + * the valid range of Yc is [2, p-1]. When encoding a BIGNUM to bytes, + * leading zeros are often stripped, in which case Yc_length might be + * less than server_dh_params_size. + */ + POSIX_GUARD(s2n_stuffer_read_uint16(Yc_in, &Yc_length)); + POSIX_ENSURE(Yc_length > 0, S2N_ERR_DH_SHARED_SECRET); + POSIX_ENSURE((int) Yc_length <= server_dh_params_size, S2N_ERR_DH_SHARED_SECRET); + + Yc.size = Yc_length; + Yc.data = s2n_stuffer_raw_read(Yc_in, Yc.size); + POSIX_ENSURE_REF(Yc.data); + + pub_key = BN_bin2bn((const unsigned char *) Yc.data, Yc.size, NULL); + POSIX_ENSURE_REF(pub_key); + POSIX_GUARD(s2n_alloc(shared_key, server_dh_params_size)); + + shared_key_size = DH_compute_key(shared_key->data, pub_key, server_dh_params->dh); + if (shared_key_size <= 0) { + BN_free(pub_key); + POSIX_BAIL(S2N_ERR_DH_SHARED_SECRET); + } + + shared_key->size = shared_key_size; + + BN_free(pub_key); + + return S2N_SUCCESS; +} + +int s2n_dh_params_check(struct s2n_dh_params *dh_params) +{ + POSIX_ENSURE_REF(dh_params); + POSIX_ENSURE_REF(dh_params->dh); + int codes = 0; + + POSIX_GUARD_OSSL(DH_check(dh_params->dh, &codes), S2N_ERR_DH_PARAMETER_CHECK); + POSIX_ENSURE(codes == 0, S2N_ERR_DH_PARAMETER_CHECK); + + return S2N_SUCCESS; +} + +int s2n_dh_params_copy(struct s2n_dh_params *from, struct s2n_dh_params *to) +{ + POSIX_GUARD(s2n_check_p_g_dh_params(from)); + POSIX_ENSURE_REF(to); + + to->dh = DHparams_dup(from->dh); + POSIX_ENSURE(to->dh != NULL, S2N_ERR_DH_COPYING_PARAMETERS); + + return S2N_SUCCESS; +} + +int s2n_dh_generate_ephemeral_key(struct s2n_dh_params *dh_params) +{ + POSIX_GUARD(s2n_check_p_g_dh_params(dh_params)); + + POSIX_GUARD_OSSL(DH_generate_key(dh_params->dh), S2N_ERR_DH_GENERATING_PARAMETERS); + + return S2N_SUCCESS; +} + +int s2n_dh_params_free(struct s2n_dh_params *dh_params) +{ + POSIX_ENSURE_REF(dh_params); + DH_free(dh_params->dh); + dh_params->dh = NULL; + + return S2N_SUCCESS; +} diff --git a/crypto/s2n_hkdf.c b/crypto/s2n_hkdf.c index 06273c1413c..d96a129897a 100644 --- a/crypto/s2n_hkdf.c +++ b/crypto/s2n_hkdf.c @@ -1,395 +1,395 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_hkdf.h" - -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hmac.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF - #include -#endif - -#define MAX_DIGEST_SIZE 64 /* Current highest is SHA512 */ -#define MAX_HKDF_ROUNDS 255 - -/* Reference: RFC 5869 */ - -struct s2n_hkdf_impl { - int (*hkdf)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output); - int (*hkdf_extract)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key); - int (*hkdf_expand)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, - const struct s2n_blob *info, struct s2n_blob *output); -}; - -static int s2n_custom_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - uint8_t hmac_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); - POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); - pseudo_rand_key->size = hmac_size; - - POSIX_GUARD(s2n_hmac_init(hmac, alg, salt->data, salt->size)); - POSIX_GUARD(s2n_hmac_update(hmac, key->data, key->size)); - POSIX_GUARD(s2n_hmac_digest(hmac, pseudo_rand_key->data, pseudo_rand_key->size)); - - POSIX_GUARD(s2n_hmac_reset(hmac)); - - return S2N_SUCCESS; -} - -static int s2n_custom_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - uint8_t prev[MAX_DIGEST_SIZE] = { 0 }; - - uint32_t done_len = 0; - uint8_t hash_len = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &hash_len)); - POSIX_ENSURE_GT(hash_len, 0); - uint32_t total_rounds = output->size / hash_len; - if (output->size % hash_len) { - total_rounds++; - } - - POSIX_ENSURE(total_rounds > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - POSIX_ENSURE(total_rounds <= MAX_HKDF_ROUNDS, S2N_ERR_HKDF_OUTPUT_SIZE); - - for (uint32_t curr_round = 1; curr_round <= total_rounds; curr_round++) { - uint32_t cat_len = 0; - POSIX_GUARD(s2n_hmac_init(hmac, alg, pseudo_rand_key->data, pseudo_rand_key->size)); - if (curr_round != 1) { - POSIX_GUARD(s2n_hmac_update(hmac, prev, hash_len)); - } - POSIX_GUARD(s2n_hmac_update(hmac, info->data, info->size)); - uint8_t curr_round_byte = curr_round; - POSIX_GUARD(s2n_hmac_update(hmac, &curr_round_byte, 1)); - POSIX_GUARD(s2n_hmac_digest(hmac, prev, hash_len)); - - cat_len = hash_len; - if (done_len + hash_len > output->size) { - cat_len = output->size - done_len; - } - - POSIX_CHECKED_MEMCPY(output->data + done_len, prev, cat_len); - - done_len += cat_len; - - POSIX_GUARD(s2n_hmac_reset(hmac)); - } - - return S2N_SUCCESS; -} - -static int s2n_custom_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - uint8_t prk_pad[MAX_DIGEST_SIZE] = { 0 }; - struct s2n_blob pseudo_rand_key = { 0 }; - POSIX_GUARD(s2n_blob_init(&pseudo_rand_key, prk_pad, sizeof(prk_pad))); - - POSIX_GUARD(s2n_custom_hkdf_extract(hmac, alg, salt, key, &pseudo_rand_key)); - POSIX_GUARD(s2n_custom_hkdf_expand(hmac, alg, &pseudo_rand_key, info, output)); - - return S2N_SUCCESS; -} - -const struct s2n_hkdf_impl s2n_custom_hkdf_impl = { - .hkdf = &s2n_custom_hkdf, - .hkdf_extract = &s2n_custom_hkdf_extract, - .hkdf_expand = &s2n_custom_hkdf_expand, -}; - -#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF -static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - const EVP_MD *digest = NULL; - POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); - - /* The out_len argument of HKDF_extract is set to the number of bytes written to out_key, and - * is not used to ensure that out_key is large enough to contain the PRK. Ensure that the PRK - * output will fit in the blob. - */ - uint8_t hmac_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); - POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); - - size_t bytes_written = 0; - POSIX_GUARD_OSSL(HKDF_extract(pseudo_rand_key->data, &bytes_written, digest, key->data, key->size, - salt->data, salt->size), - S2N_ERR_HKDF); - - /* HKDF_extract updates the out_len argument based on the digest size. Update the blob's size based on this. */ - POSIX_ENSURE_LTE(bytes_written, pseudo_rand_key->size); - pseudo_rand_key->size = bytes_written; - - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - - const EVP_MD *digest = NULL; - POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); - - POSIX_GUARD_OSSL(HKDF_expand(output->data, output->size, digest, pseudo_rand_key->data, pseudo_rand_key->size, - info->data, info->size), - S2N_ERR_HKDF); - - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - - const EVP_MD *digest = NULL; - POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); - - POSIX_GUARD_OSSL(HKDF(output->data, output->size, digest, key->data, key->size, salt->data, salt->size, - info->data, info->size), - S2N_ERR_HKDF); - - return S2N_SUCCESS; -} - -bool s2n_libcrypto_supports_hkdf() -{ - return true; -} - -#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0) - - #include "crypto/s2n_kdf.h" - -static S2N_RESULT s2n_hkdf_kdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *salt, const struct s2n_blob *key, const struct s2n_blob *info, - struct s2n_blob *output, int mode) -{ - /* As an optimization, we should be able to fetch and cache this EVP_KDF* - * once when s2n_init is called. - */ - DEFER_CLEANUP(EVP_KDF *hkdf_impl = EVP_KDF_fetch(NULL, "HKDF", NULL), - EVP_KDF_free_pointer); - RESULT_ENSURE(hkdf_impl, S2N_ERR_PRF_INVALID_ALGORITHM); - - DEFER_CLEANUP(EVP_KDF_CTX *hkdf_ctx = EVP_KDF_CTX_new(hkdf_impl), - EVP_KDF_CTX_free_pointer); - RESULT_ENSURE_REF(hkdf_ctx); - - const EVP_MD *digest = NULL; - RESULT_GUARD(s2n_hmac_md_from_alg(alg, &digest)); - RESULT_ENSURE_REF(digest); - const char *digest_name = EVP_MD_get0_name(digest); - RESULT_ENSURE_REF(digest_name); - - OSSL_PARAM params[] = { - S2N_OSSL_PARAM_INT(OSSL_KDF_PARAM_MODE, mode), - S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_KEY, key), - S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_INFO, info), - S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SALT, salt), - /* Casting away the const is safe because providers are forbidden from - * modifying any OSSL_PARAM value other than return_size. - * Even the examples in the Openssl documentation cast const strings to - * non-const void pointers when setting up OSSL_PARAMs. - */ - S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name), - OSSL_PARAM_END, - }; - - /* From the HKDF docs (https://docs.openssl.org/3.1/man7/EVP_KDF-HKDF/): - * > When using EVP_KDF_HKDF_MODE_EXTRACT_ONLY the keylen parameter must equal - * > the size of the intermediate fixed-length pseudorandom key otherwise an - * > error will occur. - */ - if (mode == EVP_KDF_HKDF_MODE_EXTRACT_ONLY) { - RESULT_GUARD_OSSL(EVP_KDF_CTX_set_params(hkdf_ctx, params), S2N_ERR_HKDF); - size_t key_size = EVP_KDF_CTX_get_kdf_size(hkdf_ctx); - RESULT_ENSURE(key_size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); - RESULT_ENSURE(key_size <= output->size, S2N_ERR_HKDF_OUTPUT_SIZE); - output->size = key_size; - } - - RESULT_GUARD_OSSL(EVP_KDF_derive(hkdf_ctx, output->data, output->size, params), - S2N_ERR_HKDF); - return S2N_RESULT_OK; -} - -static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *salt, const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - struct s2n_blob empty_info = { 0 }; - POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, &empty_info, pseudo_rand_key, - EVP_KDF_HKDF_MODE_EXTRACT_ONLY)); - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - struct s2n_blob empty_salt = { 0 }; - POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, &empty_salt, pseudo_rand_key, info, output, - EVP_KDF_HKDF_MODE_EXPAND_ONLY)); - return S2N_SUCCESS; -} - -static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, info, output, - EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND)); - return S2N_SUCCESS; -} - -bool s2n_libcrypto_supports_hkdf() -{ - return true; -} - -#else - -static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, - const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -bool s2n_libcrypto_supports_hkdf() -{ - return false; -} - -#endif /* S2N_LIBCRYPTO_SUPPORTS_HKDF */ - -const struct s2n_hkdf_impl s2n_libcrypto_hkdf_impl = { - .hkdf = &s2n_libcrypto_hkdf, - .hkdf_extract = &s2n_libcrypto_hkdf_extract, - .hkdf_expand = &s2n_libcrypto_hkdf_expand, -}; - -static const struct s2n_hkdf_impl *s2n_get_hkdf_implementation() -{ - /* By default, s2n-tls uses a custom HKDF implementation. When operating in FIPS mode, the - * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. - */ - if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_hkdf()) { - return &s2n_libcrypto_hkdf_impl; - } - - return &s2n_custom_hkdf_impl; -} - -int s2n_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) -{ - POSIX_ENSURE_REF(hmac); - POSIX_ENSURE_REF(salt); - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(pseudo_rand_key); - - const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); - POSIX_ENSURE_REF(hkdf_implementation); - - POSIX_GUARD(hkdf_implementation->hkdf_extract(hmac, alg, salt, key, pseudo_rand_key)); - - return S2N_SUCCESS; -} - -static int s2n_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, - const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(hmac); - POSIX_ENSURE_REF(pseudo_rand_key); - POSIX_ENSURE_REF(info); - POSIX_ENSURE_REF(output); - - const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); - POSIX_ENSURE_REF(hkdf_implementation); - - POSIX_GUARD(hkdf_implementation->hkdf_expand(hmac, alg, pseudo_rand_key, info, output)); - - return S2N_SUCCESS; -} - -int s2n_hkdf_expand_label(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *secret, const struct s2n_blob *label, - const struct s2n_blob *context, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(label); - POSIX_ENSURE_REF(context); - POSIX_ENSURE_REF(output); - - /* Per RFC8446: 7.1, a HKDF label is a 2 byte length field, and two 1...255 byte arrays with a one byte length field each. */ - uint8_t hkdf_label_buf[2 + 256 + 256]; - struct s2n_blob hkdf_label_blob = { 0 }; - struct s2n_stuffer hkdf_label = { 0 }; - - POSIX_ENSURE_LTE(label->size, S2N_MAX_HKDF_EXPAND_LABEL_LENGTH); - - POSIX_GUARD(s2n_blob_init(&hkdf_label_blob, hkdf_label_buf, sizeof(hkdf_label_buf))); - POSIX_GUARD(s2n_stuffer_init(&hkdf_label, &hkdf_label_blob)); - POSIX_GUARD(s2n_stuffer_write_uint16(&hkdf_label, output->size)); - POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, label->size + sizeof("tls13 ") - 1)); - POSIX_GUARD(s2n_stuffer_write_str(&hkdf_label, "tls13 ")); - POSIX_GUARD(s2n_stuffer_write(&hkdf_label, label)); - POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, context->size)); - POSIX_GUARD(s2n_stuffer_write(&hkdf_label, context)); - - hkdf_label_blob.size = s2n_stuffer_data_available(&hkdf_label); - POSIX_GUARD(s2n_hkdf_expand(hmac, alg, secret, &hkdf_label_blob, output)); - - return S2N_SUCCESS; -} - -int s2n_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, - const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(hmac); - POSIX_ENSURE_REF(salt); - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(info); - POSIX_ENSURE_REF(output); - - const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); - POSIX_ENSURE_REF(hkdf_implementation); - - POSIX_GUARD(hkdf_implementation->hkdf(hmac, alg, salt, key, info, output)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hkdf.h" + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hmac.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF + #include +#endif + +#define MAX_DIGEST_SIZE 64 /* Current highest is SHA512 */ +#define MAX_HKDF_ROUNDS 255 + +/* Reference: RFC 5869 */ + +struct s2n_hkdf_impl { + int (*hkdf)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output); + int (*hkdf_extract)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key); + int (*hkdf_expand)(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, + const struct s2n_blob *info, struct s2n_blob *output); +}; + +static int s2n_custom_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + uint8_t hmac_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); + POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); + pseudo_rand_key->size = hmac_size; + + POSIX_GUARD(s2n_hmac_init(hmac, alg, salt->data, salt->size)); + POSIX_GUARD(s2n_hmac_update(hmac, key->data, key->size)); + POSIX_GUARD(s2n_hmac_digest(hmac, pseudo_rand_key->data, pseudo_rand_key->size)); + + POSIX_GUARD(s2n_hmac_reset(hmac)); + + return S2N_SUCCESS; +} + +static int s2n_custom_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + uint8_t prev[MAX_DIGEST_SIZE] = { 0 }; + + uint32_t done_len = 0; + uint8_t hash_len = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &hash_len)); + POSIX_ENSURE_GT(hash_len, 0); + uint32_t total_rounds = output->size / hash_len; + if (output->size % hash_len) { + total_rounds++; + } + + POSIX_ENSURE(total_rounds > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + POSIX_ENSURE(total_rounds <= MAX_HKDF_ROUNDS, S2N_ERR_HKDF_OUTPUT_SIZE); + + for (uint32_t curr_round = 1; curr_round <= total_rounds; curr_round++) { + uint32_t cat_len = 0; + POSIX_GUARD(s2n_hmac_init(hmac, alg, pseudo_rand_key->data, pseudo_rand_key->size)); + if (curr_round != 1) { + POSIX_GUARD(s2n_hmac_update(hmac, prev, hash_len)); + } + POSIX_GUARD(s2n_hmac_update(hmac, info->data, info->size)); + uint8_t curr_round_byte = curr_round; + POSIX_GUARD(s2n_hmac_update(hmac, &curr_round_byte, 1)); + POSIX_GUARD(s2n_hmac_digest(hmac, prev, hash_len)); + + cat_len = hash_len; + if (done_len + hash_len > output->size) { + cat_len = output->size - done_len; + } + + POSIX_CHECKED_MEMCPY(output->data + done_len, prev, cat_len); + + done_len += cat_len; + + POSIX_GUARD(s2n_hmac_reset(hmac)); + } + + return S2N_SUCCESS; +} + +static int s2n_custom_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + uint8_t prk_pad[MAX_DIGEST_SIZE] = { 0 }; + struct s2n_blob pseudo_rand_key = { 0 }; + POSIX_GUARD(s2n_blob_init(&pseudo_rand_key, prk_pad, sizeof(prk_pad))); + + POSIX_GUARD(s2n_custom_hkdf_extract(hmac, alg, salt, key, &pseudo_rand_key)); + POSIX_GUARD(s2n_custom_hkdf_expand(hmac, alg, &pseudo_rand_key, info, output)); + + return S2N_SUCCESS; +} + +const struct s2n_hkdf_impl s2n_custom_hkdf_impl = { + .hkdf = &s2n_custom_hkdf, + .hkdf_extract = &s2n_custom_hkdf_extract, + .hkdf_expand = &s2n_custom_hkdf_expand, +}; + +#ifdef S2N_LIBCRYPTO_SUPPORTS_HKDF +static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + const EVP_MD *digest = NULL; + POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); + + /* The out_len argument of HKDF_extract is set to the number of bytes written to out_key, and + * is not used to ensure that out_key is large enough to contain the PRK. Ensure that the PRK + * output will fit in the blob. + */ + uint8_t hmac_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &hmac_size)); + POSIX_ENSURE(hmac_size <= pseudo_rand_key->size, S2N_ERR_HKDF_OUTPUT_SIZE); + + size_t bytes_written = 0; + POSIX_GUARD_OSSL(HKDF_extract(pseudo_rand_key->data, &bytes_written, digest, key->data, key->size, + salt->data, salt->size), + S2N_ERR_HKDF); + + /* HKDF_extract updates the out_len argument based on the digest size. Update the blob's size based on this. */ + POSIX_ENSURE_LTE(bytes_written, pseudo_rand_key->size); + pseudo_rand_key->size = bytes_written; + + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + + const EVP_MD *digest = NULL; + POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); + + POSIX_GUARD_OSSL(HKDF_expand(output->data, output->size, digest, pseudo_rand_key->data, pseudo_rand_key->size, + info->data, info->size), + S2N_ERR_HKDF); + + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE(output->size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + + const EVP_MD *digest = NULL; + POSIX_GUARD_RESULT(s2n_hmac_md_from_alg(alg, &digest)); + + POSIX_GUARD_OSSL(HKDF(output->data, output->size, digest, key->data, key->size, salt->data, salt->size, + info->data, info->size), + S2N_ERR_HKDF); + + return S2N_SUCCESS; +} + +bool s2n_libcrypto_supports_hkdf() +{ + return true; +} + +#elif S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0) + + #include "crypto/s2n_kdf.h" + +static S2N_RESULT s2n_hkdf_kdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *salt, const struct s2n_blob *key, const struct s2n_blob *info, + struct s2n_blob *output, int mode) +{ + /* As an optimization, we should be able to fetch and cache this EVP_KDF* + * once when s2n_init is called. + */ + DEFER_CLEANUP(EVP_KDF *hkdf_impl = EVP_KDF_fetch(NULL, "HKDF", NULL), + EVP_KDF_free_pointer); + RESULT_ENSURE(hkdf_impl, S2N_ERR_PRF_INVALID_ALGORITHM); + + DEFER_CLEANUP(EVP_KDF_CTX *hkdf_ctx = EVP_KDF_CTX_new(hkdf_impl), + EVP_KDF_CTX_free_pointer); + RESULT_ENSURE_REF(hkdf_ctx); + + const EVP_MD *digest = NULL; + RESULT_GUARD(s2n_hmac_md_from_alg(alg, &digest)); + RESULT_ENSURE_REF(digest); + const char *digest_name = EVP_MD_get0_name(digest); + RESULT_ENSURE_REF(digest_name); + + OSSL_PARAM params[] = { + S2N_OSSL_PARAM_INT(OSSL_KDF_PARAM_MODE, mode), + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_KEY, key), + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_INFO, info), + S2N_OSSL_PARAM_BLOB(OSSL_KDF_PARAM_SALT, salt), + /* Casting away the const is safe because providers are forbidden from + * modifying any OSSL_PARAM value other than return_size. + * Even the examples in the Openssl documentation cast const strings to + * non-const void pointers when setting up OSSL_PARAMs. + */ + S2N_OSSL_PARAM_STR(OSSL_KDF_PARAM_DIGEST, (void *) (uintptr_t) digest_name), + OSSL_PARAM_END, + }; + + /* From the HKDF docs (https://docs.openssl.org/3.1/man7/EVP_KDF-HKDF/): + * > When using EVP_KDF_HKDF_MODE_EXTRACT_ONLY the keylen parameter must equal + * > the size of the intermediate fixed-length pseudorandom key otherwise an + * > error will occur. + */ + if (mode == EVP_KDF_HKDF_MODE_EXTRACT_ONLY) { + RESULT_GUARD_OSSL(EVP_KDF_CTX_set_params(hkdf_ctx, params), S2N_ERR_HKDF); + size_t key_size = EVP_KDF_CTX_get_kdf_size(hkdf_ctx); + RESULT_ENSURE(key_size > 0, S2N_ERR_HKDF_OUTPUT_SIZE); + RESULT_ENSURE(key_size <= output->size, S2N_ERR_HKDF_OUTPUT_SIZE); + output->size = key_size; + } + + RESULT_GUARD_OSSL(EVP_KDF_derive(hkdf_ctx, output->data, output->size, params), + S2N_ERR_HKDF); + return S2N_RESULT_OK; +} + +static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *salt, const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + struct s2n_blob empty_info = { 0 }; + POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, &empty_info, pseudo_rand_key, + EVP_KDF_HKDF_MODE_EXTRACT_ONLY)); + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + struct s2n_blob empty_salt = { 0 }; + POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, &empty_salt, pseudo_rand_key, info, output, + EVP_KDF_HKDF_MODE_EXPAND_ONLY)); + return S2N_SUCCESS; +} + +static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_GUARD_RESULT(s2n_hkdf_kdf(hmac, alg, salt, key, info, output, + EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND)); + return S2N_SUCCESS; +} + +bool s2n_libcrypto_supports_hkdf() +{ + return true; +} + +#else + +static int s2n_libcrypto_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static int s2n_libcrypto_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, + const struct s2n_blob *pseudo_rand_key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static int s2n_libcrypto_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +bool s2n_libcrypto_supports_hkdf() +{ + return false; +} + +#endif /* S2N_LIBCRYPTO_SUPPORTS_HKDF */ + +const struct s2n_hkdf_impl s2n_libcrypto_hkdf_impl = { + .hkdf = &s2n_libcrypto_hkdf, + .hkdf_extract = &s2n_libcrypto_hkdf_extract, + .hkdf_expand = &s2n_libcrypto_hkdf_expand, +}; + +static const struct s2n_hkdf_impl *s2n_get_hkdf_implementation() +{ + /* By default, s2n-tls uses a custom HKDF implementation. When operating in FIPS mode, the + * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. + */ + if (s2n_is_in_fips_mode() && s2n_libcrypto_supports_hkdf()) { + return &s2n_libcrypto_hkdf_impl; + } + + return &s2n_custom_hkdf_impl; +} + +int s2n_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key) +{ + POSIX_ENSURE_REF(hmac); + POSIX_ENSURE_REF(salt); + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(pseudo_rand_key); + + const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); + POSIX_ENSURE_REF(hkdf_implementation); + + POSIX_GUARD(hkdf_implementation->hkdf_extract(hmac, alg, salt, key, pseudo_rand_key)); + + return S2N_SUCCESS; +} + +static int s2n_hkdf_expand(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *pseudo_rand_key, + const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(hmac); + POSIX_ENSURE_REF(pseudo_rand_key); + POSIX_ENSURE_REF(info); + POSIX_ENSURE_REF(output); + + const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); + POSIX_ENSURE_REF(hkdf_implementation); + + POSIX_GUARD(hkdf_implementation->hkdf_expand(hmac, alg, pseudo_rand_key, info, output)); + + return S2N_SUCCESS; +} + +int s2n_hkdf_expand_label(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *secret, const struct s2n_blob *label, + const struct s2n_blob *context, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(label); + POSIX_ENSURE_REF(context); + POSIX_ENSURE_REF(output); + + /* Per RFC8446: 7.1, a HKDF label is a 2 byte length field, and two 1...255 byte arrays with a one byte length field each. */ + uint8_t hkdf_label_buf[2 + 256 + 256]; + struct s2n_blob hkdf_label_blob = { 0 }; + struct s2n_stuffer hkdf_label = { 0 }; + + POSIX_ENSURE_LTE(label->size, S2N_MAX_HKDF_EXPAND_LABEL_LENGTH); + + POSIX_GUARD(s2n_blob_init(&hkdf_label_blob, hkdf_label_buf, sizeof(hkdf_label_buf))); + POSIX_GUARD(s2n_stuffer_init(&hkdf_label, &hkdf_label_blob)); + POSIX_GUARD(s2n_stuffer_write_uint16(&hkdf_label, output->size)); + POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, label->size + sizeof("tls13 ") - 1)); + POSIX_GUARD(s2n_stuffer_write_str(&hkdf_label, "tls13 ")); + POSIX_GUARD(s2n_stuffer_write(&hkdf_label, label)); + POSIX_GUARD(s2n_stuffer_write_uint8(&hkdf_label, context->size)); + POSIX_GUARD(s2n_stuffer_write(&hkdf_label, context)); + + hkdf_label_blob.size = s2n_stuffer_data_available(&hkdf_label); + POSIX_GUARD(s2n_hkdf_expand(hmac, alg, secret, &hkdf_label_blob, output)); + + return S2N_SUCCESS; +} + +int s2n_hkdf(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, + const struct s2n_blob *key, const struct s2n_blob *info, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(hmac); + POSIX_ENSURE_REF(salt); + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(info); + POSIX_ENSURE_REF(output); + + const struct s2n_hkdf_impl *hkdf_implementation = s2n_get_hkdf_implementation(); + POSIX_ENSURE_REF(hkdf_implementation); + + POSIX_GUARD(hkdf_implementation->hkdf(hmac, alg, salt, key, info, output)); + + return S2N_SUCCESS; +} diff --git a/crypto/s2n_locking.c b/crypto/s2n_locking.c index 42e8379acb2..1130cdfc093 100644 --- a/crypto/s2n_locking.c +++ b/crypto/s2n_locking.c @@ -1,120 +1,120 @@ -/* - * 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 "crypto/s2n_locking.h" - -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "crypto/s2n_openssl.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* Writing multithreaded applications using Openssl-1.0.2 - * requires calling CRYPTO_set_locking_callback. - * If the callback is not set, locks are no-ops and unexpected - * behavior may occur, particularly for RSA and X509. - * - * In the past s2n-tls relied on customers setting the callback - * themselves, but that seems unnecessary since other parts of - * the library (like fork detection) already rely on the pthreads library. - * - * For more information: - * https://www.openssl.org/blog/blog/2017/02/21/threads/ - * https://www.openssl.org/docs/man1.0.2/man3/threads.html - */ - -#define S2N_MUTEXES(mem) ((pthread_mutex_t *) (void *) (mem).data) - -/* While the locking-related APIs "exist" in later versions of - * Openssl, they tend to be placeholders or hardcoded values like: - * #define CRYPTO_get_locking_callback() (NULL) - * So the code will compile with strange warnings / errors like - * loop conditions always being false. - */ -#if !(S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) - -static struct s2n_blob mutexes_mem = { 0 }; -static size_t mutexes_count = 0; - -static void s2n_locking_cb(int mode, int n, char *file, int line) -{ - pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); - if (!mutexes_mem.data || n < 0 || (size_t) n >= mutexes_count) { - return; - } - - if (mode & CRYPTO_LOCK) { - pthread_mutex_lock(&(mutexes[n])); - } else { - pthread_mutex_unlock(&(mutexes[n])); - } -} - -S2N_RESULT s2n_locking_init(void) -{ - if (CRYPTO_get_locking_callback() != NULL) { - return S2N_RESULT_OK; - } - - int num_locks = CRYPTO_num_locks(); - RESULT_ENSURE_GTE(num_locks, 0); - - RESULT_GUARD_POSIX(s2n_realloc(&mutexes_mem, num_locks * sizeof(pthread_mutex_t))); - - pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); - mutexes_count = 0; - for (size_t i = 0; i < (size_t) num_locks; i++) { - RESULT_ENSURE_EQ(pthread_mutex_init(&(mutexes[i]), NULL), 0); - mutexes_count++; - } - - CRYPTO_set_locking_callback((void (*)()) s2n_locking_cb); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_locking_cleanup(void) -{ - if (CRYPTO_get_locking_callback() == (void (*)()) s2n_locking_cb) { - CRYPTO_set_locking_callback(NULL); - } - - pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); - if (mutexes) { - while (mutexes_count > 0) { - RESULT_ENSURE_EQ(pthread_mutex_destroy(&(mutexes[mutexes_count - 1])), 0); - mutexes_count--; - } - RESULT_GUARD_POSIX(s2n_free(&mutexes_mem)); - } - - return S2N_RESULT_OK; -} - -#else - -S2N_RESULT s2n_locking_init(void) -{ - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_locking_cleanup(void) -{ - return S2N_RESULT_OK; -} - -#endif +/* + * 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 "crypto/s2n_locking.h" + +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "crypto/s2n_openssl.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* Writing multithreaded applications using Openssl-1.0.2 + * requires calling CRYPTO_set_locking_callback. + * If the callback is not set, locks are no-ops and unexpected + * behavior may occur, particularly for RSA and X509. + * + * In the past s2n-tls relied on customers setting the callback + * themselves, but that seems unnecessary since other parts of + * the library (like fork detection) already rely on the pthreads library. + * + * For more information: + * https://www.openssl.org/blog/blog/2017/02/21/threads/ + * https://www.openssl.org/docs/man1.0.2/man3/threads.html + */ + +#define S2N_MUTEXES(mem) ((pthread_mutex_t *) (void *) (mem).data) + +/* While the locking-related APIs "exist" in later versions of + * Openssl, they tend to be placeholders or hardcoded values like: + * #define CRYPTO_get_locking_callback() (NULL) + * So the code will compile with strange warnings / errors like + * loop conditions always being false. + */ +#if !(S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) + +static struct s2n_blob mutexes_mem = { 0 }; +static size_t mutexes_count = 0; + +static void s2n_locking_cb(int mode, int n, char *file, int line) +{ + pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); + if (!mutexes_mem.data || n < 0 || (size_t) n >= mutexes_count) { + return; + } + + if (mode & CRYPTO_LOCK) { + pthread_mutex_lock(&(mutexes[n])); + } else { + pthread_mutex_unlock(&(mutexes[n])); + } +} + +S2N_RESULT s2n_locking_init(void) +{ + if (CRYPTO_get_locking_callback() != NULL) { + return S2N_RESULT_OK; + } + + int num_locks = CRYPTO_num_locks(); + RESULT_ENSURE_GTE(num_locks, 0); + + RESULT_GUARD_POSIX(s2n_realloc(&mutexes_mem, num_locks * sizeof(pthread_mutex_t))); + + pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); + mutexes_count = 0; + for (size_t i = 0; i < (size_t) num_locks; i++) { + RESULT_ENSURE_EQ(pthread_mutex_init(&(mutexes[i]), NULL), 0); + mutexes_count++; + } + + CRYPTO_set_locking_callback((void (*)()) s2n_locking_cb); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_locking_cleanup(void) +{ + if (CRYPTO_get_locking_callback() == (void (*)()) s2n_locking_cb) { + CRYPTO_set_locking_callback(NULL); + } + + pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem); + if (mutexes) { + while (mutexes_count > 0) { + RESULT_ENSURE_EQ(pthread_mutex_destroy(&(mutexes[mutexes_count - 1])), 0); + mutexes_count--; + } + RESULT_GUARD_POSIX(s2n_free(&mutexes_mem)); + } + + return S2N_RESULT_OK; +} + +#else + +S2N_RESULT s2n_locking_init(void) +{ + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_locking_cleanup(void) +{ + return S2N_RESULT_OK; +} + +#endif diff --git a/crypto/s2n_openssl_x509.c b/crypto/s2n_openssl_x509.c index 17b76c93fb6..3e4ba1edd3f 100644 --- a/crypto/s2n_openssl_x509.c +++ b/crypto/s2n_openssl_x509.c @@ -1,150 +1,150 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_openssl_x509.h" - -#include "api/s2n.h" - -DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY *, EVP_PKEY_free); -DEFINE_POINTER_CLEANUP_FUNC(EC_KEY *, EC_KEY_free); - -S2N_CLEANUP_RESULT s2n_openssl_x509_stack_pop_free(STACK_OF(X509) **cert_chain) -{ - RESULT_ENSURE_REF(*cert_chain); - sk_X509_pop_free(*cert_chain, X509_free); - *cert_chain = NULL; - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_openssl_asn1_time_free_pointer(ASN1_GENERALIZEDTIME **time_ptr) -{ - /* The ANS1_*TIME structs are just typedef wrappers around ASN1_STRING - * - * The ASN1_TIME, ASN1_UTCTIME and ASN1_GENERALIZEDTIME structures are - * represented as an ASN1_STRING internally and can be freed up using - * ASN1_STRING_free(). - * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_tm.html - */ - RESULT_ENSURE_REF(*time_ptr); - ASN1_STRING_free((ASN1_STRING *) *time_ptr); - *time_ptr = NULL; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_parse_impl(struct s2n_blob *asn1der, X509 **cert_out, uint32_t *parsed_length) -{ - RESULT_ENSURE_REF(asn1der); - RESULT_ENSURE_REF(asn1der->data); - RESULT_ENSURE_REF(cert_out); - RESULT_ENSURE_REF(parsed_length); - - uint8_t *cert_to_parse = asn1der->data; - *cert_out = d2i_X509(NULL, (const unsigned char **) (void *) &cert_to_parse, asn1der->size); - RESULT_ENSURE(*cert_out != NULL, S2N_ERR_DECODE_CERTIFICATE); - - /* If cert parsing is successful, d2i_X509 increments *cert_to_parse to the byte following the parsed data */ - *parsed_length = cert_to_parse - asn1der->data; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_parse_without_length_validation(struct s2n_blob *asn1der, X509 **cert_out) -{ - RESULT_ENSURE_REF(asn1der); - RESULT_ENSURE_REF(cert_out); - - uint32_t parsed_len = 0; - RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_parse(struct s2n_blob *asn1der, X509 **cert_out) -{ - RESULT_ENSURE_REF(asn1der); - RESULT_ENSURE_REF(cert_out); - - uint32_t parsed_len = 0; - RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); - - /* Some TLS clients in the wild send extra trailing bytes after the Certificate. - * Allow this in s2n for backwards compatibility with existing clients. */ - uint32_t trailing_bytes = asn1der->size - parsed_len; - RESULT_ENSURE(trailing_bytes <= S2N_MAX_ALLOWED_CERT_TRAILING_BYTES, S2N_ERR_DECODE_CERTIFICATE); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_openssl_x509_get_cert_info(X509 *cert, struct s2n_cert_info *info) -{ - RESULT_ENSURE_REF(cert); - RESULT_ENSURE_REF(info); - - X509_NAME *issuer_name = X509_get_issuer_name(cert); - RESULT_ENSURE_REF(issuer_name); - - X509_NAME *subject_name = X509_get_subject_name(cert); - RESULT_ENSURE_REF(subject_name); - - if (X509_NAME_cmp(issuer_name, subject_name) == 0) { - info->self_signed = true; - } else { - info->self_signed = false; - } - -#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x02070000f) - RESULT_ENSURE_REF(cert->sig_alg); - info->signature_nid = OBJ_obj2nid(cert->sig_alg->algorithm); -#else - info->signature_nid = X509_get_signature_nid(cert); -#endif - - /* There is no method to directly retrieve the signature digest from the X509* - * that is available in all libcryptos, so instead we use find_sigid_algs. For - * a signature with NID_ecdsa_with_SHA256 this will return NID_SHA256 - * - * signature_digest_nid may not always be set. ML-DSA does not have an associated digest. - */ - int find_result = OBJ_find_sigid_algs(info->signature_nid, &info->signature_digest_nid, NULL); - if (find_result != 1) { - /* OBJ_find_sigid_algs may fail for ML-DSA-44 and ML-DSA-87, depending on the - * version of AWS-LC. See https://github.com/aws/aws-lc/issues/2347. - * - * In order to handle this bug, we interpret failures from OBJ_find_sigid_algs - * as signature_digest_nid==0, which is equivalent to an undefined digest. - */ - info->signature_digest_nid = 0; - } - - DEFER_CLEANUP(EVP_PKEY *pubkey = X509_get_pubkey(cert), EVP_PKEY_free_pointer); - RESULT_ENSURE(pubkey != NULL, S2N_ERR_DECODE_CERTIFICATE); - - info->public_key_bits = EVP_PKEY_bits(pubkey); - RESULT_ENSURE(info->public_key_bits > 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); - - if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { - DEFER_CLEANUP(EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pubkey), EC_KEY_free_pointer); - RESULT_ENSURE_REF(ec_key); - const EC_GROUP *ec_group = EC_KEY_get0_group(ec_key); - RESULT_ENSURE_REF(ec_group); - info->public_key_nid = EC_GROUP_get_curve_name(ec_group); - } else { - info->public_key_nid = EVP_PKEY_id(pubkey); - } - RESULT_ENSURE(info->public_key_nid != NID_undef, S2N_ERR_CERT_TYPE_UNSUPPORTED); - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_openssl_x509.h" + +#include "api/s2n.h" + +DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY *, EVP_PKEY_free); +DEFINE_POINTER_CLEANUP_FUNC(EC_KEY *, EC_KEY_free); + +S2N_CLEANUP_RESULT s2n_openssl_x509_stack_pop_free(STACK_OF(X509) **cert_chain) +{ + RESULT_ENSURE_REF(*cert_chain); + sk_X509_pop_free(*cert_chain, X509_free); + *cert_chain = NULL; + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_openssl_asn1_time_free_pointer(ASN1_GENERALIZEDTIME **time_ptr) +{ + /* The ANS1_*TIME structs are just typedef wrappers around ASN1_STRING + * + * The ASN1_TIME, ASN1_UTCTIME and ASN1_GENERALIZEDTIME structures are + * represented as an ASN1_STRING internally and can be freed up using + * ASN1_STRING_free(). + * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_tm.html + */ + RESULT_ENSURE_REF(*time_ptr); + ASN1_STRING_free((ASN1_STRING *) *time_ptr); + *time_ptr = NULL; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_parse_impl(struct s2n_blob *asn1der, X509 **cert_out, uint32_t *parsed_length) +{ + RESULT_ENSURE_REF(asn1der); + RESULT_ENSURE_REF(asn1der->data); + RESULT_ENSURE_REF(cert_out); + RESULT_ENSURE_REF(parsed_length); + + uint8_t *cert_to_parse = asn1der->data; + *cert_out = d2i_X509(NULL, (const unsigned char **) (void *) &cert_to_parse, asn1der->size); + RESULT_ENSURE(*cert_out != NULL, S2N_ERR_DECODE_CERTIFICATE); + + /* If cert parsing is successful, d2i_X509 increments *cert_to_parse to the byte following the parsed data */ + *parsed_length = cert_to_parse - asn1der->data; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_parse_without_length_validation(struct s2n_blob *asn1der, X509 **cert_out) +{ + RESULT_ENSURE_REF(asn1der); + RESULT_ENSURE_REF(cert_out); + + uint32_t parsed_len = 0; + RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_parse(struct s2n_blob *asn1der, X509 **cert_out) +{ + RESULT_ENSURE_REF(asn1der); + RESULT_ENSURE_REF(cert_out); + + uint32_t parsed_len = 0; + RESULT_GUARD(s2n_openssl_x509_parse_impl(asn1der, cert_out, &parsed_len)); + + /* Some TLS clients in the wild send extra trailing bytes after the Certificate. + * Allow this in s2n for backwards compatibility with existing clients. */ + uint32_t trailing_bytes = asn1der->size - parsed_len; + RESULT_ENSURE(trailing_bytes <= S2N_MAX_ALLOWED_CERT_TRAILING_BYTES, S2N_ERR_DECODE_CERTIFICATE); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_openssl_x509_get_cert_info(X509 *cert, struct s2n_cert_info *info) +{ + RESULT_ENSURE_REF(cert); + RESULT_ENSURE_REF(info); + + X509_NAME *issuer_name = X509_get_issuer_name(cert); + RESULT_ENSURE_REF(issuer_name); + + X509_NAME *subject_name = X509_get_subject_name(cert); + RESULT_ENSURE_REF(subject_name); + + if (X509_NAME_cmp(issuer_name, subject_name) == 0) { + info->self_signed = true; + } else { + info->self_signed = false; + } + +#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x02070000f) + RESULT_ENSURE_REF(cert->sig_alg); + info->signature_nid = OBJ_obj2nid(cert->sig_alg->algorithm); +#else + info->signature_nid = X509_get_signature_nid(cert); +#endif + + /* There is no method to directly retrieve the signature digest from the X509* + * that is available in all libcryptos, so instead we use find_sigid_algs. For + * a signature with NID_ecdsa_with_SHA256 this will return NID_SHA256 + * + * signature_digest_nid may not always be set. ML-DSA does not have an associated digest. + */ + int find_result = OBJ_find_sigid_algs(info->signature_nid, &info->signature_digest_nid, NULL); + if (find_result != 1) { + /* OBJ_find_sigid_algs may fail for ML-DSA-44 and ML-DSA-87, depending on the + * version of AWS-LC. See https://github.com/aws/aws-lc/issues/2347. + * + * In order to handle this bug, we interpret failures from OBJ_find_sigid_algs + * as signature_digest_nid==0, which is equivalent to an undefined digest. + */ + info->signature_digest_nid = 0; + } + + DEFER_CLEANUP(EVP_PKEY *pubkey = X509_get_pubkey(cert), EVP_PKEY_free_pointer); + RESULT_ENSURE(pubkey != NULL, S2N_ERR_DECODE_CERTIFICATE); + + info->public_key_bits = EVP_PKEY_bits(pubkey); + RESULT_ENSURE(info->public_key_bits > 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); + + if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) { + DEFER_CLEANUP(EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pubkey), EC_KEY_free_pointer); + RESULT_ENSURE_REF(ec_key); + const EC_GROUP *ec_group = EC_KEY_get0_group(ec_key); + RESULT_ENSURE_REF(ec_group); + info->public_key_nid = EC_GROUP_get_curve_name(ec_group); + } else { + info->public_key_nid = EVP_PKEY_id(pubkey); + } + RESULT_ENSURE(info->public_key_nid != NID_undef, S2N_ERR_CERT_TYPE_UNSUPPORTED); + + return S2N_RESULT_OK; +} diff --git a/crypto/s2n_pkey_evp.c b/crypto/s2n_pkey_evp.c index 13a3ec8846c..9f865d30b65 100644 --- a/crypto/s2n_pkey_evp.c +++ b/crypto/s2n_pkey_evp.c @@ -1,374 +1,374 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_pkey_evp.h" - -#include -#include - -#include "crypto/s2n_evp.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_pkey.h" -#include "crypto/s2n_rsa_pss.h" -#include "error/s2n_errno.h" -#include "tls/s2n_signature_algorithms.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free); - -static S2N_RESULT s2n_evp_md_ctx_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pctx) -{ -#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX - EVP_MD_CTX_set_pkey_ctx(ctx, pctx); - return S2N_RESULT_OK; -#else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -#endif -} - -static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx) -{ -#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) - RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST), S2N_ERR_PKEY_CTX_INIT); - return S2N_RESULT_OK; -#else - RESULT_BAIL(S2N_ERR_RSA_PSS_NOT_SUPPORTED); -#endif -} - -static S2N_RESULT s2n_pkey_evp_validate_sig_alg(const struct s2n_pkey *key, s2n_signature_algorithm sig_alg) -{ - RESULT_ENSURE_REF(key); - - /* Ensure that the signature algorithm type matches the key type. */ - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - RESULT_GUARD(s2n_pkey_get_type(key->pkey, &pkey_type)); - s2n_pkey_type sig_alg_type = S2N_PKEY_TYPE_UNKNOWN; - RESULT_GUARD(s2n_signature_algorithm_get_pkey_type(sig_alg, &sig_alg_type)); - RESULT_ENSURE(pkey_type == sig_alg_type, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - - return S2N_RESULT_OK; -} - -static EVP_PKEY_CTX *s2n_evp_pkey_ctx_new(EVP_PKEY *pkey, s2n_hash_algorithm hash_alg) -{ - PTR_ENSURE_REF(pkey); - switch (hash_alg) { -#if S2N_LIBCRYPTO_SUPPORTS_PROVIDERS - /* For openssl-3.0, pkey methods will do an implicit fetch for the signing - * algorithm, which includes the hash algorithm. If using a legacy hash - * algorithm, specify the non-fips version. - */ - case S2N_HASH_MD5: - case S2N_HASH_MD5_SHA1: - case S2N_HASH_SHA1: - return EVP_PKEY_CTX_new_from_pkey(NULL, pkey, "-fips"); -#endif - default: - return EVP_PKEY_CTX_new(pkey, NULL); - } -} - -/* Our "digest-and-sign" EVP signing logic is intended to support FIPS 140-3. - * FIPS 140-3 does not allow signing or verifying externally calculated digests - * for RSA and ECDSA verify. - * See https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures, - * and note that "component" tests only exist for ECDSA sign. - * - * In order to avoid signing externally calculated digests, we naively would - * need access to the full message to be signed at the time of signing. That's - * a problem for TLS1.2, where the client cert verify message requires signing - * every handshake message sent or received before the client cert verify message. - * To avoid storing every single handshake message in its entirety, we instead - * keep a running hash of the messages in an EVP hash state. Then, instead of - * digesting that hash state, we pass it unmodified to EVP_DigestSignFinal. - * That would normally not be allowed, since the hash state was initialized without - * a key using EVP_DigestInit instead of with a key using EVP_DigestSignInit. - * We make it work by using the EVP_MD_CTX_set_pkey_ctx method to attach a key - * to an existing hash state. - * - * All that means that "digest-and-sign" requires two things: - * - A single EVP hash state to sign. So we must not use a custom MD5_SHA1 hash, - * which doesn't produce a single hash state. - * - EVP_MD_CTX_set_pkey_ctx to exist and to behave as expected. Existence - * alone is not sufficient: the method exists in openssl-3.0-fips, but - * it cannot be used to setup a hash state for EVP_DigestSignFinal. - * - * Currently only awslc-fips meets both these requirements. New libcryptos - * should be assumed not to meet these requirements until proven otherwise. - */ -static int s2n_pkey_evp_digest_and_sign(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - /* Custom MD5_SHA1 involves combining separate MD5 and SHA1 hashes. - * That involves two hash states instead of the single hash state this - * method requires. - */ - POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); - - /* Not all implementations of EVP_MD_CTX_set_pkey_ctx behave as required - * by this method. Using EVP_MD_CTX_set_pkey_ctx to convert a hash initialized - * with EVP_DigestInit to one that can be finalized with EVP_DigestSignFinal - * is not entirely standard. - * - * However, this behavior is known to work with awslc-fips. - */ - POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); - - EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; - POSIX_ENSURE_REF(ctx); - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); - - size_t signature_size = signature->size; - POSIX_GUARD_OSSL(EVP_DigestSignFinal(ctx, signature->data, &signature_size), S2N_ERR_SIGN); - POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); - signature->size = signature_size; - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); - - return S2N_SUCCESS; -} - -/* See s2n_evp_digest_and_sign for more information */ -static bool s2n_pkey_evp_digest_and_sign_is_required(s2n_signature_algorithm sig_alg) -{ - if (sig_alg == S2N_SIGNATURE_MLDSA) { - /* The FIPS restrictions do not apply to ML-DSA */ - return false; - } - return s2n_libcrypto_is_awslc_fips(); -} - -/* "digest-then-sign" means that we calculate the digest for a hash state, - * then sign the digest bytes. That is not allowed by FIPS 140-3, but is allowed - * in all other cases. - */ -static int s2n_pkey_evp_digest_then_sign(EVP_PKEY_CTX *pctx, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - uint8_t digest_length = 0; - POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); - POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); - - uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); - - size_t signature_size = signature->size; - POSIX_GUARD_OSSL(EVP_PKEY_sign(pctx, signature->data, &signature_size, - digest_out, digest_length), - S2N_ERR_SIGN); - POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); - signature->size = signature_size; - - return S2N_SUCCESS; -} - -int s2n_pkey_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(priv); - POSIX_ENSURE_REF(hash_state); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(priv->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_sign_init(pctx), S2N_ERR_PKEY_CTX_INIT); - - if (sig_alg != S2N_SIGNATURE_MLDSA) { - POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); - } - - if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); - POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); - } - - if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { - POSIX_GUARD(s2n_pkey_evp_digest_and_sign(pctx, sig_alg, hash_state, signature)); - } else { - POSIX_GUARD(s2n_pkey_evp_digest_then_sign(pctx, hash_state, signature)); - } - - return S2N_SUCCESS; -} - -/* See s2n_evp_digest_and_sign for more information */ -static int s2n_pkey_evp_digest_and_verify(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - /* See digest-and-sign requirements */ - POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); - POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); - - EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; - POSIX_ENSURE_REF(ctx); - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); - - POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE); - POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); - - return S2N_SUCCESS; -} - -/* See s2n_evp_digest_then_sign for more information */ -static int s2n_pkey_evp_digest_then_verify(EVP_PKEY_CTX *pctx, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pctx); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - - uint8_t digest_length = 0; - POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); - POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); - - uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); - - POSIX_GUARD_OSSL(EVP_PKEY_verify(pctx, signature->data, signature->size, - digest_out, digest_length), - S2N_ERR_VERIFY_SIGNATURE); - return S2N_SUCCESS; -} - -int s2n_pkey_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *hash_state, struct s2n_blob *signature) -{ - POSIX_ENSURE_REF(pub); - POSIX_ENSURE_REF(hash_state); - POSIX_ENSURE_REF(signature); - POSIX_GUARD_RESULT(s2n_pkey_evp_validate_sig_alg(pub, sig_alg)); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(pub->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_verify_init(pctx), S2N_ERR_PKEY_CTX_INIT); - - if (sig_alg != S2N_SIGNATURE_MLDSA) { - POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); - } - - if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); - POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); - } - - if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { - POSIX_GUARD(s2n_pkey_evp_digest_and_verify(pctx, sig_alg, hash_state, signature)); - } else { - POSIX_GUARD(s2n_pkey_evp_digest_then_verify(pctx, hash_state, signature)); - } - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_pkey_evp_size(const struct s2n_pkey *pkey, uint32_t *size_out) -{ - RESULT_ENSURE_REF(pkey); - RESULT_ENSURE_REF(pkey->pkey); - RESULT_ENSURE_REF(size_out); - - const int size = EVP_PKEY_size(pkey->pkey); - RESULT_ENSURE_GT(size, 0); - *size_out = size; - - return S2N_RESULT_OK; -} - -int s2n_pkey_evp_encrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(in); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(key->pkey); - - s2n_pkey_type type = 0; - POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); - POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_encrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING), S2N_ERR_PKEY_CTX_INIT); - - size_t out_size = out->size; - POSIX_GUARD_OSSL(EVP_PKEY_encrypt(pctx, out->data, &out_size, in->data, in->size), S2N_ERR_ENCRYPT); - POSIX_ENSURE(out_size == out->size, S2N_ERR_SIZE_MISMATCH); - - return S2N_SUCCESS; -} - -int s2n_pkey_evp_decrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(key); - POSIX_ENSURE_REF(in); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(key->pkey); - - s2n_pkey_type type = 0; - POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); - POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); - - uint32_t expected_size = 0; - POSIX_GUARD_RESULT(s2n_pkey_size(key, &expected_size)); - - /* RSA decryption requires more output memory than the size of the final decrypted message */ - struct s2n_blob buffer = { 0 }; - uint8_t buffer_bytes[4096] = { 0 }; - POSIX_GUARD(s2n_blob_init(&buffer, buffer_bytes, sizeof(buffer_bytes))); - POSIX_ENSURE(out->size <= buffer.size, S2N_ERR_NOMEM); - POSIX_ENSURE(expected_size <= buffer.size, S2N_ERR_NOMEM); - - DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); - POSIX_ENSURE_REF(pctx); - POSIX_GUARD_OSSL(EVP_PKEY_decrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); - /* The padding is actually RSA_PKCS1_PADDING, but we'll handle the padding later */ - POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING), S2N_ERR_PKEY_CTX_INIT); - - size_t out_size = buffer.size; - POSIX_GUARD_OSSL(EVP_PKEY_decrypt(pctx, buffer.data, &out_size, in->data, in->size), S2N_ERR_DECRYPT); - POSIX_ENSURE(out_size == expected_size, S2N_ERR_SIZE_MISMATCH); - - /* Handle padding in constant time to avoid Bleichenbacher oracles. - * If the padding is wrong, we return random output rather than failing. - * That ensures that padding failures are treated the same as wrong outputs. - */ - POSIX_GUARD_RESULT(s2n_get_public_random_data(out)); - s2n_constant_time_pkcs1_unpad_or_dont(out->data, buffer.data, out_size, out->size); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_pkey_evp_init(struct s2n_pkey *pkey) -{ - RESULT_ENSURE_REF(pkey); - pkey->size = &s2n_pkey_evp_size; - pkey->sign = &s2n_pkey_evp_sign; - pkey->verify = &s2n_pkey_evp_verify; - pkey->encrypt = s2n_pkey_evp_encrypt; - pkey->decrypt = s2n_pkey_evp_decrypt; - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_pkey_evp.h" + +#include +#include + +#include "crypto/s2n_evp.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_pkey.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "tls/s2n_signature_algorithms.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +DEFINE_POINTER_CLEANUP_FUNC(EVP_PKEY_CTX *, EVP_PKEY_CTX_free); + +static S2N_RESULT s2n_evp_md_ctx_set_pkey_ctx(EVP_MD_CTX *ctx, EVP_PKEY_CTX *pctx) +{ +#ifdef S2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + EVP_MD_CTX_set_pkey_ctx(ctx, pctx); + return S2N_RESULT_OK; +#else + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +#endif +} + +static S2N_RESULT s2n_evp_pkey_set_rsa_pss_saltlen(EVP_PKEY_CTX *pctx) +{ +#if defined(S2N_LIBCRYPTO_SUPPORTS_RSA_PSS_SIGNING) + RESULT_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST), S2N_ERR_PKEY_CTX_INIT); + return S2N_RESULT_OK; +#else + RESULT_BAIL(S2N_ERR_RSA_PSS_NOT_SUPPORTED); +#endif +} + +static S2N_RESULT s2n_pkey_evp_validate_sig_alg(const struct s2n_pkey *key, s2n_signature_algorithm sig_alg) +{ + RESULT_ENSURE_REF(key); + + /* Ensure that the signature algorithm type matches the key type. */ + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + RESULT_GUARD(s2n_pkey_get_type(key->pkey, &pkey_type)); + s2n_pkey_type sig_alg_type = S2N_PKEY_TYPE_UNKNOWN; + RESULT_GUARD(s2n_signature_algorithm_get_pkey_type(sig_alg, &sig_alg_type)); + RESULT_ENSURE(pkey_type == sig_alg_type, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + + return S2N_RESULT_OK; +} + +static EVP_PKEY_CTX *s2n_evp_pkey_ctx_new(EVP_PKEY *pkey, s2n_hash_algorithm hash_alg) +{ + PTR_ENSURE_REF(pkey); + switch (hash_alg) { +#if S2N_LIBCRYPTO_SUPPORTS_PROVIDERS + /* For openssl-3.0, pkey methods will do an implicit fetch for the signing + * algorithm, which includes the hash algorithm. If using a legacy hash + * algorithm, specify the non-fips version. + */ + case S2N_HASH_MD5: + case S2N_HASH_MD5_SHA1: + case S2N_HASH_SHA1: + return EVP_PKEY_CTX_new_from_pkey(NULL, pkey, "-fips"); +#endif + default: + return EVP_PKEY_CTX_new(pkey, NULL); + } +} + +/* Our "digest-and-sign" EVP signing logic is intended to support FIPS 140-3. + * FIPS 140-3 does not allow signing or verifying externally calculated digests + * for RSA and ECDSA verify. + * See https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures, + * and note that "component" tests only exist for ECDSA sign. + * + * In order to avoid signing externally calculated digests, we naively would + * need access to the full message to be signed at the time of signing. That's + * a problem for TLS1.2, where the client cert verify message requires signing + * every handshake message sent or received before the client cert verify message. + * To avoid storing every single handshake message in its entirety, we instead + * keep a running hash of the messages in an EVP hash state. Then, instead of + * digesting that hash state, we pass it unmodified to EVP_DigestSignFinal. + * That would normally not be allowed, since the hash state was initialized without + * a key using EVP_DigestInit instead of with a key using EVP_DigestSignInit. + * We make it work by using the EVP_MD_CTX_set_pkey_ctx method to attach a key + * to an existing hash state. + * + * All that means that "digest-and-sign" requires two things: + * - A single EVP hash state to sign. So we must not use a custom MD5_SHA1 hash, + * which doesn't produce a single hash state. + * - EVP_MD_CTX_set_pkey_ctx to exist and to behave as expected. Existence + * alone is not sufficient: the method exists in openssl-3.0-fips, but + * it cannot be used to setup a hash state for EVP_DigestSignFinal. + * + * Currently only awslc-fips meets both these requirements. New libcryptos + * should be assumed not to meet these requirements until proven otherwise. + */ +static int s2n_pkey_evp_digest_and_sign(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + /* Custom MD5_SHA1 involves combining separate MD5 and SHA1 hashes. + * That involves two hash states instead of the single hash state this + * method requires. + */ + POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); + + /* Not all implementations of EVP_MD_CTX_set_pkey_ctx behave as required + * by this method. Using EVP_MD_CTX_set_pkey_ctx to convert a hash initialized + * with EVP_DigestInit to one that can be finalized with EVP_DigestSignFinal + * is not entirely standard. + * + * However, this behavior is known to work with awslc-fips. + */ + POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); + + EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; + POSIX_ENSURE_REF(ctx); + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); + + size_t signature_size = signature->size; + POSIX_GUARD_OSSL(EVP_DigestSignFinal(ctx, signature->data, &signature_size), S2N_ERR_SIGN); + POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); + signature->size = signature_size; + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); + + return S2N_SUCCESS; +} + +/* See s2n_evp_digest_and_sign for more information */ +static bool s2n_pkey_evp_digest_and_sign_is_required(s2n_signature_algorithm sig_alg) +{ + if (sig_alg == S2N_SIGNATURE_MLDSA) { + /* The FIPS restrictions do not apply to ML-DSA */ + return false; + } + return s2n_libcrypto_is_awslc_fips(); +} + +/* "digest-then-sign" means that we calculate the digest for a hash state, + * then sign the digest bytes. That is not allowed by FIPS 140-3, but is allowed + * in all other cases. + */ +static int s2n_pkey_evp_digest_then_sign(EVP_PKEY_CTX *pctx, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + uint8_t digest_length = 0; + POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); + POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); + + uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); + + size_t signature_size = signature->size; + POSIX_GUARD_OSSL(EVP_PKEY_sign(pctx, signature->data, &signature_size, + digest_out, digest_length), + S2N_ERR_SIGN); + POSIX_ENSURE(signature_size <= signature->size, S2N_ERR_SIZE_MISMATCH); + signature->size = signature_size; + + return S2N_SUCCESS; +} + +int s2n_pkey_evp_sign(const struct s2n_pkey *priv, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(priv); + POSIX_ENSURE_REF(hash_state); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(priv->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_sign_init(pctx), S2N_ERR_PKEY_CTX_INIT); + + if (sig_alg != S2N_SIGNATURE_MLDSA) { + POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); + } + + if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); + POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); + } + + if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { + POSIX_GUARD(s2n_pkey_evp_digest_and_sign(pctx, sig_alg, hash_state, signature)); + } else { + POSIX_GUARD(s2n_pkey_evp_digest_then_sign(pctx, hash_state, signature)); + } + + return S2N_SUCCESS; +} + +/* See s2n_evp_digest_and_sign for more information */ +static int s2n_pkey_evp_digest_and_verify(EVP_PKEY_CTX *pctx, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + /* See digest-and-sign requirements */ + POSIX_ENSURE(!s2n_hash_use_custom_md5_sha1(), S2N_ERR_SAFETY); + POSIX_ENSURE(s2n_libcrypto_is_awslc_fips(), S2N_ERR_SAFETY); + + EVP_MD_CTX *ctx = hash_state->digest.high_level.evp.ctx; + POSIX_ENSURE_REF(ctx); + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, pctx)); + + POSIX_GUARD_OSSL(EVP_DigestVerifyFinal(ctx, signature->data, signature->size), S2N_ERR_VERIFY_SIGNATURE); + POSIX_GUARD_RESULT(s2n_evp_md_ctx_set_pkey_ctx(ctx, NULL)); + + return S2N_SUCCESS; +} + +/* See s2n_evp_digest_then_sign for more information */ +static int s2n_pkey_evp_digest_then_verify(EVP_PKEY_CTX *pctx, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pctx); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + + uint8_t digest_length = 0; + POSIX_GUARD(s2n_hash_digest_size(hash_state->alg, &digest_length)); + POSIX_ENSURE_LTE(digest_length, S2N_MAX_DIGEST_LEN); + + uint8_t digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, digest_length)); + + POSIX_GUARD_OSSL(EVP_PKEY_verify(pctx, signature->data, signature->size, + digest_out, digest_length), + S2N_ERR_VERIFY_SIGNATURE); + return S2N_SUCCESS; +} + +int s2n_pkey_evp_verify(const struct s2n_pkey *pub, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *hash_state, struct s2n_blob *signature) +{ + POSIX_ENSURE_REF(pub); + POSIX_ENSURE_REF(hash_state); + POSIX_ENSURE_REF(signature); + POSIX_GUARD_RESULT(s2n_pkey_evp_validate_sig_alg(pub, sig_alg)); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = s2n_evp_pkey_ctx_new(pub->pkey, hash_state->alg), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_verify_init(pctx), S2N_ERR_PKEY_CTX_INIT); + + if (sig_alg != S2N_SIGNATURE_MLDSA) { + POSIX_GUARD_OSSL(S2N_EVP_PKEY_CTX_set_signature_md(pctx, s2n_hash_alg_to_evp_md(hash_state->alg)), S2N_ERR_PKEY_CTX_INIT); + } + + if (sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE || sig_alg == S2N_SIGNATURE_RSA_PSS_PSS) { + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING), S2N_ERR_PKEY_CTX_INIT); + POSIX_GUARD_RESULT(s2n_evp_pkey_set_rsa_pss_saltlen(pctx)); + } + + if (s2n_pkey_evp_digest_and_sign_is_required(sig_alg)) { + POSIX_GUARD(s2n_pkey_evp_digest_and_verify(pctx, sig_alg, hash_state, signature)); + } else { + POSIX_GUARD(s2n_pkey_evp_digest_then_verify(pctx, hash_state, signature)); + } + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_pkey_evp_size(const struct s2n_pkey *pkey, uint32_t *size_out) +{ + RESULT_ENSURE_REF(pkey); + RESULT_ENSURE_REF(pkey->pkey); + RESULT_ENSURE_REF(size_out); + + const int size = EVP_PKEY_size(pkey->pkey); + RESULT_ENSURE_GT(size, 0); + *size_out = size; + + return S2N_RESULT_OK; +} + +int s2n_pkey_evp_encrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(in); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(key->pkey); + + s2n_pkey_type type = 0; + POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); + POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_encrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING), S2N_ERR_PKEY_CTX_INIT); + + size_t out_size = out->size; + POSIX_GUARD_OSSL(EVP_PKEY_encrypt(pctx, out->data, &out_size, in->data, in->size), S2N_ERR_ENCRYPT); + POSIX_ENSURE(out_size == out->size, S2N_ERR_SIZE_MISMATCH); + + return S2N_SUCCESS; +} + +int s2n_pkey_evp_decrypt(const struct s2n_pkey *key, struct s2n_blob *in, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(in); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(key->pkey); + + s2n_pkey_type type = 0; + POSIX_GUARD_RESULT(s2n_pkey_get_type(key->pkey, &type)); + POSIX_ENSURE(type == S2N_PKEY_TYPE_RSA, S2N_ERR_UNIMPLEMENTED); + + uint32_t expected_size = 0; + POSIX_GUARD_RESULT(s2n_pkey_size(key, &expected_size)); + + /* RSA decryption requires more output memory than the size of the final decrypted message */ + struct s2n_blob buffer = { 0 }; + uint8_t buffer_bytes[4096] = { 0 }; + POSIX_GUARD(s2n_blob_init(&buffer, buffer_bytes, sizeof(buffer_bytes))); + POSIX_ENSURE(out->size <= buffer.size, S2N_ERR_NOMEM); + POSIX_ENSURE(expected_size <= buffer.size, S2N_ERR_NOMEM); + + DEFER_CLEANUP(EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(key->pkey, NULL), EVP_PKEY_CTX_free_pointer); + POSIX_ENSURE_REF(pctx); + POSIX_GUARD_OSSL(EVP_PKEY_decrypt_init(pctx), S2N_ERR_PKEY_CTX_INIT); + /* The padding is actually RSA_PKCS1_PADDING, but we'll handle the padding later */ + POSIX_GUARD_OSSL(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING), S2N_ERR_PKEY_CTX_INIT); + + size_t out_size = buffer.size; + POSIX_GUARD_OSSL(EVP_PKEY_decrypt(pctx, buffer.data, &out_size, in->data, in->size), S2N_ERR_DECRYPT); + POSIX_ENSURE(out_size == expected_size, S2N_ERR_SIZE_MISMATCH); + + /* Handle padding in constant time to avoid Bleichenbacher oracles. + * If the padding is wrong, we return random output rather than failing. + * That ensures that padding failures are treated the same as wrong outputs. + */ + POSIX_GUARD_RESULT(s2n_get_public_random_data(out)); + s2n_constant_time_pkcs1_unpad_or_dont(out->data, buffer.data, out_size, out->size); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_pkey_evp_init(struct s2n_pkey *pkey) +{ + RESULT_ENSURE_REF(pkey); + pkey->size = &s2n_pkey_evp_size; + pkey->sign = &s2n_pkey_evp_sign; + pkey->verify = &s2n_pkey_evp_verify; + pkey->encrypt = s2n_pkey_evp_encrypt; + pkey->decrypt = s2n_pkey_evp_decrypt; + return S2N_RESULT_OK; +} diff --git a/error/s2n_errno.c b/error/s2n_errno.c index b000bc8eeb3..0dfc6b04dca 100644 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -1,516 +1,516 @@ -/* - * 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 "error/s2n_errno.h" - -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "utils/s2n_map.h" -#include "utils/s2n_safety.h" - -#ifdef S2N_STACKTRACE - #include -#endif - -__thread int s2n_errno; -__thread struct s2n_debug_info _s2n_debug_info = { .debug_str = "", .source = "" }; - -/** - * Returns the address of the thread-local `s2n_errno` variable - */ -int *s2n_errno_location() -{ - return &s2n_errno; -} - -static const char *no_such_language = "Language is not supported for error translation"; -static const char *no_such_error = "Internal s2n error"; - -/* - * Define error entries with descriptions in this macro once - * to generate code in next 2 following functions. - */ -/* clang-format off */ -#define ERR_ENTRIES(ERR_ENTRY) \ - ERR_ENTRY(S2N_ERR_OK, "no error") \ - ERR_ENTRY(S2N_ERR_IO, "underlying I/O operation failed, check system errno") \ - ERR_ENTRY(S2N_ERR_CLOSED, "connection is closed") \ - ERR_ENTRY(S2N_ERR_IO_BLOCKED, "underlying I/O operation would block") \ - ERR_ENTRY(S2N_ERR_ASYNC_BLOCKED, "blocked on external async function invocation") \ - ERR_ENTRY(S2N_ERR_ALERT, "TLS alert received") \ - ERR_ENTRY(S2N_ERR_ENCRYPT, "error encrypting data") \ - ERR_ENTRY(S2N_ERR_DECRYPT, "error decrypting data") \ - ERR_ENTRY(S2N_ERR_BAD_MESSAGE, "Bad message encountered") \ - ERR_ENTRY(S2N_ERR_KEY_INIT, "error initializing encryption key") \ - ERR_ENTRY(S2N_ERR_KEY_DESTROY, "error destroying encryption key") \ - ERR_ENTRY(S2N_ERR_DH_SERIALIZING, "error serializing Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_DH_SHARED_SECRET, "error computing Diffie-Hellman shared secret") \ - ERR_ENTRY(S2N_ERR_DH_WRITING_PUBLIC_KEY, "error writing Diffie-Hellman public key") \ - ERR_ENTRY(S2N_ERR_DH_FAILED_SIGNING, "error signing Diffie-Hellman values") \ - ERR_ENTRY(S2N_ERR_DH_COPYING_PARAMETERS, "error copying Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_DH_GENERATING_PARAMETERS, "error generating Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_CIPHER_NOT_SUPPORTED, "Cipher is not supported") \ - ERR_ENTRY(S2N_ERR_NO_APPLICATION_PROTOCOL, "No supported application protocol to negotiate") \ - ERR_ENTRY(S2N_ERR_FALLBACK_DETECTED, "TLS fallback detected") \ - ERR_ENTRY(S2N_ERR_HASH_DIGEST_FAILED, "failed to create hash digest") \ - ERR_ENTRY(S2N_ERR_HASH_INIT_FAILED, "error initializing hash") \ - ERR_ENTRY(S2N_ERR_HASH_UPDATE_FAILED, "error updating hash") \ - ERR_ENTRY(S2N_ERR_HASH_COPY_FAILED, "error copying hash") \ - ERR_ENTRY(S2N_ERR_HASH_WIPE_FAILED, "error wiping hash") \ - ERR_ENTRY(S2N_ERR_HASH_NOT_READY, "hash not in a valid state for the attempted operation") \ - ERR_ENTRY(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED, "error allowing MD5 to be used when in FIPS mode") \ - ERR_ENTRY(S2N_ERR_DECODE_CERTIFICATE, "error decoding certificate") \ - ERR_ENTRY(S2N_ERR_DECODE_PRIVATE_KEY, "error decoding private key") \ - ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHM, "Invalid signature algorithm") \ - ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_SCHEME, "Invalid signature scheme") \ - ERR_ENTRY(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, "Unable to negotiate a supported signature scheme") \ - ERR_ENTRY(S2N_ERR_CBC_VERIFY, "Failed CBC verification") \ - ERR_ENTRY(S2N_ERR_DH_COPYING_PUBLIC_KEY, "error copying Diffie-Hellman public key") \ - ERR_ENTRY(S2N_ERR_SIGN, "error signing data") \ - ERR_ENTRY(S2N_ERR_VERIFY_SIGNATURE, "error verifying signature") \ - ERR_ENTRY(S2N_ERR_ECDHE_GEN_KEY, "Failed to generate an ECDHE key") \ - ERR_ENTRY(S2N_ERR_ECDHE_SHARED_SECRET, "Error computing ECDHE shared secret") \ - ERR_ENTRY(S2N_ERR_ECDHE_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDHE handshake") \ - ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY, "Failed to validate the peer's point on the elliptic curve") \ - ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS, "Failed to validate the peer's point on the elliptic curve, per FIPS requirements") \ - ERR_ENTRY(S2N_ERR_ECDSA_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDSA SignatureScheme handshake") \ - ERR_ENTRY(S2N_ERR_ECDHE_SERIALIZING, "Error serializing ECDHE public") \ - ERR_ENTRY(S2N_ERR_KEM_UNSUPPORTED_PARAMS, "Unsupported KEM params was presented during a handshake that uses a KEM") \ - ERR_ENTRY(S2N_ERR_SHUTDOWN_RECORD_TYPE, "Non alert record received during s2n_shutdown()") \ - ERR_ENTRY(S2N_ERR_SHUTDOWN_CLOSED, "Peer closed before sending their close_notify") \ - ERR_ENTRY(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO, "renegotiation_info should be empty") \ - ERR_ENTRY(S2N_ERR_RECORD_LIMIT, "TLS record limit reached") \ - ERR_ENTRY(S2N_ERR_CERT_UNTRUSTED, "Certificate is untrusted") \ - ERR_ENTRY(S2N_ERR_CERT_INVALID_HOSTNAME, "Certificate is not valid for the supplied hostname") \ - ERR_ENTRY(S2N_ERR_CERT_REVOKED, "Certificate has been revoked by the CA") \ - ERR_ENTRY(S2N_ERR_CERT_NOT_YET_VALID, "Certificate is not yet valid") \ - ERR_ENTRY(S2N_ERR_CERT_EXPIRED, "Certificate has expired") \ - ERR_ENTRY(S2N_ERR_CERT_TYPE_UNSUPPORTED, "Certificate Type is unsupported") \ - ERR_ENTRY(S2N_ERR_CERT_INVALID, "Certificate is invalid") \ - ERR_ENTRY(S2N_ERR_CERT_INTENT_INVALID, "Certificate intent is invalid for the current context.") \ - ERR_ENTRY(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, "The maximum certificate chain depth has been exceeded") \ - ERR_ENTRY(S2N_ERR_CERT_REJECTED, "Certificate failed custom application validation") \ - ERR_ENTRY(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical certificate extension") \ - ERR_ENTRY(S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, "The certificate key or signature is not allowed by the security policy") \ - ERR_ENTRY(S2N_ERR_CRL_LOOKUP_FAILED, "No CRL could be found for the corresponding certificate") \ - ERR_ENTRY(S2N_ERR_CRL_SIGNATURE, "The signature of the CRL is invalid") \ - ERR_ENTRY(S2N_ERR_CRL_ISSUER, "Unable to get the CRL issuer certificate") \ - ERR_ENTRY(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical CRL extension") \ - ERR_ENTRY(S2N_ERR_CRL_INVALID_THIS_UPDATE, "The CRL contains an invalid thisUpdate field") \ - ERR_ENTRY(S2N_ERR_CRL_INVALID_NEXT_UPDATE, "The CRL contains an invalid nextUpdate field") \ - ERR_ENTRY(S2N_ERR_CRL_NOT_YET_VALID, "The CRL is not yet valid") \ - ERR_ENTRY(S2N_ERR_CRL_EXPIRED, "The CRL has expired") \ - ERR_ENTRY(S2N_ERR_INVALID_MAX_FRAG_LEN, "invalid Maximum Fragmentation Length encountered") \ - ERR_ENTRY(S2N_ERR_MAX_FRAG_LEN_MISMATCH, "Negotiated Maximum Fragmentation Length from server does not match the requested length by client") \ - ERR_ENTRY(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED, "TLS protocol version is not supported by configuration") \ - ERR_ENTRY(S2N_ERR_BAD_KEY_SHARE, "Bad key share received") \ - ERR_ENTRY(S2N_ERR_CANCELLED, "handshake was cancelled") \ - ERR_ENTRY(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED, "Protocol downgrade detected by client") \ - ERR_ENTRY(S2N_ERR_MADVISE, "error calling madvise") \ - ERR_ENTRY(S2N_ERR_ALLOC, "error allocating memory") \ - ERR_ENTRY(S2N_ERR_MLOCK, "error calling mlock (Did you run prlimit?)") \ - ERR_ENTRY(S2N_ERR_MUNLOCK, "error calling munlock") \ - ERR_ENTRY(S2N_ERR_FSTAT, "error calling fstat") \ - ERR_ENTRY(S2N_ERR_OPEN, "error calling open") \ - ERR_ENTRY(S2N_ERR_MMAP, "error calling mmap") \ - ERR_ENTRY(S2N_ERR_ATEXIT, "error calling atexit") \ - ERR_ENTRY(S2N_ERR_NOMEM, "no memory") \ - ERR_ENTRY(S2N_ERR_NULL, "NULL pointer encountered") \ - ERR_ENTRY(S2N_ERR_SAFETY, "a safety check failed") \ - ERR_ENTRY(S2N_ERR_INITIALIZED, "s2n is initialized") \ - ERR_ENTRY(S2N_ERR_NOT_INITIALIZED, "s2n not initialized") \ - ERR_ENTRY(S2N_ERR_RANDOM_UNINITIALIZED, "s2n entropy not initialized") \ - ERR_ENTRY(S2N_ERR_OPEN_RANDOM, "error opening urandom") \ - ERR_ENTRY(S2N_ERR_RESIZE_STATIC_STUFFER, "cannot resize a static stuffer") \ - ERR_ENTRY(S2N_ERR_RESIZE_TAINTED_STUFFER, "cannot resize a tainted stuffer") \ - ERR_ENTRY(S2N_ERR_STUFFER_OUT_OF_DATA, "stuffer is out of data") \ - ERR_ENTRY(S2N_ERR_STUFFER_IS_FULL, "stuffer is full") \ - ERR_ENTRY(S2N_ERR_STUFFER_NOT_FOUND, "stuffer expected bytes were not found") \ - ERR_ENTRY(S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA, "stuffer has unprocessed data") \ - ERR_ENTRY(S2N_ERR_HASH_INVALID_ALGORITHM, "invalid hash algorithm") \ - ERR_ENTRY(S2N_ERR_PRF_INVALID_ALGORITHM, "invalid prf hash algorithm") \ - ERR_ENTRY(S2N_ERR_PRF_INVALID_SEED, "invalid prf seeds provided") \ - ERR_ENTRY(S2N_ERR_PRF_DERIVE, "error deriving a secret from the PRF") \ - ERR_ENTRY(S2N_ERR_P_HASH_INVALID_ALGORITHM, "invalid p_hash algorithm") \ - ERR_ENTRY(S2N_ERR_P_HASH_INIT_FAILED, "error initializing p_hash") \ - ERR_ENTRY(S2N_ERR_P_HASH_UPDATE_FAILED, "error updating p_hash") \ - ERR_ENTRY(S2N_ERR_P_HASH_FINAL_FAILED, "error creating p_hash digest") \ - ERR_ENTRY(S2N_ERR_P_HASH_WIPE_FAILED, "error wiping p_hash") \ - ERR_ENTRY(S2N_ERR_HMAC_INVALID_ALGORITHM, "invalid HMAC algorithm") \ - ERR_ENTRY(S2N_ERR_HKDF_OUTPUT_SIZE, "invalid HKDF output size") \ - ERR_ENTRY(S2N_ERR_HKDF, "error generating HKDF output") \ - ERR_ENTRY(S2N_ERR_ALERT_PRESENT, "TLS alert is already pending") \ - ERR_ENTRY(S2N_ERR_HANDSHAKE_STATE, "Invalid handshake state encountered") \ - ERR_ENTRY(S2N_ERR_SHUTDOWN_PAUSED, "s2n_shutdown() called while paused") \ - ERR_ENTRY(S2N_ERR_SIZE_MISMATCH, "size mismatch") \ - ERR_ENTRY(S2N_ERR_RANDOM, "Error generating random data") \ - ERR_ENTRY(S2N_ERR_KEY_CHECK, "Invalid key") \ - ERR_ENTRY(S2N_ERR_CIPHER_TYPE, "Unknown cipher type used") \ - ERR_ENTRY(S2N_ERR_MAP_DUPLICATE, "Duplicate map key inserted") \ - ERR_ENTRY(S2N_ERR_MAP_IMMUTABLE, "Attempt to update an immutable map") \ - ERR_ENTRY(S2N_ERR_MAP_MUTABLE, "Attempt to lookup a mutable map") \ - ERR_ENTRY(S2N_ERR_MAP_INVALID_MAP_SIZE, "Attempt to create a map with 0 capacity") \ - ERR_ENTRY(S2N_ERR_INITIAL_HMAC, "error calling EVP_CIPHER_CTX_ctrl for composite cbc cipher") \ - ERR_ENTRY(S2N_ERR_INVALID_NONCE_TYPE, "Invalid AEAD nonce type") \ - ERR_ENTRY(S2N_ERR_UNIMPLEMENTED, "Unimplemented feature") \ - ERR_ENTRY(S2N_ERR_HANDSHAKE_UNREACHABLE, "Unreachable handshake state machine handler invoked") \ - ERR_ENTRY(S2N_ERR_READ, "error calling read") \ - ERR_ENTRY(S2N_ERR_WRITE, "error calling write") \ - ERR_ENTRY(S2N_ERR_BAD_FD, "Invalid file descriptor") \ - ERR_ENTRY(S2N_ERR_RDRAND_FAILED, "Error executing rdrand instruction") \ - ERR_ENTRY(S2N_ERR_FAILED_CACHE_RETRIEVAL, "Failed cache retrieval") \ - ERR_ENTRY(S2N_ERR_X509_TRUST_STORE, "Error initializing trust store") \ - ERR_ENTRY(S2N_ERR_UNKNOWN_PROTOCOL_VERSION, "Error determining client protocol version") \ - ERR_ENTRY(S2N_ERR_NULL_CN_NAME, "Error parsing CN names") \ - ERR_ENTRY(S2N_ERR_NULL_SANS, "Error parsing SANS") \ - ERR_ENTRY(S2N_ERR_CLIENT_HELLO_VERSION, "Could not get client hello version") \ - ERR_ENTRY(S2N_ERR_CLIENT_PROTOCOL_VERSION, "Could not get client protocol version") \ - ERR_ENTRY(S2N_ERR_SERVER_PROTOCOL_VERSION, "Could not get server protocol version") \ - ERR_ENTRY(S2N_ERR_ACTUAL_PROTOCOL_VERSION, "Could not get actual protocol version") \ - ERR_ENTRY(S2N_ERR_POLLING_FROM_SOCKET, "Error polling from socket") \ - ERR_ENTRY(S2N_ERR_RECV_STUFFER_FROM_CONN, "Error receiving stuffer from connection") \ - ERR_ENTRY(S2N_ERR_SEND_STUFFER_TO_CONN, "Error sending stuffer to connection") \ - ERR_ENTRY(S2N_ERR_PRECONDITION_VIOLATION, "Precondition violation") \ - ERR_ENTRY(S2N_ERR_POSTCONDITION_VIOLATION, "Postcondition violation") \ - ERR_ENTRY(S2N_ERR_INTEGER_OVERFLOW, "Integer overflow violation") \ - ERR_ENTRY(S2N_ERR_ARRAY_INDEX_OOB, "Array index out of bounds") \ - ERR_ENTRY(S2N_ERR_FREE_STATIC_BLOB, "Cannot free a static blob") \ - ERR_ENTRY(S2N_ERR_RESIZE_STATIC_BLOB, "Cannot resize a static blob") \ - ERR_ENTRY(S2N_ERR_RECORD_LENGTH_TOO_LARGE, "Record length exceeds protocol version maximum") \ - ERR_ENTRY(S2N_ERR_SET_DUPLICATE_VALUE, "Set already contains the provided value") \ - ERR_ENTRY(S2N_ERR_ASYNC_CALLBACK_FAILED, "Callback associated with async private keys function has failed") \ - ERR_ENTRY(S2N_ERR_ASYNC_MORE_THAN_ONE, "Only one asynchronous operation can be in-progress at the same time") \ - ERR_ENTRY(S2N_ERR_NO_ALERT, "No Alert present") \ - ERR_ENTRY(S2N_ERR_SERVER_MODE, "Operation not allowed in server mode") \ - ERR_ENTRY(S2N_ERR_CLIENT_MODE, "Operation not allowed in client mode") \ - ERR_ENTRY(S2N_ERR_CLIENT_MODE_DISABLED, "client connections not allowed") \ - ERR_ENTRY(S2N_ERR_TOO_MANY_CERTIFICATES, "only 1 certificate is supported in client mode") \ - ERR_ENTRY(S2N_ERR_TOO_MANY_SIGNATURE_SCHEMES, "Max supported length of SignatureAlgorithms/SignatureSchemes list is 128") \ - ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_FIPS_MODE, "Client Auth is not supported when in FIPS mode") \ - ERR_ENTRY(S2N_ERR_INVALID_BASE64, "invalid base64 encountered") \ - ERR_ENTRY(S2N_ERR_INVALID_HEX, "invalid HEX encountered") \ - ERR_ENTRY(S2N_ERR_INVALID_PEM, "invalid PEM encountered") \ - ERR_ENTRY(S2N_ERR_DH_PARAMS_CREATE, "error creating Diffie-Hellman parameters") \ - ERR_ENTRY(S2N_ERR_DH_TOO_SMALL, "Diffie-Hellman parameters are too small") \ - ERR_ENTRY(S2N_ERR_DH_PARAMETER_CHECK, "Diffie-Hellman parameter check failed") \ - ERR_ENTRY(S2N_ERR_INVALID_PKCS3, "invalid PKCS3 encountered") \ - ERR_ENTRY(S2N_ERR_NO_CERTIFICATE_IN_PEM, "No certificate in PEM") \ - ERR_ENTRY(S2N_ERR_SERVER_NAME_TOO_LONG, "server name is too long") \ - ERR_ENTRY(S2N_ERR_NUM_DEFAULT_CERTIFICATES, "exceeded max default certificates or provided no default") \ - ERR_ENTRY(S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE, "setting multiple default certificates per auth type is not allowed") \ - ERR_ENTRY(S2N_ERR_INVALID_CIPHER_PREFERENCES, "Invalid Cipher Preferences version") \ - ERR_ENTRY(S2N_ERR_INVALID_APPLICATION_PROTOCOL, "The supplied application protocol name is invalid") \ - ERR_ENTRY(S2N_ERR_KEY_MISMATCH, "public and private key do not match") \ - ERR_ENTRY(S2N_ERR_SEND_SIZE, "Retried s2n_send() size is invalid") \ - ERR_ENTRY(S2N_ERR_CORK_SET_ON_UNMANAGED, "Attempt to set connection cork management on unmanaged IO") \ - ERR_ENTRY(S2N_ERR_UNRECOGNIZED_EXTENSION, "TLS extension not recognized") \ - ERR_ENTRY(S2N_ERR_EXTENSION_NOT_RECEIVED, "The TLS extension was not received") \ - ERR_ENTRY(S2N_ERR_INVALID_SCT_LIST, "SCT list is invalid") \ - ERR_ENTRY(S2N_ERR_INVALID_OCSP_RESPONSE, "OCSP response is invalid") \ - ERR_ENTRY(S2N_ERR_UPDATING_EXTENSION, "Updating extension data failed") \ - ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE, "Serialized session state is not in valid format") \ - ERR_ENTRY(S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG, "Serialized session state is too long") \ - ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_LONG, "Session id is too long") \ - ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_SESSION_RESUMPTION_MODE, "Client Auth is not supported in session resumption mode") \ - ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_LENGTH, "Session ticket key length cannot be zero") \ - ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH, "Session ticket key name should be unique and the name length cannot be zero") \ - ERR_ENTRY(S2N_ERR_TICKET_KEY_NOT_UNIQUE, "Cannot add session ticket key because it was added before") \ - ERR_ENTRY(S2N_ERR_TICKET_KEY_LIMIT, "Limit reached for unexpired session ticket keys") \ - ERR_ENTRY(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY, "No key in encrypt-decrypt state is available to encrypt session ticket") \ - ERR_ENTRY(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED, "Failed to select a key from keys in encrypt-decrypt state") \ - ERR_ENTRY(S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND, "Key used in already assigned session ticket not found for decryption") \ - ERR_ENTRY(S2N_ERR_SENDING_NST, "Error in session ticket status encountered before sending NST") \ - ERR_ENTRY(S2N_ERR_INVALID_DYNAMIC_THRESHOLD, "invalid dynamic record threshold") \ - ERR_ENTRY(S2N_ERR_INVALID_ARGUMENT, "invalid argument provided into a function call") \ - ERR_ENTRY(S2N_ERR_NOT_IN_UNIT_TEST, "Illegal configuration, can only be used during unit tests") \ - ERR_ENTRY(S2N_ERR_NOT_IN_TEST, "Illegal configuration, can only be used during unit or integration tests") \ - ERR_ENTRY(S2N_ERR_UNSUPPORTED_CPU, "Unsupported CPU architecture") \ - ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_SHORT, "Session id is too short") \ - ERR_ENTRY(S2N_ERR_CONNECTION_CACHING_DISALLOWED, "This connection is not allowed to be cached") \ - ERR_ENTRY(S2N_ERR_SESSION_TICKET_NOT_SUPPORTED, "Session ticket not supported for this connection") \ - ERR_ENTRY(S2N_ERR_OCSP_NOT_SUPPORTED, "OCSP stapling was requested, but is not supported") \ - ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES, "Invalid signature algorithms preferences version") \ - ERR_ENTRY(S2N_ERR_RSA_PSS_NOT_SUPPORTED, "RSA-PSS signing not supported by underlying libcrypto implementation") \ - ERR_ENTRY(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE, "Inner plaintext size exceeds limit") \ - ERR_ENTRY(S2N_ERR_INVALID_ECC_PREFERENCES, "Invalid ecc curves preferences version") \ - ERR_ENTRY(S2N_ERR_RECORD_STUFFER_SIZE, "Record stuffer out of space") \ - ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL, "Fragment length is too small") \ - ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE, "Fragment length is too large") \ - ERR_ENTRY(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING, "Record stuffer needs to be drained first") \ - ERR_ENTRY(S2N_ERR_UNSUPPORTED_EXTENSION, "Illegal use of a known, supported extension") \ - ERR_ENTRY(S2N_ERR_MISSING_EXTENSION, "Mandatory extension not received") \ - ERR_ENTRY(S2N_ERR_DUPLICATE_EXTENSION, "Extension block contains two or more extensions of the same type") \ - ERR_ENTRY(S2N_ERR_DEPRECATED_SECURITY_POLICY, "Deprecated security policy. Please choose a different security policy.") \ - ERR_ENTRY(S2N_ERR_INVALID_SECURITY_POLICY, "Invalid security policy") \ - ERR_ENTRY(S2N_ERR_INVALID_KEM_PREFERENCES, "Invalid kem preferences version") \ - ERR_ENTRY(S2N_ERR_INVALID_PARSED_EXTENSIONS, "Invalid parsed extension data") \ - ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_PERFORMED, "Async operation was already performed, cannot perform it again") \ - ERR_ENTRY(S2N_ERR_ASYNC_NOT_PERFORMED, "Async operation is not performed, cannot apply its result") \ - ERR_ENTRY(S2N_ERR_ASYNC_WRONG_CONNECTION, "Async private key operation can only be consumed by connection which initiated it") \ - ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_APPLIED, "Async operation was already applied to connection, cannot apply it again") \ - ERR_ENTRY(S2N_ERR_INVALID_HELLO_RETRY, "Invalid hello retry request") \ - ERR_ENTRY(S2N_ERR_INVALID_STATE, "Invalid state, this is the result of invalid use of an API. Check the API documentation for the function that raised this error for more info") \ - ERR_ENTRY(S2N_ERR_UNSUPPORTED_WITH_QUIC, "Functionality not supported when running with QUIC support enabled") \ - ERR_ENTRY(S2N_ERR_PQ_CRYPTO, "An error occurred in a post-quantum crypto function") \ - ERR_ENTRY(S2N_ERR_DUPLICATE_PSK_IDENTITIES, "The list of pre-shared keys provided contains duplicate psk identities") \ - ERR_ENTRY(S2N_ERR_OFFERED_PSKS_TOO_LONG, "The total pre-shared key data is too long to send over the wire") \ - ERR_ENTRY(S2N_ERR_INVALID_SESSION_TICKET, "Session ticket data is not valid") \ - ERR_ENTRY(S2N_ERR_ZERO_LIFETIME_TICKET, "Calculated session lifetime is zero") \ - ERR_ENTRY(S2N_ERR_REENTRANCY, "Original execution must complete before method can be called again") \ - ERR_ENTRY(S2N_ERR_INVALID_CERT_STATE, "Certificate validation entered an invalid state and is not able to continue") \ - ERR_ENTRY(S2N_ERR_INVALID_SUPPORTED_GROUP_STATE, "SupportedGroup preference decision entered invalid state and selected both KEM and EC Curve") \ - ERR_ENTRY(S2N_ERR_INVALID_EARLY_DATA_STATE, "Early data in invalid state") \ - ERR_ENTRY(S2N_ERR_EARLY_DATA_NOT_ALLOWED, "Early data is not allowed by the connection") \ - ERR_ENTRY(S2N_ERR_NO_CERT_FOUND, "Certificate not found") \ - ERR_ENTRY(S2N_ERR_NO_PRIVATE_KEY, "Certificate found, but no corresponding private key") \ - ERR_ENTRY(S2N_ERR_CERT_NOT_VALIDATED, "Certificate not validated") \ - ERR_ENTRY(S2N_ERR_MAX_EARLY_DATA_SIZE, "Maximum early data bytes exceeded") \ - ERR_ENTRY(S2N_ERR_EARLY_DATA_BLOCKED, "Blocked on early data") \ - ERR_ENTRY(S2N_ERR_PSK_MODE, "Mixing resumption and external PSKs is not supported") \ - ERR_ENTRY(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND, "X509 extension value not found") \ - ERR_ENTRY(S2N_ERR_INVALID_X509_EXTENSION_TYPE, "Invalid X509 extension type") \ - ERR_ENTRY(S2N_ERR_INSUFFICIENT_MEM_SIZE, "The provided buffer size is not large enough to contain the output data. Try increasing the allocation size.") \ - ERR_ENTRY(S2N_ERR_KEYING_MATERIAL_EXPIRED, "The lifetime of the connection keying material has exceeded the limit. Perform a new full handshake.") \ - ERR_ENTRY(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, "Unable to decrypt rejected early data") \ - ERR_ENTRY(S2N_ERR_PKEY_CTX_INIT, "Unable to initialize the libcrypto pkey context") \ - ERR_ENTRY(S2N_ERR_SECRET_SCHEDULE_STATE, "Correct inputs to secret calculation not available") \ - ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NUMBER_MISMATCH, "The libcrypto major version number seen at compile-time is different from the major version number seen at run-time") \ - ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NAME_MISMATCH, "The libcrypto major version name seen at compile-time is different from the major version name seen at run-time") \ - ERR_ENTRY(S2N_ERR_OSSL_PROVIDER, "Failed to load or unload an openssl provider") \ - ERR_ENTRY(S2N_ERR_CERT_OWNERSHIP, "The ownership of the certificate chain is incompatible with the operation") \ - ERR_ENTRY(S2N_ERR_INTERNAL_LIBCRYPTO_ERROR, "An internal error has occurred in the libcrypto API") \ - ERR_ENTRY(S2N_ERR_NO_RENEGOTIATION, "Only secure, server-initiated renegotiation is supported") \ - ERR_ENTRY(S2N_ERR_APP_DATA_BLOCKED, "Blocked on application data during handshake") \ - ERR_ENTRY(S2N_ERR_KTLS_MANAGED_IO, "kTLS cannot be enabled while custom I/O is configured for the connection") \ - ERR_ENTRY(S2N_ERR_HANDSHAKE_NOT_COMPLETE, "Operation is only allowed after the handshake is complete") \ - ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, "kTLS is unsupported on this platform") \ - ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \ - ERR_ENTRY(S2N_ERR_KTLS_ENABLE, "An error occurred when attempting to enable kTLS on socket. Ensure the 'tls' kernel module is enabled.") \ - ERR_ENTRY(S2N_ERR_KTLS_BAD_CMSG, "Error handling cmsghdr.") \ - ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \ - ERR_ENTRY(S2N_ERR_TEST_ASSERTION, "Test assertion failed") \ - ERR_ENTRY(S2N_ERR_KTLS_RENEG, "kTLS does not support secure renegotiation") \ - ERR_ENTRY(S2N_ERR_KTLS_KEYUPDATE, "Received KeyUpdate from peer, but kernel does not support updating tls keys") \ - ERR_ENTRY(S2N_ERR_KTLS_KEY_LIMIT, "Reached key encryption limit, but kernel does not support updating tls keys") \ - ERR_ENTRY(S2N_ERR_KTLS_SOCKOPT, "A call to sockopt failed when attempting to update the keys in the kernel") \ - ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client forbids mutual authentication, but server requested a cert") \ - ERR_ENTRY(S2N_ERR_MISSING_CERT_REQUEST, "Client requires mutual authentication, but server did not request a cert") \ - ERR_ENTRY(S2N_ERR_MISSING_CLIENT_CERT, "Server requires client certificate") \ - ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ - ERR_ENTRY(S2N_ERR_TOO_MANY_CAS, "Too many certificate authorities in trust store"); \ - ERR_ENTRY(S2N_ERR_BAD_HEX, "Could not parse malformed hex string"); \ - ERR_ENTRY(S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK, "Config set to NULL before client hello callback. This should not be possible outside of tests."); \ - ERR_ENTRY(S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO, "The invoked s2n-tls API is not supported by the libcrypto"); \ - ERR_ENTRY(S2N_ERR_FIPS_MODE_UNSUPPORTED, "FIPS mode is not supported for the libcrypto"); \ - /* clang-format on */ - -#define ERR_STR_CASE(ERR, str) \ - case ERR: \ - return str; -#define ERR_NAME_CASE(ERR, str) \ - case ERR: \ - return #ERR; - -const char *s2n_strerror(int error, const char *lang) -{ - if (lang == NULL) { - lang = "EN"; - } - - if (strcasecmp(lang, "EN")) { - return no_such_language; - } - - s2n_error err = error; - switch (err) { - ERR_ENTRIES(ERR_STR_CASE) - - /* Skip block ends */ - case S2N_ERR_T_OK_END: - case S2N_ERR_T_IO_END: - case S2N_ERR_T_CLOSED_END: - case S2N_ERR_T_BLOCKED_END: - case S2N_ERR_T_ALERT_END: - case S2N_ERR_T_PROTO_END: - case S2N_ERR_T_INTERNAL_END: - case S2N_ERR_T_USAGE_END: - break; - - /* No default to make compiler fail on missing values */ - } - - return no_such_error; -} - -const char *s2n_strerror_name(int error) -{ - s2n_error err = error; - switch (err) { - ERR_ENTRIES(ERR_NAME_CASE) - - /* Skip block ends */ - case S2N_ERR_T_OK_END: - case S2N_ERR_T_IO_END: - case S2N_ERR_T_CLOSED_END: - case S2N_ERR_T_BLOCKED_END: - case S2N_ERR_T_ALERT_END: - case S2N_ERR_T_PROTO_END: - case S2N_ERR_T_INTERNAL_END: - case S2N_ERR_T_USAGE_END: - break; - - /* No default to make compiler fail on missing values */ - } - - return no_such_error; -} - -const char *s2n_strerror_debug(int error, const char *lang) -{ - if (lang == NULL) { - lang = "EN"; - } - - if (strcasecmp(lang, "EN")) { - return no_such_language; - } - - /* No error, just return the no error string */ - if (error == S2N_ERR_OK) { - return s2n_strerror(error, lang); - } - - return _s2n_debug_info.debug_str; -} - -const char *s2n_strerror_source(int error) -{ - /* No error, just return the no error string */ - if (error == S2N_ERR_OK) { - return s2n_strerror(error, "EN"); - } - - return _s2n_debug_info.source; -} - -int s2n_error_get_type(int error) -{ - return (error >> S2N_ERR_NUM_VALUE_BITS); -} - -/* https://www.gnu.org/software/libc/manual/html_node/Backtraces.html */ -static bool s_s2n_stack_traces_enabled = false; - -bool s2n_stack_traces_enabled() -{ - return s_s2n_stack_traces_enabled; -} - -int s2n_stack_traces_enabled_set(bool newval) -{ - s_s2n_stack_traces_enabled = newval; - return S2N_SUCCESS; -} - -void s2n_debug_info_reset(void) -{ - _s2n_debug_info.debug_str = ""; - _s2n_debug_info.source = ""; -} - -#ifdef S2N_STACKTRACE - - #define MAX_BACKTRACE_DEPTH 20 -__thread struct s2n_stacktrace tl_stacktrace = { 0 }; - -int s2n_free_stacktrace(void) -{ - if (tl_stacktrace.trace != NULL) { - free(tl_stacktrace.trace); - struct s2n_stacktrace zero_stacktrace = { 0 }; - tl_stacktrace = zero_stacktrace; - } - return S2N_SUCCESS; -} - -int s2n_calculate_stacktrace(void) -{ - if (!s_s2n_stack_traces_enabled) { - return S2N_SUCCESS; - } - - int old_errno = errno; - POSIX_GUARD(s2n_free_stacktrace()); - void *array[MAX_BACKTRACE_DEPTH]; - tl_stacktrace.trace_size = backtrace(array, MAX_BACKTRACE_DEPTH); - tl_stacktrace.trace = backtrace_symbols(array, tl_stacktrace.trace_size); - errno = old_errno; - return S2N_SUCCESS; -} - -int s2n_get_stacktrace(struct s2n_stacktrace *trace) -{ - *trace = tl_stacktrace; - return S2N_SUCCESS; -} - -int s2n_print_stacktrace(FILE *fptr) -{ - if (!s_s2n_stack_traces_enabled) { - fprintf(fptr, "%s\n%s\n", - "NOTE: Some details are omitted, run with S2N_PRINT_STACKTRACE=1 for a verbose backtrace.", - "See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide"); - return S2N_SUCCESS; - } - - fprintf(fptr, "\nStacktrace is:\n"); - for (int i = 0; i < tl_stacktrace.trace_size; ++i) { - fprintf(fptr, "%s\n", tl_stacktrace.trace[i]); - } - return S2N_SUCCESS; -} - -#else /* !S2N_STACKTRACE */ -int s2n_free_stacktrace(void) -{ - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_calculate_stacktrace(void) -{ - if (!s_s2n_stack_traces_enabled) { - return S2N_SUCCESS; - } - - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_get_stacktrace(struct s2n_stacktrace *trace) -{ - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_print_stacktrace(FILE *fptr) -{ - S2N_ERROR(S2N_ERR_UNIMPLEMENTED); -} -#endif /* S2N_STACKTRACE */ +/* + * 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 "error/s2n_errno.h" + +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "utils/s2n_map.h" +#include "utils/s2n_safety.h" + +#ifdef S2N_STACKTRACE + #include +#endif + +__thread int s2n_errno; +__thread struct s2n_debug_info _s2n_debug_info = { .debug_str = "", .source = "" }; + +/** + * Returns the address of the thread-local `s2n_errno` variable + */ +int *s2n_errno_location() +{ + return &s2n_errno; +} + +static const char *no_such_language = "Language is not supported for error translation"; +static const char *no_such_error = "Internal s2n error"; + +/* + * Define error entries with descriptions in this macro once + * to generate code in next 2 following functions. + */ +/* clang-format off */ +#define ERR_ENTRIES(ERR_ENTRY) \ + ERR_ENTRY(S2N_ERR_OK, "no error") \ + ERR_ENTRY(S2N_ERR_IO, "underlying I/O operation failed, check system errno") \ + ERR_ENTRY(S2N_ERR_CLOSED, "connection is closed") \ + ERR_ENTRY(S2N_ERR_IO_BLOCKED, "underlying I/O operation would block") \ + ERR_ENTRY(S2N_ERR_ASYNC_BLOCKED, "blocked on external async function invocation") \ + ERR_ENTRY(S2N_ERR_ALERT, "TLS alert received") \ + ERR_ENTRY(S2N_ERR_ENCRYPT, "error encrypting data") \ + ERR_ENTRY(S2N_ERR_DECRYPT, "error decrypting data") \ + ERR_ENTRY(S2N_ERR_BAD_MESSAGE, "Bad message encountered") \ + ERR_ENTRY(S2N_ERR_KEY_INIT, "error initializing encryption key") \ + ERR_ENTRY(S2N_ERR_KEY_DESTROY, "error destroying encryption key") \ + ERR_ENTRY(S2N_ERR_DH_SERIALIZING, "error serializing Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_DH_SHARED_SECRET, "error computing Diffie-Hellman shared secret") \ + ERR_ENTRY(S2N_ERR_DH_WRITING_PUBLIC_KEY, "error writing Diffie-Hellman public key") \ + ERR_ENTRY(S2N_ERR_DH_FAILED_SIGNING, "error signing Diffie-Hellman values") \ + ERR_ENTRY(S2N_ERR_DH_COPYING_PARAMETERS, "error copying Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_DH_GENERATING_PARAMETERS, "error generating Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_CIPHER_NOT_SUPPORTED, "Cipher is not supported") \ + ERR_ENTRY(S2N_ERR_NO_APPLICATION_PROTOCOL, "No supported application protocol to negotiate") \ + ERR_ENTRY(S2N_ERR_FALLBACK_DETECTED, "TLS fallback detected") \ + ERR_ENTRY(S2N_ERR_HASH_DIGEST_FAILED, "failed to create hash digest") \ + ERR_ENTRY(S2N_ERR_HASH_INIT_FAILED, "error initializing hash") \ + ERR_ENTRY(S2N_ERR_HASH_UPDATE_FAILED, "error updating hash") \ + ERR_ENTRY(S2N_ERR_HASH_COPY_FAILED, "error copying hash") \ + ERR_ENTRY(S2N_ERR_HASH_WIPE_FAILED, "error wiping hash") \ + ERR_ENTRY(S2N_ERR_HASH_NOT_READY, "hash not in a valid state for the attempted operation") \ + ERR_ENTRY(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED, "error allowing MD5 to be used when in FIPS mode") \ + ERR_ENTRY(S2N_ERR_DECODE_CERTIFICATE, "error decoding certificate") \ + ERR_ENTRY(S2N_ERR_DECODE_PRIVATE_KEY, "error decoding private key") \ + ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHM, "Invalid signature algorithm") \ + ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_SCHEME, "Invalid signature scheme") \ + ERR_ENTRY(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, "Unable to negotiate a supported signature scheme") \ + ERR_ENTRY(S2N_ERR_CBC_VERIFY, "Failed CBC verification") \ + ERR_ENTRY(S2N_ERR_DH_COPYING_PUBLIC_KEY, "error copying Diffie-Hellman public key") \ + ERR_ENTRY(S2N_ERR_SIGN, "error signing data") \ + ERR_ENTRY(S2N_ERR_VERIFY_SIGNATURE, "error verifying signature") \ + ERR_ENTRY(S2N_ERR_ECDHE_GEN_KEY, "Failed to generate an ECDHE key") \ + ERR_ENTRY(S2N_ERR_ECDHE_SHARED_SECRET, "Error computing ECDHE shared secret") \ + ERR_ENTRY(S2N_ERR_ECDHE_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDHE handshake") \ + ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY, "Failed to validate the peer's point on the elliptic curve") \ + ERR_ENTRY(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS, "Failed to validate the peer's point on the elliptic curve, per FIPS requirements") \ + ERR_ENTRY(S2N_ERR_ECDSA_UNSUPPORTED_CURVE, "Unsupported EC curve was presented during an ECDSA SignatureScheme handshake") \ + ERR_ENTRY(S2N_ERR_ECDHE_SERIALIZING, "Error serializing ECDHE public") \ + ERR_ENTRY(S2N_ERR_KEM_UNSUPPORTED_PARAMS, "Unsupported KEM params was presented during a handshake that uses a KEM") \ + ERR_ENTRY(S2N_ERR_SHUTDOWN_RECORD_TYPE, "Non alert record received during s2n_shutdown()") \ + ERR_ENTRY(S2N_ERR_SHUTDOWN_CLOSED, "Peer closed before sending their close_notify") \ + ERR_ENTRY(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO, "renegotiation_info should be empty") \ + ERR_ENTRY(S2N_ERR_RECORD_LIMIT, "TLS record limit reached") \ + ERR_ENTRY(S2N_ERR_CERT_UNTRUSTED, "Certificate is untrusted") \ + ERR_ENTRY(S2N_ERR_CERT_INVALID_HOSTNAME, "Certificate is not valid for the supplied hostname") \ + ERR_ENTRY(S2N_ERR_CERT_REVOKED, "Certificate has been revoked by the CA") \ + ERR_ENTRY(S2N_ERR_CERT_NOT_YET_VALID, "Certificate is not yet valid") \ + ERR_ENTRY(S2N_ERR_CERT_EXPIRED, "Certificate has expired") \ + ERR_ENTRY(S2N_ERR_CERT_TYPE_UNSUPPORTED, "Certificate Type is unsupported") \ + ERR_ENTRY(S2N_ERR_CERT_INVALID, "Certificate is invalid") \ + ERR_ENTRY(S2N_ERR_CERT_INTENT_INVALID, "Certificate intent is invalid for the current context.") \ + ERR_ENTRY(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, "The maximum certificate chain depth has been exceeded") \ + ERR_ENTRY(S2N_ERR_CERT_REJECTED, "Certificate failed custom application validation") \ + ERR_ENTRY(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical certificate extension") \ + ERR_ENTRY(S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, "The certificate key or signature is not allowed by the security policy") \ + ERR_ENTRY(S2N_ERR_CRL_LOOKUP_FAILED, "No CRL could be found for the corresponding certificate") \ + ERR_ENTRY(S2N_ERR_CRL_SIGNATURE, "The signature of the CRL is invalid") \ + ERR_ENTRY(S2N_ERR_CRL_ISSUER, "Unable to get the CRL issuer certificate") \ + ERR_ENTRY(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION, "Unhandled critical CRL extension") \ + ERR_ENTRY(S2N_ERR_CRL_INVALID_THIS_UPDATE, "The CRL contains an invalid thisUpdate field") \ + ERR_ENTRY(S2N_ERR_CRL_INVALID_NEXT_UPDATE, "The CRL contains an invalid nextUpdate field") \ + ERR_ENTRY(S2N_ERR_CRL_NOT_YET_VALID, "The CRL is not yet valid") \ + ERR_ENTRY(S2N_ERR_CRL_EXPIRED, "The CRL has expired") \ + ERR_ENTRY(S2N_ERR_INVALID_MAX_FRAG_LEN, "invalid Maximum Fragmentation Length encountered") \ + ERR_ENTRY(S2N_ERR_MAX_FRAG_LEN_MISMATCH, "Negotiated Maximum Fragmentation Length from server does not match the requested length by client") \ + ERR_ENTRY(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED, "TLS protocol version is not supported by configuration") \ + ERR_ENTRY(S2N_ERR_BAD_KEY_SHARE, "Bad key share received") \ + ERR_ENTRY(S2N_ERR_CANCELLED, "handshake was cancelled") \ + ERR_ENTRY(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED, "Protocol downgrade detected by client") \ + ERR_ENTRY(S2N_ERR_MADVISE, "error calling madvise") \ + ERR_ENTRY(S2N_ERR_ALLOC, "error allocating memory") \ + ERR_ENTRY(S2N_ERR_MLOCK, "error calling mlock (Did you run prlimit?)") \ + ERR_ENTRY(S2N_ERR_MUNLOCK, "error calling munlock") \ + ERR_ENTRY(S2N_ERR_FSTAT, "error calling fstat") \ + ERR_ENTRY(S2N_ERR_OPEN, "error calling open") \ + ERR_ENTRY(S2N_ERR_MMAP, "error calling mmap") \ + ERR_ENTRY(S2N_ERR_ATEXIT, "error calling atexit") \ + ERR_ENTRY(S2N_ERR_NOMEM, "no memory") \ + ERR_ENTRY(S2N_ERR_NULL, "NULL pointer encountered") \ + ERR_ENTRY(S2N_ERR_SAFETY, "a safety check failed") \ + ERR_ENTRY(S2N_ERR_INITIALIZED, "s2n is initialized") \ + ERR_ENTRY(S2N_ERR_NOT_INITIALIZED, "s2n not initialized") \ + ERR_ENTRY(S2N_ERR_RANDOM_UNINITIALIZED, "s2n entropy not initialized") \ + ERR_ENTRY(S2N_ERR_OPEN_RANDOM, "error opening urandom") \ + ERR_ENTRY(S2N_ERR_RESIZE_STATIC_STUFFER, "cannot resize a static stuffer") \ + ERR_ENTRY(S2N_ERR_RESIZE_TAINTED_STUFFER, "cannot resize a tainted stuffer") \ + ERR_ENTRY(S2N_ERR_STUFFER_OUT_OF_DATA, "stuffer is out of data") \ + ERR_ENTRY(S2N_ERR_STUFFER_IS_FULL, "stuffer is full") \ + ERR_ENTRY(S2N_ERR_STUFFER_NOT_FOUND, "stuffer expected bytes were not found") \ + ERR_ENTRY(S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA, "stuffer has unprocessed data") \ + ERR_ENTRY(S2N_ERR_HASH_INVALID_ALGORITHM, "invalid hash algorithm") \ + ERR_ENTRY(S2N_ERR_PRF_INVALID_ALGORITHM, "invalid prf hash algorithm") \ + ERR_ENTRY(S2N_ERR_PRF_INVALID_SEED, "invalid prf seeds provided") \ + ERR_ENTRY(S2N_ERR_PRF_DERIVE, "error deriving a secret from the PRF") \ + ERR_ENTRY(S2N_ERR_P_HASH_INVALID_ALGORITHM, "invalid p_hash algorithm") \ + ERR_ENTRY(S2N_ERR_P_HASH_INIT_FAILED, "error initializing p_hash") \ + ERR_ENTRY(S2N_ERR_P_HASH_UPDATE_FAILED, "error updating p_hash") \ + ERR_ENTRY(S2N_ERR_P_HASH_FINAL_FAILED, "error creating p_hash digest") \ + ERR_ENTRY(S2N_ERR_P_HASH_WIPE_FAILED, "error wiping p_hash") \ + ERR_ENTRY(S2N_ERR_HMAC_INVALID_ALGORITHM, "invalid HMAC algorithm") \ + ERR_ENTRY(S2N_ERR_HKDF_OUTPUT_SIZE, "invalid HKDF output size") \ + ERR_ENTRY(S2N_ERR_HKDF, "error generating HKDF output") \ + ERR_ENTRY(S2N_ERR_ALERT_PRESENT, "TLS alert is already pending") \ + ERR_ENTRY(S2N_ERR_HANDSHAKE_STATE, "Invalid handshake state encountered") \ + ERR_ENTRY(S2N_ERR_SHUTDOWN_PAUSED, "s2n_shutdown() called while paused") \ + ERR_ENTRY(S2N_ERR_SIZE_MISMATCH, "size mismatch") \ + ERR_ENTRY(S2N_ERR_RANDOM, "Error generating random data") \ + ERR_ENTRY(S2N_ERR_KEY_CHECK, "Invalid key") \ + ERR_ENTRY(S2N_ERR_CIPHER_TYPE, "Unknown cipher type used") \ + ERR_ENTRY(S2N_ERR_MAP_DUPLICATE, "Duplicate map key inserted") \ + ERR_ENTRY(S2N_ERR_MAP_IMMUTABLE, "Attempt to update an immutable map") \ + ERR_ENTRY(S2N_ERR_MAP_MUTABLE, "Attempt to lookup a mutable map") \ + ERR_ENTRY(S2N_ERR_MAP_INVALID_MAP_SIZE, "Attempt to create a map with 0 capacity") \ + ERR_ENTRY(S2N_ERR_INITIAL_HMAC, "error calling EVP_CIPHER_CTX_ctrl for composite cbc cipher") \ + ERR_ENTRY(S2N_ERR_INVALID_NONCE_TYPE, "Invalid AEAD nonce type") \ + ERR_ENTRY(S2N_ERR_UNIMPLEMENTED, "Unimplemented feature") \ + ERR_ENTRY(S2N_ERR_HANDSHAKE_UNREACHABLE, "Unreachable handshake state machine handler invoked") \ + ERR_ENTRY(S2N_ERR_READ, "error calling read") \ + ERR_ENTRY(S2N_ERR_WRITE, "error calling write") \ + ERR_ENTRY(S2N_ERR_BAD_FD, "Invalid file descriptor") \ + ERR_ENTRY(S2N_ERR_RDRAND_FAILED, "Error executing rdrand instruction") \ + ERR_ENTRY(S2N_ERR_FAILED_CACHE_RETRIEVAL, "Failed cache retrieval") \ + ERR_ENTRY(S2N_ERR_X509_TRUST_STORE, "Error initializing trust store") \ + ERR_ENTRY(S2N_ERR_UNKNOWN_PROTOCOL_VERSION, "Error determining client protocol version") \ + ERR_ENTRY(S2N_ERR_NULL_CN_NAME, "Error parsing CN names") \ + ERR_ENTRY(S2N_ERR_NULL_SANS, "Error parsing SANS") \ + ERR_ENTRY(S2N_ERR_CLIENT_HELLO_VERSION, "Could not get client hello version") \ + ERR_ENTRY(S2N_ERR_CLIENT_PROTOCOL_VERSION, "Could not get client protocol version") \ + ERR_ENTRY(S2N_ERR_SERVER_PROTOCOL_VERSION, "Could not get server protocol version") \ + ERR_ENTRY(S2N_ERR_ACTUAL_PROTOCOL_VERSION, "Could not get actual protocol version") \ + ERR_ENTRY(S2N_ERR_POLLING_FROM_SOCKET, "Error polling from socket") \ + ERR_ENTRY(S2N_ERR_RECV_STUFFER_FROM_CONN, "Error receiving stuffer from connection") \ + ERR_ENTRY(S2N_ERR_SEND_STUFFER_TO_CONN, "Error sending stuffer to connection") \ + ERR_ENTRY(S2N_ERR_PRECONDITION_VIOLATION, "Precondition violation") \ + ERR_ENTRY(S2N_ERR_POSTCONDITION_VIOLATION, "Postcondition violation") \ + ERR_ENTRY(S2N_ERR_INTEGER_OVERFLOW, "Integer overflow violation") \ + ERR_ENTRY(S2N_ERR_ARRAY_INDEX_OOB, "Array index out of bounds") \ + ERR_ENTRY(S2N_ERR_FREE_STATIC_BLOB, "Cannot free a static blob") \ + ERR_ENTRY(S2N_ERR_RESIZE_STATIC_BLOB, "Cannot resize a static blob") \ + ERR_ENTRY(S2N_ERR_RECORD_LENGTH_TOO_LARGE, "Record length exceeds protocol version maximum") \ + ERR_ENTRY(S2N_ERR_SET_DUPLICATE_VALUE, "Set already contains the provided value") \ + ERR_ENTRY(S2N_ERR_ASYNC_CALLBACK_FAILED, "Callback associated with async private keys function has failed") \ + ERR_ENTRY(S2N_ERR_ASYNC_MORE_THAN_ONE, "Only one asynchronous operation can be in-progress at the same time") \ + ERR_ENTRY(S2N_ERR_NO_ALERT, "No Alert present") \ + ERR_ENTRY(S2N_ERR_SERVER_MODE, "Operation not allowed in server mode") \ + ERR_ENTRY(S2N_ERR_CLIENT_MODE, "Operation not allowed in client mode") \ + ERR_ENTRY(S2N_ERR_CLIENT_MODE_DISABLED, "client connections not allowed") \ + ERR_ENTRY(S2N_ERR_TOO_MANY_CERTIFICATES, "only 1 certificate is supported in client mode") \ + ERR_ENTRY(S2N_ERR_TOO_MANY_SIGNATURE_SCHEMES, "Max supported length of SignatureAlgorithms/SignatureSchemes list is 128") \ + ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_FIPS_MODE, "Client Auth is not supported when in FIPS mode") \ + ERR_ENTRY(S2N_ERR_INVALID_BASE64, "invalid base64 encountered") \ + ERR_ENTRY(S2N_ERR_INVALID_HEX, "invalid HEX encountered") \ + ERR_ENTRY(S2N_ERR_INVALID_PEM, "invalid PEM encountered") \ + ERR_ENTRY(S2N_ERR_DH_PARAMS_CREATE, "error creating Diffie-Hellman parameters") \ + ERR_ENTRY(S2N_ERR_DH_TOO_SMALL, "Diffie-Hellman parameters are too small") \ + ERR_ENTRY(S2N_ERR_DH_PARAMETER_CHECK, "Diffie-Hellman parameter check failed") \ + ERR_ENTRY(S2N_ERR_INVALID_PKCS3, "invalid PKCS3 encountered") \ + ERR_ENTRY(S2N_ERR_NO_CERTIFICATE_IN_PEM, "No certificate in PEM") \ + ERR_ENTRY(S2N_ERR_SERVER_NAME_TOO_LONG, "server name is too long") \ + ERR_ENTRY(S2N_ERR_NUM_DEFAULT_CERTIFICATES, "exceeded max default certificates or provided no default") \ + ERR_ENTRY(S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE, "setting multiple default certificates per auth type is not allowed") \ + ERR_ENTRY(S2N_ERR_INVALID_CIPHER_PREFERENCES, "Invalid Cipher Preferences version") \ + ERR_ENTRY(S2N_ERR_INVALID_APPLICATION_PROTOCOL, "The supplied application protocol name is invalid") \ + ERR_ENTRY(S2N_ERR_KEY_MISMATCH, "public and private key do not match") \ + ERR_ENTRY(S2N_ERR_SEND_SIZE, "Retried s2n_send() size is invalid") \ + ERR_ENTRY(S2N_ERR_CORK_SET_ON_UNMANAGED, "Attempt to set connection cork management on unmanaged IO") \ + ERR_ENTRY(S2N_ERR_UNRECOGNIZED_EXTENSION, "TLS extension not recognized") \ + ERR_ENTRY(S2N_ERR_EXTENSION_NOT_RECEIVED, "The TLS extension was not received") \ + ERR_ENTRY(S2N_ERR_INVALID_SCT_LIST, "SCT list is invalid") \ + ERR_ENTRY(S2N_ERR_INVALID_OCSP_RESPONSE, "OCSP response is invalid") \ + ERR_ENTRY(S2N_ERR_UPDATING_EXTENSION, "Updating extension data failed") \ + ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE, "Serialized session state is not in valid format") \ + ERR_ENTRY(S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG, "Serialized session state is too long") \ + ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_LONG, "Session id is too long") \ + ERR_ENTRY(S2N_ERR_CLIENT_AUTH_NOT_SUPPORTED_IN_SESSION_RESUMPTION_MODE, "Client Auth is not supported in session resumption mode") \ + ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_LENGTH, "Session ticket key length cannot be zero") \ + ERR_ENTRY(S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH, "Session ticket key name should be unique and the name length cannot be zero") \ + ERR_ENTRY(S2N_ERR_TICKET_KEY_NOT_UNIQUE, "Cannot add session ticket key because it was added before") \ + ERR_ENTRY(S2N_ERR_TICKET_KEY_LIMIT, "Limit reached for unexpired session ticket keys") \ + ERR_ENTRY(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY, "No key in encrypt-decrypt state is available to encrypt session ticket") \ + ERR_ENTRY(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED, "Failed to select a key from keys in encrypt-decrypt state") \ + ERR_ENTRY(S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND, "Key used in already assigned session ticket not found for decryption") \ + ERR_ENTRY(S2N_ERR_SENDING_NST, "Error in session ticket status encountered before sending NST") \ + ERR_ENTRY(S2N_ERR_INVALID_DYNAMIC_THRESHOLD, "invalid dynamic record threshold") \ + ERR_ENTRY(S2N_ERR_INVALID_ARGUMENT, "invalid argument provided into a function call") \ + ERR_ENTRY(S2N_ERR_NOT_IN_UNIT_TEST, "Illegal configuration, can only be used during unit tests") \ + ERR_ENTRY(S2N_ERR_NOT_IN_TEST, "Illegal configuration, can only be used during unit or integration tests") \ + ERR_ENTRY(S2N_ERR_UNSUPPORTED_CPU, "Unsupported CPU architecture") \ + ERR_ENTRY(S2N_ERR_SESSION_ID_TOO_SHORT, "Session id is too short") \ + ERR_ENTRY(S2N_ERR_CONNECTION_CACHING_DISALLOWED, "This connection is not allowed to be cached") \ + ERR_ENTRY(S2N_ERR_SESSION_TICKET_NOT_SUPPORTED, "Session ticket not supported for this connection") \ + ERR_ENTRY(S2N_ERR_OCSP_NOT_SUPPORTED, "OCSP stapling was requested, but is not supported") \ + ERR_ENTRY(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES, "Invalid signature algorithms preferences version") \ + ERR_ENTRY(S2N_ERR_RSA_PSS_NOT_SUPPORTED, "RSA-PSS signing not supported by underlying libcrypto implementation") \ + ERR_ENTRY(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE, "Inner plaintext size exceeds limit") \ + ERR_ENTRY(S2N_ERR_INVALID_ECC_PREFERENCES, "Invalid ecc curves preferences version") \ + ERR_ENTRY(S2N_ERR_RECORD_STUFFER_SIZE, "Record stuffer out of space") \ + ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL, "Fragment length is too small") \ + ERR_ENTRY(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE, "Fragment length is too large") \ + ERR_ENTRY(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING, "Record stuffer needs to be drained first") \ + ERR_ENTRY(S2N_ERR_UNSUPPORTED_EXTENSION, "Illegal use of a known, supported extension") \ + ERR_ENTRY(S2N_ERR_MISSING_EXTENSION, "Mandatory extension not received") \ + ERR_ENTRY(S2N_ERR_DUPLICATE_EXTENSION, "Extension block contains two or more extensions of the same type") \ + ERR_ENTRY(S2N_ERR_DEPRECATED_SECURITY_POLICY, "Deprecated security policy. Please choose a different security policy.") \ + ERR_ENTRY(S2N_ERR_INVALID_SECURITY_POLICY, "Invalid security policy") \ + ERR_ENTRY(S2N_ERR_INVALID_KEM_PREFERENCES, "Invalid kem preferences version") \ + ERR_ENTRY(S2N_ERR_INVALID_PARSED_EXTENSIONS, "Invalid parsed extension data") \ + ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_PERFORMED, "Async operation was already performed, cannot perform it again") \ + ERR_ENTRY(S2N_ERR_ASYNC_NOT_PERFORMED, "Async operation is not performed, cannot apply its result") \ + ERR_ENTRY(S2N_ERR_ASYNC_WRONG_CONNECTION, "Async private key operation can only be consumed by connection which initiated it") \ + ERR_ENTRY(S2N_ERR_ASYNC_ALREADY_APPLIED, "Async operation was already applied to connection, cannot apply it again") \ + ERR_ENTRY(S2N_ERR_INVALID_HELLO_RETRY, "Invalid hello retry request") \ + ERR_ENTRY(S2N_ERR_INVALID_STATE, "Invalid state, this is the result of invalid use of an API. Check the API documentation for the function that raised this error for more info") \ + ERR_ENTRY(S2N_ERR_UNSUPPORTED_WITH_QUIC, "Functionality not supported when running with QUIC support enabled") \ + ERR_ENTRY(S2N_ERR_PQ_CRYPTO, "An error occurred in a post-quantum crypto function") \ + ERR_ENTRY(S2N_ERR_DUPLICATE_PSK_IDENTITIES, "The list of pre-shared keys provided contains duplicate psk identities") \ + ERR_ENTRY(S2N_ERR_OFFERED_PSKS_TOO_LONG, "The total pre-shared key data is too long to send over the wire") \ + ERR_ENTRY(S2N_ERR_INVALID_SESSION_TICKET, "Session ticket data is not valid") \ + ERR_ENTRY(S2N_ERR_ZERO_LIFETIME_TICKET, "Calculated session lifetime is zero") \ + ERR_ENTRY(S2N_ERR_REENTRANCY, "Original execution must complete before method can be called again") \ + ERR_ENTRY(S2N_ERR_INVALID_CERT_STATE, "Certificate validation entered an invalid state and is not able to continue") \ + ERR_ENTRY(S2N_ERR_INVALID_SUPPORTED_GROUP_STATE, "SupportedGroup preference decision entered invalid state and selected both KEM and EC Curve") \ + ERR_ENTRY(S2N_ERR_INVALID_EARLY_DATA_STATE, "Early data in invalid state") \ + ERR_ENTRY(S2N_ERR_EARLY_DATA_NOT_ALLOWED, "Early data is not allowed by the connection") \ + ERR_ENTRY(S2N_ERR_NO_CERT_FOUND, "Certificate not found") \ + ERR_ENTRY(S2N_ERR_NO_PRIVATE_KEY, "Certificate found, but no corresponding private key") \ + ERR_ENTRY(S2N_ERR_CERT_NOT_VALIDATED, "Certificate not validated") \ + ERR_ENTRY(S2N_ERR_MAX_EARLY_DATA_SIZE, "Maximum early data bytes exceeded") \ + ERR_ENTRY(S2N_ERR_EARLY_DATA_BLOCKED, "Blocked on early data") \ + ERR_ENTRY(S2N_ERR_PSK_MODE, "Mixing resumption and external PSKs is not supported") \ + ERR_ENTRY(S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND, "X509 extension value not found") \ + ERR_ENTRY(S2N_ERR_INVALID_X509_EXTENSION_TYPE, "Invalid X509 extension type") \ + ERR_ENTRY(S2N_ERR_INSUFFICIENT_MEM_SIZE, "The provided buffer size is not large enough to contain the output data. Try increasing the allocation size.") \ + ERR_ENTRY(S2N_ERR_KEYING_MATERIAL_EXPIRED, "The lifetime of the connection keying material has exceeded the limit. Perform a new full handshake.") \ + ERR_ENTRY(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, "Unable to decrypt rejected early data") \ + ERR_ENTRY(S2N_ERR_PKEY_CTX_INIT, "Unable to initialize the libcrypto pkey context") \ + ERR_ENTRY(S2N_ERR_SECRET_SCHEDULE_STATE, "Correct inputs to secret calculation not available") \ + ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NUMBER_MISMATCH, "The libcrypto major version number seen at compile-time is different from the major version number seen at run-time") \ + ERR_ENTRY(S2N_ERR_LIBCRYPTO_VERSION_NAME_MISMATCH, "The libcrypto major version name seen at compile-time is different from the major version name seen at run-time") \ + ERR_ENTRY(S2N_ERR_OSSL_PROVIDER, "Failed to load or unload an openssl provider") \ + ERR_ENTRY(S2N_ERR_CERT_OWNERSHIP, "The ownership of the certificate chain is incompatible with the operation") \ + ERR_ENTRY(S2N_ERR_INTERNAL_LIBCRYPTO_ERROR, "An internal error has occurred in the libcrypto API") \ + ERR_ENTRY(S2N_ERR_NO_RENEGOTIATION, "Only secure, server-initiated renegotiation is supported") \ + ERR_ENTRY(S2N_ERR_APP_DATA_BLOCKED, "Blocked on application data during handshake") \ + ERR_ENTRY(S2N_ERR_KTLS_MANAGED_IO, "kTLS cannot be enabled while custom I/O is configured for the connection") \ + ERR_ENTRY(S2N_ERR_HANDSHAKE_NOT_COMPLETE, "Operation is only allowed after the handshake is complete") \ + ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, "kTLS is unsupported on this platform") \ + ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \ + ERR_ENTRY(S2N_ERR_KTLS_ENABLE, "An error occurred when attempting to enable kTLS on socket. Ensure the 'tls' kernel module is enabled.") \ + ERR_ENTRY(S2N_ERR_KTLS_BAD_CMSG, "Error handling cmsghdr.") \ + ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \ + ERR_ENTRY(S2N_ERR_TEST_ASSERTION, "Test assertion failed") \ + ERR_ENTRY(S2N_ERR_KTLS_RENEG, "kTLS does not support secure renegotiation") \ + ERR_ENTRY(S2N_ERR_KTLS_KEYUPDATE, "Received KeyUpdate from peer, but kernel does not support updating tls keys") \ + ERR_ENTRY(S2N_ERR_KTLS_KEY_LIMIT, "Reached key encryption limit, but kernel does not support updating tls keys") \ + ERR_ENTRY(S2N_ERR_KTLS_SOCKOPT, "A call to sockopt failed when attempting to update the keys in the kernel") \ + ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client forbids mutual authentication, but server requested a cert") \ + ERR_ENTRY(S2N_ERR_MISSING_CERT_REQUEST, "Client requires mutual authentication, but server did not request a cert") \ + ERR_ENTRY(S2N_ERR_MISSING_CLIENT_CERT, "Server requires client certificate") \ + ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ + ERR_ENTRY(S2N_ERR_TOO_MANY_CAS, "Too many certificate authorities in trust store"); \ + ERR_ENTRY(S2N_ERR_BAD_HEX, "Could not parse malformed hex string"); \ + ERR_ENTRY(S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK, "Config set to NULL before client hello callback. This should not be possible outside of tests."); \ + ERR_ENTRY(S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO, "The invoked s2n-tls API is not supported by the libcrypto"); \ + ERR_ENTRY(S2N_ERR_FIPS_MODE_UNSUPPORTED, "FIPS mode is not supported for the libcrypto"); \ + /* clang-format on */ + +#define ERR_STR_CASE(ERR, str) \ + case ERR: \ + return str; +#define ERR_NAME_CASE(ERR, str) \ + case ERR: \ + return #ERR; + +const char *s2n_strerror(int error, const char *lang) +{ + if (lang == NULL) { + lang = "EN"; + } + + if (strcasecmp(lang, "EN")) { + return no_such_language; + } + + s2n_error err = error; + switch (err) { + ERR_ENTRIES(ERR_STR_CASE) + + /* Skip block ends */ + case S2N_ERR_T_OK_END: + case S2N_ERR_T_IO_END: + case S2N_ERR_T_CLOSED_END: + case S2N_ERR_T_BLOCKED_END: + case S2N_ERR_T_ALERT_END: + case S2N_ERR_T_PROTO_END: + case S2N_ERR_T_INTERNAL_END: + case S2N_ERR_T_USAGE_END: + break; + + /* No default to make compiler fail on missing values */ + } + + return no_such_error; +} + +const char *s2n_strerror_name(int error) +{ + s2n_error err = error; + switch (err) { + ERR_ENTRIES(ERR_NAME_CASE) + + /* Skip block ends */ + case S2N_ERR_T_OK_END: + case S2N_ERR_T_IO_END: + case S2N_ERR_T_CLOSED_END: + case S2N_ERR_T_BLOCKED_END: + case S2N_ERR_T_ALERT_END: + case S2N_ERR_T_PROTO_END: + case S2N_ERR_T_INTERNAL_END: + case S2N_ERR_T_USAGE_END: + break; + + /* No default to make compiler fail on missing values */ + } + + return no_such_error; +} + +const char *s2n_strerror_debug(int error, const char *lang) +{ + if (lang == NULL) { + lang = "EN"; + } + + if (strcasecmp(lang, "EN")) { + return no_such_language; + } + + /* No error, just return the no error string */ + if (error == S2N_ERR_OK) { + return s2n_strerror(error, lang); + } + + return _s2n_debug_info.debug_str; +} + +const char *s2n_strerror_source(int error) +{ + /* No error, just return the no error string */ + if (error == S2N_ERR_OK) { + return s2n_strerror(error, "EN"); + } + + return _s2n_debug_info.source; +} + +int s2n_error_get_type(int error) +{ + return (error >> S2N_ERR_NUM_VALUE_BITS); +} + +/* https://www.gnu.org/software/libc/manual/html_node/Backtraces.html */ +static bool s_s2n_stack_traces_enabled = false; + +bool s2n_stack_traces_enabled() +{ + return s_s2n_stack_traces_enabled; +} + +int s2n_stack_traces_enabled_set(bool newval) +{ + s_s2n_stack_traces_enabled = newval; + return S2N_SUCCESS; +} + +void s2n_debug_info_reset(void) +{ + _s2n_debug_info.debug_str = ""; + _s2n_debug_info.source = ""; +} + +#ifdef S2N_STACKTRACE + + #define MAX_BACKTRACE_DEPTH 20 +__thread struct s2n_stacktrace tl_stacktrace = { 0 }; + +int s2n_free_stacktrace(void) +{ + if (tl_stacktrace.trace != NULL) { + free(tl_stacktrace.trace); + struct s2n_stacktrace zero_stacktrace = { 0 }; + tl_stacktrace = zero_stacktrace; + } + return S2N_SUCCESS; +} + +int s2n_calculate_stacktrace(void) +{ + if (!s_s2n_stack_traces_enabled) { + return S2N_SUCCESS; + } + + int old_errno = errno; + POSIX_GUARD(s2n_free_stacktrace()); + void *array[MAX_BACKTRACE_DEPTH]; + tl_stacktrace.trace_size = backtrace(array, MAX_BACKTRACE_DEPTH); + tl_stacktrace.trace = backtrace_symbols(array, tl_stacktrace.trace_size); + errno = old_errno; + return S2N_SUCCESS; +} + +int s2n_get_stacktrace(struct s2n_stacktrace *trace) +{ + *trace = tl_stacktrace; + return S2N_SUCCESS; +} + +int s2n_print_stacktrace(FILE *fptr) +{ + if (!s_s2n_stack_traces_enabled) { + fprintf(fptr, "%s\n%s\n", + "NOTE: Some details are omitted, run with S2N_PRINT_STACKTRACE=1 for a verbose backtrace.", + "See https://github.com/aws/s2n-tls/blob/main/docs/usage-guide"); + return S2N_SUCCESS; + } + + fprintf(fptr, "\nStacktrace is:\n"); + for (int i = 0; i < tl_stacktrace.trace_size; ++i) { + fprintf(fptr, "%s\n", tl_stacktrace.trace[i]); + } + return S2N_SUCCESS; +} + +#else /* !S2N_STACKTRACE */ +int s2n_free_stacktrace(void) +{ + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_calculate_stacktrace(void) +{ + if (!s_s2n_stack_traces_enabled) { + return S2N_SUCCESS; + } + + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_get_stacktrace(struct s2n_stacktrace *trace) +{ + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_print_stacktrace(FILE *fptr) +{ + S2N_ERROR(S2N_ERR_UNIMPLEMENTED); +} +#endif /* S2N_STACKTRACE */ diff --git a/stuffer/s2n_stuffer.c b/stuffer/s2n_stuffer.c index f9f3ff178d9..0cf0fdf60cf 100644 --- a/stuffer/s2n_stuffer.c +++ b/stuffer/s2n_stuffer.c @@ -1,470 +1,470 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "stuffer/s2n_stuffer.h" - -#include "error/s2n_errno.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer) -{ - /** - * Note that we do not assert any properties on the tainted field, - * as any boolean value in that field is valid. - */ - RESULT_ENSURE_REF(stuffer); - RESULT_GUARD(s2n_blob_validate(&stuffer->blob)); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(stuffer->growable, stuffer->alloced), S2N_ERR_SAFETY); - - /* <= is valid because we can have a fully written/read stuffer */ - RESULT_DEBUG_ENSURE(stuffer->high_water_mark <= stuffer->blob.size, S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(stuffer->write_cursor <= stuffer->high_water_mark, S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(stuffer->read_cursor <= stuffer->write_cursor, S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_stuffer_reservation_validate(const struct s2n_stuffer_reservation *reservation) -{ - /** - * Note that we need two dereferences here to decrease proof complexity - * for CBMC (see https://github.com/awslabs/s2n/issues/2290). We can roll back - * this change once CBMC can handle common subexpression elimination. - */ - RESULT_ENSURE_REF(reservation); - const struct s2n_stuffer_reservation reserve_obj = *reservation; - RESULT_GUARD(s2n_stuffer_validate(reserve_obj.stuffer)); - const struct s2n_stuffer stuffer_obj = *(reserve_obj.stuffer); - - /* Verify that write_cursor + length can be represented as a uint32_t without overflow */ - RESULT_ENSURE_LTE(reserve_obj.write_cursor, UINT32_MAX - reserve_obj.length); - /* The entire reservation must fit between the stuffer read and write cursors */ - RESULT_ENSURE_LTE(reserve_obj.write_cursor + reserve_obj.length, stuffer_obj.write_cursor); - RESULT_ENSURE_GTE(reserve_obj.write_cursor, stuffer_obj.read_cursor); - - return S2N_RESULT_OK; -} - -/** - * Initialize a stuffer to reference some data `in`. - * - * `stuffer` will not own the data, and the caller is responsible for ensuring that - * the data pointed to by `in` outlives `stuffer`. - */ -int s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in) -{ - POSIX_ENSURE_MUT(stuffer); - POSIX_PRECONDITION(s2n_blob_validate(in)); - stuffer->blob = *in; - stuffer->read_cursor = 0; - stuffer->write_cursor = 0; - stuffer->high_water_mark = 0; - stuffer->alloced = 0; - stuffer->growable = 0; - stuffer->tainted = 0; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in) -{ - POSIX_ENSURE_REF(in); - POSIX_GUARD(s2n_stuffer_init(stuffer, in)); - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, in->size)); - return S2N_SUCCESS; -} - -int s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_ENSURE_REF(stuffer); - *stuffer = (struct s2n_stuffer){ 0 }; - POSIX_GUARD(s2n_alloc(&stuffer->blob, size)); - POSIX_GUARD(s2n_stuffer_init(stuffer, &stuffer->blob)); - - stuffer->alloced = 1; - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_GUARD(s2n_stuffer_alloc(stuffer, size)); - - stuffer->growable = 1; - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_free(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (stuffer->alloced) { - POSIX_GUARD(s2n_free(&stuffer->blob)); - } - *stuffer = (struct s2n_stuffer){ 0 }; - return S2N_SUCCESS; -} - -int s2n_stuffer_free_without_wipe(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (stuffer->alloced) { - POSIX_GUARD(s2n_free_without_wipe(&stuffer->blob)); - } - *stuffer = (struct s2n_stuffer){ 0 }; - return S2N_SUCCESS; -} - -int s2n_stuffer_resize(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); - POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); - - if (size == stuffer->blob.size) { - return S2N_SUCCESS; - } - - if (size == 0) { - s2n_stuffer_wipe(stuffer); - return s2n_free(&stuffer->blob); - } - - if (size < stuffer->blob.size) { - POSIX_CHECKED_MEMSET(stuffer->blob.data + size, S2N_WIPE_PATTERN, (stuffer->blob.size - size)); - if (stuffer->read_cursor > size) { - stuffer->read_cursor = size; - } - if (stuffer->write_cursor > size) { - stuffer->write_cursor = size; - } - if (stuffer->high_water_mark > size) { - stuffer->high_water_mark = size; - } - stuffer->blob.size = size; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; - } - - POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_resize_if_empty(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (stuffer->blob.data == NULL) { - POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); - POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); - POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); - } - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -/** - * Reset read and write progress. - * - * This sets the read and write cursors to zero. - */ -int s2n_stuffer_rewrite(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - stuffer->write_cursor = 0; - stuffer->read_cursor = 0; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_rewind_read(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE(stuffer->read_cursor >= size, S2N_ERR_STUFFER_OUT_OF_DATA); - stuffer->read_cursor -= size; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -/** - * Reset read progress. - * - * This sets the read cursor to zero. - */ -int s2n_stuffer_reread(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - stuffer->read_cursor = 0; - return S2N_SUCCESS; -} - -int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - uint32_t wipe_size = S2N_MIN(size, stuffer->write_cursor); - - stuffer->write_cursor -= wipe_size; - stuffer->read_cursor = S2N_MIN(stuffer->read_cursor, stuffer->write_cursor); - POSIX_CHECKED_MEMSET(stuffer->blob.data + stuffer->write_cursor, S2N_WIPE_PATTERN, wipe_size); - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer) -{ - return stuffer && (stuffer->read_cursor == stuffer->write_cursor) && !stuffer->tainted; -} - -int s2n_stuffer_wipe(struct s2n_stuffer *stuffer) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (!s2n_stuffer_is_wiped(stuffer)) { - POSIX_CHECKED_MEMSET(stuffer->blob.data, S2N_WIPE_PATTERN, stuffer->high_water_mark); - } - - stuffer->tainted = 0; - stuffer->write_cursor = 0; - stuffer->read_cursor = 0; - stuffer->high_water_mark = 0; - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_skip_read(struct s2n_stuffer *stuffer, uint32_t n) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE(s2n_stuffer_data_available(stuffer) >= n, S2N_ERR_STUFFER_OUT_OF_DATA); - - stuffer->read_cursor += n; - return S2N_SUCCESS; -} - -void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len) -{ - PTR_GUARD_POSIX(s2n_stuffer_skip_read(stuffer, data_len)); - - stuffer->tainted = 1; - - return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - data_len) : NULL; -} - -int s2n_stuffer_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(out); - - return s2n_stuffer_read_bytes(stuffer, out->data, out->size); -} - -int s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) -{ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, out->size)); - - void *ptr = (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - out->size) : NULL; - POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, out->size), S2N_ERR_NULL); - - POSIX_CHECKED_MEMCPY(out->data, ptr, out->size); - POSIX_CHECKED_MEMSET(ptr, 0, out->size); - - return S2N_SUCCESS; -} - -int s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) -{ - POSIX_ENSURE_REF(data); - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); - POSIX_ENSURE_REF(stuffer->blob.data); - void *ptr = stuffer->blob.data + stuffer->read_cursor - size; - - POSIX_CHECKED_MEMCPY(data, ptr, size); - - return S2N_SUCCESS; -} - -int s2n_stuffer_erase_and_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) -{ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); - POSIX_ENSURE_REF(stuffer->blob.data); - void *ptr = stuffer->blob.data + stuffer->read_cursor - size; - - POSIX_CHECKED_MEMCPY(data, ptr, size); - POSIX_CHECKED_MEMSET(ptr, 0, size); - - return S2N_SUCCESS; -} - -int s2n_stuffer_skip_write(struct s2n_stuffer *stuffer, const uint32_t n) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, n)); - stuffer->write_cursor += n; - stuffer->high_water_mark = S2N_MAX(stuffer->write_cursor, stuffer->high_water_mark); - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -void *s2n_stuffer_raw_write(struct s2n_stuffer *stuffer, const uint32_t data_len) -{ - PTR_GUARD_POSIX(s2n_stuffer_skip_write(stuffer, data_len)); - - stuffer->tainted = 1; - - return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->write_cursor - data_len) : NULL; -} - -int s2n_stuffer_write(struct s2n_stuffer *stuffer, const struct s2n_blob *in) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_PRECONDITION(s2n_blob_validate(in)); - return s2n_stuffer_write_bytes(stuffer, in->data, in->size); -} - -int s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *data, const uint32_t size) -{ - if (size == 0) { - return S2N_SUCCESS; - } - - POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, size)); - - void *ptr = stuffer->blob.data + stuffer->write_cursor - size; - POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); - - if (ptr == data) { - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; - } - - POSIX_CHECKED_MEMCPY(ptr, data, size); - - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -int s2n_stuffer_writev_bytes(struct s2n_stuffer *stuffer, const struct iovec *iov, size_t iov_count, uint32_t offs, - uint32_t size) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE_REF(iov); - void *ptr = s2n_stuffer_raw_write(stuffer, size); - POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); - - size_t size_left = size, to_skip = offs; - for (size_t i = 0; i < iov_count; i++) { - if (to_skip >= iov[i].iov_len) { - to_skip -= iov[i].iov_len; - continue; - } - size_t iov_len_op = iov[i].iov_len - to_skip; - POSIX_ENSURE_LTE(iov_len_op, UINT32_MAX); - uint32_t iov_len = (uint32_t) iov_len_op; - uint32_t iov_size_to_take = S2N_MIN(size_left, iov_len); - POSIX_ENSURE_REF(iov[i].iov_base); - POSIX_ENSURE_LT(to_skip, iov[i].iov_len); - POSIX_CHECKED_MEMCPY(ptr, ((uint8_t *) (iov[i].iov_base)) + to_skip, iov_size_to_take); - size_left -= iov_size_to_take; - if (size_left == 0) { - break; - } - ptr = (void *) ((uint8_t *) ptr + iov_size_to_take); - to_skip = 0; - } - - return S2N_SUCCESS; -} - -static int s2n_stuffer_copy_impl(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) -{ - POSIX_GUARD(s2n_stuffer_skip_read(from, len)); - POSIX_GUARD(s2n_stuffer_skip_write(to, len)); - - uint8_t *from_ptr = (from->blob.data) ? (from->blob.data + from->read_cursor - len) : NULL; - uint8_t *to_ptr = (to->blob.data) ? (to->blob.data + to->write_cursor - len) : NULL; - - POSIX_CHECKED_MEMCPY(to_ptr, from_ptr, len); - - return S2N_SUCCESS; -} - -int s2n_stuffer_reserve_space(struct s2n_stuffer *stuffer, uint32_t n) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - if (s2n_stuffer_space_remaining(stuffer) < n) { - POSIX_ENSURE(stuffer->growable, S2N_ERR_STUFFER_IS_FULL); - /* Always grow a stuffer by at least 1k */ - const uint32_t growth = S2N_MAX(n - s2n_stuffer_space_remaining(stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES); - uint32_t new_size = 0; - POSIX_GUARD(s2n_add_overflow(stuffer->blob.size, growth, &new_size)); - POSIX_GUARD(s2n_stuffer_resize(stuffer, new_size)); - } - POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); - return S2N_SUCCESS; -} - -/* Copies "len" bytes from "from" to "to". - * If the copy cannot succeed (i.e. there are either not enough bytes available, or there is not enough space to write them - * restore the old value of the stuffer */ -int s2n_stuffer_copy(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) -{ - const uint32_t orig_read_cursor = from->read_cursor; - const uint32_t orig_write_cursor = to->write_cursor; - - if (s2n_stuffer_copy_impl(from, to, len) < 0) { - from->read_cursor = orig_read_cursor; - to->write_cursor = orig_write_cursor; - S2N_ERROR_PRESERVE_ERRNO(); - } - - return S2N_SUCCESS; -} - -int s2n_stuffer_extract_blob(struct s2n_stuffer *stuffer, struct s2n_blob *out) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - POSIX_ENSURE_REF(out); - POSIX_GUARD(s2n_realloc(out, s2n_stuffer_data_available(stuffer))); - - if (s2n_stuffer_data_available(stuffer) > 0) { - POSIX_CHECKED_MEMCPY(out->data, stuffer->blob.data + stuffer->read_cursor, s2n_stuffer_data_available(stuffer)); - } - - POSIX_POSTCONDITION(s2n_blob_validate(out)); - return S2N_SUCCESS; -} - -int s2n_stuffer_shift(struct s2n_stuffer *stuffer) -{ - POSIX_ENSURE_REF(stuffer); - struct s2n_stuffer copy = *stuffer; - POSIX_GUARD(s2n_stuffer_rewrite(©)); - - uint8_t *data = stuffer->blob.data; - /* Adding 0 to a NULL value is undefined behavior */ - if (stuffer->read_cursor != 0) { - data += stuffer->read_cursor; - } - - uint32_t data_size = s2n_stuffer_data_available(stuffer); - POSIX_GUARD(s2n_stuffer_write_bytes(©, data, data_size)); - *stuffer = copy; - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "stuffer/s2n_stuffer.h" + +#include "error/s2n_errno.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer) +{ + /** + * Note that we do not assert any properties on the tainted field, + * as any boolean value in that field is valid. + */ + RESULT_ENSURE_REF(stuffer); + RESULT_GUARD(s2n_blob_validate(&stuffer->blob)); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(stuffer->growable, stuffer->alloced), S2N_ERR_SAFETY); + + /* <= is valid because we can have a fully written/read stuffer */ + RESULT_DEBUG_ENSURE(stuffer->high_water_mark <= stuffer->blob.size, S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(stuffer->write_cursor <= stuffer->high_water_mark, S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(stuffer->read_cursor <= stuffer->write_cursor, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_stuffer_reservation_validate(const struct s2n_stuffer_reservation *reservation) +{ + /** + * Note that we need two dereferences here to decrease proof complexity + * for CBMC (see https://github.com/awslabs/s2n/issues/2290). We can roll back + * this change once CBMC can handle common subexpression elimination. + */ + RESULT_ENSURE_REF(reservation); + const struct s2n_stuffer_reservation reserve_obj = *reservation; + RESULT_GUARD(s2n_stuffer_validate(reserve_obj.stuffer)); + const struct s2n_stuffer stuffer_obj = *(reserve_obj.stuffer); + + /* Verify that write_cursor + length can be represented as a uint32_t without overflow */ + RESULT_ENSURE_LTE(reserve_obj.write_cursor, UINT32_MAX - reserve_obj.length); + /* The entire reservation must fit between the stuffer read and write cursors */ + RESULT_ENSURE_LTE(reserve_obj.write_cursor + reserve_obj.length, stuffer_obj.write_cursor); + RESULT_ENSURE_GTE(reserve_obj.write_cursor, stuffer_obj.read_cursor); + + return S2N_RESULT_OK; +} + +/** + * Initialize a stuffer to reference some data `in`. + * + * `stuffer` will not own the data, and the caller is responsible for ensuring that + * the data pointed to by `in` outlives `stuffer`. + */ +int s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in) +{ + POSIX_ENSURE_MUT(stuffer); + POSIX_PRECONDITION(s2n_blob_validate(in)); + stuffer->blob = *in; + stuffer->read_cursor = 0; + stuffer->write_cursor = 0; + stuffer->high_water_mark = 0; + stuffer->alloced = 0; + stuffer->growable = 0; + stuffer->tainted = 0; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in) +{ + POSIX_ENSURE_REF(in); + POSIX_GUARD(s2n_stuffer_init(stuffer, in)); + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, in->size)); + return S2N_SUCCESS; +} + +int s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_ENSURE_REF(stuffer); + *stuffer = (struct s2n_stuffer){ 0 }; + POSIX_GUARD(s2n_alloc(&stuffer->blob, size)); + POSIX_GUARD(s2n_stuffer_init(stuffer, &stuffer->blob)); + + stuffer->alloced = 1; + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_GUARD(s2n_stuffer_alloc(stuffer, size)); + + stuffer->growable = 1; + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_free(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (stuffer->alloced) { + POSIX_GUARD(s2n_free(&stuffer->blob)); + } + *stuffer = (struct s2n_stuffer){ 0 }; + return S2N_SUCCESS; +} + +int s2n_stuffer_free_without_wipe(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (stuffer->alloced) { + POSIX_GUARD(s2n_free_without_wipe(&stuffer->blob)); + } + *stuffer = (struct s2n_stuffer){ 0 }; + return S2N_SUCCESS; +} + +int s2n_stuffer_resize(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); + POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); + + if (size == stuffer->blob.size) { + return S2N_SUCCESS; + } + + if (size == 0) { + s2n_stuffer_wipe(stuffer); + return s2n_free(&stuffer->blob); + } + + if (size < stuffer->blob.size) { + POSIX_CHECKED_MEMSET(stuffer->blob.data + size, S2N_WIPE_PATTERN, (stuffer->blob.size - size)); + if (stuffer->read_cursor > size) { + stuffer->read_cursor = size; + } + if (stuffer->write_cursor > size) { + stuffer->write_cursor = size; + } + if (stuffer->high_water_mark > size) { + stuffer->high_water_mark = size; + } + stuffer->blob.size = size; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; + } + + POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_resize_if_empty(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (stuffer->blob.data == NULL) { + POSIX_ENSURE(!stuffer->tainted, S2N_ERR_RESIZE_TAINTED_STUFFER); + POSIX_ENSURE(stuffer->growable, S2N_ERR_RESIZE_STATIC_STUFFER); + POSIX_GUARD(s2n_realloc(&stuffer->blob, size)); + } + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +/** + * Reset read and write progress. + * + * This sets the read and write cursors to zero. + */ +int s2n_stuffer_rewrite(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + stuffer->write_cursor = 0; + stuffer->read_cursor = 0; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_rewind_read(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE(stuffer->read_cursor >= size, S2N_ERR_STUFFER_OUT_OF_DATA); + stuffer->read_cursor -= size; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +/** + * Reset read progress. + * + * This sets the read cursor to zero. + */ +int s2n_stuffer_reread(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + stuffer->read_cursor = 0; + return S2N_SUCCESS; +} + +int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + uint32_t wipe_size = S2N_MIN(size, stuffer->write_cursor); + + stuffer->write_cursor -= wipe_size; + stuffer->read_cursor = S2N_MIN(stuffer->read_cursor, stuffer->write_cursor); + POSIX_CHECKED_MEMSET(stuffer->blob.data + stuffer->write_cursor, S2N_WIPE_PATTERN, wipe_size); + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer) +{ + return stuffer && (stuffer->read_cursor == stuffer->write_cursor) && !stuffer->tainted; +} + +int s2n_stuffer_wipe(struct s2n_stuffer *stuffer) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (!s2n_stuffer_is_wiped(stuffer)) { + POSIX_CHECKED_MEMSET(stuffer->blob.data, S2N_WIPE_PATTERN, stuffer->high_water_mark); + } + + stuffer->tainted = 0; + stuffer->write_cursor = 0; + stuffer->read_cursor = 0; + stuffer->high_water_mark = 0; + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_skip_read(struct s2n_stuffer *stuffer, uint32_t n) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE(s2n_stuffer_data_available(stuffer) >= n, S2N_ERR_STUFFER_OUT_OF_DATA); + + stuffer->read_cursor += n; + return S2N_SUCCESS; +} + +void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len) +{ + PTR_GUARD_POSIX(s2n_stuffer_skip_read(stuffer, data_len)); + + stuffer->tainted = 1; + + return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - data_len) : NULL; +} + +int s2n_stuffer_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(out); + + return s2n_stuffer_read_bytes(stuffer, out->data, out->size); +} + +int s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out) +{ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, out->size)); + + void *ptr = (stuffer->blob.data) ? (stuffer->blob.data + stuffer->read_cursor - out->size) : NULL; + POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, out->size), S2N_ERR_NULL); + + POSIX_CHECKED_MEMCPY(out->data, ptr, out->size); + POSIX_CHECKED_MEMSET(ptr, 0, out->size); + + return S2N_SUCCESS; +} + +int s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) +{ + POSIX_ENSURE_REF(data); + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); + POSIX_ENSURE_REF(stuffer->blob.data); + void *ptr = stuffer->blob.data + stuffer->read_cursor - size; + + POSIX_CHECKED_MEMCPY(data, ptr, size); + + return S2N_SUCCESS; +} + +int s2n_stuffer_erase_and_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size) +{ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, size)); + POSIX_ENSURE_REF(stuffer->blob.data); + void *ptr = stuffer->blob.data + stuffer->read_cursor - size; + + POSIX_CHECKED_MEMCPY(data, ptr, size); + POSIX_CHECKED_MEMSET(ptr, 0, size); + + return S2N_SUCCESS; +} + +int s2n_stuffer_skip_write(struct s2n_stuffer *stuffer, const uint32_t n) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, n)); + stuffer->write_cursor += n; + stuffer->high_water_mark = S2N_MAX(stuffer->write_cursor, stuffer->high_water_mark); + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +void *s2n_stuffer_raw_write(struct s2n_stuffer *stuffer, const uint32_t data_len) +{ + PTR_GUARD_POSIX(s2n_stuffer_skip_write(stuffer, data_len)); + + stuffer->tainted = 1; + + return (stuffer->blob.data) ? (stuffer->blob.data + stuffer->write_cursor - data_len) : NULL; +} + +int s2n_stuffer_write(struct s2n_stuffer *stuffer, const struct s2n_blob *in) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_PRECONDITION(s2n_blob_validate(in)); + return s2n_stuffer_write_bytes(stuffer, in->data, in->size); +} + +int s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *data, const uint32_t size) +{ + if (size == 0) { + return S2N_SUCCESS; + } + + POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, size)); + + void *ptr = stuffer->blob.data + stuffer->write_cursor - size; + POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); + + if (ptr == data) { + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; + } + + POSIX_CHECKED_MEMCPY(ptr, data, size); + + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +int s2n_stuffer_writev_bytes(struct s2n_stuffer *stuffer, const struct iovec *iov, size_t iov_count, uint32_t offs, + uint32_t size) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE_REF(iov); + void *ptr = s2n_stuffer_raw_write(stuffer, size); + POSIX_ENSURE(S2N_MEM_IS_READABLE(ptr, size), S2N_ERR_NULL); + + size_t size_left = size, to_skip = offs; + for (size_t i = 0; i < iov_count; i++) { + if (to_skip >= iov[i].iov_len) { + to_skip -= iov[i].iov_len; + continue; + } + size_t iov_len_op = iov[i].iov_len - to_skip; + POSIX_ENSURE_LTE(iov_len_op, UINT32_MAX); + uint32_t iov_len = (uint32_t) iov_len_op; + uint32_t iov_size_to_take = S2N_MIN(size_left, iov_len); + POSIX_ENSURE_REF(iov[i].iov_base); + POSIX_ENSURE_LT(to_skip, iov[i].iov_len); + POSIX_CHECKED_MEMCPY(ptr, ((uint8_t *) (iov[i].iov_base)) + to_skip, iov_size_to_take); + size_left -= iov_size_to_take; + if (size_left == 0) { + break; + } + ptr = (void *) ((uint8_t *) ptr + iov_size_to_take); + to_skip = 0; + } + + return S2N_SUCCESS; +} + +static int s2n_stuffer_copy_impl(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) +{ + POSIX_GUARD(s2n_stuffer_skip_read(from, len)); + POSIX_GUARD(s2n_stuffer_skip_write(to, len)); + + uint8_t *from_ptr = (from->blob.data) ? (from->blob.data + from->read_cursor - len) : NULL; + uint8_t *to_ptr = (to->blob.data) ? (to->blob.data + to->write_cursor - len) : NULL; + + POSIX_CHECKED_MEMCPY(to_ptr, from_ptr, len); + + return S2N_SUCCESS; +} + +int s2n_stuffer_reserve_space(struct s2n_stuffer *stuffer, uint32_t n) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + if (s2n_stuffer_space_remaining(stuffer) < n) { + POSIX_ENSURE(stuffer->growable, S2N_ERR_STUFFER_IS_FULL); + /* Always grow a stuffer by at least 1k */ + const uint32_t growth = S2N_MAX(n - s2n_stuffer_space_remaining(stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES); + uint32_t new_size = 0; + POSIX_GUARD(s2n_add_overflow(stuffer->blob.size, growth, &new_size)); + POSIX_GUARD(s2n_stuffer_resize(stuffer, new_size)); + } + POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer)); + return S2N_SUCCESS; +} + +/* Copies "len" bytes from "from" to "to". + * If the copy cannot succeed (i.e. there are either not enough bytes available, or there is not enough space to write them + * restore the old value of the stuffer */ +int s2n_stuffer_copy(struct s2n_stuffer *from, struct s2n_stuffer *to, const uint32_t len) +{ + const uint32_t orig_read_cursor = from->read_cursor; + const uint32_t orig_write_cursor = to->write_cursor; + + if (s2n_stuffer_copy_impl(from, to, len) < 0) { + from->read_cursor = orig_read_cursor; + to->write_cursor = orig_write_cursor; + S2N_ERROR_PRESERVE_ERRNO(); + } + + return S2N_SUCCESS; +} + +int s2n_stuffer_extract_blob(struct s2n_stuffer *stuffer, struct s2n_blob *out) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + POSIX_ENSURE_REF(out); + POSIX_GUARD(s2n_realloc(out, s2n_stuffer_data_available(stuffer))); + + if (s2n_stuffer_data_available(stuffer) > 0) { + POSIX_CHECKED_MEMCPY(out->data, stuffer->blob.data + stuffer->read_cursor, s2n_stuffer_data_available(stuffer)); + } + + POSIX_POSTCONDITION(s2n_blob_validate(out)); + return S2N_SUCCESS; +} + +int s2n_stuffer_shift(struct s2n_stuffer *stuffer) +{ + POSIX_ENSURE_REF(stuffer); + struct s2n_stuffer copy = *stuffer; + POSIX_GUARD(s2n_stuffer_rewrite(©)); + + uint8_t *data = stuffer->blob.data; + /* Adding 0 to a NULL value is undefined behavior */ + if (stuffer->read_cursor != 0) { + data += stuffer->read_cursor; + } + + uint32_t data_size = s2n_stuffer_data_available(stuffer); + POSIX_GUARD(s2n_stuffer_write_bytes(©, data, data_size)); + *stuffer = copy; + return S2N_SUCCESS; +} diff --git a/stuffer/s2n_stuffer.h b/stuffer/s2n_stuffer.h index 6986d14ee42..b03888e3fee 100644 --- a/stuffer/s2n_stuffer.h +++ b/stuffer/s2n_stuffer.h @@ -1,242 +1,242 @@ -/* - * 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 - -#include -#include -#include -#include -#if defined(_MSC_VER) || defined(__MINGW32__) -#include -#else -#include -#endif - -#include "utils/s2n_blob.h" -#include "utils/s2n_result.h" - -#define S2N_MIN_STUFFER_GROWTH_IN_BYTES 1024 - -/* Using a non-zero value - * (a) makes wiped data easy to see in the debugger - * (b) makes use of wiped data obvious since this is unlikely to be a valid bit pattern - */ -#define S2N_WIPE_PATTERN 'w' - -#define SIZEOF_IN_BITS(t) (sizeof(t) * CHAR_BIT) - -#define SIZEOF_UINT24 3 - -struct s2n_stuffer { - /* The data for the s2n_stuffer */ - struct s2n_blob blob; - - /* Cursors to the current read/write position in the s2n_stuffer */ - uint32_t read_cursor; - uint32_t write_cursor; - uint32_t high_water_mark; - - /* Was this stuffer alloc()'d? - * This field controls whether the stuffer "owns" the blob. If the stuffer - * was allocated, then `blob` must be freed when the stuffer is freed. If the - * stuffer was not allocated, then the blob must not be freed by the stuffer, even if the - * blob itself is allocated. */ - unsigned int alloced : 1; - - /* Is this stuffer growable? */ - unsigned int growable : 1; - - /* Can this stuffer be safely resized? - * A growable stuffer can be temporarily tainted by a raw read/write, - * preventing it from resizing. */ - unsigned int tainted : 1; -}; - -#define s2n_stuffer_data_available(s) ((s)->write_cursor - (s)->read_cursor) -#define s2n_stuffer_space_remaining(s) ((s)->blob.size - (s)->write_cursor) -#define s2n_stuffer_is_wiped(s) ((s)->high_water_mark == 0) -#define s2n_stuffer_is_freed(s) ((s)->blob.data == NULL) -/* Check basic validity constraints on the stuffer: e.g. that cursors point within the blob */ -S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer); - -/* Initialize and destroying stuffers */ -int S2N_RESULT_MUST_USE s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in); -int S2N_RESULT_MUST_USE s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in); -int S2N_RESULT_MUST_USE s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size); -int S2N_RESULT_MUST_USE s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size); -int s2n_stuffer_free(struct s2n_stuffer *stuffer); -/** - * Frees the stuffer without zeroizing the contained data. - * - * This should only be used in scenarios where the data is encrypted or has been - * cleared with `s2n_stuffer_erase_and_read`. In most cases, prefer `s2n_stuffer_free`. - */ -int S2N_RESULT_MUST_USE s2n_stuffer_free_without_wipe(struct s2n_stuffer *stuffer); -int S2N_RESULT_MUST_USE s2n_stuffer_resize(struct s2n_stuffer *stuffer, const uint32_t size); -int S2N_RESULT_MUST_USE s2n_stuffer_resize_if_empty(struct s2n_stuffer *stuffer, const uint32_t size); -int S2N_RESULT_MUST_USE s2n_stuffer_rewind_read(struct s2n_stuffer *stuffer, const uint32_t size); -int S2N_RESULT_MUST_USE s2n_stuffer_reread(struct s2n_stuffer *stuffer); -int S2N_RESULT_MUST_USE s2n_stuffer_rewrite(struct s2n_stuffer *stuffer); -int S2N_RESULT_MUST_USE s2n_stuffer_shift(struct s2n_stuffer *stuffer); -int s2n_stuffer_wipe(struct s2n_stuffer *stuffer); -int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t n); -bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer); - -/* Basic read and write */ -int S2N_RESULT_MUST_USE s2n_stuffer_read(struct s2n_stuffer *stuffer, struct s2n_blob *out); -int S2N_RESULT_MUST_USE s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out); -int S2N_RESULT_MUST_USE s2n_stuffer_write(struct s2n_stuffer *stuffer, const struct s2n_blob *in); -int S2N_RESULT_MUST_USE s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *out, uint32_t n); -int S2N_RESULT_MUST_USE s2n_stuffer_erase_and_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size); -int S2N_RESULT_MUST_USE s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *in, const uint32_t n); -int S2N_RESULT_MUST_USE s2n_stuffer_writev_bytes(struct s2n_stuffer *stuffer, const struct iovec *iov, size_t iov_count, - uint32_t offs, uint32_t size); -int S2N_RESULT_MUST_USE s2n_stuffer_skip_read(struct s2n_stuffer *stuffer, uint32_t n); -int S2N_RESULT_MUST_USE s2n_stuffer_skip_write(struct s2n_stuffer *stuffer, const uint32_t n); - -/* Tries to reserve enough space to write n additional bytes into the stuffer.*/ -int S2N_RESULT_MUST_USE s2n_stuffer_reserve_space(struct s2n_stuffer *stuffer, uint32_t n); - -/* Raw read/write move the cursor along and give you a pointer you can - * read/write data_len bytes from/to in-place. - */ -void *s2n_stuffer_raw_write(struct s2n_stuffer *stuffer, const uint32_t data_len); -void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len); - -/* Send/receive stuffer to/from a file descriptor */ -int s2n_stuffer_recv_from_fd(struct s2n_stuffer *stuffer, const int rfd, const uint32_t len, - uint32_t *bytes_written); -int s2n_stuffer_send_to_fd(struct s2n_stuffer *stuffer, const int wfd, const uint32_t len, uint32_t *bytes_sent); - -/* Read and write integers in network order */ -int S2N_RESULT_MUST_USE s2n_stuffer_read_uint8(struct s2n_stuffer *stuffer, uint8_t *u); -int S2N_RESULT_MUST_USE s2n_stuffer_read_uint16(struct s2n_stuffer *stuffer, uint16_t *u); -int S2N_RESULT_MUST_USE s2n_stuffer_read_uint24(struct s2n_stuffer *stuffer, uint32_t *u); -int S2N_RESULT_MUST_USE s2n_stuffer_read_uint32(struct s2n_stuffer *stuffer, uint32_t *u); -int S2N_RESULT_MUST_USE s2n_stuffer_read_uint64(struct s2n_stuffer *stuffer, uint64_t *u); - -int S2N_RESULT_MUST_USE s2n_stuffer_write_uint8(struct s2n_stuffer *stuffer, const uint8_t u); -int S2N_RESULT_MUST_USE s2n_stuffer_write_uint16(struct s2n_stuffer *stuffer, const uint16_t u); -int S2N_RESULT_MUST_USE s2n_stuffer_write_uint24(struct s2n_stuffer *stuffer, const uint32_t u); -int S2N_RESULT_MUST_USE s2n_stuffer_write_uint32(struct s2n_stuffer *stuffer, const uint32_t u); -int S2N_RESULT_MUST_USE s2n_stuffer_write_uint64(struct s2n_stuffer *stuffer, const uint64_t u); - -/* Allocate space now for network order integers that will be written later. - * These are primarily intended to handle the vector type defined in the RFC: - * https://tools.ietf.org/html/rfc8446#section-3.4 */ -struct s2n_stuffer_reservation { - struct s2n_stuffer *stuffer; - uint32_t write_cursor; - uint8_t length; -}; -/* Check basic validity constraints on the s2n_stuffer_reservation: e.g. stuffer validity. */ -S2N_RESULT s2n_stuffer_reservation_validate(const struct s2n_stuffer_reservation *reservation); -int S2N_RESULT_MUST_USE s2n_stuffer_reserve_uint8(struct s2n_stuffer *stuffer, struct s2n_stuffer_reservation *reservation); -int S2N_RESULT_MUST_USE s2n_stuffer_reserve_uint16(struct s2n_stuffer *stuffer, struct s2n_stuffer_reservation *reservation); -int S2N_RESULT_MUST_USE s2n_stuffer_reserve_uint24(struct s2n_stuffer *stuffer, struct s2n_stuffer_reservation *reservation); -int S2N_RESULT_MUST_USE s2n_stuffer_write_reservation(struct s2n_stuffer_reservation *reservation, const uint32_t value); -/* Reservations are primarily intended to handle the variable-length vector type - * defined in the RFC: https://tools.ietf.org/html/rfc8446#section-3.4 - * Variable-length vectors are preceded by the total size of the vector in bytes - * (not to be confused with the number of elements in the vector). - * - * These methods calculate the size of the vector just written to a stuffer based - * on the stuffer's current write cursor. - */ -int S2N_RESULT_MUST_USE s2n_stuffer_get_vector_size(const struct s2n_stuffer_reservation *reservation, uint32_t *size); -int S2N_RESULT_MUST_USE s2n_stuffer_write_vector_size(struct s2n_stuffer_reservation *reservation); - -/* Copy one stuffer to another */ -int s2n_stuffer_copy(struct s2n_stuffer *from, struct s2n_stuffer *to, uint32_t len); - -/* Convert between hex strings and raw bytes. - * - * When reading hex, the characters can be uppercase or lowercase. - * When writing hex, lowercase characters are used. - * - * Examples: - * "1234567890ABCdef" == [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef ] - * "FF" == 255 or [ 0xff ] - * "0001" == 1 or [ 0x00, 0x01 ] - */ -S2N_RESULT s2n_hex_digit(uint8_t half_byte, uint8_t *hex_digit); -S2N_RESULT s2n_stuffer_read_hex(struct s2n_stuffer *hex_in, const struct s2n_blob *bytes_out); -S2N_RESULT s2n_stuffer_write_hex(struct s2n_stuffer *hex_out, const struct s2n_blob *bytes_in); -S2N_RESULT s2n_stuffer_read_uint8_hex(struct s2n_stuffer *stuffer, uint8_t *u); -S2N_RESULT s2n_stuffer_write_uint8_hex(struct s2n_stuffer *stuffer, uint8_t u); -S2N_RESULT s2n_stuffer_read_uint16_hex(struct s2n_stuffer *stuffer, uint16_t *u); -S2N_RESULT s2n_stuffer_write_uint16_hex(struct s2n_stuffer *stuffer, uint16_t u); - -/** - * Given base64 data in `stuffer`, write the decoded (binary) data into `out`. - * - * DANGER: If the data to be read is not a multiple of 4, any trailing bytes will - * be silently ignored. - */ -int s2n_stuffer_read_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *out); - -/* Given some binary data in `in`, write the encoded (base64) data to `stuffer`. */ -int s2n_stuffer_write_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *in); - -/* Useful for text manipulation ... */ -#define s2n_stuffer_write_char(stuffer, c) s2n_stuffer_write_uint8((stuffer), (uint8_t) (c)) -#define s2n_stuffer_read_char(stuffer, c) s2n_stuffer_read_uint8((stuffer), (uint8_t *) (c)) -#define s2n_stuffer_write_str(stuffer, c) s2n_stuffer_write_bytes((stuffer), (const uint8_t *) (c), strlen((c))) -#define s2n_stuffer_write_text(stuffer, c, n) s2n_stuffer_write_bytes((stuffer), (const uint8_t *) (c), (n)) -#define s2n_stuffer_read_text(stuffer, c, n) s2n_stuffer_read_bytes((stuffer), (uint8_t *) (c), (n)) -int S2N_RESULT_MUST_USE s2n_stuffer_read_expected_str(struct s2n_stuffer *stuffer, const char *expected); -int S2N_RESULT_MUST_USE s2n_stuffer_peek_char(struct s2n_stuffer *stuffer, char *c); -int S2N_RESULT_MUST_USE s2n_stuffer_read_token(struct s2n_stuffer *stuffer, struct s2n_stuffer *token, char delim); -int S2N_RESULT_MUST_USE s2n_stuffer_read_line(struct s2n_stuffer *stuffer, struct s2n_stuffer *token); -int S2N_RESULT_MUST_USE s2n_stuffer_peek_check_for_str(struct s2n_stuffer *s2n_stuffer, const char *expected); -int S2N_RESULT_MUST_USE s2n_stuffer_skip_whitespace(struct s2n_stuffer *stuffer, uint32_t *skipped); -int S2N_RESULT_MUST_USE s2n_stuffer_skip_to_char(struct s2n_stuffer *stuffer, char target); -int S2N_RESULT_MUST_USE s2n_stuffer_skip_expected_char(struct s2n_stuffer *stuffer, const char expected, const uint32_t min, - const uint32_t max, uint32_t *skipped); -int S2N_RESULT_MUST_USE s2n_stuffer_skip_read_until(struct s2n_stuffer *stuffer, const char *target); -int S2N_RESULT_MUST_USE s2n_stuffer_alloc_ro_from_string(struct s2n_stuffer *stuffer, const char *str); -int S2N_RESULT_MUST_USE s2n_stuffer_init_ro_from_string(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t length); - -/* Stuffer versions of sprintf methods, except: - * - They write bytes, not strings. They do not write a final '\0'. Unfortunately, - * they do still require enough space for a final '\0'-- we'd have to reimplement - * sprintf to avoid that. - * - vprintf does not consume the vargs. It calls va_copy before using - * the varg argument, so can be called repeatedly with the same vargs. - */ -int S2N_RESULT_MUST_USE s2n_stuffer_printf(struct s2n_stuffer *stuffer, const char *format, ...); -int S2N_RESULT_MUST_USE s2n_stuffer_vprintf(struct s2n_stuffer *stuffer, const char *format, va_list vargs); - -/* Read a private key from a PEM encoded stuffer to an ASN1/DER encoded one */ -int S2N_RESULT_MUST_USE s2n_stuffer_private_key_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1, int *type); - -/* Read a certificate from a PEM encoded stuffer to an ASN1/DER encoded one */ -int S2N_RESULT_MUST_USE s2n_stuffer_certificate_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1); -bool s2n_stuffer_has_pem_encapsulated_block(struct s2n_stuffer *pem); - -/* Read a CRL from a PEM encoded stuffer to an ASN1/DER encoded one */ -int S2N_RESULT_MUST_USE s2n_stuffer_crl_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1); - -/* Read DH parameters om a PEM encoded stuffer to a PKCS3 encoded one */ -int S2N_RESULT_MUST_USE s2n_stuffer_dhparams_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *pkcs3); - -bool s2n_is_base64_char(unsigned char c); - -/* Copies all valid data from "stuffer" into "out". - * The old blob "out" pointed to is freed. - * It is the responsibility of the caller to free the free "out". - */ -int S2N_RESULT_MUST_USE s2n_stuffer_extract_blob(struct s2n_stuffer *stuffer, struct s2n_blob *out); +/* + * 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 + +#include +#include +#include +#include +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#else +#include +#endif + +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" + +#define S2N_MIN_STUFFER_GROWTH_IN_BYTES 1024 + +/* Using a non-zero value + * (a) makes wiped data easy to see in the debugger + * (b) makes use of wiped data obvious since this is unlikely to be a valid bit pattern + */ +#define S2N_WIPE_PATTERN 'w' + +#define SIZEOF_IN_BITS(t) (sizeof(t) * CHAR_BIT) + +#define SIZEOF_UINT24 3 + +struct s2n_stuffer { + /* The data for the s2n_stuffer */ + struct s2n_blob blob; + + /* Cursors to the current read/write position in the s2n_stuffer */ + uint32_t read_cursor; + uint32_t write_cursor; + uint32_t high_water_mark; + + /* Was this stuffer alloc()'d? + * This field controls whether the stuffer "owns" the blob. If the stuffer + * was allocated, then `blob` must be freed when the stuffer is freed. If the + * stuffer was not allocated, then the blob must not be freed by the stuffer, even if the + * blob itself is allocated. */ + unsigned int alloced : 1; + + /* Is this stuffer growable? */ + unsigned int growable : 1; + + /* Can this stuffer be safely resized? + * A growable stuffer can be temporarily tainted by a raw read/write, + * preventing it from resizing. */ + unsigned int tainted : 1; +}; + +#define s2n_stuffer_data_available(s) ((s)->write_cursor - (s)->read_cursor) +#define s2n_stuffer_space_remaining(s) ((s)->blob.size - (s)->write_cursor) +#define s2n_stuffer_is_wiped(s) ((s)->high_water_mark == 0) +#define s2n_stuffer_is_freed(s) ((s)->blob.data == NULL) +/* Check basic validity constraints on the stuffer: e.g. that cursors point within the blob */ +S2N_RESULT s2n_stuffer_validate(const struct s2n_stuffer *stuffer); + +/* Initialize and destroying stuffers */ +int S2N_RESULT_MUST_USE s2n_stuffer_init(struct s2n_stuffer *stuffer, struct s2n_blob *in); +int S2N_RESULT_MUST_USE s2n_stuffer_init_written(struct s2n_stuffer *stuffer, struct s2n_blob *in); +int S2N_RESULT_MUST_USE s2n_stuffer_alloc(struct s2n_stuffer *stuffer, const uint32_t size); +int S2N_RESULT_MUST_USE s2n_stuffer_growable_alloc(struct s2n_stuffer *stuffer, const uint32_t size); +int s2n_stuffer_free(struct s2n_stuffer *stuffer); +/** + * Frees the stuffer without zeroizing the contained data. + * + * This should only be used in scenarios where the data is encrypted or has been + * cleared with `s2n_stuffer_erase_and_read`. In most cases, prefer `s2n_stuffer_free`. + */ +int S2N_RESULT_MUST_USE s2n_stuffer_free_without_wipe(struct s2n_stuffer *stuffer); +int S2N_RESULT_MUST_USE s2n_stuffer_resize(struct s2n_stuffer *stuffer, const uint32_t size); +int S2N_RESULT_MUST_USE s2n_stuffer_resize_if_empty(struct s2n_stuffer *stuffer, const uint32_t size); +int S2N_RESULT_MUST_USE s2n_stuffer_rewind_read(struct s2n_stuffer *stuffer, const uint32_t size); +int S2N_RESULT_MUST_USE s2n_stuffer_reread(struct s2n_stuffer *stuffer); +int S2N_RESULT_MUST_USE s2n_stuffer_rewrite(struct s2n_stuffer *stuffer); +int S2N_RESULT_MUST_USE s2n_stuffer_shift(struct s2n_stuffer *stuffer); +int s2n_stuffer_wipe(struct s2n_stuffer *stuffer); +int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t n); +bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer); + +/* Basic read and write */ +int S2N_RESULT_MUST_USE s2n_stuffer_read(struct s2n_stuffer *stuffer, struct s2n_blob *out); +int S2N_RESULT_MUST_USE s2n_stuffer_erase_and_read(struct s2n_stuffer *stuffer, struct s2n_blob *out); +int S2N_RESULT_MUST_USE s2n_stuffer_write(struct s2n_stuffer *stuffer, const struct s2n_blob *in); +int S2N_RESULT_MUST_USE s2n_stuffer_read_bytes(struct s2n_stuffer *stuffer, uint8_t *out, uint32_t n); +int S2N_RESULT_MUST_USE s2n_stuffer_erase_and_read_bytes(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t size); +int S2N_RESULT_MUST_USE s2n_stuffer_write_bytes(struct s2n_stuffer *stuffer, const uint8_t *in, const uint32_t n); +int S2N_RESULT_MUST_USE s2n_stuffer_writev_bytes(struct s2n_stuffer *stuffer, const struct iovec *iov, size_t iov_count, + uint32_t offs, uint32_t size); +int S2N_RESULT_MUST_USE s2n_stuffer_skip_read(struct s2n_stuffer *stuffer, uint32_t n); +int S2N_RESULT_MUST_USE s2n_stuffer_skip_write(struct s2n_stuffer *stuffer, const uint32_t n); + +/* Tries to reserve enough space to write n additional bytes into the stuffer.*/ +int S2N_RESULT_MUST_USE s2n_stuffer_reserve_space(struct s2n_stuffer *stuffer, uint32_t n); + +/* Raw read/write move the cursor along and give you a pointer you can + * read/write data_len bytes from/to in-place. + */ +void *s2n_stuffer_raw_write(struct s2n_stuffer *stuffer, const uint32_t data_len); +void *s2n_stuffer_raw_read(struct s2n_stuffer *stuffer, uint32_t data_len); + +/* Send/receive stuffer to/from a file descriptor */ +int s2n_stuffer_recv_from_fd(struct s2n_stuffer *stuffer, const int rfd, const uint32_t len, + uint32_t *bytes_written); +int s2n_stuffer_send_to_fd(struct s2n_stuffer *stuffer, const int wfd, const uint32_t len, uint32_t *bytes_sent); + +/* Read and write integers in network order */ +int S2N_RESULT_MUST_USE s2n_stuffer_read_uint8(struct s2n_stuffer *stuffer, uint8_t *u); +int S2N_RESULT_MUST_USE s2n_stuffer_read_uint16(struct s2n_stuffer *stuffer, uint16_t *u); +int S2N_RESULT_MUST_USE s2n_stuffer_read_uint24(struct s2n_stuffer *stuffer, uint32_t *u); +int S2N_RESULT_MUST_USE s2n_stuffer_read_uint32(struct s2n_stuffer *stuffer, uint32_t *u); +int S2N_RESULT_MUST_USE s2n_stuffer_read_uint64(struct s2n_stuffer *stuffer, uint64_t *u); + +int S2N_RESULT_MUST_USE s2n_stuffer_write_uint8(struct s2n_stuffer *stuffer, const uint8_t u); +int S2N_RESULT_MUST_USE s2n_stuffer_write_uint16(struct s2n_stuffer *stuffer, const uint16_t u); +int S2N_RESULT_MUST_USE s2n_stuffer_write_uint24(struct s2n_stuffer *stuffer, const uint32_t u); +int S2N_RESULT_MUST_USE s2n_stuffer_write_uint32(struct s2n_stuffer *stuffer, const uint32_t u); +int S2N_RESULT_MUST_USE s2n_stuffer_write_uint64(struct s2n_stuffer *stuffer, const uint64_t u); + +/* Allocate space now for network order integers that will be written later. + * These are primarily intended to handle the vector type defined in the RFC: + * https://tools.ietf.org/html/rfc8446#section-3.4 */ +struct s2n_stuffer_reservation { + struct s2n_stuffer *stuffer; + uint32_t write_cursor; + uint8_t length; +}; +/* Check basic validity constraints on the s2n_stuffer_reservation: e.g. stuffer validity. */ +S2N_RESULT s2n_stuffer_reservation_validate(const struct s2n_stuffer_reservation *reservation); +int S2N_RESULT_MUST_USE s2n_stuffer_reserve_uint8(struct s2n_stuffer *stuffer, struct s2n_stuffer_reservation *reservation); +int S2N_RESULT_MUST_USE s2n_stuffer_reserve_uint16(struct s2n_stuffer *stuffer, struct s2n_stuffer_reservation *reservation); +int S2N_RESULT_MUST_USE s2n_stuffer_reserve_uint24(struct s2n_stuffer *stuffer, struct s2n_stuffer_reservation *reservation); +int S2N_RESULT_MUST_USE s2n_stuffer_write_reservation(struct s2n_stuffer_reservation *reservation, const uint32_t value); +/* Reservations are primarily intended to handle the variable-length vector type + * defined in the RFC: https://tools.ietf.org/html/rfc8446#section-3.4 + * Variable-length vectors are preceded by the total size of the vector in bytes + * (not to be confused with the number of elements in the vector). + * + * These methods calculate the size of the vector just written to a stuffer based + * on the stuffer's current write cursor. + */ +int S2N_RESULT_MUST_USE s2n_stuffer_get_vector_size(const struct s2n_stuffer_reservation *reservation, uint32_t *size); +int S2N_RESULT_MUST_USE s2n_stuffer_write_vector_size(struct s2n_stuffer_reservation *reservation); + +/* Copy one stuffer to another */ +int s2n_stuffer_copy(struct s2n_stuffer *from, struct s2n_stuffer *to, uint32_t len); + +/* Convert between hex strings and raw bytes. + * + * When reading hex, the characters can be uppercase or lowercase. + * When writing hex, lowercase characters are used. + * + * Examples: + * "1234567890ABCdef" == [ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef ] + * "FF" == 255 or [ 0xff ] + * "0001" == 1 or [ 0x00, 0x01 ] + */ +S2N_RESULT s2n_hex_digit(uint8_t half_byte, uint8_t *hex_digit); +S2N_RESULT s2n_stuffer_read_hex(struct s2n_stuffer *hex_in, const struct s2n_blob *bytes_out); +S2N_RESULT s2n_stuffer_write_hex(struct s2n_stuffer *hex_out, const struct s2n_blob *bytes_in); +S2N_RESULT s2n_stuffer_read_uint8_hex(struct s2n_stuffer *stuffer, uint8_t *u); +S2N_RESULT s2n_stuffer_write_uint8_hex(struct s2n_stuffer *stuffer, uint8_t u); +S2N_RESULT s2n_stuffer_read_uint16_hex(struct s2n_stuffer *stuffer, uint16_t *u); +S2N_RESULT s2n_stuffer_write_uint16_hex(struct s2n_stuffer *stuffer, uint16_t u); + +/** + * Given base64 data in `stuffer`, write the decoded (binary) data into `out`. + * + * DANGER: If the data to be read is not a multiple of 4, any trailing bytes will + * be silently ignored. + */ +int s2n_stuffer_read_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *out); + +/* Given some binary data in `in`, write the encoded (base64) data to `stuffer`. */ +int s2n_stuffer_write_base64(struct s2n_stuffer *stuffer, struct s2n_stuffer *in); + +/* Useful for text manipulation ... */ +#define s2n_stuffer_write_char(stuffer, c) s2n_stuffer_write_uint8((stuffer), (uint8_t) (c)) +#define s2n_stuffer_read_char(stuffer, c) s2n_stuffer_read_uint8((stuffer), (uint8_t *) (c)) +#define s2n_stuffer_write_str(stuffer, c) s2n_stuffer_write_bytes((stuffer), (const uint8_t *) (c), strlen((c))) +#define s2n_stuffer_write_text(stuffer, c, n) s2n_stuffer_write_bytes((stuffer), (const uint8_t *) (c), (n)) +#define s2n_stuffer_read_text(stuffer, c, n) s2n_stuffer_read_bytes((stuffer), (uint8_t *) (c), (n)) +int S2N_RESULT_MUST_USE s2n_stuffer_read_expected_str(struct s2n_stuffer *stuffer, const char *expected); +int S2N_RESULT_MUST_USE s2n_stuffer_peek_char(struct s2n_stuffer *stuffer, char *c); +int S2N_RESULT_MUST_USE s2n_stuffer_read_token(struct s2n_stuffer *stuffer, struct s2n_stuffer *token, char delim); +int S2N_RESULT_MUST_USE s2n_stuffer_read_line(struct s2n_stuffer *stuffer, struct s2n_stuffer *token); +int S2N_RESULT_MUST_USE s2n_stuffer_peek_check_for_str(struct s2n_stuffer *s2n_stuffer, const char *expected); +int S2N_RESULT_MUST_USE s2n_stuffer_skip_whitespace(struct s2n_stuffer *stuffer, uint32_t *skipped); +int S2N_RESULT_MUST_USE s2n_stuffer_skip_to_char(struct s2n_stuffer *stuffer, char target); +int S2N_RESULT_MUST_USE s2n_stuffer_skip_expected_char(struct s2n_stuffer *stuffer, const char expected, const uint32_t min, + const uint32_t max, uint32_t *skipped); +int S2N_RESULT_MUST_USE s2n_stuffer_skip_read_until(struct s2n_stuffer *stuffer, const char *target); +int S2N_RESULT_MUST_USE s2n_stuffer_alloc_ro_from_string(struct s2n_stuffer *stuffer, const char *str); +int S2N_RESULT_MUST_USE s2n_stuffer_init_ro_from_string(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t length); + +/* Stuffer versions of sprintf methods, except: + * - They write bytes, not strings. They do not write a final '\0'. Unfortunately, + * they do still require enough space for a final '\0'-- we'd have to reimplement + * sprintf to avoid that. + * - vprintf does not consume the vargs. It calls va_copy before using + * the varg argument, so can be called repeatedly with the same vargs. + */ +int S2N_RESULT_MUST_USE s2n_stuffer_printf(struct s2n_stuffer *stuffer, const char *format, ...); +int S2N_RESULT_MUST_USE s2n_stuffer_vprintf(struct s2n_stuffer *stuffer, const char *format, va_list vargs); + +/* Read a private key from a PEM encoded stuffer to an ASN1/DER encoded one */ +int S2N_RESULT_MUST_USE s2n_stuffer_private_key_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1, int *type); + +/* Read a certificate from a PEM encoded stuffer to an ASN1/DER encoded one */ +int S2N_RESULT_MUST_USE s2n_stuffer_certificate_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1); +bool s2n_stuffer_has_pem_encapsulated_block(struct s2n_stuffer *pem); + +/* Read a CRL from a PEM encoded stuffer to an ASN1/DER encoded one */ +int S2N_RESULT_MUST_USE s2n_stuffer_crl_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1); + +/* Read DH parameters om a PEM encoded stuffer to a PKCS3 encoded one */ +int S2N_RESULT_MUST_USE s2n_stuffer_dhparams_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *pkcs3); + +bool s2n_is_base64_char(unsigned char c); + +/* Copies all valid data from "stuffer" into "out". + * The old blob "out" pointed to is freed. + * It is the responsibility of the caller to free the free "out". + */ +int S2N_RESULT_MUST_USE s2n_stuffer_extract_blob(struct s2n_stuffer *stuffer, struct s2n_blob *out); diff --git a/stuffer/s2n_stuffer_file.c b/stuffer/s2n_stuffer_file.c index 1d68f932289..90853110168 100644 --- a/stuffer/s2n_stuffer_file.c +++ b/stuffer/s2n_stuffer_file.c @@ -1,81 +1,81 @@ -/* - * 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 -#if defined(_MSC_VER) || defined(__MINGW32__) -#include -#else -#include -#endif - - -#if defined(_MSC_VER) -#include -#define read _read -#define write _write -#endif - -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_io.h" -#include "utils/s2n_safety.h" - -int s2n_stuffer_recv_from_fd(struct s2n_stuffer *stuffer, const int rfd, const uint32_t len, uint32_t *bytes_written) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - - /* Make sure we have enough space to write */ - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, len)); - - /* "undo" the skip write */ - stuffer->write_cursor -= len; - - ssize_t r = 0; - POSIX_ENSURE(stuffer->blob.data, S2N_ERR_READ); - S2N_IO_RETRY_EINTR(r, read(rfd, stuffer->blob.data + stuffer->write_cursor, len)); - POSIX_ENSURE(r >= 0, S2N_ERR_READ); - - /* Record just how many bytes we have written */ - POSIX_ENSURE((size_t) r <= UINT32_MAX, S2N_ERR_INTEGER_OVERFLOW); - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, (uint32_t) r)); - if (bytes_written != NULL) { - *bytes_written = r; - } - return S2N_SUCCESS; -} - -int s2n_stuffer_send_to_fd(struct s2n_stuffer *stuffer, const int wfd, const uint32_t len, uint32_t *bytes_sent) -{ - POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); - - /* Make sure we even have the data */ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, len)); - - /* "undo" the skip read */ - stuffer->read_cursor -= len; - - ssize_t w = 0; - POSIX_ENSURE(stuffer->blob.data, S2N_ERR_WRITE); - S2N_IO_RETRY_EINTR(w, write(wfd, stuffer->blob.data + stuffer->read_cursor, len)); - POSIX_ENSURE(w >= 0, S2N_ERR_WRITE); - - POSIX_ENSURE((size_t) w <= UINT32_MAX - stuffer->read_cursor, S2N_ERR_INTEGER_OVERFLOW); - stuffer->read_cursor += w; - if (bytes_sent != NULL) { - *bytes_sent = w; - } - return S2N_SUCCESS; -} +/* + * 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 +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +#else +#include +#endif + + +#if defined(_MSC_VER) +#include +#define read _read +#define write _write +#endif + +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_io.h" +#include "utils/s2n_safety.h" + +int s2n_stuffer_recv_from_fd(struct s2n_stuffer *stuffer, const int rfd, const uint32_t len, uint32_t *bytes_written) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + + /* Make sure we have enough space to write */ + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, len)); + + /* "undo" the skip write */ + stuffer->write_cursor -= len; + + ssize_t r = 0; + POSIX_ENSURE(stuffer->blob.data, S2N_ERR_READ); + S2N_IO_RETRY_EINTR(r, read(rfd, stuffer->blob.data + stuffer->write_cursor, len)); + POSIX_ENSURE(r >= 0, S2N_ERR_READ); + + /* Record just how many bytes we have written */ + POSIX_ENSURE((size_t) r <= UINT32_MAX, S2N_ERR_INTEGER_OVERFLOW); + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, (uint32_t) r)); + if (bytes_written != NULL) { + *bytes_written = r; + } + return S2N_SUCCESS; +} + +int s2n_stuffer_send_to_fd(struct s2n_stuffer *stuffer, const int wfd, const uint32_t len, uint32_t *bytes_sent) +{ + POSIX_PRECONDITION(s2n_stuffer_validate(stuffer)); + + /* Make sure we even have the data */ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, len)); + + /* "undo" the skip read */ + stuffer->read_cursor -= len; + + ssize_t w = 0; + POSIX_ENSURE(stuffer->blob.data, S2N_ERR_WRITE); + S2N_IO_RETRY_EINTR(w, write(wfd, stuffer->blob.data + stuffer->read_cursor, len)); + POSIX_ENSURE(w >= 0, S2N_ERR_WRITE); + + POSIX_ENSURE((size_t) w <= UINT32_MAX - stuffer->read_cursor, S2N_ERR_INTEGER_OVERFLOW); + stuffer->read_cursor += w; + if (bytes_sent != NULL) { + *bytes_sent = w; + } + return S2N_SUCCESS; +} diff --git a/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c b/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c index 201bf551b02..d44f2639080 100644 --- a/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c +++ b/tests/cbmc/proofs/s2n_array_insert/s2n_array_insert_harness.c @@ -1,62 +1,62 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "utils/s2n_array.h" -#include "utils/s2n_result.h" - -#include - -#include - -void s2n_array_insert_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_array *array = cbmc_allocate_s2n_array(); - __CPROVER_assume(s2n_result_is_ok(s2n_array_validate(array))); - __CPROVER_assume(s2n_array_is_bounded(array, MAX_ARRAY_LEN, MAX_ARRAY_ELEMENT_SIZE)); - uint32_t idx; - void **element = malloc(sizeof(void *)); - - nondet_s2n_mem_init(); - - struct s2n_array old_array = *array; - struct store_byte_from_buffer old_byte; - save_byte_from_array(array->mem.data, array->len, &old_byte); - - /* Operation under verification. */ - if (s2n_result_is_ok(s2n_array_insert(array, idx, element))) { - /* - * In the case s2n_array_insert is successful, we can ensure the array isn't empty - * and index is within bounds. - */ - assert(array->mem.data != NULL); - assert(array->len == (old_array.len + 1)); - assert(idx < array->len); - assert(*element == (array->mem.data + (array->element_size * idx))); - assert(s2n_result_is_ok(s2n_array_validate(array))); - if (old_array.len != 0 && idx == old_array.len) { - assert_byte_from_blob_matches(&array->mem, &old_byte); - } - - /* Verify the array capacity increases by the correct amount. */ - uint32_t old_capacity = old_array.mem.size / old_array.element_size; - if (old_array.len >= old_capacity) { - uint32_t expected_new_capacity = S2N_MAX(S2N_INITIAL_ARRAY_SIZE, old_capacity * 2) * old_array.element_size; - uint32_t new_capacity = array->mem.size; - assert(new_capacity == expected_new_capacity); - } - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "utils/s2n_array.h" +#include "utils/s2n_result.h" + +#include + +#include + +void s2n_array_insert_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_array *array = cbmc_allocate_s2n_array(); + __CPROVER_assume(s2n_result_is_ok(s2n_array_validate(array))); + __CPROVER_assume(s2n_array_is_bounded(array, MAX_ARRAY_LEN, MAX_ARRAY_ELEMENT_SIZE)); + uint32_t idx; + void **element = malloc(sizeof(void *)); + + nondet_s2n_mem_init(); + + struct s2n_array old_array = *array; + struct store_byte_from_buffer old_byte; + save_byte_from_array(array->mem.data, array->len, &old_byte); + + /* Operation under verification. */ + if (s2n_result_is_ok(s2n_array_insert(array, idx, element))) { + /* + * In the case s2n_array_insert is successful, we can ensure the array isn't empty + * and index is within bounds. + */ + assert(array->mem.data != NULL); + assert(array->len == (old_array.len + 1)); + assert(idx < array->len); + assert(*element == (array->mem.data + (array->element_size * idx))); + assert(s2n_result_is_ok(s2n_array_validate(array))); + if (old_array.len != 0 && idx == old_array.len) { + assert_byte_from_blob_matches(&array->mem, &old_byte); + } + + /* Verify the array capacity increases by the correct amount. */ + uint32_t old_capacity = old_array.mem.size / old_array.element_size; + if (old_array.len >= old_capacity) { + uint32_t expected_new_capacity = S2N_MAX(S2N_INITIAL_ARRAY_SIZE, old_capacity * 2) * old_array.element_size; + uint32_t new_capacity = array->mem.size; + assert(new_capacity == expected_new_capacity); + } + } +} diff --git a/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c b/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c index d2ad475d4c3..ab953f6f4c9 100644 --- a/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c +++ b/tests/cbmc/proofs/s2n_hash_digest_size/s2n_hash_digest_size_harness.c @@ -1,33 +1,33 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_hash.h" - -#include - -void s2n_hash_digest_size_harness() -{ - /* Non-deterministic inputs. */ - s2n_hash_algorithm alg; - uint8_t * out = malloc(sizeof(*out)); - - /* Operation under verification. */ - if (s2n_hash_digest_size(alg, out) == S2N_SUCCESS) { - assert(*out <= S2N_MAX_DIGEST_LEN); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hash.h" + +#include + +void s2n_hash_digest_size_harness() +{ + /* Non-deterministic inputs. */ + s2n_hash_algorithm alg; + uint8_t * out = malloc(sizeof(*out)); + + /* Operation under verification. */ + if (s2n_hash_digest_size(alg, out) == S2N_SUCCESS) { + assert(*out <= S2N_MAX_DIGEST_LEN); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c b/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c index e7081bed0c3..04273386fc1 100644 --- a/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_raw_write/s2n_stuffer_raw_write_harness.c @@ -1,54 +1,54 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_raw_write_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t data_len; - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare. */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - void *retval = s2n_stuffer_raw_write(stuffer, data_len); - - if (retval != NULL) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); - assert(retval == stuffer->blob.data + old_stuffer.write_cursor); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); - assert(stuffer->tainted == 1); - if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_raw_write_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t data_len; + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare. */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + void *retval = s2n_stuffer_raw_write(stuffer, data_len); + + if (retval != NULL) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); + assert(retval == stuffer->blob.data + old_stuffer.write_cursor); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); + assert(stuffer->tainted == 1); + if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c index 0cbd43dcb29..f7035818618 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve/s2n_stuffer_reserve_harness.c @@ -1,66 +1,66 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -int s2n_stuffer_reserve(struct s2n_stuffer *, struct s2n_stuffer_reservation *, const uint8_t); - -void s2n_stuffer_reserve_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); - const uint8_t length; - - /* Non-deterministically set initialized (in s2n_mem) to true. */ - if (nondet_bool()) { s2n_mem_init(); } - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve(stuffer, reservation, length) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + length); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + length, old_stuffer.high_water_mark)); - assert(reservation->length == length); - if (old_stuffer.blob.size > 0 && reservation->length > 0) { - size_t idx; - __CPROVER_assume(idx >= reservation->write_cursor - && idx < (reservation->write_cursor + reservation->length)); - assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - } - assert(stuffer == reservation->stuffer); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); - } else { - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +int s2n_stuffer_reserve(struct s2n_stuffer *, struct s2n_stuffer_reservation *, const uint8_t); + +void s2n_stuffer_reserve_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); + const uint8_t length; + + /* Non-deterministically set initialized (in s2n_mem) to true. */ + if (nondet_bool()) { s2n_mem_init(); } + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve(stuffer, reservation, length) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + length); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + length, old_stuffer.high_water_mark)); + assert(reservation->length == length); + if (old_stuffer.blob.size > 0 && reservation->length > 0) { + size_t idx; + __CPROVER_assume(idx >= reservation->write_cursor + && idx < (reservation->write_cursor + reservation->length)); + assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + } + assert(stuffer == reservation->stuffer); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); + } else { + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c index 0f0f3f21d67..814761ae4ed 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_space/s2n_stuffer_reserve_space_harness.c @@ -1,64 +1,64 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_reserve_space_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t size; - - nondet_s2n_mem_init(); - - /* Save previous state. */ - struct s2n_stuffer old_stuffer = *stuffer; - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve_space(stuffer, size) == S2N_SUCCESS) { - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - if (s2n_stuffer_space_remaining(&old_stuffer) < size) { - /* Always grow a stuffer by at least 1k */ - assert(stuffer->blob.size - == (S2N_MAX(size - s2n_stuffer_space_remaining(&old_stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES) - + old_stuffer.blob.size)); - assert(stuffer->blob.allocated >= size); - } else { - assert_stuffer_equivalence(stuffer, &old_stuffer, &old_byte_from_stuffer); - } - } else { - /* - * s2n_realloc could fail, so we can onyl guarantee equivalence of - * data pointer, but not the elements in it. - */ - assert(stuffer->blob.data == old_stuffer.blob.data); - assert(stuffer->blob.size == old_stuffer.blob.size); - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_reserve_space_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t size; + + nondet_s2n_mem_init(); + + /* Save previous state. */ + struct s2n_stuffer old_stuffer = *stuffer; + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve_space(stuffer, size) == S2N_SUCCESS) { + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + if (s2n_stuffer_space_remaining(&old_stuffer) < size) { + /* Always grow a stuffer by at least 1k */ + assert(stuffer->blob.size + == (S2N_MAX(size - s2n_stuffer_space_remaining(&old_stuffer), S2N_MIN_STUFFER_GROWTH_IN_BYTES) + + old_stuffer.blob.size)); + assert(stuffer->blob.allocated >= size); + } else { + assert_stuffer_equivalence(stuffer, &old_stuffer, &old_byte_from_stuffer); + } + } else { + /* + * s2n_realloc could fail, so we can onyl guarantee equivalence of + * data pointer, but not the elements in it. + */ + assert(stuffer->blob.data == old_stuffer.blob.data); + assert(stuffer->blob.size == old_stuffer.blob.size); + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c index 2f8ba9fb311..0deb4498511 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_uint16/s2n_stuffer_reserve_uint16_harness.c @@ -1,62 +1,62 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_reserve_uint16_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve_uint16(stuffer, reservation) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + sizeof(uint16_t)); - assert(stuffer->high_water_mark - == S2N_MAX(old_stuffer.write_cursor + sizeof(uint16_t), old_stuffer.high_water_mark)); - assert(reservation->length == sizeof(uint16_t)); - if (old_stuffer.blob.size > 0) { - size_t idx; - __CPROVER_assume(idx >= reservation->write_cursor - && idx < (reservation->write_cursor + reservation->length)); - assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - } - assert(stuffer == reservation->stuffer); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); - } else { - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_reserve_uint16_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve_uint16(stuffer, reservation) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + sizeof(uint16_t)); + assert(stuffer->high_water_mark + == S2N_MAX(old_stuffer.write_cursor + sizeof(uint16_t), old_stuffer.high_water_mark)); + assert(reservation->length == sizeof(uint16_t)); + if (old_stuffer.blob.size > 0) { + size_t idx; + __CPROVER_assume(idx >= reservation->write_cursor + && idx < (reservation->write_cursor + reservation->length)); + assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + } + assert(stuffer == reservation->stuffer); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); + } else { + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c b/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c index f99764c5c5f..7a658ee69b0 100644 --- a/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_reserve_uint24/s2n_stuffer_reserve_uint24_harness.c @@ -1,61 +1,61 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "utils/s2n_mem.h" - -void s2n_stuffer_reserve_uint24_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_reserve_uint24(stuffer, reservation) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + SIZEOF_UINT24); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + SIZEOF_UINT24, old_stuffer.high_water_mark)); - assert(reservation->length == SIZEOF_UINT24); - if (old_stuffer.blob.size > 0) { - size_t idx; - __CPROVER_assume(idx >= reservation->write_cursor - && idx < (reservation->write_cursor + reservation->length)); - assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); - } - assert(stuffer == reservation->stuffer); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); - } else { - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert(stuffer->alloced == old_stuffer.alloced); - assert(stuffer->growable == old_stuffer.growable); - assert(stuffer->tainted == old_stuffer.tainted); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +void s2n_stuffer_reserve_uint24_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_stuffer_reservation *reservation = cbmc_allocate_s2n_stuffer_reservation(); + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_reserve_uint24(stuffer, reservation) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + SIZEOF_UINT24); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + SIZEOF_UINT24, old_stuffer.high_water_mark)); + assert(reservation->length == SIZEOF_UINT24); + if (old_stuffer.blob.size > 0) { + size_t idx; + __CPROVER_assume(idx >= reservation->write_cursor + && idx < (reservation->write_cursor + reservation->length)); + assert(stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + assert(reservation->stuffer->blob.data[ idx ] == S2N_WIPE_PATTERN); + } + assert(stuffer == reservation->stuffer); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + assert(s2n_result_is_ok(s2n_stuffer_reservation_validate(reservation))); + } else { + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert(stuffer->alloced == old_stuffer.alloced); + assert(stuffer->growable == old_stuffer.growable); + assert(stuffer->tainted == old_stuffer.tainted); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c b/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c index 2a21526b568..953437270a5 100644 --- a/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_skip_write/s2n_stuffer_skip_write_harness.c @@ -1,49 +1,49 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_skip_write_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t data_len; - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - /* Store a byte from the stuffer to compare */ - struct store_byte_from_buffer old_byte_from_stuffer; - save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); - - /* Operation under verification. */ - if (s2n_stuffer_skip_write(stuffer, data_len) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); - if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_skip_write_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t data_len; + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + /* Store a byte from the stuffer to compare */ + struct store_byte_from_buffer old_byte_from_stuffer; + save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); + + /* Operation under verification. */ + if (s2n_stuffer_skip_write(stuffer, data_len) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + data_len); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + data_len, old_stuffer.high_water_mark)); + if (old_stuffer.blob.size > 0) { assert_byte_from_blob_matches(&stuffer->blob, &old_byte_from_stuffer); } + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c b/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c index fcf0c38285e..7b9e442bec5 100644 --- a/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_wipe_n/s2n_stuffer_wipe_n_harness.c @@ -1,67 +1,67 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_wipe_n_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - uint32_t n; - - /* Assume preconditions. */ - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - - /* Save previous state. */ - struct s2n_stuffer old_stuffer = *stuffer; - - /* Save byte from untouched portion to compare after the wipe */ - uint32_t expect_wiped = S2N_MIN(n, old_stuffer.write_cursor); - uint32_t expected_write_cursor = old_stuffer.write_cursor - expect_wiped; - struct store_byte_from_buffer old_byte; - save_byte_from_array(old_stuffer.blob.data, expected_write_cursor, &old_byte); - - /* Given a valid stuffer, wipe_n always succeeds */ - assert(s2n_stuffer_wipe_n(stuffer, n) == S2N_SUCCESS); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - - /* The basic stuffer fields should NOT be updated */ - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - assert(stuffer->tainted == old_stuffer.tainted); - assert(stuffer->blob == old_stuffer.blob); - assert(stuffer->blob.data == old_stuffer.blob.data); - - /* The read and write cursors should be updated */ - assert(S2N_IMPLIES(expect_wiped < n, stuffer->write_cursor == 0)); - assert(S2N_IMPLIES(expect_wiped < n, stuffer->read_cursor == 0)); - assert(stuffer->write_cursor == expected_write_cursor); - assert(stuffer->read_cursor == S2N_MIN(old_stuffer.read_cursor, stuffer->write_cursor)); - - /* Any data before the new write cursor should NOT be updated */ - if (expected_write_cursor > 0) { - assert_byte_from_buffer_matches(stuffer->blob.data, &old_byte); - } - - /* Everything after the new write cursor should be wiped */ - if (expect_wiped > 0) { - assert_all_bytes_are(stuffer->blob.data + stuffer->write_cursor, - S2N_WIPE_PATTERN, expect_wiped); - } -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_wipe_n_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + uint32_t n; + + /* Assume preconditions. */ + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + + /* Save previous state. */ + struct s2n_stuffer old_stuffer = *stuffer; + + /* Save byte from untouched portion to compare after the wipe */ + uint32_t expect_wiped = S2N_MIN(n, old_stuffer.write_cursor); + uint32_t expected_write_cursor = old_stuffer.write_cursor - expect_wiped; + struct store_byte_from_buffer old_byte; + save_byte_from_array(old_stuffer.blob.data, expected_write_cursor, &old_byte); + + /* Given a valid stuffer, wipe_n always succeeds */ + assert(s2n_stuffer_wipe_n(stuffer, n) == S2N_SUCCESS); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + + /* The basic stuffer fields should NOT be updated */ + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + assert(stuffer->tainted == old_stuffer.tainted); + assert(stuffer->blob == old_stuffer.blob); + assert(stuffer->blob.data == old_stuffer.blob.data); + + /* The read and write cursors should be updated */ + assert(S2N_IMPLIES(expect_wiped < n, stuffer->write_cursor == 0)); + assert(S2N_IMPLIES(expect_wiped < n, stuffer->read_cursor == 0)); + assert(stuffer->write_cursor == expected_write_cursor); + assert(stuffer->read_cursor == S2N_MIN(old_stuffer.read_cursor, stuffer->write_cursor)); + + /* Any data before the new write cursor should NOT be updated */ + if (expected_write_cursor > 0) { + assert_byte_from_buffer_matches(stuffer->blob.data, &old_byte); + } + + /* Everything after the new write cursor should be wiped */ + if (expect_wiped > 0) { + assert_all_bytes_are(stuffer->blob.data + stuffer->write_cursor, + S2N_WIPE_PATTERN, expect_wiped); + } +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c b/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c index 2a3c224de49..740996a1c8d 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_write/s2n_stuffer_write_harness.c @@ -1,64 +1,64 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_write_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - struct s2n_blob *blob = cbmc_allocate_s2n_blob(); - __CPROVER_assume(s2n_result_is_ok(s2n_blob_validate(blob))); - uint32_t idx; - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - - /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ - __CPROVER_assume(idx < stuffer->blob.size); - if (__CPROVER_overflow_plus(old_stuffer.write_cursor, blob->size)) { - __CPROVER_assume(idx < old_stuffer.write_cursor); - } else { - __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + blob->size); - } - uint8_t untouched_byte = stuffer->blob.data[ idx ]; - - /* Store a byte from the blob to compare. */ - struct s2n_blob old_blob = *blob; - struct store_byte_from_buffer old_byte_from_blob; - save_byte_from_blob(blob, &old_byte_from_blob); - - /* Operation under verification. */ - if (s2n_stuffer_write(stuffer, blob) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + blob->size); - assert(stuffer->blob.data[ idx ] == untouched_byte); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + blob->size, old_stuffer.high_water_mark)); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } - assert(stuffer->read_cursor == old_stuffer.read_cursor); - assert_blob_equivalence(blob, &old_blob, &old_byte_from_blob); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + struct s2n_blob *blob = cbmc_allocate_s2n_blob(); + __CPROVER_assume(s2n_result_is_ok(s2n_blob_validate(blob))); + uint32_t idx; + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + + /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ + __CPROVER_assume(idx < stuffer->blob.size); + if (__CPROVER_overflow_plus(old_stuffer.write_cursor, blob->size)) { + __CPROVER_assume(idx < old_stuffer.write_cursor); + } else { + __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + blob->size); + } + uint8_t untouched_byte = stuffer->blob.data[ idx ]; + + /* Store a byte from the blob to compare. */ + struct s2n_blob old_blob = *blob; + struct store_byte_from_buffer old_byte_from_blob; + save_byte_from_blob(blob, &old_byte_from_blob); + + /* Operation under verification. */ + if (s2n_stuffer_write(stuffer, blob) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + blob->size); + assert(stuffer->blob.data[ idx ] == untouched_byte); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + blob->size, old_stuffer.high_water_mark)); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } + assert(stuffer->read_cursor == old_stuffer.read_cursor); + assert_blob_equivalence(blob, &old_blob, &old_byte_from_blob); +} diff --git a/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c index b5eecfc8edb..9c009cdd31b 100644 --- a/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_write_bytes/s2n_stuffer_write_bytes_harness.c @@ -1,58 +1,58 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "stuffer/s2n_stuffer.h" - -void s2n_stuffer_write_bytes_harness() -{ - /* Non-deterministic inputs. */ - struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); - __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - uint32_t idx; - uint32_t size; - uint8_t *data = malloc(size); - - nondet_s2n_mem_init(); - - /* Save previous state from stuffer. */ - struct s2n_stuffer old_stuffer = *stuffer; - - /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ - __CPROVER_assume(idx < stuffer->blob.size); - if (__CPROVER_overflow_plus(old_stuffer.write_cursor, size)) { - __CPROVER_assume(idx < old_stuffer.write_cursor); - } else { - __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + size); - } - uint8_t untouched_byte = stuffer->blob.data[ idx ]; - - /* Operation under verification. */ - if (s2n_stuffer_write_bytes(stuffer, data, size) == S2N_SUCCESS) { - assert(stuffer->write_cursor == old_stuffer.write_cursor + size); - assert(stuffer->blob.data[ idx ] == untouched_byte); - assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + size, old_stuffer.high_water_mark)); - assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); - } else { - assert(stuffer->write_cursor == old_stuffer.write_cursor); - assert(stuffer->high_water_mark == old_stuffer.high_water_mark); - } - assert(stuffer->read_cursor == old_stuffer.read_cursor); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "stuffer/s2n_stuffer.h" + +void s2n_stuffer_write_bytes_harness() +{ + /* Non-deterministic inputs. */ + struct s2n_stuffer *stuffer = cbmc_allocate_s2n_stuffer(); + __CPROVER_assume(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + uint32_t idx; + uint32_t size; + uint8_t *data = malloc(size); + + nondet_s2n_mem_init(); + + /* Save previous state from stuffer. */ + struct s2n_stuffer old_stuffer = *stuffer; + + /* Store a byte from the stuffer that wont be overwritten to compare if the write succeeds. */ + __CPROVER_assume(idx < stuffer->blob.size); + if (__CPROVER_overflow_plus(old_stuffer.write_cursor, size)) { + __CPROVER_assume(idx < old_stuffer.write_cursor); + } else { + __CPROVER_assume(idx < old_stuffer.write_cursor || idx >= old_stuffer.write_cursor + size); + } + uint8_t untouched_byte = stuffer->blob.data[ idx ]; + + /* Operation under verification. */ + if (s2n_stuffer_write_bytes(stuffer, data, size) == S2N_SUCCESS) { + assert(stuffer->write_cursor == old_stuffer.write_cursor + size); + assert(stuffer->blob.data[ idx ] == untouched_byte); + assert(stuffer->high_water_mark == S2N_MAX(old_stuffer.write_cursor + size, old_stuffer.high_water_mark)); + assert(s2n_result_is_ok(s2n_stuffer_validate(stuffer))); + } else { + assert(stuffer->write_cursor == old_stuffer.write_cursor); + assert(stuffer->high_water_mark == old_stuffer.high_water_mark); + } + assert(stuffer->read_cursor == old_stuffer.read_cursor); +} 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 index 1c1211dca50..f683442fcb3 100644 --- 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 @@ -1,75 +1,75 @@ -#include "utils/s2n_prelude.h" -/* - * 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 = S2N_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); -} +#include "utils/s2n_prelude.h" +/* + * 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 = S2N_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/s2n_stuffer_write_uint16_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_uint16_hex/s2n_stuffer_write_uint16_hex_harness.c index af2ed47444c..11c759ddadf 100644 --- 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 @@ -1,64 +1,64 @@ -#include "utils/s2n_prelude.h" -/* - * 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 = S2N_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); -} +#include "utils/s2n_prelude.h" +/* + * 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 = S2N_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/s2n_stuffer_write_uint8_hex_harness.c b/tests/cbmc/proofs/s2n_stuffer_write_uint8_hex/s2n_stuffer_write_uint8_hex_harness.c index 3deeb341fa6..1eed5becbb6 100644 --- 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 @@ -1,64 +1,64 @@ -#include "utils/s2n_prelude.h" -/* - * 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 = S2N_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); -} +#include "utils/s2n_prelude.h" +/* + * 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 = S2N_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/fuzz/corpus/s2n_stuffer_pem_fuzz_test/265b600e8ad749d27ba841994c25a73c8b12d33e b/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/265b600e8ad749d27ba841994c25a73c8b12d33e index fa5be6d929c..ac7b02f658b 100644 --- a/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/265b600e8ad749d27ba841994c25a73c8b12d33e +++ b/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/265b600e8ad749d27ba841994c25a73c8b12d33e @@ -1 +1 @@ ------------------------------------------------------------------ +----------------------------------------------------------------- diff --git a/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/8c306ddd7ba5001e3000c76750badae346ec258e b/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/8c306ddd7ba5001e3000c76750badae346ec258e index 67a58142a34..68f165c5b86 100644 --- a/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/8c306ddd7ba5001e3000c76750badae346ec258e +++ b/tests/fuzz/corpus/s2n_stuffer_pem_fuzz_test/8c306ddd7ba5001e3000c76750badae346ec258e @@ -1,2 +1,2 @@ -----------------------------------------------------------------BEGIN CERTIFICATE----------- - +----------------------------------------------------------------BEGIN CERTIFICATE----------- + diff --git a/tests/fuzz/s2n_cert_req_recv_test.c b/tests/fuzz/s2n_cert_req_recv_test.c index 80e3410e74a..06faafb52be 100644 --- a/tests/fuzz/s2n_cert_req_recv_test.c +++ b/tests/fuzz/s2n_cert_req_recv_test.c @@ -1,101 +1,101 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Target Functions: s2n_cert_req_recv s2n_recv_client_cert_preferences - s2n_cert_type_to_pkey_type s2n_recv_supported_sig_scheme_list - s2n_choose_sig_scheme_from_peer_preference_list - s2n_set_cert_chain_as_client */ - -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static char *cert_chain, *private_key; -struct s2n_cert_chain_and_key *default_cert; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - /* Initialize test chain and key */ - cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(cert_chain); - private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(private_key); - default_cert = s2n_cert_chain_and_key_new(); - POSIX_ENSURE_REF(default_cert); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - - return S2N_SUCCESS; -} - -static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least one byte of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 1); - - /* Setup */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - POSIX_ENSURE_REF(client_conn); - struct s2n_config *client_config = s2n_config_new(); - POSIX_ENSURE_REF(client_config); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); - POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); - POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); - - /* Pull a byte off the libfuzzer input and use it to set parameters */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&client_conn->handshake.io, &randval)); - client_conn->actual_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_cert_req_recv(client_conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(client_conn)); - POSIX_GUARD(s2n_config_free(client_config)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(default_cert); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_cert_req_recv s2n_recv_client_cert_preferences + s2n_cert_type_to_pkey_type s2n_recv_supported_sig_scheme_list + s2n_choose_sig_scheme_from_peer_preference_list + s2n_set_cert_chain_as_client */ + +#include +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static char *cert_chain, *private_key; +struct s2n_cert_chain_and_key *default_cert; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + /* Initialize test chain and key */ + cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(cert_chain); + private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(private_key); + default_cert = s2n_cert_chain_and_key_new(); + POSIX_ENSURE_REF(default_cert); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + + return S2N_SUCCESS; +} + +static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least one byte of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 1); + + /* Setup */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + POSIX_ENSURE_REF(client_conn); + struct s2n_config *client_config = s2n_config_new(); + POSIX_ENSURE_REF(client_config); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); + + /* Pull a byte off the libfuzzer input and use it to set parameters */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&client_conn->handshake.io, &randval)); + client_conn->actual_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_cert_req_recv(client_conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_config_free(client_config)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(default_cert); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/fuzz/s2n_certificate_extensions_parse_test.c b/tests/fuzz/s2n_certificate_extensions_parse_test.c index 2b278f63662..adc9f990d2c 100644 --- a/tests/fuzz/s2n_certificate_extensions_parse_test.c +++ b/tests/fuzz/s2n_certificate_extensions_parse_test.c @@ -1,116 +1,116 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Target Functions: s2n_certificate_extensions_parse - s2n_recv_server_sct_list s2n_server_certificate_status_recv - s2n_x509_validator_validate_cert_stapled_ocsp_response */ - -#include - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls13.h" - -struct host_verify_data { - const char *name; - uint8_t found_name; - uint8_t callback_invoked; -}; - -static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return 1; -} - -/* This test is for TLS versions 1.3 and up only */ -static const uint8_t TLS_VERSIONS[] = {S2N_TLS13}; - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least one byte of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 1); - - /* Setup */ - struct s2n_stuffer fuzz_stuffer = {0}; - POSIX_GUARD(s2n_stuffer_alloc(&fuzz_stuffer, len)); - POSIX_GUARD(s2n_stuffer_write_bytes(&fuzz_stuffer, buf, len)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "20240503")); - - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - POSIX_ENSURE_REF(client_conn); - POSIX_GUARD(s2n_connection_set_config(client_conn, config)); - - /* Pull a byte off the libfuzzer input and use it to set parameters */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&fuzz_stuffer, &randval)); - client_conn->x509_validator.skip_cert_validation = (randval >> 7) % 2; - - /* Set connection to TLS 1.2 to temporary work around cert validation setup */ - client_conn->actual_protocol_version = S2N_TLS12; - - /* Set cert chain and trust store for verification of OCSP response */ - if ((randval >> 6) % 2 && OPENSSL_VERSION_NUMBER >= 0x10101000L) { - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - POSIX_GUARD(s2n_connection_set_verify_host_callback(client_conn, verify_host_accept_everything, &verify_data)); - char cert_chain[S2N_MAX_TEST_PEM_SIZE]; - POSIX_GUARD(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_x509_trust_store_add_pem(client_conn->x509_validator.trust_store, cert_chain)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(client_conn, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - POSIX_GUARD(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type; - - POSIX_GUARD_RESULT(s2n_x509_validator_validate_cert_chain(&client_conn->x509_validator, client_conn, chain_data, chain_len, &pkey_type, &public_key_out)); - POSIX_GUARD(s2n_pkey_free(&public_key_out)); - } - - client_conn->actual_protocol_version = TLS_VERSIONS[(randval & 0x07) % s2n_array_len(TLS_VERSIONS)]; - client_conn->client_protocol_version = TLS_VERSIONS[((randval >> 3) & 0x07) % s2n_array_len(TLS_VERSIONS)]; - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_extension_list_recv(S2N_EXTENSION_LIST_CERTIFICATE, client_conn, &fuzz_stuffer); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(client_conn)); - POSIX_GUARD(s2n_stuffer_free(&fuzz_stuffer)); - - return S2N_SUCCESS; -} - -S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_certificate_extensions_parse + s2n_recv_server_sct_list s2n_server_certificate_status_recv + s2n_x509_validator_validate_cert_stapled_ocsp_response */ + +#include + +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls13.h" + +struct host_verify_data { + const char *name; + uint8_t found_name; + uint8_t callback_invoked; +}; + +static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 1; +} + +/* This test is for TLS versions 1.3 and up only */ +static const uint8_t TLS_VERSIONS[] = {S2N_TLS13}; + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least one byte of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 1); + + /* Setup */ + struct s2n_stuffer fuzz_stuffer = {0}; + POSIX_GUARD(s2n_stuffer_alloc(&fuzz_stuffer, len)); + POSIX_GUARD(s2n_stuffer_write_bytes(&fuzz_stuffer, buf, len)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "20240503")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + POSIX_ENSURE_REF(client_conn); + POSIX_GUARD(s2n_connection_set_config(client_conn, config)); + + /* Pull a byte off the libfuzzer input and use it to set parameters */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&fuzz_stuffer, &randval)); + client_conn->x509_validator.skip_cert_validation = (randval >> 7) % 2; + + /* Set connection to TLS 1.2 to temporary work around cert validation setup */ + client_conn->actual_protocol_version = S2N_TLS12; + + /* Set cert chain and trust store for verification of OCSP response */ + if ((randval >> 6) % 2 && OPENSSL_VERSION_NUMBER >= 0x10101000L) { + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + POSIX_GUARD(s2n_connection_set_verify_host_callback(client_conn, verify_host_accept_everything, &verify_data)); + char cert_chain[S2N_MAX_TEST_PEM_SIZE]; + POSIX_GUARD(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_x509_trust_store_add_pem(client_conn->x509_validator.trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(client_conn, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + POSIX_GUARD(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type; + + POSIX_GUARD_RESULT(s2n_x509_validator_validate_cert_chain(&client_conn->x509_validator, client_conn, chain_data, chain_len, &pkey_type, &public_key_out)); + POSIX_GUARD(s2n_pkey_free(&public_key_out)); + } + + client_conn->actual_protocol_version = TLS_VERSIONS[(randval & 0x07) % s2n_array_len(TLS_VERSIONS)]; + client_conn->client_protocol_version = TLS_VERSIONS[((randval >> 3) & 0x07) % s2n_array_len(TLS_VERSIONS)]; + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_extension_list_recv(S2N_EXTENSION_LIST_CERTIFICATE, client_conn, &fuzz_stuffer); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_stuffer_free(&fuzz_stuffer)); + + return S2N_SUCCESS; +} + +S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) diff --git a/tests/fuzz/s2n_client_key_recv_fuzz_test.c b/tests/fuzz/s2n_client_key_recv_fuzz_test.c index ff0925a73f0..767512e9ba5 100644 --- a/tests/fuzz/s2n_client_key_recv_fuzz_test.c +++ b/tests/fuzz/s2n_client_key_recv_fuzz_test.c @@ -1,149 +1,149 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Target Functions: s2n_client_key_recv s2n_kex_client_key_recv calculate_keys - s2n_hybrid_client_action s2n_kex_tls_prf s2n_prf_key_expansion - s2n_rsa_client_key_recv s2n_dhe_client_key_recv - s2n_ecdhe_client_key_recv s2n_kem_client_key_recv */ - -#include - -#include -#include - -#include "tls/s2n_kem.h" -#include "tls/s2n_client_key_exchange.h" -#include "tls/s2n_kex.h" -#include "tls/s2n_security_policies.h" - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; - -/* Connection setup variables */ -uint8_t *cert_chain_pem = NULL; -uint8_t *private_key_pem = NULL; -char *dhparams_pem = NULL; -uint32_t cert_chain_len = 0; -uint32_t private_key_len = 0; -struct s2n_config *config; -struct s2n_cert_chain_and_key *chain_and_key; -struct s2n_cert_chain_and_key *cert; -struct s2n_cipher_suite **test_suites; -int num_suites; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - test_suites = cipher_preferences_test_all.suites; - num_suites = cipher_preferences_test_all.count; - - /* One time Diffie-Hellman negotiation to speed along fuzz tests*/ - cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE); - private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE); - dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE); - - POSIX_ENSURE_REF(cert_chain_pem); - POSIX_ENSURE_REF(private_key_pem); - POSIX_ENSURE_REF(dhparams_pem); - - s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE); - s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE); - s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE); - - config = s2n_config_new(); - chain_and_key = s2n_cert_chain_and_key_new(); - - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(chain_and_key); - - s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len); - s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key); - s2n_config_add_dhparams(config, dhparams_pem); - - cert = s2n_config_get_single_default_cert(config); - POSIX_ENSURE_REF(cert); - - return S2N_SUCCESS; -} - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least two bytes of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 2); - - /* Setup */ - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - POSIX_ENSURE_REF(server_conn); - POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->handshake.io, buf, len)); - - /* Read bytes from the libfuzzer input and use them to set parameters */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); - server_conn->server_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; - - POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); - server_conn->secure->cipher_suite = test_suites[randval % num_suites]; - - /* Skip incompatible TLS 1.3 cipher suites */ - if (server_conn->secure->cipher_suite->key_exchange_alg == NULL) { - POSIX_GUARD(s2n_connection_free(server_conn)); - return S2N_SUCCESS; - } - - server_conn->handshake_params.our_chain_and_key = cert; - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(server_conn, &ecc_preferences)); - POSIX_ENSURE_REF(ecc_preferences); - - if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_ecdhe_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.server_ecc_evp_params); - } - - if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_kem_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { - server_conn->kex_params.kem_params.kem = &s2n_mlkem_768; - } - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_client_key_recv(server_conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(server_conn)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - s2n_config_free(config); - s2n_cert_chain_and_key_free(chain_and_key); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_client_key_recv s2n_kex_client_key_recv calculate_keys + s2n_hybrid_client_action s2n_kex_tls_prf s2n_prf_key_expansion + s2n_rsa_client_key_recv s2n_dhe_client_key_recv + s2n_ecdhe_client_key_recv s2n_kem_client_key_recv */ + +#include + +#include +#include + +#include "tls/s2n_kem.h" +#include "tls/s2n_client_key_exchange.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_security_policies.h" + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static const uint8_t TLS_VERSIONS[] = {S2N_TLS10, S2N_TLS11, S2N_TLS12}; + +/* Connection setup variables */ +uint8_t *cert_chain_pem = NULL; +uint8_t *private_key_pem = NULL; +char *dhparams_pem = NULL; +uint32_t cert_chain_len = 0; +uint32_t private_key_len = 0; +struct s2n_config *config; +struct s2n_cert_chain_and_key *chain_and_key; +struct s2n_cert_chain_and_key *cert; +struct s2n_cipher_suite **test_suites; +int num_suites; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + test_suites = cipher_preferences_test_all.suites; + num_suites = cipher_preferences_test_all.count; + + /* One time Diffie-Hellman negotiation to speed along fuzz tests*/ + cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE); + private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE); + dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE); + + POSIX_ENSURE_REF(cert_chain_pem); + POSIX_ENSURE_REF(private_key_pem); + POSIX_ENSURE_REF(dhparams_pem); + + s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE); + s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE); + s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE); + + config = s2n_config_new(); + chain_and_key = s2n_cert_chain_and_key_new(); + + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(chain_and_key); + + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len); + s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key); + s2n_config_add_dhparams(config, dhparams_pem); + + cert = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(cert); + + return S2N_SUCCESS; +} + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least two bytes of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 2); + + /* Setup */ + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + POSIX_ENSURE_REF(server_conn); + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->handshake.io, buf, len)); + + /* Read bytes from the libfuzzer input and use them to set parameters */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); + server_conn->server_protocol_version = TLS_VERSIONS[randval % s2n_array_len(TLS_VERSIONS)]; + + POSIX_GUARD(s2n_stuffer_read_uint8(&server_conn->handshake.io, &randval)); + server_conn->secure->cipher_suite = test_suites[randval % num_suites]; + + /* Skip incompatible TLS 1.3 cipher suites */ + if (server_conn->secure->cipher_suite->key_exchange_alg == NULL) { + POSIX_GUARD(s2n_connection_free(server_conn)); + return S2N_SUCCESS; + } + + server_conn->handshake_params.our_chain_and_key = cert; + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(server_conn, &ecc_preferences)); + POSIX_ENSURE_REF(ecc_preferences); + + if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_ecdhe_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.server_ecc_evp_params); + } + + if (server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_kem_client_key_recv || server_conn->secure->cipher_suite->key_exchange_alg->client_key_recv == s2n_hybrid_client_key_recv) { + server_conn->kex_params.kem_params.kem = &s2n_mlkem_768; + } + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_client_key_recv(server_conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(server_conn)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + s2n_config_free(config); + s2n_cert_chain_and_key_free(chain_and_key); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/fuzz/s2n_select_server_cert_test.c b/tests/fuzz/s2n_select_server_cert_test.c index 8cb825b7930..208262c0e5e 100644 --- a/tests/fuzz/s2n_select_server_cert_test.c +++ b/tests/fuzz/s2n_select_server_cert_test.c @@ -1,280 +1,280 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Target Functions: s2n_conn_find_name_matching_certs s2n_config_add_cert_chain_and_key_to_store - s2n_server_received_server_name s2n_find_cert_matches s2n_create_wildcard_hostname */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" - -#define MAX_TOKENS 1024 -#define MAX_CERTIFICATES 256 - -/* - * Tokenize the input fuzz buffer based on NULL bytes into output array and return the number of tokens. - * Avoiding extra heap allocation here to increase fuzz test rate. - */ -size_t find_strings(const uint8_t *buf, size_t len, const char **output_strings, size_t max_strings) -{ - size_t num_strings = 0; - int cursor = 0; - while(1) { - if (cursor >= len || num_strings == max_strings) { - return num_strings; - } - const char *cur_str = (const char *) (buf + cursor); - const char *next_null = (const char *) memchr((const void *) cur_str, '\0', (len - cursor)); - if (next_null == NULL) { - return num_strings; - } - - /* We found a null byte. Move the cursor beyond it. */ - cursor = (((const uint8_t *) next_null - buf) + 1); - if (cursor >= len) { - return num_strings; - } - output_strings[num_strings] = cur_str; - num_strings++; - } -} - -GENERAL_NAME *string_to_general_name(const char *str) -{ - ASN1_IA5STRING *asn1_name_str = ASN1_IA5STRING_new(); - if (!asn1_name_str) { - return NULL; - } - - if (!ASN1_STRING_set(asn1_name_str, str, strlen(str))) { - ASN1_IA5STRING_free(asn1_name_str); - return NULL; - } - - GENERAL_NAME *san_name = GENERAL_NAME_new(); - if (!san_name) { - ASN1_IA5STRING_free(asn1_name_str); - return NULL; - } - - GENERAL_NAME_set0_value(san_name, GEN_DNS, asn1_name_str); - return san_name; -} - -static int set_x509_sans(X509* x509_cert, const char **names, size_t num_sans) -{ - GENERAL_NAMES* san_names = sk_GENERAL_NAME_new_null(); - if (!san_names) { - return -1; - } - - for (int i = 0; i < num_sans; i++) { - GENERAL_NAME *san_name = string_to_general_name(names[i]); - if (!san_name) { - continue; - } - sk_GENERAL_NAME_push(san_names, san_name); - } - - - if (X509_add1_ext_i2d(x509_cert, NID_subject_alt_name, san_names, 0, X509V3_ADD_REPLACE) <= 0) { - GENERAL_NAMES_free(san_names); - return -1; - } - - GENERAL_NAMES_free(san_names); - return S2N_SUCCESS; -} - -static int set_x509_cns(X509 *x509_cert, const char **cns, size_t num_cns) -{ - X509_NAME *x509_name = X509_NAME_new(); - if (!x509_name) { - return -1; - } - - for (int i = 0; i < num_cns; i++) { - X509_NAME_add_entry_by_NID(x509_name, NID_commonName, MBSTRING_ASC, (unsigned char *)(uintptr_t) cns[i], -1, -1, 1); - } - - X509_set_subject_name(x509_cert, x509_name); - X509_NAME_free(x509_name); - return S2N_SUCCESS; -} - -static struct s2n_cert_chain_and_key *create_cert(const char **names, int num_names) -{ - struct s2n_cert_chain_and_key *cert = s2n_cert_chain_and_key_new(); - X509 *x509_cert = X509_new(); - - if (!x509_cert || !cert) { - goto cert_cleanup; - } - - /* Figure out if this cert should use SANs or CNs. s2n will only use one or the other - * for name matching. - */ - const uint8_t is_san = names[0][0] & 0x1; - if (is_san) { - if (set_x509_sans(x509_cert, names, num_names) < 0) { - goto cert_cleanup; - } - if (s2n_cert_chain_and_key_load_sans(cert, x509_cert) < 0) { - goto cert_cleanup; - } - } else { - if (set_x509_cns(x509_cert, names, num_names) < 0) { - goto cert_cleanup; - } - if (s2n_cert_chain_and_key_load_cns(cert, x509_cert) < 0) { - goto cert_cleanup; - } - } - X509_free(x509_cert); - x509_cert = NULL; - - /* Figure out if this should be an RSA or ECDSA certificate */ - s2n_pkey_type pkey_type = (names[0][0] & 0x2) ? S2N_PKEY_TYPE_RSA: S2N_PKEY_TYPE_ECDSA; - struct s2n_cert *head = calloc(1, sizeof(struct s2n_cert)); - if (!head) { - goto cert_cleanup; - } - - head->pkey_type = pkey_type; - cert->cert_chain->head = head; - return cert; - -cert_cleanup: - if (cert) { s2n_cert_chain_and_key_free(cert); } - if (x509_cert) { X509_free(x509_cert); } - return NULL; -} - -/* - * Try to create some number of certificates with SANs/CNs provided by a list of input C strings. Return the number of - * certificates created. - */ -static size_t create_certs(const char **strings, unsigned int num_strings, struct s2n_cert_chain_and_key **out_certs, unsigned int num_certs) -{ - if (num_certs == 0) { - return S2N_SUCCESS; - } - - /* We want the fuzz input to give us at least 1 domain name string for each cert */ - if (num_strings <= num_certs) { - return S2N_SUCCESS; - } - - const int num_names_per_cert = num_strings / num_certs; - size_t num_certs_added = 0; - for (int i = 0; i < num_certs; i++) { - struct s2n_cert_chain_and_key *cert = create_cert((&strings[i*num_names_per_cert]), num_names_per_cert); - if (!cert) { - continue; - } - out_certs[num_certs_added] = cert; - num_certs_added++; - } - - return num_certs_added; -} - - -/* - * This fuzz test uses the fuzz input to: - * - Generate the data to populate in the SAN of a certificate - * - Generate the data to populate the SNI TLS extension - * - Fuzz the certificate matching function in s2n: s2n_cert_chain_and_key_matches_name - */ -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - struct s2n_config *config = s2n_config_new(); - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - struct s2n_cert_chain_and_key *certs[MAX_CERTIFICATES] = { NULL }; - - if (!config || !conn || len == 0) { - goto cleanup; - } - - /* Create an array of strings based on fuzz input. To populate the cert SANs,CN and - * input hostname. - */ - const char *strings[MAX_TOKENS] = { NULL }; - size_t num_strings = find_strings(buf, len, strings, MAX_TOKENS); - if (num_strings == 0) { - goto cleanup; - } - - if (s2n_connection_set_config(conn, config) < 0) { - goto cleanup; - } - - const int max_certs_to_create = buf[0] % MAX_CERTIFICATES; - const int num_certs = create_certs(strings, num_strings, certs, max_certs_to_create); - if (num_certs == 0) { - goto cleanup; - } - - /* Add all of the certs created by fuzz input to store. */ - for (int i = 0; i < num_certs; i++) { - if (s2n_config_add_cert_chain_and_key_to_store(config, certs[i]) < 0) { - goto cleanup; - } - } - - for (int i = 0; i < num_strings; i++) { - strncpy(conn->server_name, strings[i], S2N_MAX_SERVER_NAME); - /* Not checking the return value as we aren't using this fuzz test for matching correctness. */ - s2n_conn_find_name_matching_certs(conn); - } -cleanup: - for (int i = 0; i < MAX_CERTIFICATES; i++) { - if (certs[i]) { - free(certs[i]->cert_chain->head); - certs[i]->cert_chain->head = NULL; - s2n_cert_chain_and_key_free(certs[i]); - } - } - if (conn != NULL) { s2n_connection_free(conn); } - if (config != NULL) { s2n_config_free(config); } - - return S2N_SUCCESS; -} - -S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_conn_find_name_matching_certs s2n_config_add_cert_chain_and_key_to_store + s2n_server_received_server_name s2n_find_cert_matches s2n_create_wildcard_hostname */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" + +#define MAX_TOKENS 1024 +#define MAX_CERTIFICATES 256 + +/* + * Tokenize the input fuzz buffer based on NULL bytes into output array and return the number of tokens. + * Avoiding extra heap allocation here to increase fuzz test rate. + */ +size_t find_strings(const uint8_t *buf, size_t len, const char **output_strings, size_t max_strings) +{ + size_t num_strings = 0; + int cursor = 0; + while(1) { + if (cursor >= len || num_strings == max_strings) { + return num_strings; + } + const char *cur_str = (const char *) (buf + cursor); + const char *next_null = (const char *) memchr((const void *) cur_str, '\0', (len - cursor)); + if (next_null == NULL) { + return num_strings; + } + + /* We found a null byte. Move the cursor beyond it. */ + cursor = (((const uint8_t *) next_null - buf) + 1); + if (cursor >= len) { + return num_strings; + } + output_strings[num_strings] = cur_str; + num_strings++; + } +} + +GENERAL_NAME *string_to_general_name(const char *str) +{ + ASN1_IA5STRING *asn1_name_str = ASN1_IA5STRING_new(); + if (!asn1_name_str) { + return NULL; + } + + if (!ASN1_STRING_set(asn1_name_str, str, strlen(str))) { + ASN1_IA5STRING_free(asn1_name_str); + return NULL; + } + + GENERAL_NAME *san_name = GENERAL_NAME_new(); + if (!san_name) { + ASN1_IA5STRING_free(asn1_name_str); + return NULL; + } + + GENERAL_NAME_set0_value(san_name, GEN_DNS, asn1_name_str); + return san_name; +} + +static int set_x509_sans(X509* x509_cert, const char **names, size_t num_sans) +{ + GENERAL_NAMES* san_names = sk_GENERAL_NAME_new_null(); + if (!san_names) { + return -1; + } + + for (int i = 0; i < num_sans; i++) { + GENERAL_NAME *san_name = string_to_general_name(names[i]); + if (!san_name) { + continue; + } + sk_GENERAL_NAME_push(san_names, san_name); + } + + + if (X509_add1_ext_i2d(x509_cert, NID_subject_alt_name, san_names, 0, X509V3_ADD_REPLACE) <= 0) { + GENERAL_NAMES_free(san_names); + return -1; + } + + GENERAL_NAMES_free(san_names); + return S2N_SUCCESS; +} + +static int set_x509_cns(X509 *x509_cert, const char **cns, size_t num_cns) +{ + X509_NAME *x509_name = X509_NAME_new(); + if (!x509_name) { + return -1; + } + + for (int i = 0; i < num_cns; i++) { + X509_NAME_add_entry_by_NID(x509_name, NID_commonName, MBSTRING_ASC, (unsigned char *)(uintptr_t) cns[i], -1, -1, 1); + } + + X509_set_subject_name(x509_cert, x509_name); + X509_NAME_free(x509_name); + return S2N_SUCCESS; +} + +static struct s2n_cert_chain_and_key *create_cert(const char **names, int num_names) +{ + struct s2n_cert_chain_and_key *cert = s2n_cert_chain_and_key_new(); + X509 *x509_cert = X509_new(); + + if (!x509_cert || !cert) { + goto cert_cleanup; + } + + /* Figure out if this cert should use SANs or CNs. s2n will only use one or the other + * for name matching. + */ + const uint8_t is_san = names[0][0] & 0x1; + if (is_san) { + if (set_x509_sans(x509_cert, names, num_names) < 0) { + goto cert_cleanup; + } + if (s2n_cert_chain_and_key_load_sans(cert, x509_cert) < 0) { + goto cert_cleanup; + } + } else { + if (set_x509_cns(x509_cert, names, num_names) < 0) { + goto cert_cleanup; + } + if (s2n_cert_chain_and_key_load_cns(cert, x509_cert) < 0) { + goto cert_cleanup; + } + } + X509_free(x509_cert); + x509_cert = NULL; + + /* Figure out if this should be an RSA or ECDSA certificate */ + s2n_pkey_type pkey_type = (names[0][0] & 0x2) ? S2N_PKEY_TYPE_RSA: S2N_PKEY_TYPE_ECDSA; + struct s2n_cert *head = calloc(1, sizeof(struct s2n_cert)); + if (!head) { + goto cert_cleanup; + } + + head->pkey_type = pkey_type; + cert->cert_chain->head = head; + return cert; + +cert_cleanup: + if (cert) { s2n_cert_chain_and_key_free(cert); } + if (x509_cert) { X509_free(x509_cert); } + return NULL; +} + +/* + * Try to create some number of certificates with SANs/CNs provided by a list of input C strings. Return the number of + * certificates created. + */ +static size_t create_certs(const char **strings, unsigned int num_strings, struct s2n_cert_chain_and_key **out_certs, unsigned int num_certs) +{ + if (num_certs == 0) { + return S2N_SUCCESS; + } + + /* We want the fuzz input to give us at least 1 domain name string for each cert */ + if (num_strings <= num_certs) { + return S2N_SUCCESS; + } + + const int num_names_per_cert = num_strings / num_certs; + size_t num_certs_added = 0; + for (int i = 0; i < num_certs; i++) { + struct s2n_cert_chain_and_key *cert = create_cert((&strings[i*num_names_per_cert]), num_names_per_cert); + if (!cert) { + continue; + } + out_certs[num_certs_added] = cert; + num_certs_added++; + } + + return num_certs_added; +} + + +/* + * This fuzz test uses the fuzz input to: + * - Generate the data to populate in the SAN of a certificate + * - Generate the data to populate the SNI TLS extension + * - Fuzz the certificate matching function in s2n: s2n_cert_chain_and_key_matches_name + */ +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + struct s2n_cert_chain_and_key *certs[MAX_CERTIFICATES] = { NULL }; + + if (!config || !conn || len == 0) { + goto cleanup; + } + + /* Create an array of strings based on fuzz input. To populate the cert SANs,CN and + * input hostname. + */ + const char *strings[MAX_TOKENS] = { NULL }; + size_t num_strings = find_strings(buf, len, strings, MAX_TOKENS); + if (num_strings == 0) { + goto cleanup; + } + + if (s2n_connection_set_config(conn, config) < 0) { + goto cleanup; + } + + const int max_certs_to_create = buf[0] % MAX_CERTIFICATES; + const int num_certs = create_certs(strings, num_strings, certs, max_certs_to_create); + if (num_certs == 0) { + goto cleanup; + } + + /* Add all of the certs created by fuzz input to store. */ + for (int i = 0; i < num_certs; i++) { + if (s2n_config_add_cert_chain_and_key_to_store(config, certs[i]) < 0) { + goto cleanup; + } + } + + for (int i = 0; i < num_strings; i++) { + strncpy(conn->server_name, strings[i], S2N_MAX_SERVER_NAME); + /* Not checking the return value as we aren't using this fuzz test for matching correctness. */ + s2n_conn_find_name_matching_certs(conn); + } +cleanup: + for (int i = 0; i < MAX_CERTIFICATES; i++) { + if (certs[i]) { + free(certs[i]->cert_chain->head); + certs[i]->cert_chain->head = NULL; + s2n_cert_chain_and_key_free(certs[i]); + } + } + if (conn != NULL) { s2n_connection_free(conn); } + if (config != NULL) { s2n_config_free(config); } + + return S2N_SUCCESS; +} + +S2N_FUZZ_TARGET(NULL, s2n_fuzz_test, NULL) diff --git a/tests/fuzz/s2n_tls13_cert_req_recv_test.c b/tests/fuzz/s2n_tls13_cert_req_recv_test.c index 796976ea3b7..b314911efb1 100644 --- a/tests/fuzz/s2n_tls13_cert_req_recv_test.c +++ b/tests/fuzz/s2n_tls13_cert_req_recv_test.c @@ -1,82 +1,82 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Target Functions: s2n_tls13_cert_req_recv s2n_extension_list_recv s2n_extension_process - s2n_extension_list_process s2n_extension_parse s2n_extension_list_parse */ - -#include - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static char *cert_chain, *private_key; -struct s2n_cert_chain_and_key *default_cert; -struct s2n_config *client_config; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - /* Initialize test chain and key */ - cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(cert_chain); - private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(private_key); - default_cert = s2n_cert_chain_and_key_new(); - POSIX_ENSURE_REF(default_cert); - client_config = s2n_config_new(); - POSIX_ENSURE_REF(client_config); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); - return S2N_SUCCESS; -} - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* Setup */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_SERVER); - POSIX_ENSURE_REF(client_conn); - POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); - POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_tls13_cert_req_recv(client_conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(client_conn)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - s2n_config_free(client_config); - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(default_cert); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_tls13_cert_req_recv s2n_extension_list_recv s2n_extension_process + s2n_extension_list_process s2n_extension_parse s2n_extension_list_parse */ + +#include + +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static char *cert_chain, *private_key; +struct s2n_cert_chain_and_key *default_cert; +struct s2n_config *client_config; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + /* Initialize test chain and key */ + cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(cert_chain); + private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(private_key); + default_cert = s2n_cert_chain_and_key_new(); + POSIX_ENSURE_REF(default_cert); + client_config = s2n_config_new(); + POSIX_ENSURE_REF(client_config); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + return S2N_SUCCESS; +} + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* Setup */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_SERVER); + POSIX_ENSURE_REF(client_conn); + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_stuffer_write_bytes(&client_conn->handshake.io, buf, len)); + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_tls13_cert_req_recv(client_conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(client_conn)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + s2n_config_free(client_config); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(default_cert); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/fuzz/s2n_tls13_cert_verify_recv_test.c b/tests/fuzz/s2n_tls13_cert_verify_recv_test.c index 544ef6fda65..34f2c5601c8 100644 --- a/tests/fuzz/s2n_tls13_cert_verify_recv_test.c +++ b/tests/fuzz/s2n_tls13_cert_verify_recv_test.c @@ -1,96 +1,96 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Target Functions: s2n_tls13_cert_verify_recv s2n_signature_algorithm_recv - s2n_tls13_cert_read_and_verify_signature */ - -#include - -#include -#include - -#include "api/s2n.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -uint8_t *cert_chain = NULL; -uint8_t *private_key = NULL; -uint32_t cert_chain_len = 0; -uint32_t private_key_len = 0; -struct s2n_cert_chain_and_key *default_cert; -struct s2n_config *conn_config; - -int s2n_fuzz_init(int *argc, char **argv[]) -{ - /* Initialize test chain and key */ - cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(cert_chain); - private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - POSIX_ENSURE_REF(private_key); - default_cert = s2n_cert_chain_and_key_new(); - POSIX_ENSURE_REF(default_cert); - conn_config = s2n_config_new(); - POSIX_ENSURE_REF(conn_config); - POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(conn_config, default_cert)); - - return S2N_SUCCESS; -} - -int s2n_fuzz_test(const uint8_t *buf, size_t len) -{ - /* We need at least one byte of input to set parameters */ - S2N_FUZZ_ENSURE_MIN_LEN(len, 1); - - /* Setup */ - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_set_config(conn, conn_config)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, buf, len)); - - /* Pull a byte off the libfuzzer input and use it to set the connection mode */ - uint8_t randval = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&conn->handshake.io, &randval)); - if (randval % 2) { - conn->mode = S2N_CLIENT; - } - - /* Run Test - * Do not use GUARD macro here since the connection memory hasn't been freed. - */ - s2n_tls13_cert_verify_recv(conn); - - /* Cleanup */ - POSIX_GUARD(s2n_connection_free(conn)); - - return S2N_SUCCESS; -} - -static void s2n_fuzz_cleanup() -{ - s2n_config_free(conn_config); - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(default_cert); -} - -S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Target Functions: s2n_tls13_cert_verify_recv s2n_signature_algorithm_recv + s2n_tls13_cert_read_and_verify_signature */ + +#include + +#include +#include + +#include "api/s2n.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +uint8_t *cert_chain = NULL; +uint8_t *private_key = NULL; +uint32_t cert_chain_len = 0; +uint32_t private_key_len = 0; +struct s2n_cert_chain_and_key *default_cert; +struct s2n_config *conn_config; + +int s2n_fuzz_init(int *argc, char **argv[]) +{ + /* Initialize test chain and key */ + cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(cert_chain); + private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + POSIX_ENSURE_REF(private_key); + default_cert = s2n_cert_chain_and_key_new(); + POSIX_ENSURE_REF(default_cert); + conn_config = s2n_config_new(); + POSIX_ENSURE_REF(conn_config); + POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(conn_config, default_cert)); + + return S2N_SUCCESS; +} + +int s2n_fuzz_test(const uint8_t *buf, size_t len) +{ + /* We need at least one byte of input to set parameters */ + S2N_FUZZ_ENSURE_MIN_LEN(len, 1); + + /* Setup */ + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_set_config(conn, conn_config)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, buf, len)); + + /* Pull a byte off the libfuzzer input and use it to set the connection mode */ + uint8_t randval = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&conn->handshake.io, &randval)); + if (randval % 2) { + conn->mode = S2N_CLIENT; + } + + /* Run Test + * Do not use GUARD macro here since the connection memory hasn't been freed. + */ + s2n_tls13_cert_verify_recv(conn); + + /* Cleanup */ + POSIX_GUARD(s2n_connection_free(conn)); + + return S2N_SUCCESS; +} + +static void s2n_fuzz_cleanup() +{ + s2n_config_free(conn_config); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(default_cert); +} + +S2N_FUZZ_TARGET(s2n_fuzz_init, s2n_fuzz_test, s2n_fuzz_cleanup) diff --git a/tests/pems/rsa_2048_pkcs1_cert_crlf.pem b/tests/pems/rsa_2048_pkcs1_cert_crlf.pem index 700c71208a7..60e940e7748 100644 --- a/tests/pems/rsa_2048_pkcs1_cert_crlf.pem +++ b/tests/pems/rsa_2048_pkcs1_cert_crlf.pem @@ -1,56 +1,56 @@ ------BEGIN CERTIFICATE----- -MIICrTCCAZUCAn3VMA0GCSqGSIb3DQEBBQUAMB4xHDAaBgNVBAMME3MyblRlc3RJ -bnRlcm1lZGlhdGUwIBcNMTYwMzMwMTg1NzQzWhgPMjExNjAzMDYxODU3NDNaMBgx -FjAUBgNVBAMMDXMyblRlc3RTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDRw6AuYXAeRT0YuptCfJjRB/EDJyyGXnv+8TV2H1WJWhMLk8qND27r -79A6EjbVmJaOV9qrokVqpDmXS712Z3BDprJ+1LFMymm3A+AFuK/skeGy0skik+Tg -MmFT5XBVvmsw4uB1S9uUqktHauXgjhFPPsfvk4ewL4LulVEN2TEeI1Odj4CaMxAO -Iuowm8wI2OHVzRHlrRmyJ9hYGuHHQ2TaTGIjr3WpAFuXi9pHGGMYa0uXAVPmgjdE -XZ8t46u/ZKQ9W1uJkZEVKhcijT7G2VBrsBUq0CDiL+TDaGfthnBzUc9zt4fx/S/3 -qulC2WbKI3xrasQyjrsHTAJ75Md3rK09AgMBAAEwDQYJKoZIhvcNAQEFBQADggEB -AHHkXNA9BtgAebZC2zriW4hRfeIkJMOwvfKBXHTuY5iCLD1otis6AZljcCKXM6O9 -489eHBC4T6mJwVsXhH+/ccEKqNRD2bUfQgOij32PsteV1eOHfHIFqdJmnBVb8tYa -jxUvy7UQvXrPqaHbODrHe+7f7r1YCzerujiP5SSHphY3GQq88KemfFczp/4GnYas -sE50OYe7DQcB4zvnxmAXp51JIN4ooktUU9oKIM5y2cgEWdmJzeqPANYxf0ZIPlTg -ETknKw1Dzf8wlK5mFbbG4LPQh1mkDVcwQV3ogG6kGMRa7neH+6SFkNpAKuPCoje4 -NAE+WQ5ve1wk7nIRTQwDAF4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDKTCCAhGgAwIBAgICVxYwDQYJKoZIhvcNAQEFBQAwFjEUMBIGA1UEAwwLczJu -VGVzdFJvb3QwIBcNMTYwMzMwMTg1NzA5WhgPMjExNjAzMDYxODU3MDlaMB4xHDAa -BgNVBAMME3MyblRlc3RJbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDM/i3eclxYcvedPCEnVe6A/HYsYPeP1qKBZQhbpuuX061jFZKw -lecb0eau1PORLbcsYK40u3xUzoA5u6Q0ebDuqPbqSJkCazsh66cu9STl8ubbk7oI -8LJjUJFhhy2Jmm9krXhPyRscU+CXOCZ2G1GhBqTI8cgMYhEVHwb3qy1EHg6G3n4W -AjV+cKQcbUytq8DRmVe0bNJxDOX8ivzfAp3lUIwub+JfpxrWIUhb3iVGj5CauI98 -bNFHTWwYp7tviIIi21Q+L3nExCyE4yTUP/mebBZ62JnbvsWSs3r3//Am5d8G3WdY -BXsERoDoLBvHnqlO/oo4ppGCRI7GkDroACi/AgMBAAGjdzB1MAwGA1UdEwQFMAMB -Af8wHQYDVR0OBBYEFGqUKVWVlL03sHuOggFACdlHckPBMEYGA1UdIwQ/MD2AFE2X -AbNDryMlBpMNI6Ce927uUFwToRqkGDAWMRQwEgYDVQQDDAtzMm5UZXN0Um9vdIIJ -ANDUkH+UYdz1MA0GCSqGSIb3DQEBBQUAA4IBAQA3O3S9VT0EC1yG4xyNNUZ7+CzF -uFA6uiO38ygcN5Nz1oNPy2eQer7vYmrHtqN6gS/o1Ag5F8bLRCqeuZTsOG80O29H -kNhs5xYprdU82AqcaWwEd0kDrhC5rEvs6fj1J0NKmmhbovYxuDboj0a7If7HEqX0 -NizyU3M3JONPZgadchZ+F5DosatF1Bpt/gsQRy383IogQ0/FS+juHCCc4VIUemuk -YY1J8o5XdrGWrPBBiudTWqCobe+N541b+YLWbajT5UKzvSqJmcqpPTniJGc9eZxc -z3cCNd3cKa9bK51stEnQSlA7PQXYs3K+TD3EmSn/G2x6Hmfr7lrpbIhEaD+y ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDATCCAemgAwIBAgIJANDUkH+UYdz1MA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV -BAMMC3MyblRlc3RSb290MCAXDTE2MDMzMDE4NTYzOVoYDzIxMTYwMzA2MTg1NjM5 -WjAWMRQwEgYDVQQDDAtzMm5UZXN0Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAMY5532000oaeed7Jmo3ssx1723ZDLpn3WGz6FxpWM0zsKA/YvdD -7J6qXDvfxU6dZlmsCS+bSNAqpARKmKsBEDPTsdLmrN1V1clOxvKm6GvU1eloRTw6 -xukEUXJ+uxrQMLYvSJBiCBVGI+UYNCK5c6guNMRYBCGdk5/iayjmK0Nxz1918Cx9 -z4va8HPAgYIz0ogOdYB21O9FQGPdH1mYqRzljcSsZ7EFo1P8HJr8oKK76ZeYi2or -pjzMHGnlufHaul508wQPeFAMa1Tku3HyGZRaieRAck6+QcO2NujXxKNyCBlWON23 -FQTuBjN/CAl74MZtcAM2hVSmpm9t4cWVN5MCAwEAAaNQME4wHQYDVR0OBBYEFE2X -AbNDryMlBpMNI6Ce927uUFwTMB8GA1UdIwQYMBaAFE2XAbNDryMlBpMNI6Ce927u -UFwTMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAXkVvQdXDmozPix -uZi1o9cw4Si0syqfJ4sSunrzPbbmw/Qxhth5V7XGrnsQVNxamgnbzpjGhiBF6isM -ldj33zQYtke+ojOjFlhEvrPo6eW29RkLBEtJadGs2bkMLztJbf+cbH2u6irzr6S4 -3OgVOSuB+zG56ksTnEVmum+C/8tSIAyi3eaoStPcgEU8+3/KMrH7uuenmTOCKdD1 -FvSDHXT9qPgTttVQGXbXzJEr5tGE+Py6yib5uoJ0dJZNtjs7HOQEDk5J0wZaX0DC -MShYLiN5qLJAk0qwl+js488BJ18M9dg4TxdBYFkwHSzKXSj9TJN77Bb0RZr8LL9T -r9IyvfU= ------END CERTIFICATE----- - +-----BEGIN CERTIFICATE----- +MIICrTCCAZUCAn3VMA0GCSqGSIb3DQEBBQUAMB4xHDAaBgNVBAMME3MyblRlc3RJ +bnRlcm1lZGlhdGUwIBcNMTYwMzMwMTg1NzQzWhgPMjExNjAzMDYxODU3NDNaMBgx +FjAUBgNVBAMMDXMyblRlc3RTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDRw6AuYXAeRT0YuptCfJjRB/EDJyyGXnv+8TV2H1WJWhMLk8qND27r +79A6EjbVmJaOV9qrokVqpDmXS712Z3BDprJ+1LFMymm3A+AFuK/skeGy0skik+Tg +MmFT5XBVvmsw4uB1S9uUqktHauXgjhFPPsfvk4ewL4LulVEN2TEeI1Odj4CaMxAO +Iuowm8wI2OHVzRHlrRmyJ9hYGuHHQ2TaTGIjr3WpAFuXi9pHGGMYa0uXAVPmgjdE +XZ8t46u/ZKQ9W1uJkZEVKhcijT7G2VBrsBUq0CDiL+TDaGfthnBzUc9zt4fx/S/3 +qulC2WbKI3xrasQyjrsHTAJ75Md3rK09AgMBAAEwDQYJKoZIhvcNAQEFBQADggEB +AHHkXNA9BtgAebZC2zriW4hRfeIkJMOwvfKBXHTuY5iCLD1otis6AZljcCKXM6O9 +489eHBC4T6mJwVsXhH+/ccEKqNRD2bUfQgOij32PsteV1eOHfHIFqdJmnBVb8tYa +jxUvy7UQvXrPqaHbODrHe+7f7r1YCzerujiP5SSHphY3GQq88KemfFczp/4GnYas +sE50OYe7DQcB4zvnxmAXp51JIN4ooktUU9oKIM5y2cgEWdmJzeqPANYxf0ZIPlTg +ETknKw1Dzf8wlK5mFbbG4LPQh1mkDVcwQV3ogG6kGMRa7neH+6SFkNpAKuPCoje4 +NAE+WQ5ve1wk7nIRTQwDAF4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgICVxYwDQYJKoZIhvcNAQEFBQAwFjEUMBIGA1UEAwwLczJu +VGVzdFJvb3QwIBcNMTYwMzMwMTg1NzA5WhgPMjExNjAzMDYxODU3MDlaMB4xHDAa +BgNVBAMME3MyblRlc3RJbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDM/i3eclxYcvedPCEnVe6A/HYsYPeP1qKBZQhbpuuX061jFZKw +lecb0eau1PORLbcsYK40u3xUzoA5u6Q0ebDuqPbqSJkCazsh66cu9STl8ubbk7oI +8LJjUJFhhy2Jmm9krXhPyRscU+CXOCZ2G1GhBqTI8cgMYhEVHwb3qy1EHg6G3n4W +AjV+cKQcbUytq8DRmVe0bNJxDOX8ivzfAp3lUIwub+JfpxrWIUhb3iVGj5CauI98 +bNFHTWwYp7tviIIi21Q+L3nExCyE4yTUP/mebBZ62JnbvsWSs3r3//Am5d8G3WdY +BXsERoDoLBvHnqlO/oo4ppGCRI7GkDroACi/AgMBAAGjdzB1MAwGA1UdEwQFMAMB +Af8wHQYDVR0OBBYEFGqUKVWVlL03sHuOggFACdlHckPBMEYGA1UdIwQ/MD2AFE2X +AbNDryMlBpMNI6Ce927uUFwToRqkGDAWMRQwEgYDVQQDDAtzMm5UZXN0Um9vdIIJ +ANDUkH+UYdz1MA0GCSqGSIb3DQEBBQUAA4IBAQA3O3S9VT0EC1yG4xyNNUZ7+CzF +uFA6uiO38ygcN5Nz1oNPy2eQer7vYmrHtqN6gS/o1Ag5F8bLRCqeuZTsOG80O29H +kNhs5xYprdU82AqcaWwEd0kDrhC5rEvs6fj1J0NKmmhbovYxuDboj0a7If7HEqX0 +NizyU3M3JONPZgadchZ+F5DosatF1Bpt/gsQRy383IogQ0/FS+juHCCc4VIUemuk +YY1J8o5XdrGWrPBBiudTWqCobe+N541b+YLWbajT5UKzvSqJmcqpPTniJGc9eZxc +z3cCNd3cKa9bK51stEnQSlA7PQXYs3K+TD3EmSn/G2x6Hmfr7lrpbIhEaD+y +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDATCCAemgAwIBAgIJANDUkH+UYdz1MA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV +BAMMC3MyblRlc3RSb290MCAXDTE2MDMzMDE4NTYzOVoYDzIxMTYwMzA2MTg1NjM5 +WjAWMRQwEgYDVQQDDAtzMm5UZXN0Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMY5532000oaeed7Jmo3ssx1723ZDLpn3WGz6FxpWM0zsKA/YvdD +7J6qXDvfxU6dZlmsCS+bSNAqpARKmKsBEDPTsdLmrN1V1clOxvKm6GvU1eloRTw6 +xukEUXJ+uxrQMLYvSJBiCBVGI+UYNCK5c6guNMRYBCGdk5/iayjmK0Nxz1918Cx9 +z4va8HPAgYIz0ogOdYB21O9FQGPdH1mYqRzljcSsZ7EFo1P8HJr8oKK76ZeYi2or +pjzMHGnlufHaul508wQPeFAMa1Tku3HyGZRaieRAck6+QcO2NujXxKNyCBlWON23 +FQTuBjN/CAl74MZtcAM2hVSmpm9t4cWVN5MCAwEAAaNQME4wHQYDVR0OBBYEFE2X +AbNDryMlBpMNI6Ce927uUFwTMB8GA1UdIwQYMBaAFE2XAbNDryMlBpMNI6Ce927u +UFwTMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAXkVvQdXDmozPix +uZi1o9cw4Si0syqfJ4sSunrzPbbmw/Qxhth5V7XGrnsQVNxamgnbzpjGhiBF6isM +ldj33zQYtke+ojOjFlhEvrPo6eW29RkLBEtJadGs2bkMLztJbf+cbH2u6irzr6S4 +3OgVOSuB+zG56ksTnEVmum+C/8tSIAyi3eaoStPcgEU8+3/KMrH7uuenmTOCKdD1 +FvSDHXT9qPgTttVQGXbXzJEr5tGE+Py6yib5uoJ0dJZNtjs7HOQEDk5J0wZaX0DC +MShYLiN5qLJAk0qwl+js488BJ18M9dg4TxdBYFkwHSzKXSj9TJN77Bb0RZr8LL9T +r9IyvfU= +-----END CERTIFICATE----- + diff --git a/tests/pems/rsa_2048_pkcs1_key_crlf.pem b/tests/pems/rsa_2048_pkcs1_key_crlf.pem index 8160dff2441..e07f8e205ba 100644 --- a/tests/pems/rsa_2048_pkcs1_key_crlf.pem +++ b/tests/pems/rsa_2048_pkcs1_key_crlf.pem @@ -1,28 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA0cOgLmFwHkU9GLqbQnyY0QfxAycshl57/vE1dh9ViVoTC5PK -jQ9u6+/QOhI21ZiWjlfaq6JFaqQ5l0u9dmdwQ6ayftSxTMpptwPgBbiv7JHhstLJ -IpPk4DJhU+VwVb5rMOLgdUvblKpLR2rl4I4RTz7H75OHsC+C7pVRDdkxHiNTnY+A -mjMQDiLqMJvMCNjh1c0R5a0ZsifYWBrhx0Nk2kxiI691qQBbl4vaRxhjGGtLlwFT -5oI3RF2fLeOrv2SkPVtbiZGRFSoXIo0+xtlQa7AVKtAg4i/kw2hn7YZwc1HPc7eH -8f0v96rpQtlmyiN8a2rEMo67B0wCe+THd6ytPQIDAQABAoIBAF3evYAD+riRI5Y9 -a92FBJ4Gf8R5c2NuRO8B4nrJ6u1ccclsieg2T90lpHlYTVGoxzdL+X91Trs6Ysti -CZdDEuozXw2DARTsQAK2qTnmPFQRtH7h9UCUDoiGAygYNP0qCa4G2YukNs+Apc9/ -9v9WlEhyP+bmjoI5wM4j4/HekCx7syHuiqJ74//oTzNamT0aWHwgXAUmEYZ/1+nT -0KInmtmIOFgsWHcojwQ6sZJ3eVvy66EqHLZKQYZa2tx0YjrEJMQi1drg6VV+lLCR -rEtsoltgdN2G9v3P6KrHXsrCYaaZKhog9B1OSI2Amv3YWZHXppK12+aSy774lUUz -qVur5cECgYEA7oCOQoRZo76wztS+yDeq173B2gPHKSIrWvaLDkCAPOQPVzJZ4Qc+ -8OEDU6HB9P0MYDsKBxZY85uzWP+dAlsmcL0C86WibOuYERPKQIcAn3KSzFiIxH3R -OAbaLtSLN3lDAH50PhP9BguiSfBjI6w4Qsr7jlQgdpzG4h4LjvotbWMCgYEA4SdT -QQJhHiLtBFo91ItRUzhePvUDfV8XvNfAwZj8cY2+oenkK2+bp35xteBV6Gu1cYnd -V2yFgzMZ/jDvqfUn/8EVAGvEFrLtsUpXeyHhgmVT490RsPxC9xU9jf5LsvZ4zjsj -CsFZW0JnhKkF6M5wztWtO3yKCilmXSOIFvorTN8CgYEAoK2LKdTwbxhxFWbOgSS/ -vEji6HXTHysd+lJOrHNX8a3Th/MsCiZPiQiOrTE08k/onown3U547uXelf7fUE8I -PruX2X2lR6wQ7rBeecp56PHPZEvhGD+LTCuRoise/2h6c0K+HXRp6kC8PQPuRoIo -BRerEeArXr2QX5XOQ6zYHfECgYEAp0L9mDfaSfcMOMWJVVJCEh639PEzrHluOv3U -1n1+XCU+zy3gMVxyN9W5R7HmYAlT+4q9geq+rJ7T2oAkKxBSrK6VmYB1ZZ968NAX -eQPMcYAw+AAM2nwsiz2eQtP9DHAJgrtv5teIOEF2gZjHKRHjv+QBE0YLjkz/HIX+ -3YLvk+UCgYAgpAWk4YW4dAcZ8Y04Ke2pjMvEu44hHphOmk6AZl0Xl9tJwxlV8GVx -o3L4hbjHqyJo3+DZZYM7udMx9axbX9JHYRaLNJpc8UxQZj7d3TehC9Dw9/DzhIy/ -6sml30j/GHvnW5DOlpsdNKDlxoFX+hncXYIjyVTGRNdsSwa4VVm+Xw== ------END RSA PRIVATE KEY----- - +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0cOgLmFwHkU9GLqbQnyY0QfxAycshl57/vE1dh9ViVoTC5PK +jQ9u6+/QOhI21ZiWjlfaq6JFaqQ5l0u9dmdwQ6ayftSxTMpptwPgBbiv7JHhstLJ +IpPk4DJhU+VwVb5rMOLgdUvblKpLR2rl4I4RTz7H75OHsC+C7pVRDdkxHiNTnY+A +mjMQDiLqMJvMCNjh1c0R5a0ZsifYWBrhx0Nk2kxiI691qQBbl4vaRxhjGGtLlwFT +5oI3RF2fLeOrv2SkPVtbiZGRFSoXIo0+xtlQa7AVKtAg4i/kw2hn7YZwc1HPc7eH +8f0v96rpQtlmyiN8a2rEMo67B0wCe+THd6ytPQIDAQABAoIBAF3evYAD+riRI5Y9 +a92FBJ4Gf8R5c2NuRO8B4nrJ6u1ccclsieg2T90lpHlYTVGoxzdL+X91Trs6Ysti +CZdDEuozXw2DARTsQAK2qTnmPFQRtH7h9UCUDoiGAygYNP0qCa4G2YukNs+Apc9/ +9v9WlEhyP+bmjoI5wM4j4/HekCx7syHuiqJ74//oTzNamT0aWHwgXAUmEYZ/1+nT +0KInmtmIOFgsWHcojwQ6sZJ3eVvy66EqHLZKQYZa2tx0YjrEJMQi1drg6VV+lLCR +rEtsoltgdN2G9v3P6KrHXsrCYaaZKhog9B1OSI2Amv3YWZHXppK12+aSy774lUUz +qVur5cECgYEA7oCOQoRZo76wztS+yDeq173B2gPHKSIrWvaLDkCAPOQPVzJZ4Qc+ +8OEDU6HB9P0MYDsKBxZY85uzWP+dAlsmcL0C86WibOuYERPKQIcAn3KSzFiIxH3R +OAbaLtSLN3lDAH50PhP9BguiSfBjI6w4Qsr7jlQgdpzG4h4LjvotbWMCgYEA4SdT +QQJhHiLtBFo91ItRUzhePvUDfV8XvNfAwZj8cY2+oenkK2+bp35xteBV6Gu1cYnd +V2yFgzMZ/jDvqfUn/8EVAGvEFrLtsUpXeyHhgmVT490RsPxC9xU9jf5LsvZ4zjsj +CsFZW0JnhKkF6M5wztWtO3yKCilmXSOIFvorTN8CgYEAoK2LKdTwbxhxFWbOgSS/ +vEji6HXTHysd+lJOrHNX8a3Th/MsCiZPiQiOrTE08k/onown3U547uXelf7fUE8I +PruX2X2lR6wQ7rBeecp56PHPZEvhGD+LTCuRoise/2h6c0K+HXRp6kC8PQPuRoIo +BRerEeArXr2QX5XOQ6zYHfECgYEAp0L9mDfaSfcMOMWJVVJCEh639PEzrHluOv3U +1n1+XCU+zy3gMVxyN9W5R7HmYAlT+4q9geq+rJ7T2oAkKxBSrK6VmYB1ZZ968NAX +eQPMcYAw+AAM2nwsiz2eQtP9DHAJgrtv5teIOEF2gZjHKRHjv+QBE0YLjkz/HIX+ +3YLvk+UCgYAgpAWk4YW4dAcZ8Y04Ke2pjMvEu44hHphOmk6AZl0Xl9tJwxlV8GVx +o3L4hbjHqyJo3+DZZYM7udMx9axbX9JHYRaLNJpc8UxQZj7d3TehC9Dw9/DzhIy/ +6sml30j/GHvnW5DOlpsdNKDlxoFX+hncXYIjyVTGRNdsSwa4VVm+Xw== +-----END RSA PRIVATE KEY----- + diff --git a/tests/testlib/s2n_key_schedule_testlib.c b/tests/testlib/s2n_key_schedule_testlib.c index 5268953965c..85c34380a78 100644 --- a/tests/testlib/s2n_key_schedule_testlib.c +++ b/tests/testlib/s2n_key_schedule_testlib.c @@ -1,79 +1,79 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "testlib/s2n_testlib.h" - -S2N_RESULT s2n_connection_set_test_transcript_hash(struct s2n_connection *conn, - message_type_t message_type, const struct s2n_blob *digest) -{ - RESULT_GUARD(s2n_connection_set_test_message_type(conn, message_type)); - RESULT_CHECKED_MEMCPY(conn->handshake.hashes->transcript_hash_digest, - digest->data, digest->size); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_test_early_secret(struct s2n_connection *conn, - const struct s2n_blob *early_secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(early_secret); - RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, - early_secret->data, early_secret->size); - conn->secrets.extract_secret_type = S2N_EARLY_SECRET; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_test_handshake_secret(struct s2n_connection *conn, - const struct s2n_blob *handshake_secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(handshake_secret); - RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, - handshake_secret->data, handshake_secret->size); - conn->secrets.extract_secret_type = S2N_HANDSHAKE_SECRET; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_test_master_secret(struct s2n_connection *conn, - const struct s2n_blob *master_secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(master_secret); - RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, - master_secret->data, master_secret->size); - conn->secrets.extract_secret_type = S2N_MASTER_SECRET; - return S2N_RESULT_OK; -} - -/* This function will iterate over all rows and columns of the handshake state - * machine until it finds a valid (handshake_type, handshake_number) such that - * the active message is `expected_message_type`. If callers need to depend on a - * specific `message_number` or `handshake_type` this function should not be - * used. - */ -S2N_RESULT s2n_connection_set_test_message_type(struct s2n_connection *conn, message_type_t expected_message_type) -{ - for (uint32_t handshake = 0; handshake < S2N_HANDSHAKES_COUNT; handshake++) { - for (int message = 0; message < S2N_MAX_HANDSHAKE_LENGTH; message++) { - conn->handshake.handshake_type = handshake; - conn->handshake.message_number = message; - if (s2n_conn_get_current_message_type(conn) == expected_message_type) { - return S2N_RESULT_OK; - } - } - } - RESULT_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); -} +#include "utils/s2n_prelude.h" +/* + * 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 "testlib/s2n_testlib.h" + +S2N_RESULT s2n_connection_set_test_transcript_hash(struct s2n_connection *conn, + message_type_t message_type, const struct s2n_blob *digest) +{ + RESULT_GUARD(s2n_connection_set_test_message_type(conn, message_type)); + RESULT_CHECKED_MEMCPY(conn->handshake.hashes->transcript_hash_digest, + digest->data, digest->size); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_test_early_secret(struct s2n_connection *conn, + const struct s2n_blob *early_secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(early_secret); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, + early_secret->data, early_secret->size); + conn->secrets.extract_secret_type = S2N_EARLY_SECRET; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_test_handshake_secret(struct s2n_connection *conn, + const struct s2n_blob *handshake_secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(handshake_secret); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, + handshake_secret->data, handshake_secret->size); + conn->secrets.extract_secret_type = S2N_HANDSHAKE_SECRET; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_test_master_secret(struct s2n_connection *conn, + const struct s2n_blob *master_secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(master_secret); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.extract_secret, + master_secret->data, master_secret->size); + conn->secrets.extract_secret_type = S2N_MASTER_SECRET; + return S2N_RESULT_OK; +} + +/* This function will iterate over all rows and columns of the handshake state + * machine until it finds a valid (handshake_type, handshake_number) such that + * the active message is `expected_message_type`. If callers need to depend on a + * specific `message_number` or `handshake_type` this function should not be + * used. + */ +S2N_RESULT s2n_connection_set_test_message_type(struct s2n_connection *conn, message_type_t expected_message_type) +{ + for (uint32_t handshake = 0; handshake < S2N_HANDSHAKES_COUNT; handshake++) { + for (int message = 0; message < S2N_MAX_HANDSHAKE_LENGTH; message++) { + conn->handshake.handshake_type = handshake; + conn->handshake.message_number = message; + if (s2n_conn_get_current_message_type(conn) == expected_message_type) { + return S2N_RESULT_OK; + } + } + } + RESULT_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); +} diff --git a/tests/testlib/s2n_ktls_test_utils.c b/tests/testlib/s2n_ktls_test_utils.c index 4a1d26c2df9..41e407fefcf 100644 --- a/tests/testlib/s2n_ktls_test_utils.c +++ b/tests/testlib/s2n_ktls_test_utils.c @@ -1,250 +1,250 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "testlib/s2n_ktls_test_utils.h" - -S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, - int cmsg_type, uint8_t record_type); -S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type); - -/* Since it is possible to read partial data, we need a way to update the length - * of the previous record for the mock stuffer IO implementation. */ -static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_stuffer *io_ctx, - uint16_t remaining_len) -{ - RESULT_ENSURE_REF(io_ctx); - RESULT_ENSURE(remaining_len > 0, S2N_ERR_IO); - - /* rewind so we can read the last header with the updated len */ - RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(&io_ctx->ancillary_buffer, S2N_TEST_KTLS_MOCK_HEADER_SIZE)); - - /* get position for the last header's length */ - uint32_t rewrite_len_ptr = io_ctx->ancillary_buffer.read_cursor + S2N_TEST_KTLS_MOCK_HEADER_TAG_SIZE; - /* create a new stuffer pointing to len data and rewrite it */ - struct s2n_stuffer rewrite_len_stuffer = io_ctx->ancillary_buffer; - RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&rewrite_len_stuffer)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&rewrite_len_stuffer, rewrite_len_ptr)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&rewrite_len_stuffer, remaining_len)); - - return S2N_RESULT_OK; -} - -ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *msg) -{ - errno = EINVAL; - POSIX_ENSURE_REF(msg); - - struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; - POSIX_ENSURE_REF(io_ctx); - struct s2n_stuffer *data_buffer = &io_ctx->data_buffer; - io_ctx->sendmsg_invoked_count++; - - uint8_t record_type = 0; - struct msghdr msg_to_parse = *msg; - POSIX_GUARD_RESULT(s2n_ktls_get_control_data(&msg_to_parse, S2N_TLS_SET_RECORD_TYPE, &record_type)); - - size_t total_len = 0; - for (size_t count = 0; count < msg->msg_iovlen; count++) { - POSIX_ENSURE_REF(msg->msg_iov); - uint8_t *buf = msg->msg_iov[count].iov_base; - size_t len = msg->msg_iov[count].iov_len; - - if (s2n_stuffer_write_bytes(data_buffer, buf, len) != S2N_SUCCESS) { - size_t partial_len = S2N_MIN(len, s2n_stuffer_space_remaining(data_buffer)); - POSIX_GUARD(s2n_stuffer_write_bytes(data_buffer, buf, partial_len)); - total_len += partial_len; - if (total_len) { - break; - } - errno = EAGAIN; - return -1; - } - total_len += len; - } - if (total_len) { - /* write record_type and len after some data was written successfully */ - POSIX_GUARD(s2n_stuffer_write_uint8(&io_ctx->ancillary_buffer, record_type)); - POSIX_GUARD(s2n_stuffer_write_uint16(&io_ctx->ancillary_buffer, total_len)); - } - - return total_len; -} - -/* In userspace TLS, s2n first reads the header to determine the length of next record - * and then reads the entire record into conn->in. In kTLS it is not possible to know - * the length of the next record. Instead the socket returns the minimum of - * bytes-requested and data-available, reading multiple consecutive records if they - * are of the same type. */ -ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg) -{ - errno = EINVAL; - POSIX_ENSURE_REF(msg); - POSIX_ENSURE_REF(msg->msg_iov); - - struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; - POSIX_ENSURE_REF(io_ctx); - io_ctx->recvmsg_invoked_count++; - - uint8_t *buf = msg->msg_iov->iov_base; - POSIX_ENSURE_REF(buf); - - /* There is no data available so return blocked */ - if (!s2n_stuffer_data_available(&io_ctx->ancillary_buffer)) { - errno = EAGAIN; - return -1; - } - - /* s2n only receives using msg_iovlen of 1 */ - POSIX_ENSURE_EQ(msg->msg_iovlen, 1); - size_t size = msg->msg_iov->iov_len; - - uint8_t record_type = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&io_ctx->ancillary_buffer, &record_type)); - POSIX_GUARD_RESULT(s2n_ktls_set_control_data(msg, msg->msg_control, msg->msg_controllen, - S2N_TLS_GET_RECORD_TYPE, record_type)); - - ssize_t bytes_read = 0; - while (bytes_read < size) { - uint16_t n_avail = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(&io_ctx->ancillary_buffer, &n_avail)); - - size_t n_read = S2N_MIN(size - bytes_read, n_avail); - POSIX_ENSURE_GT(n_read, 0); - POSIX_GUARD(s2n_stuffer_read_bytes(&io_ctx->data_buffer, buf + bytes_read, n_read)); - - bytes_read += n_read; - - /* handle partially read records */ - ssize_t remaining_len = n_avail - n_read; - if (remaining_len) { - POSIX_GUARD_RESULT(s2n_test_ktls_update_prev_header_len(io_ctx, remaining_len)); - break; - } - - /* attempt to read multiple records (must be of the same type) */ - uint8_t next_record_type = 0; - int ret = s2n_stuffer_peek_char(&io_ctx->ancillary_buffer, (char *) &next_record_type); - bool no_more_records = ret != S2N_SUCCESS; - if (no_more_records) { - break; - } - - bool next_record_different_type = next_record_type != record_type; - if (next_record_different_type) { - break; - } - - POSIX_GUARD(s2n_stuffer_skip_read(&io_ctx->ancillary_buffer, sizeof(record_type))); - } - - return bytes_read; -} - -S2N_RESULT s2n_test_init_ktls_io_stuffer_send(struct s2n_connection *conn, - struct s2n_test_ktls_io_stuffer *io) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(io); - - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->data_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->ancillary_buffer, 0)); - RESULT_GUARD(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_io_stuffer, io)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server, - struct s2n_connection *client, struct s2n_test_ktls_io_stuffer_pair *io_pair) -{ - RESULT_ENSURE_REF(server); - RESULT_ENSURE_REF(client); - RESULT_ENSURE_REF(io_pair); - /* setup stuffer IO */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.data_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.ancillary_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.data_buffer, 0)); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.ancillary_buffer, 0)); - - RESULT_GUARD(s2n_ktls_set_sendmsg_cb(server, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->client_in)); - RESULT_GUARD(s2n_ktls_set_recvmsg_cb(server, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->server_in)); - RESULT_GUARD(s2n_ktls_set_sendmsg_cb(client, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->server_in)); - RESULT_GUARD(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->client_in)); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_free(struct s2n_test_ktls_io_stuffer *io) -{ - RESULT_ENSURE_REF(io); - RESULT_GUARD_POSIX(s2n_stuffer_free(&io->data_buffer)); - RESULT_GUARD_POSIX(s2n_stuffer_free(&io->ancillary_buffer)); - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_pair_free(struct s2n_test_ktls_io_stuffer_pair *pair) -{ - RESULT_ENSURE_REF(pair); - - RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->client_in)); - RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->server_in)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_validate_data(struct s2n_test_ktls_io_stuffer *ktls_io, - const uint8_t *expected_data, uint16_t expected_len) -{ - RESULT_ENSURE_REF(ktls_io); - RESULT_ENSURE_REF(expected_data); - - struct s2n_stuffer validate_data_stuffer = ktls_io->data_buffer; - RESULT_ENSURE_EQ(s2n_stuffer_data_available(&validate_data_stuffer), expected_len); - uint8_t *data_ptr = s2n_stuffer_raw_read(&validate_data_stuffer, expected_len); - RESULT_ENSURE_REF(data_ptr); - RESULT_ENSURE_EQ(memcmp(data_ptr, expected_data, expected_len), 0); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_validate_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, - uint8_t expected_record_type, uint16_t expected_len) -{ - RESULT_ENSURE_REF(ktls_io); - - struct s2n_stuffer validate_ancillary_stuffer = ktls_io->ancillary_buffer; - uint8_t tag = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&validate_ancillary_stuffer, &tag)); - RESULT_ENSURE_EQ(tag, expected_record_type); - uint16_t len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&validate_ancillary_stuffer, &len)); - RESULT_ENSURE_EQ(len, expected_len); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_records_in_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, - uint16_t expected_records) -{ - RESULT_ENSURE_REF(ktls_io); - - size_t size = s2n_stuffer_data_available(&ktls_io->ancillary_buffer); - size_t records = size / S2N_TEST_KTLS_MOCK_HEADER_SIZE; - size_t extra = size % S2N_TEST_KTLS_MOCK_HEADER_SIZE; - - RESULT_ENSURE_EQ(records, expected_records); - RESULT_ENSURE_EQ(extra, 0); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "testlib/s2n_ktls_test_utils.h" + +S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, + int cmsg_type, uint8_t record_type); +S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type); + +/* Since it is possible to read partial data, we need a way to update the length + * of the previous record for the mock stuffer IO implementation. */ +static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_stuffer *io_ctx, + uint16_t remaining_len) +{ + RESULT_ENSURE_REF(io_ctx); + RESULT_ENSURE(remaining_len > 0, S2N_ERR_IO); + + /* rewind so we can read the last header with the updated len */ + RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(&io_ctx->ancillary_buffer, S2N_TEST_KTLS_MOCK_HEADER_SIZE)); + + /* get position for the last header's length */ + uint32_t rewrite_len_ptr = io_ctx->ancillary_buffer.read_cursor + S2N_TEST_KTLS_MOCK_HEADER_TAG_SIZE; + /* create a new stuffer pointing to len data and rewrite it */ + struct s2n_stuffer rewrite_len_stuffer = io_ctx->ancillary_buffer; + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&rewrite_len_stuffer)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&rewrite_len_stuffer, rewrite_len_ptr)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&rewrite_len_stuffer, remaining_len)); + + return S2N_RESULT_OK; +} + +ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *msg) +{ + errno = EINVAL; + POSIX_ENSURE_REF(msg); + + struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; + POSIX_ENSURE_REF(io_ctx); + struct s2n_stuffer *data_buffer = &io_ctx->data_buffer; + io_ctx->sendmsg_invoked_count++; + + uint8_t record_type = 0; + struct msghdr msg_to_parse = *msg; + POSIX_GUARD_RESULT(s2n_ktls_get_control_data(&msg_to_parse, S2N_TLS_SET_RECORD_TYPE, &record_type)); + + size_t total_len = 0; + for (size_t count = 0; count < msg->msg_iovlen; count++) { + POSIX_ENSURE_REF(msg->msg_iov); + uint8_t *buf = msg->msg_iov[count].iov_base; + size_t len = msg->msg_iov[count].iov_len; + + if (s2n_stuffer_write_bytes(data_buffer, buf, len) != S2N_SUCCESS) { + size_t partial_len = S2N_MIN(len, s2n_stuffer_space_remaining(data_buffer)); + POSIX_GUARD(s2n_stuffer_write_bytes(data_buffer, buf, partial_len)); + total_len += partial_len; + if (total_len) { + break; + } + errno = EAGAIN; + return -1; + } + total_len += len; + } + if (total_len) { + /* write record_type and len after some data was written successfully */ + POSIX_GUARD(s2n_stuffer_write_uint8(&io_ctx->ancillary_buffer, record_type)); + POSIX_GUARD(s2n_stuffer_write_uint16(&io_ctx->ancillary_buffer, total_len)); + } + + return total_len; +} + +/* In userspace TLS, s2n first reads the header to determine the length of next record + * and then reads the entire record into conn->in. In kTLS it is not possible to know + * the length of the next record. Instead the socket returns the minimum of + * bytes-requested and data-available, reading multiple consecutive records if they + * are of the same type. */ +ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg) +{ + errno = EINVAL; + POSIX_ENSURE_REF(msg); + POSIX_ENSURE_REF(msg->msg_iov); + + struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context; + POSIX_ENSURE_REF(io_ctx); + io_ctx->recvmsg_invoked_count++; + + uint8_t *buf = msg->msg_iov->iov_base; + POSIX_ENSURE_REF(buf); + + /* There is no data available so return blocked */ + if (!s2n_stuffer_data_available(&io_ctx->ancillary_buffer)) { + errno = EAGAIN; + return -1; + } + + /* s2n only receives using msg_iovlen of 1 */ + POSIX_ENSURE_EQ(msg->msg_iovlen, 1); + size_t size = msg->msg_iov->iov_len; + + uint8_t record_type = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&io_ctx->ancillary_buffer, &record_type)); + POSIX_GUARD_RESULT(s2n_ktls_set_control_data(msg, msg->msg_control, msg->msg_controllen, + S2N_TLS_GET_RECORD_TYPE, record_type)); + + ssize_t bytes_read = 0; + while (bytes_read < size) { + uint16_t n_avail = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(&io_ctx->ancillary_buffer, &n_avail)); + + size_t n_read = S2N_MIN(size - bytes_read, n_avail); + POSIX_ENSURE_GT(n_read, 0); + POSIX_GUARD(s2n_stuffer_read_bytes(&io_ctx->data_buffer, buf + bytes_read, n_read)); + + bytes_read += n_read; + + /* handle partially read records */ + ssize_t remaining_len = n_avail - n_read; + if (remaining_len) { + POSIX_GUARD_RESULT(s2n_test_ktls_update_prev_header_len(io_ctx, remaining_len)); + break; + } + + /* attempt to read multiple records (must be of the same type) */ + uint8_t next_record_type = 0; + int ret = s2n_stuffer_peek_char(&io_ctx->ancillary_buffer, (char *) &next_record_type); + bool no_more_records = ret != S2N_SUCCESS; + if (no_more_records) { + break; + } + + bool next_record_different_type = next_record_type != record_type; + if (next_record_different_type) { + break; + } + + POSIX_GUARD(s2n_stuffer_skip_read(&io_ctx->ancillary_buffer, sizeof(record_type))); + } + + return bytes_read; +} + +S2N_RESULT s2n_test_init_ktls_io_stuffer_send(struct s2n_connection *conn, + struct s2n_test_ktls_io_stuffer *io) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(io); + + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->data_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io->ancillary_buffer, 0)); + RESULT_GUARD(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_io_stuffer, io)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server, + struct s2n_connection *client, struct s2n_test_ktls_io_stuffer_pair *io_pair) +{ + RESULT_ENSURE_REF(server); + RESULT_ENSURE_REF(client); + RESULT_ENSURE_REF(io_pair); + /* setup stuffer IO */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.data_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->server_in.ancillary_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.data_buffer, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&io_pair->client_in.ancillary_buffer, 0)); + + RESULT_GUARD(s2n_ktls_set_sendmsg_cb(server, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->client_in)); + RESULT_GUARD(s2n_ktls_set_recvmsg_cb(server, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->server_in)); + RESULT_GUARD(s2n_ktls_set_sendmsg_cb(client, s2n_test_ktls_sendmsg_io_stuffer, &io_pair->server_in)); + RESULT_GUARD(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_io_stuffer, &io_pair->client_in)); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_free(struct s2n_test_ktls_io_stuffer *io) +{ + RESULT_ENSURE_REF(io); + RESULT_GUARD_POSIX(s2n_stuffer_free(&io->data_buffer)); + RESULT_GUARD_POSIX(s2n_stuffer_free(&io->ancillary_buffer)); + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_pair_free(struct s2n_test_ktls_io_stuffer_pair *pair) +{ + RESULT_ENSURE_REF(pair); + + RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->client_in)); + RESULT_GUARD(s2n_ktls_io_stuffer_free(&pair->server_in)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_validate_data(struct s2n_test_ktls_io_stuffer *ktls_io, + const uint8_t *expected_data, uint16_t expected_len) +{ + RESULT_ENSURE_REF(ktls_io); + RESULT_ENSURE_REF(expected_data); + + struct s2n_stuffer validate_data_stuffer = ktls_io->data_buffer; + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&validate_data_stuffer), expected_len); + uint8_t *data_ptr = s2n_stuffer_raw_read(&validate_data_stuffer, expected_len); + RESULT_ENSURE_REF(data_ptr); + RESULT_ENSURE_EQ(memcmp(data_ptr, expected_data, expected_len), 0); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_validate_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, + uint8_t expected_record_type, uint16_t expected_len) +{ + RESULT_ENSURE_REF(ktls_io); + + struct s2n_stuffer validate_ancillary_stuffer = ktls_io->ancillary_buffer; + uint8_t tag = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(&validate_ancillary_stuffer, &tag)); + RESULT_ENSURE_EQ(tag, expected_record_type); + uint16_t len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&validate_ancillary_stuffer, &len)); + RESULT_ENSURE_EQ(len, expected_len); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_records_in_ancillary(struct s2n_test_ktls_io_stuffer *ktls_io, + uint16_t expected_records) +{ + RESULT_ENSURE_REF(ktls_io); + + size_t size = s2n_stuffer_data_available(&ktls_io->ancillary_buffer); + size_t records = size / S2N_TEST_KTLS_MOCK_HEADER_SIZE; + size_t extra = size % S2N_TEST_KTLS_MOCK_HEADER_SIZE; + + RESULT_ENSURE_EQ(records, expected_records); + RESULT_ENSURE_EQ(extra, 0); + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_test_certs.c b/tests/testlib/s2n_test_certs.c index 6d651f01ddb..f1594321296 100644 --- a/tests/testlib/s2n_test_certs.c +++ b/tests/testlib/s2n_test_certs.c @@ -1,266 +1,266 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_libcrypto.h" -#include "crypto/s2n_mldsa.h" -#include "crypto/s2n_rsa_pss.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -#define S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED 5 - -int s2n_test_cert_chain_and_key_new(struct s2n_cert_chain_and_key **chain_and_key, - const char *cert_chain_file, const char *private_key_file) -{ - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - - POSIX_GUARD(s2n_read_test_pem(cert_chain_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(private_key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - POSIX_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_test_cert_permutation_load_server_chain_from_name( - struct s2n_cert_chain_and_key **chain_and_key, const char *name) -{ - char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/permutations/%s/server-chain.pem", name); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/permutations/%s/server-key.pem", name); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - RESULT_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); - - return S2N_RESULT_OK; -} - -int s2n_test_cert_permutation_load_server_chain(struct s2n_cert_chain_and_key **chain_and_key, - const char *type, const char *signature, const char *size, const char *digest) -{ - char name_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - snprintf(name_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "%s_%s_%s_%s", type, signature, size, digest); - POSIX_GUARD_RESULT(s2n_test_cert_permutation_load_server_chain_from_name(chain_and_key, name_buffer)); - return S2N_SUCCESS; -} - -int s2n_test_cert_permutation_get_ca_path(char *output, const char *type, const char *signature, - const char *size, const char *digest) -{ - sprintf(output, "../pems/permutations/%s_%s_%s_%s/ca-cert.pem", type, signature, size, digest); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_test_cert_permutation_get_server_chain_path(char *output, const char *type, - const char *signature, const char *size, const char *digest) -{ - sprintf(output, "../pems/permutations/%s_%s_%s_%s/server-chain.pem", type, signature, size, - digest); - return S2N_RESULT_OK; -} - -int s2n_read_test_pem(const char *pem_path, char *pem_out, long int max_size) -{ - uint32_t pem_len = 0; - - POSIX_GUARD(s2n_read_test_pem_and_len(pem_path, (uint8_t *) pem_out, &pem_len, max_size - 1)); - pem_out[pem_len] = '\0'; - - return 0; -} - -int s2n_read_test_pem_and_len(const char *pem_path, uint8_t *pem_out, uint32_t *pem_len, long int max_size) -{ - FILE *pem_file = fopen(pem_path, "rb"); - if (!pem_file) { - POSIX_BAIL(S2N_ERR_NULL); - } - - /* Make sure we can fit the pem into the output buffer */ - fseek(pem_file, 0, SEEK_END); - const long int pem_file_size = ftell(pem_file); - /* one extra for the null byte */ - rewind(pem_file); - - if (max_size < (pem_file_size)) { - POSIX_BAIL(S2N_ERR_NOMEM); - } - - if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < pem_file_size) { - POSIX_BAIL(S2N_ERR_IO); - } - - *pem_len = pem_file_size; - fclose(pem_file); - - return 0; -} - -S2N_RESULT s2n_test_cert_chain_data_from_pem(struct s2n_connection *conn, const char *pem_path, - struct s2n_stuffer *cert_chain_stuffer) -{ - RESULT_ENSURE_REF(cert_chain_stuffer); - - uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t cert_chain_pem_len = 0; - RESULT_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, cert_chain_pem, &cert_chain_pem_len, S2N_MAX_TEST_PEM_SIZE)); - - RESULT_GUARD(s2n_test_cert_chain_data_from_pem_data(conn, cert_chain_pem, cert_chain_pem_len, cert_chain_stuffer)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_cert_chain_data_from_pem_data(struct s2n_connection *conn, uint8_t *pem_data, uint32_t pem_data_len, - struct s2n_stuffer *cert_chain_stuffer) -{ - DEFER_CLEANUP(struct s2n_stuffer certificate_message_stuffer = { 0 }, s2n_stuffer_free); - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&certificate_message_stuffer, 4096)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, pem_data, pem_data_len)); - - RESULT_GUARD_POSIX(s2n_send_cert_chain(conn, &certificate_message_stuffer, chain_and_key)); - - /* Skip the cert chain length */ - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&certificate_message_stuffer, 3)); - - uint32_t cert_chain_len = s2n_stuffer_data_available(&certificate_message_stuffer); - RESULT_GUARD_POSIX(s2n_stuffer_alloc(cert_chain_stuffer, cert_chain_len)); - RESULT_GUARD_POSIX(s2n_stuffer_copy(&certificate_message_stuffer, cert_chain_stuffer, cert_chain_len)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_test_cert_chains_init(struct s2n_test_cert_chain_list *chains) -{ - /* Load all permutations */ - { - DIR *root = opendir("../pems/permutations"); - RESULT_ENSURE_REF(root); - struct dirent *dir = NULL; - while ((dir = readdir(root)) != NULL) { - /* Skip ., .., the README, etc. - * This is pretty hacky: we can switch to stat later if necessary. -- */ - if (strchr(dir->d_name, '_') == NULL) { - continue; - } - if (!s2n_is_rsa_pss_certs_supported() && strstr(dir->d_name, "pss")) { - chains->skipped++; - continue; - } - if (s2n_libcrypto_is_openssl_fips() && strstr(dir->d_name, "rsae_pkcs_1024_sha1")) { - chains->skipped++; - continue; - } - - RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); - struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; - - RESULT_GUARD(s2n_test_cert_permutation_load_server_chain_from_name( - &entry->chain, dir->d_name)); - chains->count++; - } - closedir(root); - } - - /* Load all MLDSA test certs. - * MLDSA is not yet included in permutations due to difficulties generating MLDSA certs. - * We instead continue to test with the example certs from the RFC. - */ - const char *mldsa_names[] = { "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" }; - if (s2n_mldsa_is_supported()) { - for (size_t i = 0; i < s2n_array_len(mldsa_names); i++) { - RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); - struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; - - char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/mldsa/%s.crt", mldsa_names[i]); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, - "../pems/mldsa/%s-seed.priv", mldsa_names[i]); - RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - entry->chain = s2n_cert_chain_and_key_new(); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem( - entry->chain, cert_chain_pem, private_key_pem)); - - chains->count++; - } - } else { - chains->skipped += s2n_array_len(mldsa_names); - } - - /* Sanity check result */ - RESULT_ENSURE_LTE(chains->skipped, S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED); - RESULT_ENSURE_EQ(chains->skipped + chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); - - return S2N_RESULT_OK; -} - -/* - * Sets "supported" to a specific value for all chains of a given pkey_type. - * "supported" could indicate an index in an array of policies (as in s2n_security_policies_test), - * or could indicate a specific policy version, or could simply indicate true/false. - * The meaning of the value depends on the structure of the test. - */ -S2N_RESULT s2n_test_cert_chains_set_supported(struct s2n_test_cert_chain_list *chains, - s2n_pkey_type target_type, uint64_t supported) -{ - for (size_t i = 0; i < chains->count; i++) { - struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; - s2n_pkey_type entry_type = entry->chain->cert_chain->head->pkey_type; - if (entry_type == target_type) { - entry->supported = supported; - } - } - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_test_cert_chains_free(struct s2n_test_cert_chain_list *chains) -{ - for (size_t i = 0; i < chains->count; i++) { - struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(entry->chain)); - entry->supported = 0; - } - chains->count = 0; - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_libcrypto.h" +#include "crypto/s2n_mldsa.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +#define S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED 5 + +int s2n_test_cert_chain_and_key_new(struct s2n_cert_chain_and_key **chain_and_key, + const char *cert_chain_file, const char *private_key_file) +{ + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + + POSIX_GUARD(s2n_read_test_pem(cert_chain_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(private_key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + POSIX_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_test_cert_permutation_load_server_chain_from_name( + struct s2n_cert_chain_and_key **chain_and_key, const char *name) +{ + char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/permutations/%s/server-chain.pem", name); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/permutations/%s/server-key.pem", name); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + RESULT_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); + + return S2N_RESULT_OK; +} + +int s2n_test_cert_permutation_load_server_chain(struct s2n_cert_chain_and_key **chain_and_key, + const char *type, const char *signature, const char *size, const char *digest) +{ + char name_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + snprintf(name_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "%s_%s_%s_%s", type, signature, size, digest); + POSIX_GUARD_RESULT(s2n_test_cert_permutation_load_server_chain_from_name(chain_and_key, name_buffer)); + return S2N_SUCCESS; +} + +int s2n_test_cert_permutation_get_ca_path(char *output, const char *type, const char *signature, + const char *size, const char *digest) +{ + sprintf(output, "../pems/permutations/%s_%s_%s_%s/ca-cert.pem", type, signature, size, digest); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_test_cert_permutation_get_server_chain_path(char *output, const char *type, + const char *signature, const char *size, const char *digest) +{ + sprintf(output, "../pems/permutations/%s_%s_%s_%s/server-chain.pem", type, signature, size, + digest); + return S2N_RESULT_OK; +} + +int s2n_read_test_pem(const char *pem_path, char *pem_out, long int max_size) +{ + uint32_t pem_len = 0; + + POSIX_GUARD(s2n_read_test_pem_and_len(pem_path, (uint8_t *) pem_out, &pem_len, max_size - 1)); + pem_out[pem_len] = '\0'; + + return 0; +} + +int s2n_read_test_pem_and_len(const char *pem_path, uint8_t *pem_out, uint32_t *pem_len, long int max_size) +{ + FILE *pem_file = fopen(pem_path, "rb"); + if (!pem_file) { + POSIX_BAIL(S2N_ERR_NULL); + } + + /* Make sure we can fit the pem into the output buffer */ + fseek(pem_file, 0, SEEK_END); + const long int pem_file_size = ftell(pem_file); + /* one extra for the null byte */ + rewind(pem_file); + + if (max_size < (pem_file_size)) { + POSIX_BAIL(S2N_ERR_NOMEM); + } + + if (fread(pem_out, sizeof(char), pem_file_size, pem_file) < pem_file_size) { + POSIX_BAIL(S2N_ERR_IO); + } + + *pem_len = pem_file_size; + fclose(pem_file); + + return 0; +} + +S2N_RESULT s2n_test_cert_chain_data_from_pem(struct s2n_connection *conn, const char *pem_path, + struct s2n_stuffer *cert_chain_stuffer) +{ + RESULT_ENSURE_REF(cert_chain_stuffer); + + uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t cert_chain_pem_len = 0; + RESULT_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, cert_chain_pem, &cert_chain_pem_len, S2N_MAX_TEST_PEM_SIZE)); + + RESULT_GUARD(s2n_test_cert_chain_data_from_pem_data(conn, cert_chain_pem, cert_chain_pem_len, cert_chain_stuffer)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_cert_chain_data_from_pem_data(struct s2n_connection *conn, uint8_t *pem_data, uint32_t pem_data_len, + struct s2n_stuffer *cert_chain_stuffer) +{ + DEFER_CLEANUP(struct s2n_stuffer certificate_message_stuffer = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&certificate_message_stuffer, 4096)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, pem_data, pem_data_len)); + + RESULT_GUARD_POSIX(s2n_send_cert_chain(conn, &certificate_message_stuffer, chain_and_key)); + + /* Skip the cert chain length */ + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&certificate_message_stuffer, 3)); + + uint32_t cert_chain_len = s2n_stuffer_data_available(&certificate_message_stuffer); + RESULT_GUARD_POSIX(s2n_stuffer_alloc(cert_chain_stuffer, cert_chain_len)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(&certificate_message_stuffer, cert_chain_stuffer, cert_chain_len)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_cert_chains_init(struct s2n_test_cert_chain_list *chains) +{ + /* Load all permutations */ + { + DIR *root = opendir("../pems/permutations"); + RESULT_ENSURE_REF(root); + struct dirent *dir = NULL; + while ((dir = readdir(root)) != NULL) { + /* Skip ., .., the README, etc. + * This is pretty hacky: we can switch to stat later if necessary. +- */ + if (strchr(dir->d_name, '_') == NULL) { + continue; + } + if (!s2n_is_rsa_pss_certs_supported() && strstr(dir->d_name, "pss")) { + chains->skipped++; + continue; + } + if (s2n_libcrypto_is_openssl_fips() && strstr(dir->d_name, "rsae_pkcs_1024_sha1")) { + chains->skipped++; + continue; + } + + RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); + struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; + + RESULT_GUARD(s2n_test_cert_permutation_load_server_chain_from_name( + &entry->chain, dir->d_name)); + chains->count++; + } + closedir(root); + } + + /* Load all MLDSA test certs. + * MLDSA is not yet included in permutations due to difficulties generating MLDSA certs. + * We instead continue to test with the example certs from the RFC. + */ + const char *mldsa_names[] = { "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" }; + if (s2n_mldsa_is_supported()) { + for (size_t i = 0; i < s2n_array_len(mldsa_names); i++) { + RESULT_ENSURE_LT(chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); + struct s2n_test_cert_chain_entry *entry = &chains->chains[chains->count]; + + char path_buffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/mldsa/%s.crt", mldsa_names[i]); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + snprintf(path_buffer, S2N_MAX_TEST_PEM_PATH_LENGTH, + "../pems/mldsa/%s-seed.priv", mldsa_names[i]); + RESULT_GUARD_POSIX(s2n_read_test_pem(path_buffer, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + entry->chain = s2n_cert_chain_and_key_new(); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem( + entry->chain, cert_chain_pem, private_key_pem)); + + chains->count++; + } + } else { + chains->skipped += s2n_array_len(mldsa_names); + } + + /* Sanity check result */ + RESULT_ENSURE_LTE(chains->skipped, S2N_TEST_CERT_CHAIN_LIST_MAX_SKIPPED); + RESULT_ENSURE_EQ(chains->skipped + chains->count, S2N_TEST_CERT_CHAIN_LIST_MAX); + + return S2N_RESULT_OK; +} + +/* + * Sets "supported" to a specific value for all chains of a given pkey_type. + * "supported" could indicate an index in an array of policies (as in s2n_security_policies_test), + * or could indicate a specific policy version, or could simply indicate true/false. + * The meaning of the value depends on the structure of the test. + */ +S2N_RESULT s2n_test_cert_chains_set_supported(struct s2n_test_cert_chain_list *chains, + s2n_pkey_type target_type, uint64_t supported) +{ + for (size_t i = 0; i < chains->count; i++) { + struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; + s2n_pkey_type entry_type = entry->chain->cert_chain->head->pkey_type; + if (entry_type == target_type) { + entry->supported = supported; + } + } + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_test_cert_chains_free(struct s2n_test_cert_chain_list *chains) +{ + for (size_t i = 0; i < chains->count; i++) { + struct s2n_test_cert_chain_entry *entry = &chains->chains[i]; + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_free(entry->chain)); + entry->supported = 0; + } + chains->count = 0; + return S2N_RESULT_OK; +} diff --git a/tests/unit/s2n_async_pkey_test.c b/tests/unit/s2n_async_pkey_test.c index ae9c2a2e16c..a544e326b39 100644 --- a/tests/unit/s2n_async_pkey_test.c +++ b/tests/unit/s2n_async_pkey_test.c @@ -1,726 +1,726 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_async_pkey.h" - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "utils/s2n_safety.h" - -struct s2n_async_pkey_op *pkey_op = NULL; - -uint8_t test_digest_data[] = "I hashed this"; -const uint32_t test_digest_size = sizeof(test_digest_data); -const uint8_t test_signature_data[] = "I signed this"; -const uint32_t test_signature_size = sizeof(test_signature_data); -uint8_t test_encrypted_data[] = "I encrypted this"; -const uint32_t test_encrypted_size = sizeof(test_encrypted_data); -uint8_t test_decrypted_data[] = "I decrypted this"; -const uint32_t test_decrypted_size = sizeof(test_decrypted_data); - -uint8_t offload_callback_count = 0; - -typedef int(async_handler)(struct s2n_connection *conn); - -/* Declaring a flag to check if sign operation is called at least once for all cipher_suites - * while performing handshake through handler (async_handler_sign_with_different_pkey_and_apply) */ -static bool async_handler_sign_operation_called = false; - -static int async_handler_fail(struct s2n_connection *conn) -{ - FAIL_MSG("async_handler_fail should never get invoked"); - return S2N_FAILURE; -} - -static int async_handler_wipe_connection_and_apply(struct s2n_connection *conn) -{ - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); - EXPECT_NOT_NULL(chain_and_key); - - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Wipe connection */ - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* Test that we can perform pkey operation, even if original connection was wiped */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); - - /* Test that pkey op can't be applied to wiped connection */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_ASYNC_WRONG_CONNECTION); - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - return S2N_FAILURE; -} - -static int async_handler_sign_with_different_pkey_and_apply(struct s2n_connection *conn) -{ - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); - EXPECT_NOT_NULL(chain_and_key); - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Test that we can perform pkey operation */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); - - /* Get type for pkey_op */ - s2n_async_pkey_op_type type = { 0 }; - EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(pkey_op, &type)); - - /* Test apply with different certificate chain only for sign operation */ - if (type == S2N_ASYNC_SIGN) { - /* Create new chain and key, and modify current server conn */ - struct s2n_cert_chain_and_key *chain_and_key_2 = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key_2, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - /* Change server conn cert data */ - EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); - conn->handshake_params.our_chain_and_key = chain_and_key_2; - - /* Test that async sign operation will fail as signature was performed over different private key */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_VERIFY_SIGNATURE); - - /* Set pkey_op's validation mode to S2N_ASYNC_PKEY_VALIDATION_FAST and test that async sign apply will pass now */ - EXPECT_SUCCESS(s2n_async_pkey_op_set_validation_mode(pkey_op, S2N_ASYNC_PKEY_VALIDATION_FAST)); - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - - /* Set chain_and_key back to original value and free new chain_and_key */ - conn->handshake_params.our_chain_and_key = chain_and_key; - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key_2)); - - /* Update async_handler_sign_operation_called flag to true */ - async_handler_sign_operation_called = true; - } else { - /* Test decrypt operation passes */ - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - } - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - return S2N_SUCCESS; -} - -static int async_handler_free_pkey_op(struct s2n_connection *conn) -{ - static int function_entered = 0; - - /* Return failure on the second entrance into function so that we drop from try_handshake */ - if (function_entered++ % 2 == 1) { - return S2N_FAILURE; - } - - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - /* Return success so that try_handshake calls s2n_negotiate again */ - return S2N_SUCCESS; -} - -static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn, async_handler handler) -{ - s2n_blocked_status server_blocked; - s2n_blocked_status client_blocked; - - int tries = 0; - do { - int client_rc = s2n_negotiate(client_conn, &client_blocked); - if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return S2N_FAILURE; - } - - int server_rc = s2n_negotiate(server_conn, &server_blocked); - if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return S2N_FAILURE; - } - - if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { - POSIX_GUARD(handler(server_conn)); - } - - EXPECT_NOT_EQUAL(++tries, 5); - } while (client_blocked || server_blocked); - - POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - return S2N_SUCCESS; -} - -int async_pkey_apply_in_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - /* Check that we have op */ - EXPECT_NOT_NULL(op); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); - EXPECT_NOT_NULL(chain_and_key); - - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Perform the op */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(op, pkey)); - - /* Test that op can be applied inside the callback */ - EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); - - /* Free the op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(op)); - - return S2N_SUCCESS; -} - -int async_pkey_store_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - return S2N_SUCCESS; -} - -int async_pkey_signature_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - - s2n_async_pkey_op_type type = { 0 }; - EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); - EXPECT_EQUAL(type, S2N_ASYNC_SIGN); - - uint8_t expected_size = 0; - EXPECT_SUCCESS(s2n_hash_digest_size(S2N_HASH_SHA256, &expected_size)); - - uint32_t input_size = 0; - EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); - EXPECT_EQUAL(input_size, expected_size); - - struct s2n_blob input1 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input1, input_size)); - - struct s2n_blob input2 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input2, input_size)); - - struct s2n_blob expected_digest = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&expected_digest, expected_size)); - - struct s2n_hash_state digest = { 0 }; - EXPECT_SUCCESS(s2n_hash_new(&digest)); - EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); - EXPECT_SUCCESS(s2n_hash_digest(&digest, expected_digest.data, expected_digest.size)); - EXPECT_SUCCESS(s2n_hash_free(&digest)); - - /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input1.data, input1.size)); - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input2.data, input2.size)); - - EXPECT_EQUAL(input1.size, input2.size); - EXPECT_BYTEARRAY_EQUAL(input1.data, input2.data, input1.size); - EXPECT_BYTEARRAY_EQUAL(input1.data, expected_digest.data, expected_size); - - EXPECT_SUCCESS(s2n_free(&input1)); - EXPECT_SUCCESS(s2n_free(&input2)); - EXPECT_SUCCESS(s2n_free(&expected_digest)); - - EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_signature_data, test_signature_size)); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int async_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - - s2n_async_pkey_op_type type = { 0 }; - EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); - EXPECT_EQUAL(type, S2N_ASYNC_DECRYPT); - - uint32_t input_size = 0; - EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); - EXPECT_EQUAL(input_size, test_encrypted_size); - - struct s2n_blob input_buffer1 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input_buffer1, input_size)); - - struct s2n_blob input_buffer2 = { 0 }; - EXPECT_SUCCESS(s2n_alloc(&input_buffer2, input_size)); - - /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer1.data, input_buffer1.size)); - EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, test_encrypted_data, test_encrypted_size); - - EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer2.data, input_buffer2.size)); - EXPECT_BYTEARRAY_EQUAL(input_buffer2.data, test_encrypted_data, test_encrypted_size); - - EXPECT_EQUAL(input_buffer1.size, input_buffer2.size); - EXPECT_EQUAL(input_buffer1.size, test_encrypted_size); - EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, input_buffer2.data, test_encrypted_size); - - EXPECT_SUCCESS(s2n_free(&input_buffer1)); - EXPECT_SUCCESS(s2n_free(&input_buffer2)); - - EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_decrypted_data, test_decrypted_size)); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int s2n_async_sign_complete(struct s2n_connection *conn, struct s2n_blob *signature) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(signature); - - EXPECT_EQUAL(signature->size, test_signature_size); - EXPECT_BYTEARRAY_EQUAL(signature->data, test_signature_data, test_signature_size); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int s2n_async_decrypt_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(decrypted); - EXPECT_FALSE(rsa_failed); - - EXPECT_EQUAL(decrypted->size, test_decrypted_size); - EXPECT_BYTEARRAY_EQUAL(decrypted->data, test_decrypted_data, test_decrypted_size); - offload_callback_count++; - - return S2N_SUCCESS; -} - -int async_pkey_invalid_input_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - pkey_op = op; - - EXPECT_FAILURE(s2n_async_pkey_op_get_op_type(op, NULL)); - EXPECT_FAILURE(s2n_async_pkey_op_get_input_size(op, NULL)); - EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, NULL, 0)); - - uint32_t input_size = 0; - EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); - - uint8_t placeholder_buffer[] = { 0x0, 0x0, 0x0, 0x0 }; - - /* Buffer too small to contain data. */ - EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, placeholder_buffer, input_size - 1)); - - EXPECT_FAILURE(s2n_async_pkey_op_set_output(op, NULL, test_signature_size)); - offload_callback_count++; - - return S2N_FAILURE; -} - -int async_pkey_invalid_complete(struct s2n_connection *conn, struct s2n_blob *signature) -{ - FAIL_MSG("Invalid async pkey callback was invoked. The callback should never be invoked if there was an earlier" - " failure in the async_pkey_op."); - return S2N_FAILURE; -} - -static int s2n_test_bad_sign(const struct s2n_pkey *pub_key, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *digest, struct s2n_blob *signature) -{ - /* Just write all zeroes. - * This could accidentally be the correct signature, but it's very unlikely. - */ - POSIX_GUARD(s2n_blob_zero(signature)); - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - /* Run all tests for 2 cipher suites to test both sign and decrypt operations */ - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - }; - - for (size_t i = 0; i < s2n_array_len(test_cipher_suites); i++) { - struct s2n_cipher_preferences server_cipher_preferences = { - .count = 1, - .suites = &test_cipher_suites[i], - }; - - struct s2n_security_policy server_security_policy = { - .minimum_protocol_version = S2N_TLS12, - .cipher_preferences = &server_cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20200310, - }; - - EXPECT_TRUE(test_cipher_suites[i]->available); - - TEST_DEBUG_PRINT("Testing %s\n", test_cipher_suites[i]->name); - - /* Test: apply while invoking callback */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_apply_in_callback)); - server_config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_fail)); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: wipe connection and then perform and apply pkey op */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_EQUAL(try_handshake(server_conn, client_conn, async_handler_wipe_connection_and_apply), S2N_FAILURE); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: free the pkey op and try s2n_negotiate again */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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( - try_handshake(server_conn, client_conn, async_handler_free_pkey_op), S2N_ERR_ASYNC_BLOCKED); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: Apply invalid signature */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - /* Enable signature validation for async sign call */ - EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(server_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: Apply invalid signature, when signature validation is enabled for all sync / async signatures */ - { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); - server_config->security_policy = &server_security_policy; - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - /* Security policy must support all cipher suites in test_cipher_suites above */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - 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)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - 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)); - - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); - } - } - - /* Test if sign operation was called at least once for 'Test: Apply invalid signature', - * the flag holds the value after executing handshakes for all cipher_suites */ - EXPECT_EQUAL(async_handler_sign_operation_called, true); - - DEFER_CLEANUP(struct s2n_hash_state digest = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&digest)); - EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); - - /* Test: signature offload. */ - { - EXPECT_EQUAL(0, offload_callback_count); - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - conn->config->async_pkey_cb = async_pkey_signature_callback; - - EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, s2n_async_sign_complete))); - EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(1, offload_callback_count); - - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - EXPECT_EQUAL(2, offload_callback_count); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Test: decrypt offload. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - conn->config->async_pkey_cb = async_pkey_decrypt_callback; - - struct s2n_blob encrypted_data = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&encrypted_data, test_encrypted_data, test_encrypted_size)); - - struct s2n_blob decrypted_data = { 0 }; - /* Re-use the encrypted data buffer to make sure that the data was actually transformed in the callback. - * If we filled this with the decrypted data, we would not know if the decryption happened in the callback. */ - EXPECT_SUCCESS(s2n_blob_init(&decrypted_data, test_encrypted_data, test_encrypted_size)); - - EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_decrypt(conn, &encrypted_data, &decrypted_data, s2n_async_decrypt_complete))); - EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(3, offload_callback_count); - - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - EXPECT_EQUAL(4, offload_callback_count); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Test: errors in callback. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - conn->config->async_pkey_cb = async_pkey_invalid_input_callback; - - EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, async_pkey_invalid_complete))); - EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_CALLBACK_FAILED); - EXPECT_EQUAL(5, offload_callback_count); - - EXPECT_FAILURE(s2n_async_pkey_op_apply(pkey_op, conn)); - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - EXPECT_EQUAL(5, offload_callback_count); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - /* Test: Apply invalid signature to sync operation */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - - 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, config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_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)); - - /* Start the handshake. - * We need to perform enough of the handshake to choose a certificate / private key. - */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); - - /* Setup the pkey verify operation to fail for the chosen private key */ - EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key); - EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key->private_key); - struct s2n_pkey *original_pkey = server_conn->handshake_params.our_chain_and_key->private_key; - struct s2n_pkey bad_pkey = *original_pkey; - bad_pkey.sign = s2n_test_bad_sign; - server_conn->handshake_params.our_chain_and_key->private_key = &bad_pkey; - - /* Verify after sign should fail */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_VERIFY_SIGNATURE); - - /* Reset pkey for cleanup */ - server_conn->handshake_params.our_chain_and_key->private_key = original_pkey; - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_async_pkey.h" + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +struct s2n_async_pkey_op *pkey_op = NULL; + +uint8_t test_digest_data[] = "I hashed this"; +const uint32_t test_digest_size = sizeof(test_digest_data); +const uint8_t test_signature_data[] = "I signed this"; +const uint32_t test_signature_size = sizeof(test_signature_data); +uint8_t test_encrypted_data[] = "I encrypted this"; +const uint32_t test_encrypted_size = sizeof(test_encrypted_data); +uint8_t test_decrypted_data[] = "I decrypted this"; +const uint32_t test_decrypted_size = sizeof(test_decrypted_data); + +uint8_t offload_callback_count = 0; + +typedef int(async_handler)(struct s2n_connection *conn); + +/* Declaring a flag to check if sign operation is called at least once for all cipher_suites + * while performing handshake through handler (async_handler_sign_with_different_pkey_and_apply) */ +static bool async_handler_sign_operation_called = false; + +static int async_handler_fail(struct s2n_connection *conn) +{ + FAIL_MSG("async_handler_fail should never get invoked"); + return S2N_FAILURE; +} + +static int async_handler_wipe_connection_and_apply(struct s2n_connection *conn) +{ + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Test that we can perform pkey operation, even if original connection was wiped */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Test that pkey op can't be applied to wiped connection */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_ASYNC_WRONG_CONNECTION); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + return S2N_FAILURE; +} + +static int async_handler_sign_with_different_pkey_and_apply(struct s2n_connection *conn) +{ + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Test that we can perform pkey operation */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Get type for pkey_op */ + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(pkey_op, &type)); + + /* Test apply with different certificate chain only for sign operation */ + if (type == S2N_ASYNC_SIGN) { + /* Create new chain and key, and modify current server conn */ + struct s2n_cert_chain_and_key *chain_and_key_2 = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key_2, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Change server conn cert data */ + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + conn->handshake_params.our_chain_and_key = chain_and_key_2; + + /* Test that async sign operation will fail as signature was performed over different private key */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_VERIFY_SIGNATURE); + + /* Set pkey_op's validation mode to S2N_ASYNC_PKEY_VALIDATION_FAST and test that async sign apply will pass now */ + EXPECT_SUCCESS(s2n_async_pkey_op_set_validation_mode(pkey_op, S2N_ASYNC_PKEY_VALIDATION_FAST)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + + /* Set chain_and_key back to original value and free new chain_and_key */ + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key_2)); + + /* Update async_handler_sign_operation_called flag to true */ + async_handler_sign_operation_called = true; + } else { + /* Test decrypt operation passes */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + } + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + return S2N_SUCCESS; +} + +static int async_handler_free_pkey_op(struct s2n_connection *conn) +{ + static int function_entered = 0; + + /* Return failure on the second entrance into function so that we drop from try_handshake */ + if (function_entered++ % 2 == 1) { + return S2N_FAILURE; + } + + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + /* Return success so that try_handshake calls s2n_negotiate again */ + return S2N_SUCCESS; +} + +static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn, async_handler handler) +{ + s2n_blocked_status server_blocked; + s2n_blocked_status client_blocked; + + int tries = 0; + do { + int client_rc = s2n_negotiate(client_conn, &client_blocked); + if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + int server_rc = s2n_negotiate(server_conn, &server_blocked); + if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { + POSIX_GUARD(handler(server_conn)); + } + + EXPECT_NOT_EQUAL(++tries, 5); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + return S2N_SUCCESS; +} + +int async_pkey_apply_in_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + /* Check that we have op */ + EXPECT_NOT_NULL(op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Perform the op */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(op, pkey)); + + /* Test that op can be applied inside the callback */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); + + /* Free the op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(op)); + + return S2N_SUCCESS; +} + +int async_pkey_store_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + return S2N_SUCCESS; +} + +int async_pkey_signature_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); + EXPECT_EQUAL(type, S2N_ASYNC_SIGN); + + uint8_t expected_size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(S2N_HASH_SHA256, &expected_size)); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_EQUAL(input_size, expected_size); + + struct s2n_blob input1 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input1, input_size)); + + struct s2n_blob input2 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input2, input_size)); + + struct s2n_blob expected_digest = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&expected_digest, expected_size)); + + struct s2n_hash_state digest = { 0 }; + EXPECT_SUCCESS(s2n_hash_new(&digest)); + EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); + EXPECT_SUCCESS(s2n_hash_digest(&digest, expected_digest.data, expected_digest.size)); + EXPECT_SUCCESS(s2n_hash_free(&digest)); + + /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input1.data, input1.size)); + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input2.data, input2.size)); + + EXPECT_EQUAL(input1.size, input2.size); + EXPECT_BYTEARRAY_EQUAL(input1.data, input2.data, input1.size); + EXPECT_BYTEARRAY_EQUAL(input1.data, expected_digest.data, expected_size); + + EXPECT_SUCCESS(s2n_free(&input1)); + EXPECT_SUCCESS(s2n_free(&input2)); + EXPECT_SUCCESS(s2n_free(&expected_digest)); + + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_signature_data, test_signature_size)); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int async_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); + EXPECT_EQUAL(type, S2N_ASYNC_DECRYPT); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_EQUAL(input_size, test_encrypted_size); + + struct s2n_blob input_buffer1 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input_buffer1, input_size)); + + struct s2n_blob input_buffer2 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input_buffer2, input_size)); + + /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer1.data, input_buffer1.size)); + EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, test_encrypted_data, test_encrypted_size); + + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer2.data, input_buffer2.size)); + EXPECT_BYTEARRAY_EQUAL(input_buffer2.data, test_encrypted_data, test_encrypted_size); + + EXPECT_EQUAL(input_buffer1.size, input_buffer2.size); + EXPECT_EQUAL(input_buffer1.size, test_encrypted_size); + EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, input_buffer2.data, test_encrypted_size); + + EXPECT_SUCCESS(s2n_free(&input_buffer1)); + EXPECT_SUCCESS(s2n_free(&input_buffer2)); + + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_decrypted_data, test_decrypted_size)); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int s2n_async_sign_complete(struct s2n_connection *conn, struct s2n_blob *signature) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(signature); + + EXPECT_EQUAL(signature->size, test_signature_size); + EXPECT_BYTEARRAY_EQUAL(signature->data, test_signature_data, test_signature_size); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int s2n_async_decrypt_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(decrypted); + EXPECT_FALSE(rsa_failed); + + EXPECT_EQUAL(decrypted->size, test_decrypted_size); + EXPECT_BYTEARRAY_EQUAL(decrypted->data, test_decrypted_data, test_decrypted_size); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int async_pkey_invalid_input_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + EXPECT_FAILURE(s2n_async_pkey_op_get_op_type(op, NULL)); + EXPECT_FAILURE(s2n_async_pkey_op_get_input_size(op, NULL)); + EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, NULL, 0)); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + + uint8_t placeholder_buffer[] = { 0x0, 0x0, 0x0, 0x0 }; + + /* Buffer too small to contain data. */ + EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, placeholder_buffer, input_size - 1)); + + EXPECT_FAILURE(s2n_async_pkey_op_set_output(op, NULL, test_signature_size)); + offload_callback_count++; + + return S2N_FAILURE; +} + +int async_pkey_invalid_complete(struct s2n_connection *conn, struct s2n_blob *signature) +{ + FAIL_MSG("Invalid async pkey callback was invoked. The callback should never be invoked if there was an earlier" + " failure in the async_pkey_op."); + return S2N_FAILURE; +} + +static int s2n_test_bad_sign(const struct s2n_pkey *pub_key, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature) +{ + /* Just write all zeroes. + * This could accidentally be the correct signature, but it's very unlikely. + */ + POSIX_GUARD(s2n_blob_zero(signature)); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Run all tests for 2 cipher suites to test both sign and decrypt operations */ + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + }; + + for (size_t i = 0; i < s2n_array_len(test_cipher_suites); i++) { + struct s2n_cipher_preferences server_cipher_preferences = { + .count = 1, + .suites = &test_cipher_suites[i], + }; + + struct s2n_security_policy server_security_policy = { + .minimum_protocol_version = S2N_TLS12, + .cipher_preferences = &server_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + EXPECT_TRUE(test_cipher_suites[i]->available); + + TEST_DEBUG_PRINT("Testing %s\n", test_cipher_suites[i]->name); + + /* Test: apply while invoking callback */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_apply_in_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_fail)); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: wipe connection and then perform and apply pkey op */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_EQUAL(try_handshake(server_conn, client_conn, async_handler_wipe_connection_and_apply), S2N_FAILURE); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: free the pkey op and try s2n_negotiate again */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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( + try_handshake(server_conn, client_conn, async_handler_free_pkey_op), S2N_ERR_ASYNC_BLOCKED); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: Apply invalid signature */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + /* Enable signature validation for async sign call */ + EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(server_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: Apply invalid signature, when signature validation is enabled for all sync / async signatures */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + 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)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + 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)); + + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); + } + } + + /* Test if sign operation was called at least once for 'Test: Apply invalid signature', + * the flag holds the value after executing handshakes for all cipher_suites */ + EXPECT_EQUAL(async_handler_sign_operation_called, true); + + DEFER_CLEANUP(struct s2n_hash_state digest = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&digest)); + EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); + + /* Test: signature offload. */ + { + EXPECT_EQUAL(0, offload_callback_count); + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_signature_callback; + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, s2n_async_sign_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(1, offload_callback_count); + + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(2, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test: decrypt offload. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_decrypt_callback; + + struct s2n_blob encrypted_data = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&encrypted_data, test_encrypted_data, test_encrypted_size)); + + struct s2n_blob decrypted_data = { 0 }; + /* Re-use the encrypted data buffer to make sure that the data was actually transformed in the callback. + * If we filled this with the decrypted data, we would not know if the decryption happened in the callback. */ + EXPECT_SUCCESS(s2n_blob_init(&decrypted_data, test_encrypted_data, test_encrypted_size)); + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_decrypt(conn, &encrypted_data, &decrypted_data, s2n_async_decrypt_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(3, offload_callback_count); + + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(4, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test: errors in callback. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_invalid_input_callback; + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, async_pkey_invalid_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_CALLBACK_FAILED); + EXPECT_EQUAL(5, offload_callback_count); + + EXPECT_FAILURE(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(5, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Test: Apply invalid signature to sync operation */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + 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, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_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)); + + /* Start the handshake. + * We need to perform enough of the handshake to choose a certificate / private key. + */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + + /* Setup the pkey verify operation to fail for the chosen private key */ + EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key); + EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key->private_key); + struct s2n_pkey *original_pkey = server_conn->handshake_params.our_chain_and_key->private_key; + struct s2n_pkey bad_pkey = *original_pkey; + bad_pkey.sign = s2n_test_bad_sign; + server_conn->handshake_params.our_chain_and_key->private_key = &bad_pkey; + + /* Verify after sign should fail */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_VERIFY_SIGNATURE); + + /* Reset pkey for cleanup */ + server_conn->handshake_params.our_chain_and_key->private_key = original_pkey; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cbc_test.c b/tests/unit/s2n_cbc_test.c index 5b652d94a5d..f26dbae2c16 100644 --- a/tests/unit/s2n_cbc_test.c +++ b/tests/unit/s2n_cbc_test.c @@ -1,110 +1,110 @@ -#include "utils/s2n_prelude.h" -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - /* Self-talk test */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - - /* Test both composite and non-composite CBC ciphers for all CBC cipher suites. */ - size_t record_algs_tested = 0; - for (size_t cipher_suite_idx = 0; cipher_suite_idx < cipher_preferences_test_all.count; cipher_suite_idx++) { - uint8_t record_algs = cipher_preferences_test_all.suites[cipher_suite_idx]->num_record_algs; - for (size_t record_alg_idx = 0; record_alg_idx < record_algs; record_alg_idx++) { - struct s2n_cipher_suite test_cipher_suite = *cipher_preferences_test_all.suites[cipher_suite_idx]; - test_cipher_suite.record_alg = test_cipher_suite.all_record_algs[record_alg_idx]; - - /* Skip non-CBC ciphers. */ - uint8_t cipher = test_cipher_suite.record_alg->cipher->type; - if (cipher != S2N_CBC && cipher != S2N_COMPOSITE) { - continue; - } - - /* Skip unsupported ciphers. */ - if (!test_cipher_suite.record_alg->cipher->is_available()) { - continue; - } - - struct s2n_cipher_suite *test_cipher_suite_ptr = &test_cipher_suite; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = 1, - .suites = &test_cipher_suite_ptr, - }; - - struct s2n_security_policy test_security_policy = security_policy_test_all; - test_security_policy.cipher_preferences = &test_cipher_preferences; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - client->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - server->security_policy_override = &test_security_policy; - - 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, &io_pair)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - uint8_t negotiated_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server, negotiated_cipher_suite, - negotiated_cipher_suite + 1)); - EXPECT_BYTEARRAY_EQUAL(negotiated_cipher_suite, test_cipher_suite.iana_value, - S2N_TLS_CIPHER_SUITE_LEN); - - EXPECT_OK(s2n_send_and_recv_test(client, server)); - EXPECT_OK(s2n_send_and_recv_test(server, client)); - - record_algs_tested += 1; - } - } - - EXPECT_TRUE(record_algs_tested > 0); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Self-talk test */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + + /* Test both composite and non-composite CBC ciphers for all CBC cipher suites. */ + size_t record_algs_tested = 0; + for (size_t cipher_suite_idx = 0; cipher_suite_idx < cipher_preferences_test_all.count; cipher_suite_idx++) { + uint8_t record_algs = cipher_preferences_test_all.suites[cipher_suite_idx]->num_record_algs; + for (size_t record_alg_idx = 0; record_alg_idx < record_algs; record_alg_idx++) { + struct s2n_cipher_suite test_cipher_suite = *cipher_preferences_test_all.suites[cipher_suite_idx]; + test_cipher_suite.record_alg = test_cipher_suite.all_record_algs[record_alg_idx]; + + /* Skip non-CBC ciphers. */ + uint8_t cipher = test_cipher_suite.record_alg->cipher->type; + if (cipher != S2N_CBC && cipher != S2N_COMPOSITE) { + continue; + } + + /* Skip unsupported ciphers. */ + if (!test_cipher_suite.record_alg->cipher->is_available()) { + continue; + } + + struct s2n_cipher_suite *test_cipher_suite_ptr = &test_cipher_suite; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = 1, + .suites = &test_cipher_suite_ptr, + }; + + struct s2n_security_policy test_security_policy = security_policy_test_all; + test_security_policy.cipher_preferences = &test_cipher_preferences; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + client->security_policy_override = &test_security_policy; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + server->security_policy_override = &test_security_policy; + + 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, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + uint8_t negotiated_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(server, negotiated_cipher_suite, + negotiated_cipher_suite + 1)); + EXPECT_BYTEARRAY_EQUAL(negotiated_cipher_suite, test_cipher_suite.iana_value, + S2N_TLS_CIPHER_SUITE_LEN); + + EXPECT_OK(s2n_send_and_recv_test(client, server)); + EXPECT_OK(s2n_send_and_recv_test(server, client)); + + record_algs_tested += 1; + } + } + + EXPECT_TRUE(record_algs_tested > 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cert_chain_and_key_load_test.c b/tests/unit/s2n_cert_chain_and_key_load_test.c index 0bf3f360905..c283e21712d 100644 --- a/tests/unit/s2n_cert_chain_and_key_load_test.c +++ b/tests/unit/s2n_cert_chain_and_key_load_test.c @@ -1,172 +1,172 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Test each combination of s2n_pkey_types to validate that only keys of - * the same type can be compared */ - { - struct s2n_cert_chain_and_key *chain_and_key = NULL; - char rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Keys of the same type can be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS( - s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - /* Keys of different types cannot be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, ecdsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO( - s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_pss_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, ecdsa_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_pss_private_key_pem), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - }; - - /* Test the same as above but with non null terminated chain and key and - * api that accepts length */ - { - struct s2n_cert_chain_and_key *chain_and_key = NULL; - uint8_t rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint8_t ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - uint32_t rsa_cert_chain_len = 0; - uint32_t rsa_pss_cert_chain_len = 0; - uint32_t ecdsa_cert_chain_len = 0; - uint32_t rsa_private_key_len = 0; - uint32_t rsa_pss_private_key_len = 0; - uint32_t ecdsa_private_key_len = 0; - - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, &rsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, &rsa_pss_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, &ecdsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, &rsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, &rsa_pss_private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, &ecdsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); - - /* Keys of the same type can be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS( - s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - /* Keys of different types cannot be compared */ - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - if (s2n_is_rsa_pss_certs_supported()) { - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO( - s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), - S2N_ERR_KEY_MISMATCH); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test each combination of s2n_pkey_types to validate that only keys of + * the same type can be compared */ + { + struct s2n_cert_chain_and_key *chain_and_key = NULL; + char rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Keys of the same type can be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS( + s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + /* Keys of different types cannot be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, ecdsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO( + s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_pss_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, ecdsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_pss_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + }; + + /* Test the same as above but with non null terminated chain and key and + * api that accepts length */ + { + struct s2n_cert_chain_and_key *chain_and_key = NULL; + uint8_t rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + uint32_t rsa_cert_chain_len = 0; + uint32_t rsa_pss_cert_chain_len = 0; + uint32_t ecdsa_cert_chain_len = 0; + uint32_t rsa_private_key_len = 0; + uint32_t rsa_pss_private_key_len = 0; + uint32_t ecdsa_private_key_len = 0; + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, &rsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, &rsa_pss_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, &ecdsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, &rsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, &rsa_pss_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, &ecdsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Keys of the same type can be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS( + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + /* Keys of different types cannot be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO( + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_cert_chain_and_key_test.c b/tests/unit/s2n_cert_chain_and_key_test.c index 7f322fe002b..3c77d775601 100644 --- a/tests/unit/s2n_cert_chain_and_key_test.c +++ b/tests/unit/s2n_cert_chain_and_key_test.c @@ -1,229 +1,229 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -#define NUM_TIED_CERTS 100 - -struct s2n_connection *create_conn(s2n_mode mode, struct s2n_config *config) -{ - PTR_GUARD_RESULT(s2n_config_set_tls12_security_policy(config)); - struct s2n_connection *conn = s2n_connection_new(mode); - PTR_GUARD_POSIX(s2n_connection_set_config(conn, config)); - return conn; -} - -static int num_times_cb_executed = 0; -static struct s2n_cert_chain_and_key *test_cert_tiebreak_cb(struct s2n_cert_chain_and_key *cert1, - struct s2n_cert_chain_and_key *cert2, - uint8_t *name, - uint32_t name_len) -{ - const int priority1 = *((int *) s2n_cert_chain_and_key_get_ctx(cert1)); - const int priority2 = *((int *) s2n_cert_chain_and_key_get_ctx(cert2)); - num_times_cb_executed++; - return (priority1 > priority2 ? cert1 : cert2); -} - -int main(int argc, char **argv) -{ - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - char *alligator_cert = NULL; - char *alligator_key = NULL; - char *cert_chain = NULL; - char *private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(alligator_cert = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(alligator_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, alligator_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, alligator_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - /* Create config with s2n_config_add_cert_chain_and_key_to_store API with multiple certs */ - { - struct s2n_cert_chain_and_key *default_cert = NULL; - /* Associated data to attach to each certificate to use in the tiebreak callback. */ - int tiebreak_priorites[NUM_TIED_CERTS] = { 0 }; - /* Collection of certs with the same domain name that need to have ties resolved. */ - struct s2n_cert_chain_and_key *tied_certs[NUM_TIED_CERTS] = { NULL }; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cert_tiebreak_callback(server_config, test_cert_tiebreak_cb)); - - /* Need to add at least one cert with a different domain name to make cert lookup utilize hashmap */ - EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); - - /* Add NUM_TIED_CERTS that are actually the same certificate(www.alligator.com) to trigger the tiebreak callback. */ - for (unsigned int i = 0; i < NUM_TIED_CERTS; i++) { - EXPECT_NOT_NULL(tied_certs[i] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tied_certs[i], alligator_cert, alligator_key)); - tiebreak_priorites[i] = i; - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ctx(tied_certs[i], (void *) &tiebreak_priorites[i])); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tied_certs[i])); - } - - EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); - EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "www.alligator.com")); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_EQUAL(num_times_cb_executed, NUM_TIED_CERTS - 1); - struct s2n_cert_chain_and_key *selected_cert = s2n_connection_get_selected_cert(server_conn); - /* The last alligator certificate should have the highest priority */ - EXPECT_EQUAL(selected_cert, tied_certs[(NUM_TIED_CERTS - 1)]); - EXPECT_EQUAL(s2n_cert_chain_and_key_get_ctx(selected_cert), (void *) &tiebreak_priorites[(NUM_TIED_CERTS - 1)]); - EXPECT_EQUAL(*((int *) s2n_cert_chain_and_key_get_ctx(selected_cert)), NUM_TIED_CERTS - 1); - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - for (size_t i = 0; i < NUM_TIED_CERTS; i++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tied_certs[i])); - } - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Create config with deprecated s2n_config_add_cert_chain_and_key API */ - { - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - - EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); - EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Do not allow configs to call both - * s2n_config_add_cert_chain_and_key and s2n_config_add_cert_chain_and_key_to_store */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Config first uses s2n_config_add_cert_chain_and_key: library owns chain */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* Add first chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - /* Try to add second chain of same type */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), - S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - /* Try to add chain using other method */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key_to_store(config, chain), - S2N_ERR_CERT_OWNERSHIP); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - }; - - /* Config first uses s2n_config_add_cert_chain_and_key_to_store: application owns chain */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* Add first chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Add second chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Try to add chain using other method */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), - S2N_ERR_CERT_OWNERSHIP); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - }; - }; - - /* s2n_cert_chain_and_key_load_pem */ - { - /* when loading a chain, all certs have a info associated with them and root is self-signed */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&chain, "ec", "ecdsa", - "p384", "sha256")); - struct s2n_cert *leaf = chain->cert_chain->head; - EXPECT_EQUAL(leaf->info.self_signed, false); - EXPECT_EQUAL(leaf->info.signature_nid, NID_ecdsa_with_SHA256); - EXPECT_EQUAL(leaf->info.signature_digest_nid, NID_sha256); - - struct s2n_cert *intermediate = leaf->next; - EXPECT_NOT_NULL(intermediate); - EXPECT_EQUAL(intermediate->info.self_signed, false); - EXPECT_EQUAL(intermediate->info.signature_nid, NID_ecdsa_with_SHA256); - EXPECT_EQUAL(intermediate->info.signature_digest_nid, NID_sha256); - - struct s2n_cert *root = intermediate->next; - EXPECT_NOT_NULL(intermediate); - EXPECT_NULL(root->next); - EXPECT_EQUAL(root->info.self_signed, true); - EXPECT_EQUAL(root->info.signature_nid, NID_ecdsa_with_SHA256); - EXPECT_EQUAL(root->info.signature_digest_nid, NID_sha256); - }; - }; - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(cert_chain); - free(private_key); - free(alligator_cert); - free(alligator_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +#define NUM_TIED_CERTS 100 + +struct s2n_connection *create_conn(s2n_mode mode, struct s2n_config *config) +{ + PTR_GUARD_RESULT(s2n_config_set_tls12_security_policy(config)); + struct s2n_connection *conn = s2n_connection_new(mode); + PTR_GUARD_POSIX(s2n_connection_set_config(conn, config)); + return conn; +} + +static int num_times_cb_executed = 0; +static struct s2n_cert_chain_and_key *test_cert_tiebreak_cb(struct s2n_cert_chain_and_key *cert1, + struct s2n_cert_chain_and_key *cert2, + uint8_t *name, + uint32_t name_len) +{ + const int priority1 = *((int *) s2n_cert_chain_and_key_get_ctx(cert1)); + const int priority2 = *((int *) s2n_cert_chain_and_key_get_ctx(cert2)); + num_times_cb_executed++; + return (priority1 > priority2 ? cert1 : cert2); +} + +int main(int argc, char **argv) +{ + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + char *alligator_cert = NULL; + char *alligator_key = NULL; + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(alligator_cert = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(alligator_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, alligator_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, alligator_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + /* Create config with s2n_config_add_cert_chain_and_key_to_store API with multiple certs */ + { + struct s2n_cert_chain_and_key *default_cert = NULL; + /* Associated data to attach to each certificate to use in the tiebreak callback. */ + int tiebreak_priorites[NUM_TIED_CERTS] = { 0 }; + /* Collection of certs with the same domain name that need to have ties resolved. */ + struct s2n_cert_chain_and_key *tied_certs[NUM_TIED_CERTS] = { NULL }; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cert_tiebreak_callback(server_config, test_cert_tiebreak_cb)); + + /* Need to add at least one cert with a different domain name to make cert lookup utilize hashmap */ + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + + /* Add NUM_TIED_CERTS that are actually the same certificate(www.alligator.com) to trigger the tiebreak callback. */ + for (unsigned int i = 0; i < NUM_TIED_CERTS; i++) { + EXPECT_NOT_NULL(tied_certs[i] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tied_certs[i], alligator_cert, alligator_key)); + tiebreak_priorites[i] = i; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ctx(tied_certs[i], (void *) &tiebreak_priorites[i])); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tied_certs[i])); + } + + EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); + EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "www.alligator.com")); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(num_times_cb_executed, NUM_TIED_CERTS - 1); + struct s2n_cert_chain_and_key *selected_cert = s2n_connection_get_selected_cert(server_conn); + /* The last alligator certificate should have the highest priority */ + EXPECT_EQUAL(selected_cert, tied_certs[(NUM_TIED_CERTS - 1)]); + EXPECT_EQUAL(s2n_cert_chain_and_key_get_ctx(selected_cert), (void *) &tiebreak_priorites[(NUM_TIED_CERTS - 1)]); + EXPECT_EQUAL(*((int *) s2n_cert_chain_and_key_get_ctx(selected_cert)), NUM_TIED_CERTS - 1); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + for (size_t i = 0; i < NUM_TIED_CERTS; i++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tied_certs[i])); + } + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Create config with deprecated s2n_config_add_cert_chain_and_key API */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + + EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); + EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Do not allow configs to call both + * s2n_config_add_cert_chain_and_key and s2n_config_add_cert_chain_and_key_to_store */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Config first uses s2n_config_add_cert_chain_and_key: library owns chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Add first chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + /* Try to add second chain of same type */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + /* Try to add chain using other method */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key_to_store(config, chain), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + }; + + /* Config first uses s2n_config_add_cert_chain_and_key_to_store: application owns chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Add first chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Add second chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Try to add chain using other method */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + }; + }; + + /* s2n_cert_chain_and_key_load_pem */ + { + /* when loading a chain, all certs have a info associated with them and root is self-signed */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&chain, "ec", "ecdsa", + "p384", "sha256")); + struct s2n_cert *leaf = chain->cert_chain->head; + EXPECT_EQUAL(leaf->info.self_signed, false); + EXPECT_EQUAL(leaf->info.signature_nid, NID_ecdsa_with_SHA256); + EXPECT_EQUAL(leaf->info.signature_digest_nid, NID_sha256); + + struct s2n_cert *intermediate = leaf->next; + EXPECT_NOT_NULL(intermediate); + EXPECT_EQUAL(intermediate->info.self_signed, false); + EXPECT_EQUAL(intermediate->info.signature_nid, NID_ecdsa_with_SHA256); + EXPECT_EQUAL(intermediate->info.signature_digest_nid, NID_sha256); + + struct s2n_cert *root = intermediate->next; + EXPECT_NOT_NULL(intermediate); + EXPECT_NULL(root->next); + EXPECT_EQUAL(root->info.self_signed, true); + EXPECT_EQUAL(root->info.signature_nid, NID_ecdsa_with_SHA256); + EXPECT_EQUAL(root->info.signature_digest_nid, NID_sha256); + }; + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(cert_chain); + free(private_key); + free(alligator_cert); + free(alligator_key); + END_TEST(); +} diff --git a/tests/unit/s2n_cert_status_extension_test.c b/tests/unit/s2n_cert_status_extension_test.c index 75b72b07ad2..cd66098717c 100644 --- a/tests/unit/s2n_cert_status_extension_test.c +++ b/tests/unit/s2n_cert_status_extension_test.c @@ -1,409 +1,409 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_cert_status.h" - -const uint8_t ocsp_data[] = "OCSP DATA"; - -int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) -{ - conn->status_type = S2N_STATUS_REQUEST_OCSP; - conn->handshake_params.our_chain_and_key = chain_and_key; - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); - conn->x509_validator.state = VALIDATED; - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* should_send */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Don't send by default */ - EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); - - /* Send if all prerequisites met */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); - - /* Send if client */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - conn->mode = S2N_CLIENT; - EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); - - /* Send if server */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - conn->mode = S2N_SERVER; - EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); - - /* Don't send if no certificate set */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - conn->handshake_params.our_chain_and_key = NULL; - EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); - - /* Don't send if no ocsp data */ - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->ocsp_status)); - EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test send */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); - - uint8_t request_type = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &request_type)); - EXPECT_EQUAL(request_type, S2N_STATUS_REQUEST_OCSP); - - uint32_t ocsp_size = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &ocsp_size)); - EXPECT_EQUAL(ocsp_size, s2n_stuffer_data_available(&stuffer)); - EXPECT_EQUAL(ocsp_size, s2n_array_len(ocsp_data)); - - uint8_t *actual_ocsp_data = NULL; - EXPECT_NOT_NULL(actual_ocsp_data = s2n_stuffer_raw_read(&stuffer, ocsp_size)); - EXPECT_BYTEARRAY_EQUAL(actual_ocsp_data, ocsp_data, ocsp_size); - - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* 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 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); - - EXPECT_EQUAL(conn->status_response.size, 0); - EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); - EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, ocsp_data, s2n_array_len(ocsp_data)); - - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test recv - not ocsp */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, S2N_STATUS_REQUEST_NONE)); - - EXPECT_EQUAL(conn->status_response.size, 0); - EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); - EXPECT_EQUAL(conn->status_response.size, 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test recv - bad ocsp data */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - - EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); - - if (s2n_x509_ocsp_stapling_supported()) { - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), - S2N_ERR_INVALID_OCSP_RESPONSE); - } else { - /* s2n_x509_validator_validate_cert_stapled_ocsp_response returns untrusted error if ocsp is not supported */ - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), - S2N_ERR_CERT_UNTRUSTED); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Self-talk tests */ - if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { - uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t ocsp_response_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_TRUE(ocsp_response_len > 0); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); - - /* Client requests OCSP staple, and server sends OCSP response */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - EXPECT_NOT_NULL(client_received_ocsp_response); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - /* Only the client requested a response, the server should not have received one. */ - EXPECT_NULL(server_received_ocsp_response); - - /* The server sent an OCSP response, and the client received an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - } - - /* Server requests OCSP staple, and client sends OCSP response */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); - - /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is - * being used as a client certificate. Intent verification is disabled to allow this - * certificate to be used as a client certificate anyway. - */ - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - /* Only the server requested a response, the client should not have received one. */ - EXPECT_NULL(client_received_ocsp_response); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - EXPECT_NOT_NULL(server_received_ocsp_response); - - /* The server did not send an OCSP response, and the client did not receive an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - } - - /* Client and server both request OCSP staples, and client and server both send responses */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); - - /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is - * being used as a client certificate. Intent verification is disabled to allow this - * certificate to be used as a client certificate anyway. - */ - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - EXPECT_NOT_NULL(client_received_ocsp_response); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - EXPECT_NOT_NULL(server_received_ocsp_response); - - /* The server sent an OCSP response, and the client received an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - } - - /* Server sets an OCSP response but client does not request OCSP stapling */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - - uint32_t client_received_ocsp_response_len = 0; - const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, - &client_received_ocsp_response_len); - - uint32_t server_received_ocsp_response_len = 0; - const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, - &server_received_ocsp_response_len); - - /* Both the server and client did not request OCSP responses, so neither should have received them. */ - EXPECT_NULL(client_received_ocsp_response); - EXPECT_NULL(server_received_ocsp_response); - - /* The server did not send an OCSP response, and the client did not receive an OCSP response */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_status.h" + +const uint8_t ocsp_data[] = "OCSP DATA"; + +int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) +{ + conn->status_type = S2N_STATUS_REQUEST_OCSP; + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); + conn->x509_validator.state = VALIDATED; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* should_send */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Don't send by default */ + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + /* Send if all prerequisites met */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Send if client */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_CLIENT; + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Send if server */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_SERVER; + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Don't send if no certificate set */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->handshake_params.our_chain_and_key = NULL; + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + /* Don't send if no ocsp data */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->ocsp_status)); + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + uint8_t request_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &request_type)); + EXPECT_EQUAL(request_type, S2N_STATUS_REQUEST_OCSP); + + uint32_t ocsp_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &ocsp_size)); + EXPECT_EQUAL(ocsp_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(ocsp_size, s2n_array_len(ocsp_data)); + + uint8_t *actual_ocsp_data = NULL; + EXPECT_NOT_NULL(actual_ocsp_data = s2n_stuffer_raw_read(&stuffer, ocsp_size)); + EXPECT_BYTEARRAY_EQUAL(actual_ocsp_data, ocsp_data, ocsp_size); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* 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 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); + EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, ocsp_data, s2n_array_len(ocsp_data)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - not ocsp */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, S2N_STATUS_REQUEST_NONE)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->status_response.size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - bad ocsp data */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + if (s2n_x509_ocsp_stapling_supported()) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), + S2N_ERR_INVALID_OCSP_RESPONSE); + } else { + /* s2n_x509_validator_validate_cert_stapled_ocsp_response returns untrusted error if ocsp is not supported */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), + S2N_ERR_CERT_UNTRUSTED); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Self-talk tests */ + if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { + uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t ocsp_response_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_TRUE(ocsp_response_len > 0); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); + + /* Client requests OCSP staple, and server sends OCSP response */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + EXPECT_NOT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + /* Only the client requested a response, the server should not have received one. */ + EXPECT_NULL(server_received_ocsp_response); + + /* The server sent an OCSP response, and the client received an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + } + + /* Server requests OCSP staple, and client sends OCSP response */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); + + /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is + * being used as a client certificate. Intent verification is disabled to allow this + * certificate to be used as a client certificate anyway. + */ + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + /* Only the server requested a response, the client should not have received one. */ + EXPECT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + EXPECT_NOT_NULL(server_received_ocsp_response); + + /* The server did not send an OCSP response, and the client did not receive an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + } + + /* Client and server both request OCSP staples, and client and server both send responses */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_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_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); + + /* The OCSP certificate chain was not issued with the purpose of TLS clientAuth, but is + * being used as a client certificate. Intent verification is disabled to allow this + * certificate to be used as a client certificate anyway. + */ + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + EXPECT_NOT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + EXPECT_NOT_NULL(server_received_ocsp_response); + + /* The server sent an OCSP response, and the client received an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + } + + /* Server sets an OCSP response but client does not request OCSP stapling */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + + /* Both the server and client did not request OCSP responses, so neither should have received them. */ + EXPECT_NULL(client_received_ocsp_response); + EXPECT_NULL(server_received_ocsp_response); + + /* The server did not send an OCSP response, and the client did not receive an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cert_validation_callback_test.c b/tests/unit/s2n_cert_validation_callback_test.c index 7ffa74cdc49..cbdfb819cb1 100644 --- a/tests/unit/s2n_cert_validation_callback_test.c +++ b/tests/unit/s2n_cert_validation_callback_test.c @@ -1,577 +1,577 @@ -#include "utils/s2n_prelude.h" -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_x509_validator.h" - -struct s2n_cert_validation_data { - unsigned call_accept_or_reject : 1; - unsigned accept : 1; - unsigned return_success : 1; - - int invoked_count; - struct s2n_cert_validation_info *info; -}; - -static int s2n_test_cert_validation_callback(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *ctx) -{ - struct s2n_cert_validation_data *data = (struct s2n_cert_validation_data *) ctx; - - data->invoked_count += 1; - /* Pass the `s2n_cert_validation_info` struct to application-defined `ctx` */ - data->info = info; - - int ret = S2N_FAILURE; - if (data->return_success) { - ret = S2N_SUCCESS; - } - - if (!data->call_accept_or_reject) { - return ret; - } - - if (data->accept) { - EXPECT_SUCCESS(s2n_cert_validation_accept(info)); - } else { - EXPECT_SUCCESS(s2n_cert_validation_reject(info)); - } - - return ret; -} - -static int s2n_test_cert_validation_callback_self_talk(struct s2n_connection *conn, - struct s2n_cert_validation_info *info, void *ctx) -{ - DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(peer_cert_chain); - - /* Ensure that the peer's certificate chain can be retrieved at the time the callback is invoked */ - EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(conn, peer_cert_chain)); - uint32_t peer_cert_chain_len = 0; - EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); - EXPECT_TRUE(peer_cert_chain_len > 0); - - return s2n_test_cert_validation_callback(conn, info, ctx); -} - -static int s2n_test_cert_validation_callback_self_talk_server(struct s2n_connection *conn, - struct s2n_cert_validation_info *info, void *ctx) -{ - /* Ensure that the callback was invoked on the server connection */ - EXPECT_EQUAL(conn->mode, S2N_SERVER); - - /* Ensure that the client's certificate chain can be retrieved at the time the callback was invoked */ - uint8_t *der_cert_chain = 0; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(conn, &der_cert_chain, &cert_chain_len)); - EXPECT_TRUE(cert_chain_len > 0); - - return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); -} - -static int s2n_test_cert_validation_callback_self_talk_ocsp(struct s2n_connection *conn, - struct s2n_cert_validation_info *info, void *ctx) -{ - /* Ensure that the OCSP response was received prior to invoking the callback */ - uint32_t ocsp_response_length = 0; - const uint8_t *ocsp_response = s2n_connection_get_ocsp_response(conn, &ocsp_response_length); - EXPECT_NOT_NULL(ocsp_response); - EXPECT_TRUE(ocsp_response_length > 0); - - return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* Accept/reject tests */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(NULL), S2N_ERR_NULL); - - /* Accept sets the proper state */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); - - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, true); - } - - /* Reject sets the proper state */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); - - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, false); - } - - /* Calls to accept/reject fail if accept has already been called */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); - - for (int i = 0; i < 10; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); - } - - /* State was updated from the successful call */ - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, true); - } - - /* Calls to accept/reject fail if reject has already been called */ - { - struct s2n_cert_validation_info info = { 0 }; - - EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); - - for (int i = 0; i < 10; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); - EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); - } - - /* State was updated from the successful call */ - EXPECT_EQUAL(info.finished, true); - EXPECT_EQUAL(info.accepted, false); - } - } - - /* Test s2n_cert_validation_callback */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* clang-format off */ - struct { - const struct s2n_cert_validation_data data; - s2n_error expected_error; - } test_cases[] = { - /* No error when accept is called from the callback */ - { - .data = { .call_accept_or_reject = true, .accept = true, .return_success = true }, - .expected_error = S2N_ERR_OK - }, - - /* Error if reject was called from the callback */ - { - .data = { .call_accept_or_reject = true, .accept = false, .return_success = true }, - .expected_error = S2N_ERR_CERT_REJECTED - }, - - /* Error if the callback doesn't return successfully */ - { - .data = { .call_accept_or_reject = true, .accept = true, .return_success = false }, - .expected_error = S2N_ERR_CANCELLED - }, - { - .data = { .call_accept_or_reject = true, .accept = false, .return_success = false }, - .expected_error = S2N_ERR_CANCELLED - }, - }; - /* clang-format on */ - - /* s2n_x509_validator test */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out)); - } else { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, - chain_len, &pkey_type, &public_key_out), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* The callback is invoked even if cert verification is disabled */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - /* Initialize the x509_validator with skip_cert_validation enabled */ - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out)); - } else { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, - chain_len, &pkey_type, &public_key_out), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Self-talk: callback is invoked on the client after receiving the server's certificate */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Self-talk: callback is invoked on the server after receiving the client's certificate */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, - s2n_test_cert_validation_callback_self_talk_server, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - 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)); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Self-talk: callback is invoked after an OCSP response is received in TLS 1.3 - * - * Currently, the cert validation callback is invoked after validating the certificate - * chain and after processing the Certificate message extensions. In TLS 1.3, the OCSP - * response is sent in a Certificate message extension, and should be accessible to the - * cert validation callback. - * - * In TLS 1.2, the OCSP response is sent in a separate CertificateStatus message which is - * received after the cert validation callback is invoked. So, OCSP information won't be - * accessible from the callback in TLS 1.2. - */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - if (!s2n_x509_ocsp_stapling_supported() || !s2n_is_tls13_fully_supported()) { - break; - } - - uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t ocsp_response_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, - S2N_MAX_TEST_PEM_SIZE)); - EXPECT_TRUE(ocsp_response_len > 0); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, - S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - struct s2n_cert_validation_data data = test_cases[i].data; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(client_config, - s2n_test_cert_validation_callback_self_talk_ocsp, &data)); - - 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, "s2n Test Cert")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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)); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - expected_error); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* For async cases, accept or reject API will be called outside of the validation callback. - * Iterate over both TLS 1.3 and 1.2 policies to ensure the stuffer reset logic works in all cases. - */ - struct s2n_cert_validation_data async_test_cases[] = { - { .call_accept_or_reject = false, .accept = true, .return_success = true }, - { .call_accept_or_reject = false, .accept = false, .return_success = true }, - }; - const char *versions[] = { "20240501", "20170210" }; - - /* Async callback is invoked on the client after receiving the server's certificate */ - for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { - for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, versions[version_idx])); - - struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - for (int i = 0; i < 3; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Ensure that the server's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ - DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(peer_cert_chain); - EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, peer_cert_chain)); - /* Ensure the certificate chain is non-empty */ - uint32_t peer_cert_chain_len = 0; - EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); - EXPECT_TRUE(peer_cert_chain_len > 0); - - struct s2n_cert_validation_info *info = data.info; - EXPECT_NOT_NULL(info); - - if (async_test_cases[test_case_idx].accept) { - EXPECT_SUCCESS(s2n_cert_validation_accept(info)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_SUCCESS(s2n_cert_validation_reject(info)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_REJECTED); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - } - - /* Async callback is invoked on the server after receiving the client's certificate */ - for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { - for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, versions[version_idx])); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, - s2n_test_cert_validation_callback_self_talk_server, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, versions[version_idx])); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - 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)); - - for (int i = 0; i < 3; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_ASYNC_BLOCKED); - EXPECT_EQUAL(data.invoked_count, 1); - } - - /* Ensure that the client's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ - uint8_t *der_cert_chain = 0; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(server_conn, &der_cert_chain, &cert_chain_len)); - /* Ensure the certificate chain is non-empty */ - EXPECT_TRUE(cert_chain_len > 0); - - struct s2n_cert_validation_info *info = data.info; - EXPECT_NOT_NULL(info); - - if (async_test_cases[test_case_idx].accept) { - EXPECT_SUCCESS(s2n_cert_validation_accept(info)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else { - EXPECT_SUCCESS(s2n_cert_validation_reject(info)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_REJECTED); - } - - EXPECT_EQUAL(data.invoked_count, 1); - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_x509_validator.h" + +struct s2n_cert_validation_data { + unsigned call_accept_or_reject : 1; + unsigned accept : 1; + unsigned return_success : 1; + + int invoked_count; + struct s2n_cert_validation_info *info; +}; + +static int s2n_test_cert_validation_callback(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *ctx) +{ + struct s2n_cert_validation_data *data = (struct s2n_cert_validation_data *) ctx; + + data->invoked_count += 1; + /* Pass the `s2n_cert_validation_info` struct to application-defined `ctx` */ + data->info = info; + + int ret = S2N_FAILURE; + if (data->return_success) { + ret = S2N_SUCCESS; + } + + if (!data->call_accept_or_reject) { + return ret; + } + + if (data->accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + } + + return ret; +} + +static int s2n_test_cert_validation_callback_self_talk(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(peer_cert_chain); + + /* Ensure that the peer's certificate chain can be retrieved at the time the callback is invoked */ + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(conn, peer_cert_chain)); + uint32_t peer_cert_chain_len = 0; + EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); + EXPECT_TRUE(peer_cert_chain_len > 0); + + return s2n_test_cert_validation_callback(conn, info, ctx); +} + +static int s2n_test_cert_validation_callback_self_talk_server(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + /* Ensure that the callback was invoked on the server connection */ + EXPECT_EQUAL(conn->mode, S2N_SERVER); + + /* Ensure that the client's certificate chain can be retrieved at the time the callback was invoked */ + uint8_t *der_cert_chain = 0; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(conn, &der_cert_chain, &cert_chain_len)); + EXPECT_TRUE(cert_chain_len > 0); + + return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); +} + +static int s2n_test_cert_validation_callback_self_talk_ocsp(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + /* Ensure that the OCSP response was received prior to invoking the callback */ + uint32_t ocsp_response_length = 0; + const uint8_t *ocsp_response = s2n_connection_get_ocsp_response(conn, &ocsp_response_length); + EXPECT_NOT_NULL(ocsp_response); + EXPECT_TRUE(ocsp_response_length > 0); + + return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Accept/reject tests */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(NULL), S2N_ERR_NULL); + + /* Accept sets the proper state */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); + + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, true); + } + + /* Reject sets the proper state */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); + + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, false); + } + + /* Calls to accept/reject fail if accept has already been called */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); + + for (int i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); + } + + /* State was updated from the successful call */ + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, true); + } + + /* Calls to accept/reject fail if reject has already been called */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); + + for (int i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); + } + + /* State was updated from the successful call */ + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, false); + } + } + + /* Test s2n_cert_validation_callback */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* clang-format off */ + struct { + const struct s2n_cert_validation_data data; + s2n_error expected_error; + } test_cases[] = { + /* No error when accept is called from the callback */ + { + .data = { .call_accept_or_reject = true, .accept = true, .return_success = true }, + .expected_error = S2N_ERR_OK + }, + + /* Error if reject was called from the callback */ + { + .data = { .call_accept_or_reject = true, .accept = false, .return_success = true }, + .expected_error = S2N_ERR_CERT_REJECTED + }, + + /* Error if the callback doesn't return successfully */ + { + .data = { .call_accept_or_reject = true, .accept = true, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + { + .data = { .call_accept_or_reject = true, .accept = false, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + }; + /* clang-format on */ + + /* s2n_x509_validator test */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, + chain_len, &pkey_type, &public_key_out), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* The callback is invoked even if cert verification is disabled */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + /* Initialize the x509_validator with skip_cert_validation enabled */ + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, + chain_len, &pkey_type, &public_key_out), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked on the client after receiving the server's certificate */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked on the server after receiving the client's certificate */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, + s2n_test_cert_validation_callback_self_talk_server, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + 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)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked after an OCSP response is received in TLS 1.3 + * + * Currently, the cert validation callback is invoked after validating the certificate + * chain and after processing the Certificate message extensions. In TLS 1.3, the OCSP + * response is sent in a Certificate message extension, and should be accessible to the + * cert validation callback. + * + * In TLS 1.2, the OCSP response is sent in a separate CertificateStatus message which is + * received after the cert validation callback is invoked. So, OCSP information won't be + * accessible from the callback in TLS 1.2. + */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + if (!s2n_x509_ocsp_stapling_supported() || !s2n_is_tls13_fully_supported()) { + break; + } + + uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t ocsp_response_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, + S2N_MAX_TEST_PEM_SIZE)); + EXPECT_TRUE(ocsp_response_len > 0); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, + S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(client_config, + s2n_test_cert_validation_callback_self_talk_ocsp, &data)); + + 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, "s2n Test Cert")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* For async cases, accept or reject API will be called outside of the validation callback. + * Iterate over both TLS 1.3 and 1.2 policies to ensure the stuffer reset logic works in all cases. + */ + struct s2n_cert_validation_data async_test_cases[] = { + { .call_accept_or_reject = false, .accept = true, .return_success = true }, + { .call_accept_or_reject = false, .accept = false, .return_success = true }, + }; + const char *versions[] = { "20240501", "20170210" }; + + /* Async callback is invoked on the client after receiving the server's certificate */ + for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { + for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, versions[version_idx])); + + struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + for (int i = 0; i < 3; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Ensure that the server's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(peer_cert_chain); + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, peer_cert_chain)); + /* Ensure the certificate chain is non-empty */ + uint32_t peer_cert_chain_len = 0; + EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); + EXPECT_TRUE(peer_cert_chain_len > 0); + + struct s2n_cert_validation_info *info = data.info; + EXPECT_NOT_NULL(info); + + if (async_test_cases[test_case_idx].accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_REJECTED); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + } + + /* Async callback is invoked on the server after receiving the client's certificate */ + for (int test_case_idx = 0; test_case_idx < s2n_array_len(async_test_cases); test_case_idx++) { + for (int version_idx = 0; version_idx < s2n_array_len(versions); version_idx++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, versions[version_idx])); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_cert_validation_data data = async_test_cases[test_case_idx]; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, + s2n_test_cert_validation_callback_self_talk_server, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, versions[version_idx])); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + 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)); + + for (int i = 0; i < 3; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Ensure that the client's certificate chain can be retrieved after `S2N_ERR_ASYNC_BLOCKED` */ + uint8_t *der_cert_chain = 0; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(server_conn, &der_cert_chain, &cert_chain_len)); + /* Ensure the certificate chain is non-empty */ + EXPECT_TRUE(cert_chain_len > 0); + + struct s2n_cert_validation_info *info = data.info; + EXPECT_NOT_NULL(info); + + if (async_test_cases[test_case_idx].accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_REJECTED); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cipher_suite_match_test.c b/tests/unit/s2n_cipher_suite_match_test.c index 74305f680a5..52a49b41363 100644 --- a/tests/unit/s2n_cipher_suite_match_test.c +++ b/tests/unit/s2n_cipher_suite_match_test.c @@ -1,1362 +1,1362 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_ecc_evp.h" -#include "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" - -static s2n_result s2n_conn_set_chosen_psk(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - uint8_t psk_identity[] = "psk identity"; - RESULT_GUARD(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &conn->psk_params.chosen_psk)); - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - RESULT_GUARD(s2n_psk_init(conn->psk_params.chosen_psk, S2N_PSK_TYPE_EXTERNAL)); - RESULT_GUARD_POSIX(s2n_psk_set_identity(conn->psk_params.chosen_psk, psk_identity, sizeof(psk_identity))); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, - S2N_MAX_TEST_PEM_SIZE)); - - /* Test client cipher selection */ - { - /* Setup connections */ - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - /* Setup config */ - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Test that the client allows the server to select ciphers that were offered in ClientHello */ - { - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - - /* The client will offer the default tls13 ciphersuites */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); - - /* The server will send a TLS13 cipher over the wire */ - uint8_t valid_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256 - }; - - /* We expect to succeed because the cipher was offered by the client */ - EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_wire_ciphers)); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test that the client rejects a cipher that was not originally offered in ClientHello */ - { - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - - /* The client will offer the default tls13 ciphersuites */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); - - /* The server will send a TLS12 cipher over the wire */ - uint8_t invalid_wire_ciphers[] = { - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - }; - - /* We expect to fail because the cipher was not offered by the client */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, invalid_wire_ciphers), S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /** Clients MUST verify - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *= type=test - *# that the server selected a cipher suite - *# indicating a Hash associated with the PSK - **/ - { - /* If chosen PSK is set, test error case for incorrect hash match */ - { - s2n_connection_set_cipher_preferences(conn, "default_tls13"); - - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - - uint8_t valid_tls13_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, - }; - - /* S2N_HMAC_SHA1 is not a matching hmac algorithm */ - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers), - S2N_ERR_CIPHER_NOT_SUPPORTED); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_null_cipher_suite); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* If chosen PSK is set, test success case for matching hash algorithm */ - { - s2n_connection_set_cipher_preferences(conn, "default_tls13"); - - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - - uint8_t valid_tls13_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, - }; - - /* S2N_HMAC_SHA256 is a matching hmac algorithm for the cipher suite present in valid_tls13_wire_ciphers */ - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; - EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test server cipher selection and scsv detection */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *server_config = NULL; - char *rsa_cert_chain_pem = NULL, *rsa_private_key_pem = NULL, *ecdsa_cert_chain_pem = NULL, *ecdsa_private_key_pem = NULL; - struct s2n_cert_chain_and_key *rsa_cert = NULL, *ecdsa_cert = NULL; - /* Allocate all of the objects and PEMs we'll need for this test. */ - EXPECT_NOT_NULL(rsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(rsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(ecdsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(ecdsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(rsa_cert = s2n_cert_chain_and_key_new()); - EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(rsa_cert, rsa_cert_chain_pem, rsa_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(ecdsa_cert, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); - - uint8_t wire_ciphers[] = { - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - }; - const uint8_t cipher_count = sizeof(wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; - - uint8_t wire_ciphers_fallback[] = { - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_FALLBACK_SCSV, /* At the end to verify it isn't missed */ - }; - const uint8_t cipher_count_fallback = sizeof(wire_ciphers_fallback) / S2N_TLS_CIPHER_SUITE_LEN; - - uint8_t wire_ciphers_renegotiation[] = { - TLS_RSA_WITH_RC4_128_MD5, - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_256_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* At the end to verify it isn't missed */ - }; - const uint8_t cipher_count_renegotiation = sizeof(wire_ciphers_renegotiation) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Only two ciphers for testing RSA vs ECDSA. */ - uint8_t wire_ciphers_with_ecdsa[] = { - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - }; - const uint8_t cipher_count_ecdsa = sizeof(wire_ciphers_with_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Only ECDSA ciphers */ - uint8_t wire_ciphers_only_ecdsa[] = { - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - }; - const uint8_t cipher_count_only_ecdsa = sizeof(wire_ciphers_only_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; - - uint8_t wire_ciphers_rsa_fallback[] = { - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - }; - const uint8_t cipher_count_rsa_fallback = sizeof(wire_ciphers_rsa_fallback) / S2N_TLS_CIPHER_SUITE_LEN; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - /* Security policy must allow all test cipher suites */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - /* TEST RSA */ - conn->actual_protocol_version = S2N_TLS10; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); - EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* TEST RENEGOTIATION - * - *= https://www.rfc-editor.org/rfc/rfc5746#3.6 - *= type=test - *# o When a ClientHello is received, the server MUST check if it - *# includes the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, - *# set the secure_renegotiation flag to TRUE. - */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_renegotiation, cipher_count_renegotiation)); - EXPECT_EQUAL(conn->secure_renegotiation, 1); - EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); - EXPECT_EQUAL(-1, s2n_connection_is_valid_for_cipher_preferences(conn, "not_exist")); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* Simulate a TLSv11 client to trigger the fallback error */ - conn->actual_protocol_version = S2N_TLS11; - EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers_fallback, cipher_count_fallback)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); - EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2018")); - EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2019")); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* TEST RSA cipher chosen when ECDSA cipher is at top */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - - const struct s2n_ecc_preferences *ecc_pref = NULL; - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - EXPECT_NOT_NULL(ecc_pref); - - /* Assume default for negotiated curve. */ - /* Shouldn't be necessary unless the test fails, but we want the failure to be obvious. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - const struct s2n_cipher_suite *expected_rsa_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_rsa_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* Clean+free to setup for ECDSA tests */ - EXPECT_SUCCESS(s2n_config_free(server_config)); - - /* Set ECDSA CERT in s2n_config */ - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - EXPECT_NOT_NULL(ecc_pref); - - /* TEST ECDSA */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); - const struct s2n_cipher_suite *expected_ecdsa_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - /* Assume default for negotiated curve. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - - /* TEST ECDSA cipher chosen when RSA cipher is at top */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - /* Assume default for negotiated curve. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - - /* TEST two certificates. Use two certs with different key types(RSA, ECDSA) and add them to a single - * s2n_config. - */ - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends both RSA and ECDSA ciphers, server only configures RSA ciphers, - * ECDSA + RSA cert is configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - /* 20170328 only supports RSA ciphers */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170328")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends both RSA and ECDSA ciphers, server only configures ECDSA ciphers, ECDSA + RSA cert is - * configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client only sends RSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is - * configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_rc4_128_md5; - if (!expected_wire_choice->available) { - expected_wire_choice = &s2n_rsa_with_3des_ede_cbc_sha; - } - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client only sends ECDSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is - * configured. - */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_only_ecdsa, cipher_count_only_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends ECDHE-ECDSA, RSA, ECDHE-RSA ciphers. Server prioritizes ECDSA but also supports RSA. - * No mutually supported elliptic curves between client and server. ECDSA + RSA cert is configured. - */ - { - /* If there are no shared elliptic curves, we must fall through to a cipher that supports RSA kx. - * This is the first RSA kx cipher that CloudFront-Upstream supports. - */ - const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_aes_256_gcm_sha384; - /* Selecting this preference list because it prioritizes ECDHE-ECDSA and ECDHE-RSA over plain RSA kx. */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "CloudFront-Upstream")); - /* No shared curve */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_rsa_fallback, cipher_count_rsa_fallback)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - EXPECT_SUCCESS(s2n_config_free(server_config)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - /* Override auto-chosen defaults with only RSA cert default. ECDSA still loaded, but not default. */ - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured, - * only RSA is default. Expect default RSA used instead of previous test that expects ECDSA for this case. */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Override auto-chosen defaults with only ECDSA cert default. RSA still loaded, but not default. */ - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &ecdsa_cert, 1)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured, - * only ECDSA is default. Expect default ECDSA used instead of previous test that expects RSA for this case. */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test override back to both RSA and ECDSA defaults. */ - struct s2n_cert_chain_and_key *certs_list[] = { rsa_cert, ecdsa_cert }; - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, certs_list, 2)); - - /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test that defaults are not overriden after failures to set new default certificates */ - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, NULL, 0), S2N_ERR_NULL); - EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NULL"), 0); - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 0), - S2N_ERR_NUM_DEFAULT_CERTIFICATES); - EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NUM_DEFAULT_CERTIFICATES"), 0); - struct s2n_cert_chain_and_key *rsa_certs_list[] = { rsa_cert, rsa_cert }; - EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, rsa_certs_list, 2), - S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE"), 0); - - /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured. - * RSA default certificate should be chosen. */ - { - const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->actual_protocol_version = conn->server_protocol_version; - EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); - EXPECT_EQUAL(conn->secure_renegotiation, 0); - EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - struct s2n_cipher_suite *tls12_cipher_suite = cipher_preferences_20170210.suites[cipher_preferences_20170210.count - 1]; - uint8_t wire_ciphers_with_tls13[] = { - TLS_AES_128_GCM_SHA256, - TLS_AES_256_GCM_SHA384, - TLS_CHACHA20_POLY1305_SHA256, - tls12_cipher_suite->iana_value[0], tls12_cipher_suite->iana_value[1] - }; - const uint8_t cipher_count_tls13 = sizeof(wire_ciphers_with_tls13) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Client sends TLS1.3 cipher suites, but server does not support TLS1.3 */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite, tls12_cipher_suite); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Client sends TLS1.3 cipher suites, server selects correct TLS1.3 ciphersuite */ - if (s2n_is_tls13_fully_supported()) { - struct test_case { - const char *cipher_pref; - const struct s2n_cipher_suite *expected_cipher_wire; - }; - - struct test_case test_cases[] = { - { .cipher_pref = "default_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, - { .cipher_pref = "test_all", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, - { .cipher_pref = "test_all_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].cipher_pref)); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite, test_cases[i].expected_cipher_wire); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - } - } - - /* Check wire's cipher suites with preferred tls12 ordering does not affect tls13 selection */ - { - uint8_t wire_ciphers2[] = { - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - TLS_CHACHA20_POLY1305_SHA256, /* tls 1.3 */ - }; - - const uint8_t count = sizeof(wire_ciphers2) / S2N_TLS_CIPHER_SUITE_LEN; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - conn->server_protocol_version = S2N_TLS13; - - if (s2n_chacha20_poly1305.is_available()) { - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); - } else { - EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); - } - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Test cipher suite with a required version higher than what connection supports should not be selected */ - { - uint8_t test_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - }; - - const uint8_t count = sizeof(test_wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->actual_protocol_version = S2N_TLS12; - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, count)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* We should skip cipher suites with a minimum protocol version unsupported by the connection. - * If no valid cipher suite is found, we should fall back to a cipher suite with a higher protocol version, - * but we should NEVER use a TLS1.3 suite on a pre-TLS1.3 connection or vice versa. */ - { - /* Skip but fall back to cipher suite with protocol version higher than connection */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - uint8_t test_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* ssl v3 */ - }; - - conn->actual_protocol_version = S2N_TLS10; - - /* If a match exists, skip the invalid cipher and choose it */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 3)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_cbc_sha); - - /* If a match does not exist, choose the invalid cipher */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* Skip and do NOT fall back to a TLS1.3 cipher suite if using TLS1.2 */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - uint8_t test_wire_ciphers[] = { - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - }; - - conn->actual_protocol_version = S2N_TLS12; - - /* If a match exists, skip the invalid cipher and choose it */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); - - /* If a match does not exist, fail to negotiate a cipher suite. - * We cannot fall back to the TLS1.3 choice. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - } - - /* Skip and do NOT fall back to a TLS1.2 cipher suite if using TLS1.3 */ - { - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - uint8_t test_wire_ciphers[] = { - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ - TLS_AES_128_GCM_SHA256, /* tls 1.3 */ - }; - - conn->actual_protocol_version = S2N_TLS13; - - /* If a match exists, skip the invalid cipher and choose it */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); - EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - - /* If a match does not exist, fail to negotiate a cipher suite. - * We cannot fall back to the TLS1.2 choice. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - } - }; - - /* If a PSK is being used, then the cipher suite must match the PSK's HMAC algorithm. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *= type=test - *# The server MUST ensure that it selects a compatible PSK - *# (if any) and cipher suite. - **/ - { - /* If chosen PSK is set, a cipher suite with matching HMAC algorithm must be selected */ - { - s2n_connection_set_cipher_preferences(conn, "test_all"); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->actual_protocol_version = S2N_TLS13; - - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); - - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA384; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); - EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - - /* If chosen PSK is set but there is no matching cipher, the server MUST fail to set a cipher */ - { - s2n_connection_set_cipher_preferences(conn, "test_all"); - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->actual_protocol_version = S2N_TLS13; - - /* S2N_HMAC_SHA1 is not a matching HMAC algorithm for any TLS1.3 cipher */ - EXPECT_OK(s2n_conn_set_chosen_psk(conn)); - conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; - - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - }; - }; - - /* Client sends cipher which is not in the configured suite */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - uint8_t invalid_cipher_pref[] = { - TLS_NULL_WITH_NULL_NULL - }; - - const uint8_t invalid_cipher_count = sizeof(invalid_cipher_pref) / S2N_TLS_CIPHER_SUITE_LEN; - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); - conn->client_protocol_version = S2N_TLS13; - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, invalid_cipher_pref, invalid_cipher_count), S2N_ERR_CIPHER_NOT_SUPPORTED); - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Client sends cipher that requires DH params */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - /* The client only offers one cipher suite, which requires dh kex */ - uint8_t wire[] = { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 }; - - /* By default, the server does not accept cipher suites with dh kex. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(server, wire, 1), - S2N_ERR_CIPHER_NOT_SUPPORTED); - - /* With dh params configured, the server accepts cipher suites with dh kex. */ - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(server, wire, 1)); - EXPECT_EQUAL(server->secure->cipher_suite, &s2n_dhe_rsa_with_aes_128_gcm_sha256); - }; - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); - free(ecdsa_cert_chain_pem); - free(ecdsa_private_key_pem); - free(rsa_cert_chain_pem); - free(rsa_private_key_pem); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test chacha20 boosting behaviour */ - { - /* Setup cipher preferences + security policy */ - struct s2n_cipher_preferences cipher_preferences = { 0 }; - struct s2n_security_policy security_policy = { - .minimum_protocol_version = S2N_SSLv2, - .cipher_preferences = &cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20201021, - .ecc_preferences = &s2n_ecc_preferences_test_all, - }; - - /* Initialise config and relevant certs */ - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - struct s2n_cert_chain_and_key *rsa_cert = NULL; - struct s2n_cert_chain_and_key *ecdsa_cert = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(rsa_cert); - EXPECT_NOT_NULL(ecdsa_cert); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - - if (s2n_chacha20_poly1305.is_available()) { - /* Test chacha20 boosting when ciphersuites fail auth validation */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - DEFER_CLEANUP(struct s2n_config *rsa_only_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(rsa_only_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_only_config, rsa_cert)); - /* Connection only supports rsa auth. */ - EXPECT_SUCCESS(s2n_connection_set_config(connection, rsa_only_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Not negotiated because invalid (ecdsa) */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - /* Only negotiated if chacha20 boosting is disabled */ - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - /* First valid chacha20 cipher suite and is negotiated */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting; valid chacha20 ciphersuite and is negotiated */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Not negotiated because invalid (ecdsa) */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify chacha20 RSA ciphersuite chosen with chacha20 boosting enabled */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: non-chacha20 RSA ciphersuite chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_gcm_sha384); - }; - - /* Server is able to negotiate its most preferred chacha20 ciphersuite */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Skipped because it is not a chacha20 ciphersuite. Is negotiated if chacha20 boosting is disabled. */ - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - /* First chacha20 ciphersuite and is negotiated */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - /* Second chacha20 ciphersuite and is not negotiated (not server's most preferred chacha20 ciphersuite) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting: not negotiated because it's not server's most preferred chacha20 ciphersuite */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is on */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify most preferred chacha20 ciphersuite is chosen with chacha20 boosting enabled */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred cipehrsuite is chosen when chacha20 boosting is off */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); - }; - - /* Server's most preferred chacha20 is not offered by the client */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Server's most preferred chacha20 ciphersuite; not offered by the client */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - /* Negotiated if chacha20 boosting is off */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - /* First valid chacha20 ciphersuite; negotiated if chacha20 boosting is on */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting; negotiated if chacha20 boosting is on */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - /* Verify server selects its second most preferred chacha20 ciphersuite */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256); - }; - - /* Server does not negotiate the client's most preferred chacha20 ciphersuite */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Skipped because not a chacha20 ciphersuite. If chacha20 boosting is off then this is negotiated.*/ - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - /* First chacha20 ciphersuite and is negotiated (client's second preferred ciphersuite) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - /* Second chacha20 ciphersuite and is the client's most preferred ciphersuite. Not negotiated. */ - &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. This is not negotiated as it's not the server's most preferred chacha20 ciphersuite */ - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is on */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify client's second preferred (ecdhe_rsa_chacha20) is negotiated when chacha20 boosting enabled */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); - }; - - /* Chacha20 boosting is disabled when client did not indicate chacha20 preference */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Negotiated because client did not signal chacha20 boosting and it is present in client wire */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - /* Never considered; if client did signal chacha20 boosting we expect this to be negotiated */ - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Chacha20 boosting is off. This ciphersuite is negotiated. */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - /* Not negotiated */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that client negotiates non-chacha20 ciphersuite when chacha20 boosting is not signalled by client */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); - }; - - /* Server negotiates its most preferred chacha20 ciphersuite for tls 1.3 */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Most preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ - &s2n_tls13_aes_128_gcm_sha256, - /* Second preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ - &s2n_tls13_aes_256_gcm_sha384, - /* Negotiated if chacha20 boosting behaviour is on. Otherwise, one of the two ciphersuites above is choosen. */ - &s2n_tls13_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t test_wire_1[] = { - /* Client signalled chacha20 boosting. Negotiated. */ - TLS_CHACHA20_POLY1305_SHA256, - /* Not negotiated even when chacha20 boosting is off. Server prefers aes 128 gcm. */ - TLS_AES_256_GCM_SHA384, - /* Negotiated if chacha20 boosting is off. This is the server's most preferred ciphersuite. */ - TLS_AES_128_GCM_SHA256, - }; - uint8_t count = sizeof(test_wire_1) / S2N_TLS_CIPHER_SUITE_LEN; - - cipher_preferences.allow_chacha20_boosting = true; - /* Verify that client negotiates chacha20 when chacha20 boosting is on */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - - /* - * Client did not signal chacha20 boosting. - * TLS_AES_256_GCM_SHA384 > TLS_CHACHA20_POLY1305_SHA256 - */ - uint8_t test_wire_2[] = { - /* Client did not signal chacha20 boosting. Negotiated if chacha20 boosting is off. */ - TLS_AES_256_GCM_SHA384, - /* Not negotiated since the server prefers aes 256 gcm over chacha20 */ - TLS_CHACHA20_POLY1305_SHA256, - }; - count = sizeof(test_wire_2) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that client negotiates server preferred non-chacha20 aes_256 when client did not signal */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_2, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_256_gcm_sha384); - }; - - /* Server can negotiate chacha20 ciphersuite, even when boosting is disabled */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Negotiated (only available option) */ - &s2n_tls13_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = false, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Negotiated as this is the only negotiable option. */ - TLS_CHACHA20_POLY1305_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify server can still negotiate chacha20 if it's the only option even when chacha20 boosting is off */ - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); - }; - - /* - * sslv2 server correctly negotiates ciphersuite when chacha20 boosting is enabled. - */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_SSLv2)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* First 'valid' ciphersuite that is saved as a higher_vers_match and is negotiated */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ - &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ - &s2n_dhe_rsa_with_aes_256_gcm_sha384 - }; - - /* cppcheck-suppress redundantAssignment */ - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Not negotiated as it's not offered by the server. */ - 0x00, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Not saved as a higher_vers_match as ecdhe_ecdsa_aes_128 was already saved as one; not negotiated */ - 0x00, - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Saved by server as higher_vers_match; negotiated as a 'fall-back' ciphersuite */ - 0x00, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - }; - uint8_t count = sizeof(wire) / S2N_SSLv2_CIPHER_SUITE_LEN; - - /* Verify server negotiate its higher_vers_match ecdhe_ecdsa_aes_128_cbc instead of a chacha20 ciphersuite */ - cipher_preferences.allow_chacha20_boosting = true; - /* - * For an sslv2 connection, all of the ciphersuites in test_cipher_suite_list will not meet the minimum - * required tls version validation (they all require at least sslv3). This means that the server will save this - * cipher as a higher_vers_match and will use this ciphersuite as a fallback if no other ciphersuite can be identified. - * When this logic happens, we bypass chacha20 boosting altogether; therefore because the first ciphersuite - * ecdhe_ecdsa_with_aes_128_cbc is offered by both the client and server and only fails the minimum required version check, - * then we save this ciphersuite as a higher_vers_match and continue. - */ - EXPECT_SUCCESS(s2n_set_cipher_as_sslv2_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); - }; - - /* Server is able to negotiate a ciphersuite even without chacha20 in its cipher pref */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Negotiated because it is the server's most preferred */ - &s2n_tls13_aes_128_gcm_sha256, - /* Not considered */ - &s2n_tls13_aes_256_gcm_sha384, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting; not negotiated as server does not have any chacha20 ciphersuites */ - TLS_CHACHA20_POLY1305_SHA256, - /* Not negotiated as the server prefers aes 128 over aes 256 */ - TLS_AES_256_GCM_SHA384, - /* Negotiated */ - TLS_AES_128_GCM_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that server is able to negotiate aes_128 even without any chacha20 ciphersuites in its preferences */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test chacha20 boosting when the most preferred ciphersuite fails version validation */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Only negotiated if chacha20 boosting is off */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - /* Invalid; never use tls 1.3 ciphers on pre-tls 1.3 connections */ - &s2n_tls13_chacha20_poly1305_sha256, - /* Negotiated (if chacha20 boosting is on) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Not negotiated by server since it is invalid for the connection. */ - TLS_CHACHA20_POLY1305_SHA256, - /* Negotiated if chacha20 boosting is off */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - /* Negotiated if chacha20 boosting is on */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify server negotiates second preferred chacha20 ciphersuite ecdhe_rsa_chacha20 */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); - - /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ - /* cppcheck-suppress redundantAssignment */ - cipher_preferences.allow_chacha20_boosting = false; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); - }; - } - - if (!s2n_chacha20_poly1305.is_available()) { - /* Chacha20 can't be negotiated when it's not available in libcrypto */ - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - connection->security_policy_override = &security_policy; - connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); - EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); - - static struct s2n_cipher_suite *test_cipher_suite_list[] = { - /* Invalid (no libcrypto) */ - &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - /* Negotiated (only valid option) */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - /* Not considered + invalid (no libcrypto) */ - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - }; - - cipher_preferences = (struct s2n_cipher_preferences){ - .count = s2n_array_len(test_cipher_suite_list), - .suites = test_cipher_suite_list, - .allow_chacha20_boosting = true, - }; - - uint8_t wire[] = { - /* Client signalled chacha20 boosting. Not negotiated because of missing libcrypto for chacha20 */ - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Not negotiated because of missing libcrypto for chacha20 */ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - /* Negotiated whether chacha20 boosting is enabled or not by server */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - }; - uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; - - /* Verify that server negotiated non-chacha20 ciphersuite ecdhe_ecdsa_aes_128 */ - cipher_preferences.allow_chacha20_boosting = true; - EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); - EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); - } - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_ecc_evp.h" +#include "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" + +static s2n_result s2n_conn_set_chosen_psk(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + uint8_t psk_identity[] = "psk identity"; + RESULT_GUARD(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &conn->psk_params.chosen_psk)); + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + RESULT_GUARD(s2n_psk_init(conn->psk_params.chosen_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(conn->psk_params.chosen_psk, psk_identity, sizeof(psk_identity))); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, + S2N_MAX_TEST_PEM_SIZE)); + + /* Test client cipher selection */ + { + /* Setup connections */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Test that the client allows the server to select ciphers that were offered in ClientHello */ + { + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + /* The client will offer the default tls13 ciphersuites */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + + /* The server will send a TLS13 cipher over the wire */ + uint8_t valid_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256 + }; + + /* We expect to succeed because the cipher was offered by the client */ + EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_wire_ciphers)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test that the client rejects a cipher that was not originally offered in ClientHello */ + { + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + /* The client will offer the default tls13 ciphersuites */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); + + /* The server will send a TLS12 cipher over the wire */ + uint8_t invalid_wire_ciphers[] = { + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + + /* We expect to fail because the cipher was not offered by the client */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, invalid_wire_ciphers), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /** Clients MUST verify + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# that the server selected a cipher suite + *# indicating a Hash associated with the PSK + **/ + { + /* If chosen PSK is set, test error case for incorrect hash match */ + { + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + uint8_t valid_tls13_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, + }; + + /* S2N_HMAC_SHA1 is not a matching hmac algorithm */ + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers), + S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_null_cipher_suite); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* If chosen PSK is set, test success case for matching hash algorithm */ + { + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + uint8_t valid_tls13_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, + }; + + /* S2N_HMAC_SHA256 is a matching hmac algorithm for the cipher suite present in valid_tls13_wire_ciphers */ + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test server cipher selection and scsv detection */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *server_config = NULL; + char *rsa_cert_chain_pem = NULL, *rsa_private_key_pem = NULL, *ecdsa_cert_chain_pem = NULL, *ecdsa_private_key_pem = NULL; + struct s2n_cert_chain_and_key *rsa_cert = NULL, *ecdsa_cert = NULL; + /* Allocate all of the objects and PEMs we'll need for this test. */ + EXPECT_NOT_NULL(rsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(rsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(rsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(rsa_cert, rsa_cert_chain_pem, rsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(ecdsa_cert, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); + + uint8_t wire_ciphers[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + const uint8_t cipher_count = sizeof(wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_fallback[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_FALLBACK_SCSV, /* At the end to verify it isn't missed */ + }; + const uint8_t cipher_count_fallback = sizeof(wire_ciphers_fallback) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_renegotiation[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* At the end to verify it isn't missed */ + }; + const uint8_t cipher_count_renegotiation = sizeof(wire_ciphers_renegotiation) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Only two ciphers for testing RSA vs ECDSA. */ + uint8_t wire_ciphers_with_ecdsa[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + }; + const uint8_t cipher_count_ecdsa = sizeof(wire_ciphers_with_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Only ECDSA ciphers */ + uint8_t wire_ciphers_only_ecdsa[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + }; + const uint8_t cipher_count_only_ecdsa = sizeof(wire_ciphers_only_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_rsa_fallback[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + }; + const uint8_t cipher_count_rsa_fallback = sizeof(wire_ciphers_rsa_fallback) / S2N_TLS_CIPHER_SUITE_LEN; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + /* Security policy must allow all test cipher suites */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* TEST RSA */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST RENEGOTIATION + * + *= https://www.rfc-editor.org/rfc/rfc5746#3.6 + *= type=test + *# o When a ClientHello is received, the server MUST check if it + *# includes the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, + *# set the secure_renegotiation flag to TRUE. + */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_renegotiation, cipher_count_renegotiation)); + EXPECT_EQUAL(conn->secure_renegotiation, 1); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); + EXPECT_EQUAL(-1, s2n_connection_is_valid_for_cipher_preferences(conn, "not_exist")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Simulate a TLSv11 client to trigger the fallback error */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers_fallback, cipher_count_fallback)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2018")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2019")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST RSA cipher chosen when ECDSA cipher is at top */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Assume default for negotiated curve. */ + /* Shouldn't be necessary unless the test fails, but we want the failure to be obvious. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + const struct s2n_cipher_suite *expected_rsa_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_rsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Clean+free to setup for ECDSA tests */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* Set ECDSA CERT in s2n_config */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* TEST ECDSA */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); + const struct s2n_cipher_suite *expected_ecdsa_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + /* Assume default for negotiated curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST ECDSA cipher chosen when RSA cipher is at top */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + /* Assume default for negotiated curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* TEST two certificates. Use two certs with different key types(RSA, ECDSA) and add them to a single + * s2n_config. + */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends both RSA and ECDSA ciphers, server only configures RSA ciphers, + * ECDSA + RSA cert is configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + /* 20170328 only supports RSA ciphers */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170328")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends both RSA and ECDSA ciphers, server only configures ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client only sends RSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_rc4_128_md5; + if (!expected_wire_choice->available) { + expected_wire_choice = &s2n_rsa_with_3des_ede_cbc_sha; + } + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client only sends ECDSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_only_ecdsa, cipher_count_only_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends ECDHE-ECDSA, RSA, ECDHE-RSA ciphers. Server prioritizes ECDSA but also supports RSA. + * No mutually supported elliptic curves between client and server. ECDSA + RSA cert is configured. + */ + { + /* If there are no shared elliptic curves, we must fall through to a cipher that supports RSA kx. + * This is the first RSA kx cipher that CloudFront-Upstream supports. + */ + const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_aes_256_gcm_sha384; + /* Selecting this preference list because it prioritizes ECDHE-ECDSA and ECDHE-RSA over plain RSA kx. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "CloudFront-Upstream")); + /* No shared curve */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_rsa_fallback, cipher_count_rsa_fallback)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + EXPECT_SUCCESS(s2n_config_free(server_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + /* Override auto-chosen defaults with only RSA cert default. ECDSA still loaded, but not default. */ + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured, + * only RSA is default. Expect default RSA used instead of previous test that expects ECDSA for this case. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Override auto-chosen defaults with only ECDSA cert default. RSA still loaded, but not default. */ + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &ecdsa_cert, 1)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured, + * only ECDSA is default. Expect default ECDSA used instead of previous test that expects RSA for this case. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test override back to both RSA and ECDSA defaults. */ + struct s2n_cert_chain_and_key *certs_list[] = { rsa_cert, ecdsa_cert }; + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, certs_list, 2)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test that defaults are not overriden after failures to set new default certificates */ + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, NULL, 0), S2N_ERR_NULL); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NULL"), 0); + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 0), + S2N_ERR_NUM_DEFAULT_CERTIFICATES); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NUM_DEFAULT_CERTIFICATES"), 0); + struct s2n_cert_chain_and_key *rsa_certs_list[] = { rsa_cert, rsa_cert }; + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, rsa_certs_list, 2), + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE"), 0); + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured. + * RSA default certificate should be chosen. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + struct s2n_cipher_suite *tls12_cipher_suite = cipher_preferences_20170210.suites[cipher_preferences_20170210.count - 1]; + uint8_t wire_ciphers_with_tls13[] = { + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + tls12_cipher_suite->iana_value[0], tls12_cipher_suite->iana_value[1] + }; + const uint8_t cipher_count_tls13 = sizeof(wire_ciphers_with_tls13) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Client sends TLS1.3 cipher suites, but server does not support TLS1.3 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite, tls12_cipher_suite); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends TLS1.3 cipher suites, server selects correct TLS1.3 ciphersuite */ + if (s2n_is_tls13_fully_supported()) { + struct test_case { + const char *cipher_pref; + const struct s2n_cipher_suite *expected_cipher_wire; + }; + + struct test_case test_cases[] = { + { .cipher_pref = "default_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher_pref = "test_all", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher_pref = "test_all_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].cipher_pref)); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite, test_cases[i].expected_cipher_wire); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + } + + /* Check wire's cipher suites with preferred tls12 ordering does not affect tls13 selection */ + { + uint8_t wire_ciphers2[] = { + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_CHACHA20_POLY1305_SHA256, /* tls 1.3 */ + }; + + const uint8_t count = sizeof(wire_ciphers2) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + } else { + EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); + } + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test cipher suite with a required version higher than what connection supports should not be selected */ + { + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + }; + + const uint8_t count = sizeof(test_wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->actual_protocol_version = S2N_TLS12; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, count)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* We should skip cipher suites with a minimum protocol version unsupported by the connection. + * If no valid cipher suite is found, we should fall back to a cipher suite with a higher protocol version, + * but we should NEVER use a TLS1.3 suite on a pre-TLS1.3 connection or vice versa. */ + { + /* Skip but fall back to cipher suite with protocol version higher than connection */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* ssl v3 */ + }; + + conn->actual_protocol_version = S2N_TLS10; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 3)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_cbc_sha); + + /* If a match does not exist, choose the invalid cipher */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Skip and do NOT fall back to a TLS1.3 cipher suite if using TLS1.2 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + }; + + conn->actual_protocol_version = S2N_TLS12; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + + /* If a match does not exist, fail to negotiate a cipher suite. + * We cannot fall back to the TLS1.3 choice. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + + /* Skip and do NOT fall back to a TLS1.2 cipher suite if using TLS1.3 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + }; + + conn->actual_protocol_version = S2N_TLS13; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + /* If a match does not exist, fail to negotiate a cipher suite. + * We cannot fall back to the TLS1.2 choice. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + }; + + /* If a PSK is being used, then the cipher suite must match the PSK's HMAC algorithm. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# The server MUST ensure that it selects a compatible PSK + *# (if any) and cipher suite. + **/ + { + /* If chosen PSK is set, a cipher suite with matching HMAC algorithm must be selected */ + { + s2n_connection_set_cipher_preferences(conn, "test_all"); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); + + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA384; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* If chosen PSK is set but there is no matching cipher, the server MUST fail to set a cipher */ + { + s2n_connection_set_cipher_preferences(conn, "test_all"); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->actual_protocol_version = S2N_TLS13; + + /* S2N_HMAC_SHA1 is not a matching HMAC algorithm for any TLS1.3 cipher */ + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + }; + + /* Client sends cipher which is not in the configured suite */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + uint8_t invalid_cipher_pref[] = { + TLS_NULL_WITH_NULL_NULL + }; + + const uint8_t invalid_cipher_count = sizeof(invalid_cipher_pref) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, invalid_cipher_pref, invalid_cipher_count), S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Client sends cipher that requires DH params */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* The client only offers one cipher suite, which requires dh kex */ + uint8_t wire[] = { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 }; + + /* By default, the server does not accept cipher suites with dh kex. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(server, wire, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + /* With dh params configured, the server accepts cipher suites with dh kex. */ + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(server, wire, 1)); + EXPECT_EQUAL(server->secure->cipher_suite, &s2n_dhe_rsa_with_aes_128_gcm_sha256); + }; + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + free(ecdsa_cert_chain_pem); + free(ecdsa_private_key_pem); + free(rsa_cert_chain_pem); + free(rsa_private_key_pem); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test chacha20 boosting behaviour */ + { + /* Setup cipher preferences + security policy */ + struct s2n_cipher_preferences cipher_preferences = { 0 }; + struct s2n_security_policy security_policy = { + .minimum_protocol_version = S2N_SSLv2, + .cipher_preferences = &cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20201021, + .ecc_preferences = &s2n_ecc_preferences_test_all, + }; + + /* Initialise config and relevant certs */ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + struct s2n_cert_chain_and_key *rsa_cert = NULL; + struct s2n_cert_chain_and_key *ecdsa_cert = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(rsa_cert); + EXPECT_NOT_NULL(ecdsa_cert); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + + if (s2n_chacha20_poly1305.is_available()) { + /* Test chacha20 boosting when ciphersuites fail auth validation */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + DEFER_CLEANUP(struct s2n_config *rsa_only_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(rsa_only_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_only_config, rsa_cert)); + /* Connection only supports rsa auth. */ + EXPECT_SUCCESS(s2n_connection_set_config(connection, rsa_only_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Not negotiated because invalid (ecdsa) */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Only negotiated if chacha20 boosting is disabled */ + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + /* First valid chacha20 cipher suite and is negotiated */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; valid chacha20 ciphersuite and is negotiated */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not negotiated because invalid (ecdsa) */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify chacha20 RSA ciphersuite chosen with chacha20 boosting enabled */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: non-chacha20 RSA ciphersuite chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_gcm_sha384); + }; + + /* Server is able to negotiate its most preferred chacha20 ciphersuite */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Skipped because it is not a chacha20 ciphersuite. Is negotiated if chacha20 boosting is disabled. */ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + /* First chacha20 ciphersuite and is negotiated */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Second chacha20 ciphersuite and is not negotiated (not server's most preferred chacha20 ciphersuite) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting: not negotiated because it's not server's most preferred chacha20 ciphersuite */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify most preferred chacha20 ciphersuite is chosen with chacha20 boosting enabled */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred cipehrsuite is chosen when chacha20 boosting is off */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); + }; + + /* Server's most preferred chacha20 is not offered by the client */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Server's most preferred chacha20 ciphersuite; not offered by the client */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Negotiated if chacha20 boosting is off */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + /* First valid chacha20 ciphersuite; negotiated if chacha20 boosting is on */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + /* Verify server selects its second most preferred chacha20 ciphersuite */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256); + }; + + /* Server does not negotiate the client's most preferred chacha20 ciphersuite */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Skipped because not a chacha20 ciphersuite. If chacha20 boosting is off then this is negotiated.*/ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + /* First chacha20 ciphersuite and is negotiated (client's second preferred ciphersuite) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + /* Second chacha20 ciphersuite and is the client's most preferred ciphersuite. Not negotiated. */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. This is not negotiated as it's not the server's most preferred chacha20 ciphersuite */ + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify client's second preferred (ecdhe_rsa_chacha20) is negotiated when chacha20 boosting enabled */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); + }; + + /* Chacha20 boosting is disabled when client did not indicate chacha20 preference */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated because client did not signal chacha20 boosting and it is present in client wire */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Never considered; if client did signal chacha20 boosting we expect this to be negotiated */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Chacha20 boosting is off. This ciphersuite is negotiated. */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + /* Not negotiated */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that client negotiates non-chacha20 ciphersuite when chacha20 boosting is not signalled by client */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + }; + + /* Server negotiates its most preferred chacha20 ciphersuite for tls 1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Most preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ + &s2n_tls13_aes_128_gcm_sha256, + /* Second preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ + &s2n_tls13_aes_256_gcm_sha384, + /* Negotiated if chacha20 boosting behaviour is on. Otherwise, one of the two ciphersuites above is choosen. */ + &s2n_tls13_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t test_wire_1[] = { + /* Client signalled chacha20 boosting. Negotiated. */ + TLS_CHACHA20_POLY1305_SHA256, + /* Not negotiated even when chacha20 boosting is off. Server prefers aes 128 gcm. */ + TLS_AES_256_GCM_SHA384, + /* Negotiated if chacha20 boosting is off. This is the server's most preferred ciphersuite. */ + TLS_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(test_wire_1) / S2N_TLS_CIPHER_SUITE_LEN; + + cipher_preferences.allow_chacha20_boosting = true; + /* Verify that client negotiates chacha20 when chacha20 boosting is on */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + /* + * Client did not signal chacha20 boosting. + * TLS_AES_256_GCM_SHA384 > TLS_CHACHA20_POLY1305_SHA256 + */ + uint8_t test_wire_2[] = { + /* Client did not signal chacha20 boosting. Negotiated if chacha20 boosting is off. */ + TLS_AES_256_GCM_SHA384, + /* Not negotiated since the server prefers aes 256 gcm over chacha20 */ + TLS_CHACHA20_POLY1305_SHA256, + }; + count = sizeof(test_wire_2) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that client negotiates server preferred non-chacha20 aes_256 when client did not signal */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_2, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_256_gcm_sha384); + }; + + /* Server can negotiate chacha20 ciphersuite, even when boosting is disabled */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated (only available option) */ + &s2n_tls13_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = false, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Negotiated as this is the only negotiable option. */ + TLS_CHACHA20_POLY1305_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify server can still negotiate chacha20 if it's the only option even when chacha20 boosting is off */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + }; + + /* + * sslv2 server correctly negotiates ciphersuite when chacha20 boosting is enabled. + */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_SSLv2)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* First 'valid' ciphersuite that is saved as a higher_vers_match and is negotiated */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ + &s2n_dhe_rsa_with_aes_256_gcm_sha384 + }; + + /* cppcheck-suppress redundantAssignment */ + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated as it's not offered by the server. */ + 0x00, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not saved as a higher_vers_match as ecdhe_ecdsa_aes_128 was already saved as one; not negotiated */ + 0x00, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Saved by server as higher_vers_match; negotiated as a 'fall-back' ciphersuite */ + 0x00, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + }; + uint8_t count = sizeof(wire) / S2N_SSLv2_CIPHER_SUITE_LEN; + + /* Verify server negotiate its higher_vers_match ecdhe_ecdsa_aes_128_cbc instead of a chacha20 ciphersuite */ + cipher_preferences.allow_chacha20_boosting = true; + /* + * For an sslv2 connection, all of the ciphersuites in test_cipher_suite_list will not meet the minimum + * required tls version validation (they all require at least sslv3). This means that the server will save this + * cipher as a higher_vers_match and will use this ciphersuite as a fallback if no other ciphersuite can be identified. + * When this logic happens, we bypass chacha20 boosting altogether; therefore because the first ciphersuite + * ecdhe_ecdsa_with_aes_128_cbc is offered by both the client and server and only fails the minimum required version check, + * then we save this ciphersuite as a higher_vers_match and continue. + */ + EXPECT_SUCCESS(s2n_set_cipher_as_sslv2_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + }; + + /* Server is able to negotiate a ciphersuite even without chacha20 in its cipher pref */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated because it is the server's most preferred */ + &s2n_tls13_aes_128_gcm_sha256, + /* Not considered */ + &s2n_tls13_aes_256_gcm_sha384, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; not negotiated as server does not have any chacha20 ciphersuites */ + TLS_CHACHA20_POLY1305_SHA256, + /* Not negotiated as the server prefers aes 128 over aes 256 */ + TLS_AES_256_GCM_SHA384, + /* Negotiated */ + TLS_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that server is able to negotiate aes_128 even without any chacha20 ciphersuites in its preferences */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test chacha20 boosting when the most preferred ciphersuite fails version validation */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Only negotiated if chacha20 boosting is off */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Invalid; never use tls 1.3 ciphers on pre-tls 1.3 connections */ + &s2n_tls13_chacha20_poly1305_sha256, + /* Negotiated (if chacha20 boosting is on) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated by server since it is invalid for the connection. */ + TLS_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify server negotiates second preferred chacha20 ciphersuite ecdhe_rsa_chacha20 */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + }; + } + + if (!s2n_chacha20_poly1305.is_available()) { + /* Chacha20 can't be negotiated when it's not available in libcrypto */ + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Invalid (no libcrypto) */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + /* Negotiated (only valid option) */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Not considered + invalid (no libcrypto) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated because of missing libcrypto for chacha20 */ + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not negotiated because of missing libcrypto for chacha20 */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated whether chacha20 boosting is enabled or not by server */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that server negotiated non-chacha20 ciphersuite ecdhe_ecdsa_aes_128 */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_auth_handshake_test.c b/tests/unit/s2n_client_auth_handshake_test.c index 8166315fb8b..e8266aa98a6 100644 --- a/tests/unit/s2n_client_auth_handshake_test.c +++ b/tests/unit/s2n_client_auth_handshake_test.c @@ -1,533 +1,533 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* To get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_tls13_handshake.c" - -int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2n_config *client_config, struct s2n_cert_chain_and_key *ecdsa_cert, bool no_cert) -{ - /* Set up client and server connections */ - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->x509_validator.skip_cert_validation = 1; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->client_protocol_version = S2N_TLS13; - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; - client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_sha256; - client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - if (!no_cert) { - client_conn->handshake_params.our_chain_and_key = ecdsa_cert; - } - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - server_conn->server_protocol_version = S2N_TLS13; - server_conn->client_protocol_version = S2N_TLS13; - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; - server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - if (no_cert) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); - } else { - server_conn->x509_validator.skip_cert_validation = 1; - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - } - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); - EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(server_conn), no_cert); - EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(client_conn), no_cert); - - const char *app_data_str = "APPLICATION_DATA"; - EXPECT_EQUAL(strcmp(app_data_str, s2n_connection_get_last_message_name(client_conn)), 0); - - /* Clean up */ - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - - return 0; -} - -/* Test to verify the explicit ordering of client_auth handshake with and without a client - * certificate. This includes some pre and post condition checks that pertain to client - * authentication between messages. - */ -int s2n_test_client_auth_message_by_message(bool no_cert) -{ - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); - - char *cert_chain = NULL; - char *private_key = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_cert_chain_and_key *default_cert = NULL; - EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); - if (!no_cert) { - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); - } - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - if (no_cert) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); - } else { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - client_conn->x509_validator.skip_cert_validation = 1; - server_conn->x509_validator.skip_cert_validation = 1; - } - - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - /* Client sends ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, 0); - - EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); - - /* Server reads ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); - - /* Server sends ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends EncryptedExtensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CertificateRequest */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_REQ); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Client reads ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads EncryptedExtensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - if (no_cert) { - /* Client reads CertificateRequest but expects Cert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); - } else { - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_REQ); - } - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - /* Client reads ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Client sends ClientCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - if (no_cert) { - EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); - } else { - EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - /* Client sends CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - } - - /* Client sends ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Server reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - /* Server reads ClientCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - if (no_cert) { - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); - } else { - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - /* Server reads CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - } - - /* Server reads ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(private_key); - free(cert_chain); - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* client_auth handshake negotiation */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - uint8_t *cert_chain_pem = NULL; - uint8_t *private_key_pem = NULL; - uint32_t cert_chain_len = 0; - uint32_t private_key_len = 0; - struct s2n_cert_chain_and_key *ecdsa_cert = NULL; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); - - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(ecdsa_cert, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); - - /* client_auth with no cert */ - EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 1)); - - /* client_auth with cert */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ecdsa_cert)); - EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 0)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - free(cert_chain_pem); - free(private_key_pem); - }; - - /* Test each message is sent and in the correct order */ - { - /* Test messsage by message with no cert */ - s2n_test_client_auth_message_by_message(1); - - /* Test message by message with a cert */ - s2n_test_client_auth_message_by_message(0); - }; - - /* Test unexpected certificate request */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - /* Enable client auth on the server, but not on the client */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), - S2N_ERR_UNEXPECTED_CERT_REQUEST); - }; - - /* Test missing certificate request */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - /* Require client auth on the client, but not on the server */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_NONE)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), - S2N_ERR_MISSING_CERT_REQUEST); - }; - - /* By default, client accepts certificate requests */ - { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - - /* Enable client auth on the server */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - }; - - /* Test: all combinations of client and server mutual auth settings produce useful errors - * - * Customers have struggled to correctly configure client auth in the past. - * We should provide very specific and helpful errors to facilitate debugging. - * Do not allow any generic errors like S2N_ERR_BAD_MESSAGE. - */ - { - const int S2N_CERT_AUTH_DEFAULT = -255; - int all_options[] = { - S2N_CERT_AUTH_DEFAULT, - S2N_CERT_AUTH_NONE, - S2N_CERT_AUTH_OPTIONAL, - S2N_CERT_AUTH_REQUIRED - }; - - struct { - int client_auth_type; - int server_auth_type; - bool client_cert_exists; - uint8_t version; - } test_cases[100] = { 0 }; - size_t test_case_count = 0; - - for (size_t client_i = 0; client_i < s2n_array_len(all_options); client_i++) { - for (size_t server_i = 0; server_i < s2n_array_len(all_options); server_i++) { - for (size_t cert_i = 0; cert_i <= 1; cert_i++) { - for (size_t version = S2N_TLS12; version <= S2N_TLS13; version++) { - EXPECT_TRUE(test_case_count < s2n_array_len(test_cases)); - test_cases[test_case_count].client_auth_type = all_options[client_i]; - test_cases[test_case_count].server_auth_type = all_options[server_i]; - test_cases[test_case_count].client_cert_exists = (cert_i == 1); - test_cases[test_case_count].version = version; - test_case_count++; - } - } - } - } - - for (size_t i = 0; i < test_case_count; i++) { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - if (test_cases[i].client_cert_exists) { - 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_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - server->server_protocol_version = test_cases[i].version; - - if (test_cases[i].client_auth_type != S2N_CERT_AUTH_DEFAULT) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, test_cases[i].client_auth_type)); - } - - if (test_cases[i].server_auth_type != S2N_CERT_AUTH_DEFAULT) { - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, test_cases[i].server_auth_type)); - } - - 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_connections_set_io_pair(client, server, &io_pair)); - - int result = s2n_negotiate_test_server_and_client(server, client); - EXPECT_EQUAL(client->actual_protocol_version, test_cases[i].version); - EXPECT_EQUAL(server->actual_protocol_version, test_cases[i].version); - if (result != S2N_SUCCESS) { - EXPECT_TRUE(s2n_errno == S2N_ERR_MISSING_CERT_REQUEST - || s2n_errno == S2N_ERR_UNEXPECTED_CERT_REQUEST - || s2n_errno == S2N_ERR_MISSING_CLIENT_CERT); - } - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* To get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_tls13_handshake.c" + +int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2n_config *client_config, struct s2n_cert_chain_and_key *ecdsa_cert, bool no_cert) +{ + /* Set up client and server connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->x509_validator.skip_cert_validation = 1; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->client_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; + client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_sha256; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + if (!no_cert) { + client_conn->handshake_params.our_chain_and_key = ecdsa_cert; + } + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + server_conn->server_protocol_version = S2N_TLS13; + server_conn->client_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + if (no_cert) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + } else { + server_conn->x509_validator.skip_cert_validation = 1; + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + } + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(server_conn), no_cert); + EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(client_conn), no_cert); + + const char *app_data_str = "APPLICATION_DATA"; + EXPECT_EQUAL(strcmp(app_data_str, s2n_connection_get_last_message_name(client_conn)), 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + + return 0; +} + +/* Test to verify the explicit ordering of client_auth handshake with and without a client + * certificate. This includes some pre and post condition checks that pertain to client + * authentication between messages. + */ +int s2n_test_client_auth_message_by_message(bool no_cert) +{ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); + + char *cert_chain = NULL; + char *private_key = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_cert_chain_and_key *default_cert = NULL; + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + if (!no_cert) { + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + } + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + if (no_cert) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + } else { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + client_conn->x509_validator.skip_cert_validation = 1; + server_conn->x509_validator.skip_cert_validation = 1; + } + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Client sends ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, 0); + + EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); + + /* Server sends ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertificateRequest */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_REQ); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + if (no_cert) { + /* Client reads CertificateRequest but expects Cert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + } else { + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_REQ); + } + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Client reads ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Client sends ClientCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + if (no_cert) { + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); + } else { + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Client sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + } + + /* Client sends ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Server reads ClientCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + if (no_cert) { + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); + } else { + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Server reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + } + + /* Server reads ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(private_key); + free(cert_chain); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* client_auth handshake negotiation */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + uint8_t *cert_chain_pem = NULL; + uint8_t *private_key_pem = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + struct s2n_cert_chain_and_key *ecdsa_cert = NULL; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(ecdsa_cert, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); + + /* client_auth with no cert */ + EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 1)); + + /* client_auth with cert */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 0)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + free(cert_chain_pem); + free(private_key_pem); + }; + + /* Test each message is sent and in the correct order */ + { + /* Test messsage by message with no cert */ + s2n_test_client_auth_message_by_message(1); + + /* Test message by message with a cert */ + s2n_test_client_auth_message_by_message(0); + }; + + /* Test unexpected certificate request */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* Enable client auth on the server, but not on the client */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_UNEXPECTED_CERT_REQUEST); + }; + + /* Test missing certificate request */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* Require client auth on the client, but not on the server */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_NONE)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_MISSING_CERT_REQUEST); + }; + + /* By default, client accepts certificate requests */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + /* Enable client auth on the server */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_OPTIONAL)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + }; + + /* Test: all combinations of client and server mutual auth settings produce useful errors + * + * Customers have struggled to correctly configure client auth in the past. + * We should provide very specific and helpful errors to facilitate debugging. + * Do not allow any generic errors like S2N_ERR_BAD_MESSAGE. + */ + { + const int S2N_CERT_AUTH_DEFAULT = -255; + int all_options[] = { + S2N_CERT_AUTH_DEFAULT, + S2N_CERT_AUTH_NONE, + S2N_CERT_AUTH_OPTIONAL, + S2N_CERT_AUTH_REQUIRED + }; + + struct { + int client_auth_type; + int server_auth_type; + bool client_cert_exists; + uint8_t version; + } test_cases[100] = { 0 }; + size_t test_case_count = 0; + + for (size_t client_i = 0; client_i < s2n_array_len(all_options); client_i++) { + for (size_t server_i = 0; server_i < s2n_array_len(all_options); server_i++) { + for (size_t cert_i = 0; cert_i <= 1; cert_i++) { + for (size_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + EXPECT_TRUE(test_case_count < s2n_array_len(test_cases)); + test_cases[test_case_count].client_auth_type = all_options[client_i]; + test_cases[test_case_count].server_auth_type = all_options[server_i]; + test_cases[test_case_count].client_cert_exists = (cert_i == 1); + test_cases[test_case_count].version = version; + test_case_count++; + } + } + } + } + + for (size_t i = 0; i < test_case_count; i++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + if (test_cases[i].client_cert_exists) { + 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_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + server->server_protocol_version = test_cases[i].version; + + if (test_cases[i].client_auth_type != S2N_CERT_AUTH_DEFAULT) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, test_cases[i].client_auth_type)); + } + + if (test_cases[i].server_auth_type != S2N_CERT_AUTH_DEFAULT) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, test_cases[i].server_auth_type)); + } + + 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_connections_set_io_pair(client, server, &io_pair)); + + int result = s2n_negotiate_test_server_and_client(server, client); + EXPECT_EQUAL(client->actual_protocol_version, test_cases[i].version); + EXPECT_EQUAL(server->actual_protocol_version, test_cases[i].version); + if (result != S2N_SUCCESS) { + EXPECT_TRUE(s2n_errno == S2N_ERR_MISSING_CERT_REQUEST + || s2n_errno == S2N_ERR_UNEXPECTED_CERT_REQUEST + || s2n_errno == S2N_ERR_MISSING_CLIENT_CERT); + } + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_extensions_test.c b/tests/unit/s2n_client_extensions_test.c index 5088d943df1..4331e499319 100644 --- a/tests/unit/s2n_client_extensions_test.c +++ b/tests/unit/s2n_client_extensions_test.c @@ -1,1266 +1,1266 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -static uint8_t server_ocsp_status_bytes[] = { - /* clang-format off */ - 0x30, 0x82, 0x06, 0x45, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x06, 0x3e, 0x30, 0x82, 0x06, 0x3a, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01, 0x04, 0x82, 0x06, 0x2b, 0x30, 0x82, 0x06, 0x27, 0x30, 0x81, 0xeb, 0xa1, 0x70, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x66, 0x30, 0x64, 0x30, 0x3c, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x65, 0x68, 0x87, 0x4f, 0x40, 0x75, 0x0f, 0x01, 0x6a, 0x34, 0x75, 0x62, 0x5e, 0x1f, 0x5c, 0x93, 0xe5, 0xa2, 0x6d, 0x58, 0x04, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x02, 0x03, 0x0f, 0x87, 0x2c, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, - 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x33, 0x30, 0x31, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0x16, 0x25, 0xa2, 0x0f, 0x46, 0xc2, 0xa6, 0xac, 0xb1, 0x6e, 0x54, 0xc8, 0xf1, 0x7f, 0xa9, 0xbe, 0x58, 0xf0, 0xdb, 0x81, 0x37, 0x23, 0x76, 0x65, 0x56, 0x90, 0x15, 0xb1, 0x30, 0x6f, 0x43, 0xe2, 0x59, 0x0d, 0x97, 0xa8, 0xa6, 0x05, 0x25, 0xe7, 0x94, 0x21, 0xd5, 0xda, 0x4b, 0x55, 0x13, 0xc7, 0xdf, 0x5d, 0xf6, 0x31, 0xe8, 0x2f, 0x0d, 0xa0, 0xac, 0xd4, 0xfe, 0xf8, 0x22, 0xe7, 0x12, 0xf4, 0x32, 0xcd, 0x53, - 0x03, 0x56, 0x98, 0x0a, 0xf8, 0x9e, 0xda, 0x2c, 0x0a, 0x43, 0x66, 0x6e, 0x0e, 0x9c, 0x9b, 0xf2, 0x0c, 0x66, 0x65, 0x1c, 0x65, 0xc4, 0xf0, 0x82, 0xc3, 0x17, 0x3d, 0x27, 0x11, 0xcc, 0xac, 0x37, 0xe3, 0xa8, 0x35, 0x46, 0x26, 0xcd, 0x08, 0x04, 0xfa, 0xb4, 0xdf, 0x9d, 0x12, 0xdf, 0x45, 0x8d, 0xf2, 0xef, 0x1a, 0xd1, 0x53, 0x50, 0x9a, 0xe3, 0xe8, 0x22, 0xda, 0xec, 0xeb, 0xc0, 0xa8, 0xea, 0xc4, 0x83, 0xc4, 0x47, 0xf2, 0x05, 0x3c, 0x14, 0x11, 0x3b, 0x25, 0xdc, 0xb9, 0x09, 0x5c, 0xd7, 0x74, 0x88, 0x96, 0x82, 0x4d, 0xbb, 0x8b, 0x7f, 0x6a, 0xbf, 0xa1, 0x44, 0x1b, 0x89, 0x67, 0xce, 0x45, 0xab, 0xca, 0xef, 0x48, 0xa6, 0x80, 0x76, 0x7d, 0xbe, 0xb7, 0x8a, 0xdf, 0x7a, 0x32, 0x8c, 0xa5, 0x86, 0x4e, 0x26, 0xf7, 0x15, 0x63, 0xbb, - 0xb1, 0xcc, 0xe0, 0x32, 0x82, 0x02, 0x5d, 0x2b, 0x60, 0x39, 0xdb, 0xd2, 0x04, 0x56, 0xb4, 0x7e, 0xe6, 0x3a, 0x69, 0x0c, 0x8a, 0xf0, 0x00, 0xf4, 0x56, 0xb0, 0xa7, 0x1a, 0x37, 0x05, 0x4b, 0xeb, 0x8c, 0x87, 0x05, 0x37, 0x92, 0xf7, 0x93, 0x5d, 0x93, 0x32, 0x7d, 0x6e, 0xa6, 0xda, 0x10, 0x4b, 0x49, 0xae, 0x86, 0xe4, 0xb4, 0x4d, 0x98, 0x42, 0x3e, 0xd3, 0x42, 0x46, 0x5d, 0xdd, 0x2f, 0x97, 0xd4, 0xb9, 0x7f, 0xbe, 0xa0, 0x82, 0x04, 0x21, 0x30, 0x82, 0x04, 0x1d, 0x30, 0x82, 0x04, 0x19, 0x30, 0x82, 0x03, 0x01, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x15, 0xfa, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, - 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x31, 0x32, 0x32, 0x31, 0x38, 0x33, 0x35, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x31, 0x36, 0x33, 0x34, 0x5a, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, - 0x23, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x56, 0x1b, 0x4c, 0x45, 0x31, 0x87, 0x17, 0x17, 0x80, 0x84, 0xe9, 0x6e, 0x17, 0x8d, 0xf2, 0x25, 0x5e, 0x18, 0xed, 0x8d, 0x8e, 0xcc, 0x7c, 0x2b, 0x7b, 0x51, 0xa6, 0xc1, 0xc2, 0xe6, 0xbf, 0x0a, 0xa3, 0x60, 0x30, 0x66, 0xf1, 0x32, 0xfe, 0x10, 0xae, 0x97, 0xb5, 0x0e, 0x99, 0xfa, 0x24, 0xb8, 0x3f, 0xc5, - 0x3d, 0xd2, 0x77, 0x74, 0x96, 0x38, 0x7d, 0x14, 0xe1, 0xc3, 0xa9, 0xb6, 0xa4, 0x93, 0x3e, 0x2a, 0xc1, 0x24, 0x13, 0xd0, 0x85, 0x57, 0x0a, 0x95, 0xb8, 0x14, 0x74, 0x14, 0xa0, 0xbc, 0x00, 0x7c, 0x7b, 0xcf, 0x22, 0x24, 0x46, 0xef, 0x7f, 0x1a, 0x15, 0x6d, 0x7e, 0xa1, 0xc5, 0x77, 0xfc, 0x5f, 0x0f, 0xac, 0xdf, 0xd4, 0x2e, 0xb0, 0xf5, 0x97, 0x49, 0x90, 0xcb, 0x2f, 0x5c, 0xef, 0xeb, 0xce, 0xef, 0x4d, 0x1b, 0xdc, 0x7a, 0xe5, 0xc1, 0x07, 0x5c, 0x5a, 0x99, 0xa9, 0x31, 0x71, 0xf2, 0xb0, 0x84, 0x5b, 0x4f, 0xf0, 0x86, 0x4e, 0x97, 0x3f, 0xcf, 0xe3, 0x2f, 0x9d, 0x75, 0x11, 0xff, 0x87, 0xa3, 0xe9, 0x43, 0x41, 0x0c, 0x90, 0xa4, 0x49, 0x3a, 0x30, 0x6b, 0x69, 0x44, 0x35, 0x93, 0x40, 0xa9, 0xca, 0x96, 0xf0, 0x2b, 0x66, 0xce, 0x67, - 0xf0, 0x28, 0xdf, 0x29, 0x80, 0xa6, 0xaa, 0xee, 0x8d, 0x5d, 0x5d, 0x45, 0x2b, 0x8b, 0x0e, 0xb9, 0x3f, 0x92, 0x3c, 0xc1, 0xe2, 0x3f, 0xcc, 0xcb, 0xdb, 0xe7, 0xff, 0xcb, 0x11, 0x4d, 0x08, 0xfa, 0x7a, 0x6a, 0x3c, 0x40, 0x4f, 0x82, 0x5d, 0x1a, 0x0e, 0x71, 0x59, 0x35, 0xcf, 0x62, 0x3a, 0x8c, 0x7b, 0x59, 0x67, 0x00, 0x14, 0xed, 0x06, 0x22, 0xf6, 0x08, 0x9a, 0x94, 0x47, 0xa7, 0xa1, 0x90, 0x10, 0xf7, 0xfe, 0x58, 0xf8, 0x41, 0x29, 0xa2, 0x76, 0x5e, 0xa3, 0x67, 0x82, 0x4d, 0x1c, 0x3b, 0xb2, 0xfd, 0xa3, 0x08, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa0, 0x30, 0x81, 0x9d, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, - 0x30, 0x1e, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x17, 0x30, 0x15, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0xe0, 0xa3, 0x66, 0x95, 0x41, 0x4c, 0x5d, 0xd4, 0x49, 0xbc, 0x00, 0xe3, 0x3c, 0xdc, 0xdb, 0xd2, 0x34, 0x3e, 0x17, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x42, 0xcd, 0x4c, 0x03, 0xd2, 0x9a, 0x55, 0xb2, 0xd6, 0x3e, 0x90, 0x4c, 0x89, 0x27, 0xd0, 0xcf, 0x87, 0xf6, 0x91, 0x9b, 0x86, 0x6a, 0x6d, 0x76, 0xd9, 0x5e, 0xbc, 0xc8, 0xfe, 0x74, 0xbe, 0x97, 0x29, 0xd1, 0xac, 0x92, 0x9b, 0x9e, 0x48, 0xab, 0xb1, 0xf4, 0xbe, 0xd5, 0x3f, 0xa8, 0x4c, 0xce, 0x0e, 0x2f, 0x39, 0x96, 0x4b, 0xde, 0xda, 0xac, 0x40, 0xce, 0xbb, 0x93, 0xdb, 0x1c, 0x39, 0x02, 0x03, 0x25, 0x32, 0x45, 0xde, 0x94, 0x5a, 0x63, 0xaf, 0xf7, 0xb0, 0x70, 0xc8, 0xcc, 0x2b, 0x34, 0x7b, 0x5f, 0x7d, 0xc6, 0x96, 0x1d, 0x59, - 0x1d, 0xdd, 0x8f, 0x7e, 0x55, 0xc4, 0x92, 0x11, 0x8d, 0xd9, 0x11, 0x11, 0x22, 0x20, 0xd3, 0x56, 0x1e, 0x11, 0xae, 0x97, 0xf2, 0x71, 0xea, 0x8c, 0xf5, 0x15, 0x2d, 0xb1, 0x59, 0xdd, 0x3e, 0x43, 0x9c, 0xf1, 0xda, 0x81, 0xd7, 0xc8, 0x6c, 0xf6, 0x08, 0x5d, 0x6f, 0xdf, 0x26, 0xa8, 0xfe, 0x84, 0xa2, 0x08, 0xaf, 0xdb, 0x9b, 0x39, 0xf5, 0x46, 0xfa, 0x5b, 0xfa, 0x97, 0x64, 0x1d, 0xf1, 0xd4, 0xbc, 0xb0, 0xa4, 0x2f, 0x36, 0xf1, 0x90, 0xb5, 0x3b, 0x67, 0x0b, 0x5b, 0xf3, 0x24, 0x50, 0x27, 0x63, 0xdc, 0xeb, 0xb6, 0x55, 0x0f, 0xb7, 0xbe, 0xee, 0x2e, 0xfb, 0xc8, 0x6a, 0x10, 0xab, 0xee, 0x9a, 0x27, 0xe4, 0x13, 0x16, 0xcf, 0xdd, 0x13, 0xa7, 0x0f, 0xde, 0x61, 0x8c, 0xfa, 0xed, 0x2d, 0x00, 0x60, 0xf9, 0xc4, 0x3d, 0xad, 0xd6, 0xa2, - 0xc0, 0xa3, 0x29, 0x11, 0x61, 0x0b, 0x65, 0xdb, 0x14, 0x79, 0xb1, 0x7d, 0x8a, 0x57, 0x91, 0x59, 0xa4, 0xfc, 0x4c, 0x60, 0x4f, 0x3c, 0xc8, 0x31, 0x9b, 0x69, 0x70, 0xb9, 0xae, 0xed, 0xb1, 0xde, 0x58, 0x8d, 0x62, 0x30, 0xb4, 0x7b, 0x46, 0xf2, 0xda, 0x7b, 0xbb, 0x72, 0xcf, 0xf0, 0x47, 0x8b, 0x84, - /* clang-format on */ -}; - -/* This data format is bogus, but sufficient to test the server is able - to return correctly what has been configured. Once the client does - validation we will need real data here. - */ -static uint8_t sct_list[] = { - 0xff, 0xff, 0xff, 0xff, 0xff -}; - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - /* Client doesn't use the server name extension. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server didn't receive the server name. */ - EXPECT_NULL(s2n_get_server_name(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client uses the server name extension. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - const char *sent_server_name = "www.alligator.com"; - const char *received_server_name = NULL; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - /* Set the server name */ - EXPECT_SUCCESS(s2n_set_server_name(client_conn, sent_server_name)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server name was received intact. */ - EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); - EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); - EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends multiple server names. */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - const char *sent_server_name = "svr"; - const char *received_server_name = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - uint32_t cert_chain_len = 0; - uint32_t private_key_len = 0; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x0C, - /* All server names len */ - 0x00, - 0x0A, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - /* Second server name type - host name */ - 0x00, - /* Second server name len */ - 0x00, - 0x01, - /* Second server name */ - 0xFF, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version garbage value. s2n should still accept this. */ - 0x01, - 0x01, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, (uint8_t *) cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, (uint8_t *) private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, (uint8_t *) cert_chain, cert_chain_len, (uint8_t *) private_key, private_key_len)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that the CLIENT HELLO is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - - /* Verify that the server name was received intact. */ - EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); - EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); - EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - EXPECT_TRUE(server_conn->alert_sent); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client sends duplicate server name extension */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x0C, - /* All server names len */ - 0x00, - 0x0A, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - /* Second server name type - host name */ - 0x00, - /* Second server name len */ - 0x00, - 0x01, - /* Second server name */ - 0xFF, - /* And all that again... */ - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x0C, - /* All server names len */ - 0x00, - 0x0A, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - /* Second server name type - host name */ - 0x00, - /* Second server name len */ - 0x00, - 0x01, - /* Second server name */ - 0xFF, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that we fail for duplicated extension type Bad Message */ - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - s2n_negotiate(server_conn, &server_blocked); - EXPECT_EQUAL(s2n_errno, S2N_ERR_DUPLICATE_EXTENSION); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client sends a valid initial renegotiation_info */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x01, - /* Empty renegotiated_connection */ - 0x00, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that the CLIENT HELLO is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type & NEGOTIATED, NEGOTIATED); - - /* Verify that the that we detected secure_renegotiation */ - EXPECT_EQUAL(server_conn->secure_renegotiation, 1); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - EXPECT_TRUE(server_conn->alert_sent); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client sends a non-empty initial renegotiation_info */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - uint8_t buf[5120]; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x21, - /* renegotiated_connection len */ - 0x20, - /* fake renegotiated_connection */ - ZERO_TO_THIRTY_ONE, - }; - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int body_len = sizeof(client_hello_message) + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); - - /* Verify that we fail for non-empty renegotiated_connection */ - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - s2n_negotiate(server_conn, &server_blocked); - EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - - /* Clear pipe since negotiation failed mid-handshake */ - EXPECT_SUCCESS(read(io_pair.client, buf, sizeof(buf))); - }; - - /* Client doesn't use the OCSP extension. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - uint32_t length = 0; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server didn't send an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - - /* Verify that the client didn't receive an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Cannot enable OCSP stapling if there's no support for it */ - if (!s2n_x509_ocsp_stapling_supported()) { - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_FAILURE(s2n_config_set_check_stapled_ocsp_response(client_config, 1)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Server doesn't support the OCSP extension. We can't run this test if ocsp isn't supported by the client. */ - if (s2n_x509_ocsp_stapling_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - uint32_t length = 0; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server didn't send an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); - - /* Verify that the client didn't receive an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); - EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test with s2n_config_set_extension_data(). Can be removed once API is deprecated */ - if (s2n_x509_ocsp_stapling_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - const uint8_t *server_ocsp_reply = NULL; - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server sent an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - - /* Verify that the client received an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); - - for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { - EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Server and client support the OCSP extension. Test only runs if ocsp stapled responses are supported by the client */ - if (s2n_x509_ocsp_stapling_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - const uint8_t *server_ocsp_reply = NULL; - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server sent an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - - /* Verify that the client received an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); - - for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { - EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Server and client support the OCSP extension. Test Behavior for TLS 1.3 */ - if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - const uint8_t *server_ocsp_reply = NULL; - uint32_t length = 0; - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->client_protocol_version = S2N_TLS13; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->server_protocol_version = S2N_TLS13; - server_conn->client_protocol_version = S2N_TLS13; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, - server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server sent an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); - - /* Verify that the client received an OCSP response. */ - EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); - - EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); - - for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { - EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* Client does not request SCT, but server is configured to serve them. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - - uint32_t length = 0; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, - sct_list, sizeof(sct_list))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the client did *not* receive an SCT list */ - EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Client requests SCT and server does have it. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - /* Indicate that the client wants CT if available */ - EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, - sct_list, sizeof(sct_list))); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the client did receive an SCT list */ - EXPECT_NOT_NULL(s2n_connection_get_sct_list(client_conn, &length)); - EXPECT_EQUAL(length, sizeof(sct_list)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client requests SCT and server does *not* have it. */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - uint32_t length = 0; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - /* Indicate that the client wants CT if available */ - EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the client does not get a list */ - EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); - EXPECT_EQUAL(length, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client requests 512, 1024, 2048, and 4096 maximum fragment lengths */ - for (uint8_t mfl_code = S2N_TLS_MAX_FRAG_LEN_512; mfl_code <= S2N_TLS_MAX_FRAG_LEN_4096; mfl_code++) { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, mfl_code)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Preference should be ignored as the TlS Maximum Fragment Length Extension is Set */ - EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, mfl_code_to_length[mfl_code]); - EXPECT_EQUAL(server_conn->negotiated_mfl_code, mfl_code); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Client requests invalid maximum fragment length */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_FAILURE(s2n_config_send_max_fragment_length(client_config, 5)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* check that max_fragment_length did not get set due to invalid mfl_code */ - EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Server ignores client's request of S2N_TLS_MAX_FRAG_LEN_2048 maximum fragment length when accept_mfl is not set*/ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_2048)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* check that max_fragment_length did not get set since accept_mfl is not set */ - EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +static uint8_t server_ocsp_status_bytes[] = { + /* clang-format off */ + 0x30, 0x82, 0x06, 0x45, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x06, 0x3e, 0x30, 0x82, 0x06, 0x3a, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01, 0x04, 0x82, 0x06, 0x2b, 0x30, 0x82, 0x06, 0x27, 0x30, 0x81, 0xeb, 0xa1, 0x70, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x66, 0x30, 0x64, 0x30, 0x3c, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x65, 0x68, 0x87, 0x4f, 0x40, 0x75, 0x0f, 0x01, 0x6a, 0x34, 0x75, 0x62, 0x5e, 0x1f, 0x5c, 0x93, 0xe5, 0xa2, 0x6d, 0x58, 0x04, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x02, 0x03, 0x0f, 0x87, 0x2c, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, + 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x33, 0x30, 0x31, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0x16, 0x25, 0xa2, 0x0f, 0x46, 0xc2, 0xa6, 0xac, 0xb1, 0x6e, 0x54, 0xc8, 0xf1, 0x7f, 0xa9, 0xbe, 0x58, 0xf0, 0xdb, 0x81, 0x37, 0x23, 0x76, 0x65, 0x56, 0x90, 0x15, 0xb1, 0x30, 0x6f, 0x43, 0xe2, 0x59, 0x0d, 0x97, 0xa8, 0xa6, 0x05, 0x25, 0xe7, 0x94, 0x21, 0xd5, 0xda, 0x4b, 0x55, 0x13, 0xc7, 0xdf, 0x5d, 0xf6, 0x31, 0xe8, 0x2f, 0x0d, 0xa0, 0xac, 0xd4, 0xfe, 0xf8, 0x22, 0xe7, 0x12, 0xf4, 0x32, 0xcd, 0x53, + 0x03, 0x56, 0x98, 0x0a, 0xf8, 0x9e, 0xda, 0x2c, 0x0a, 0x43, 0x66, 0x6e, 0x0e, 0x9c, 0x9b, 0xf2, 0x0c, 0x66, 0x65, 0x1c, 0x65, 0xc4, 0xf0, 0x82, 0xc3, 0x17, 0x3d, 0x27, 0x11, 0xcc, 0xac, 0x37, 0xe3, 0xa8, 0x35, 0x46, 0x26, 0xcd, 0x08, 0x04, 0xfa, 0xb4, 0xdf, 0x9d, 0x12, 0xdf, 0x45, 0x8d, 0xf2, 0xef, 0x1a, 0xd1, 0x53, 0x50, 0x9a, 0xe3, 0xe8, 0x22, 0xda, 0xec, 0xeb, 0xc0, 0xa8, 0xea, 0xc4, 0x83, 0xc4, 0x47, 0xf2, 0x05, 0x3c, 0x14, 0x11, 0x3b, 0x25, 0xdc, 0xb9, 0x09, 0x5c, 0xd7, 0x74, 0x88, 0x96, 0x82, 0x4d, 0xbb, 0x8b, 0x7f, 0x6a, 0xbf, 0xa1, 0x44, 0x1b, 0x89, 0x67, 0xce, 0x45, 0xab, 0xca, 0xef, 0x48, 0xa6, 0x80, 0x76, 0x7d, 0xbe, 0xb7, 0x8a, 0xdf, 0x7a, 0x32, 0x8c, 0xa5, 0x86, 0x4e, 0x26, 0xf7, 0x15, 0x63, 0xbb, + 0xb1, 0xcc, 0xe0, 0x32, 0x82, 0x02, 0x5d, 0x2b, 0x60, 0x39, 0xdb, 0xd2, 0x04, 0x56, 0xb4, 0x7e, 0xe6, 0x3a, 0x69, 0x0c, 0x8a, 0xf0, 0x00, 0xf4, 0x56, 0xb0, 0xa7, 0x1a, 0x37, 0x05, 0x4b, 0xeb, 0x8c, 0x87, 0x05, 0x37, 0x92, 0xf7, 0x93, 0x5d, 0x93, 0x32, 0x7d, 0x6e, 0xa6, 0xda, 0x10, 0x4b, 0x49, 0xae, 0x86, 0xe4, 0xb4, 0x4d, 0x98, 0x42, 0x3e, 0xd3, 0x42, 0x46, 0x5d, 0xdd, 0x2f, 0x97, 0xd4, 0xb9, 0x7f, 0xbe, 0xa0, 0x82, 0x04, 0x21, 0x30, 0x82, 0x04, 0x1d, 0x30, 0x82, 0x04, 0x19, 0x30, 0x82, 0x03, 0x01, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x15, 0xfa, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x31, 0x32, 0x32, 0x31, 0x38, 0x33, 0x35, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x31, 0x36, 0x33, 0x34, 0x5a, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x23, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x56, 0x1b, 0x4c, 0x45, 0x31, 0x87, 0x17, 0x17, 0x80, 0x84, 0xe9, 0x6e, 0x17, 0x8d, 0xf2, 0x25, 0x5e, 0x18, 0xed, 0x8d, 0x8e, 0xcc, 0x7c, 0x2b, 0x7b, 0x51, 0xa6, 0xc1, 0xc2, 0xe6, 0xbf, 0x0a, 0xa3, 0x60, 0x30, 0x66, 0xf1, 0x32, 0xfe, 0x10, 0xae, 0x97, 0xb5, 0x0e, 0x99, 0xfa, 0x24, 0xb8, 0x3f, 0xc5, + 0x3d, 0xd2, 0x77, 0x74, 0x96, 0x38, 0x7d, 0x14, 0xe1, 0xc3, 0xa9, 0xb6, 0xa4, 0x93, 0x3e, 0x2a, 0xc1, 0x24, 0x13, 0xd0, 0x85, 0x57, 0x0a, 0x95, 0xb8, 0x14, 0x74, 0x14, 0xa0, 0xbc, 0x00, 0x7c, 0x7b, 0xcf, 0x22, 0x24, 0x46, 0xef, 0x7f, 0x1a, 0x15, 0x6d, 0x7e, 0xa1, 0xc5, 0x77, 0xfc, 0x5f, 0x0f, 0xac, 0xdf, 0xd4, 0x2e, 0xb0, 0xf5, 0x97, 0x49, 0x90, 0xcb, 0x2f, 0x5c, 0xef, 0xeb, 0xce, 0xef, 0x4d, 0x1b, 0xdc, 0x7a, 0xe5, 0xc1, 0x07, 0x5c, 0x5a, 0x99, 0xa9, 0x31, 0x71, 0xf2, 0xb0, 0x84, 0x5b, 0x4f, 0xf0, 0x86, 0x4e, 0x97, 0x3f, 0xcf, 0xe3, 0x2f, 0x9d, 0x75, 0x11, 0xff, 0x87, 0xa3, 0xe9, 0x43, 0x41, 0x0c, 0x90, 0xa4, 0x49, 0x3a, 0x30, 0x6b, 0x69, 0x44, 0x35, 0x93, 0x40, 0xa9, 0xca, 0x96, 0xf0, 0x2b, 0x66, 0xce, 0x67, + 0xf0, 0x28, 0xdf, 0x29, 0x80, 0xa6, 0xaa, 0xee, 0x8d, 0x5d, 0x5d, 0x45, 0x2b, 0x8b, 0x0e, 0xb9, 0x3f, 0x92, 0x3c, 0xc1, 0xe2, 0x3f, 0xcc, 0xcb, 0xdb, 0xe7, 0xff, 0xcb, 0x11, 0x4d, 0x08, 0xfa, 0x7a, 0x6a, 0x3c, 0x40, 0x4f, 0x82, 0x5d, 0x1a, 0x0e, 0x71, 0x59, 0x35, 0xcf, 0x62, 0x3a, 0x8c, 0x7b, 0x59, 0x67, 0x00, 0x14, 0xed, 0x06, 0x22, 0xf6, 0x08, 0x9a, 0x94, 0x47, 0xa7, 0xa1, 0x90, 0x10, 0xf7, 0xfe, 0x58, 0xf8, 0x41, 0x29, 0xa2, 0x76, 0x5e, 0xa3, 0x67, 0x82, 0x4d, 0x1c, 0x3b, 0xb2, 0xfd, 0xa3, 0x08, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa0, 0x30, 0x81, 0x9d, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, + 0x30, 0x1e, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x17, 0x30, 0x15, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0xe0, 0xa3, 0x66, 0x95, 0x41, 0x4c, 0x5d, 0xd4, 0x49, 0xbc, 0x00, 0xe3, 0x3c, 0xdc, 0xdb, 0xd2, 0x34, 0x3e, 0x17, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x42, 0xcd, 0x4c, 0x03, 0xd2, 0x9a, 0x55, 0xb2, 0xd6, 0x3e, 0x90, 0x4c, 0x89, 0x27, 0xd0, 0xcf, 0x87, 0xf6, 0x91, 0x9b, 0x86, 0x6a, 0x6d, 0x76, 0xd9, 0x5e, 0xbc, 0xc8, 0xfe, 0x74, 0xbe, 0x97, 0x29, 0xd1, 0xac, 0x92, 0x9b, 0x9e, 0x48, 0xab, 0xb1, 0xf4, 0xbe, 0xd5, 0x3f, 0xa8, 0x4c, 0xce, 0x0e, 0x2f, 0x39, 0x96, 0x4b, 0xde, 0xda, 0xac, 0x40, 0xce, 0xbb, 0x93, 0xdb, 0x1c, 0x39, 0x02, 0x03, 0x25, 0x32, 0x45, 0xde, 0x94, 0x5a, 0x63, 0xaf, 0xf7, 0xb0, 0x70, 0xc8, 0xcc, 0x2b, 0x34, 0x7b, 0x5f, 0x7d, 0xc6, 0x96, 0x1d, 0x59, + 0x1d, 0xdd, 0x8f, 0x7e, 0x55, 0xc4, 0x92, 0x11, 0x8d, 0xd9, 0x11, 0x11, 0x22, 0x20, 0xd3, 0x56, 0x1e, 0x11, 0xae, 0x97, 0xf2, 0x71, 0xea, 0x8c, 0xf5, 0x15, 0x2d, 0xb1, 0x59, 0xdd, 0x3e, 0x43, 0x9c, 0xf1, 0xda, 0x81, 0xd7, 0xc8, 0x6c, 0xf6, 0x08, 0x5d, 0x6f, 0xdf, 0x26, 0xa8, 0xfe, 0x84, 0xa2, 0x08, 0xaf, 0xdb, 0x9b, 0x39, 0xf5, 0x46, 0xfa, 0x5b, 0xfa, 0x97, 0x64, 0x1d, 0xf1, 0xd4, 0xbc, 0xb0, 0xa4, 0x2f, 0x36, 0xf1, 0x90, 0xb5, 0x3b, 0x67, 0x0b, 0x5b, 0xf3, 0x24, 0x50, 0x27, 0x63, 0xdc, 0xeb, 0xb6, 0x55, 0x0f, 0xb7, 0xbe, 0xee, 0x2e, 0xfb, 0xc8, 0x6a, 0x10, 0xab, 0xee, 0x9a, 0x27, 0xe4, 0x13, 0x16, 0xcf, 0xdd, 0x13, 0xa7, 0x0f, 0xde, 0x61, 0x8c, 0xfa, 0xed, 0x2d, 0x00, 0x60, 0xf9, 0xc4, 0x3d, 0xad, 0xd6, 0xa2, + 0xc0, 0xa3, 0x29, 0x11, 0x61, 0x0b, 0x65, 0xdb, 0x14, 0x79, 0xb1, 0x7d, 0x8a, 0x57, 0x91, 0x59, 0xa4, 0xfc, 0x4c, 0x60, 0x4f, 0x3c, 0xc8, 0x31, 0x9b, 0x69, 0x70, 0xb9, 0xae, 0xed, 0xb1, 0xde, 0x58, 0x8d, 0x62, 0x30, 0xb4, 0x7b, 0x46, 0xf2, 0xda, 0x7b, 0xbb, 0x72, 0xcf, 0xf0, 0x47, 0x8b, 0x84, + /* clang-format on */ +}; + +/* This data format is bogus, but sufficient to test the server is able + to return correctly what has been configured. Once the client does + validation we will need real data here. + */ +static uint8_t sct_list[] = { + 0xff, 0xff, 0xff, 0xff, 0xff +}; + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Client doesn't use the server name extension. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't receive the server name. */ + EXPECT_NULL(s2n_get_server_name(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client uses the server name extension. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + const char *sent_server_name = "www.alligator.com"; + const char *received_server_name = NULL; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Set the server name */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, sent_server_name)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server name was received intact. */ + EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); + EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); + EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends multiple server names. */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + const char *sent_server_name = "svr"; + const char *received_server_name = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version garbage value. s2n should still accept this. */ + 0x01, + 0x01, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, (uint8_t *) cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, (uint8_t *) private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, (uint8_t *) cert_chain, cert_chain_len, (uint8_t *) private_key, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that the CLIENT HELLO is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + /* Verify that the server name was received intact. */ + EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); + EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); + EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + EXPECT_TRUE(server_conn->alert_sent); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends duplicate server name extension */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + /* And all that again... */ + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that we fail for duplicated extension type Bad Message */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_errno, S2N_ERR_DUPLICATE_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends a valid initial renegotiation_info */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x01, + /* Empty renegotiated_connection */ + 0x00, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that the CLIENT HELLO is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type & NEGOTIATED, NEGOTIATED); + + /* Verify that the that we detected secure_renegotiation */ + EXPECT_EQUAL(server_conn->secure_renegotiation, 1); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + EXPECT_TRUE(server_conn->alert_sent); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends a non-empty initial renegotiation_info */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + uint8_t buf[5120]; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x21, + /* renegotiated_connection len */ + 0x20, + /* fake renegotiated_connection */ + ZERO_TO_THIRTY_ONE, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that we fail for non-empty renegotiated_connection */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* Clear pipe since negotiation failed mid-handshake */ + EXPECT_SUCCESS(read(io_pair.client, buf, sizeof(buf))); + }; + + /* Client doesn't use the OCSP extension. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + uint32_t length = 0; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't send an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + + /* Verify that the client didn't receive an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Cannot enable OCSP stapling if there's no support for it */ + if (!s2n_x509_ocsp_stapling_supported()) { + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_FAILURE(s2n_config_set_check_stapled_ocsp_response(client_config, 1)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server doesn't support the OCSP extension. We can't run this test if ocsp isn't supported by the client. */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + uint32_t length = 0; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't send an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + + /* Verify that the client didn't receive an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test with s2n_config_set_extension_data(). Can be removed once API is deprecated */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + const uint8_t *server_ocsp_reply = NULL; + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); + + for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server and client support the OCSP extension. Test only runs if ocsp stapled responses are supported by the client */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + const uint8_t *server_ocsp_reply = NULL; + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); + + for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server and client support the OCSP extension. Test Behavior for TLS 1.3 */ + if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + const uint8_t *server_ocsp_reply = NULL; + uint32_t length = 0; + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->client_protocol_version = S2N_TLS13; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server_protocol_version = S2N_TLS13; + server_conn->client_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status_bytes, sizeof(server_ocsp_status_bytes))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status_bytes)); + + for (size_t i = 0; i < sizeof(server_ocsp_status_bytes); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status_bytes[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Client does not request SCT, but server is configured to serve them. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + + uint32_t length = 0; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, + sct_list, sizeof(sct_list))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client did *not* receive an SCT list */ + EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client requests SCT and server does have it. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Indicate that the client wants CT if available */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, + sct_list, sizeof(sct_list))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client did receive an SCT list */ + EXPECT_NOT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(sct_list)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client requests SCT and server does *not* have it. */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + uint32_t length = 0; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Indicate that the client wants CT if available */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client does not get a list */ + EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client requests 512, 1024, 2048, and 4096 maximum fragment lengths */ + for (uint8_t mfl_code = S2N_TLS_MAX_FRAG_LEN_512; mfl_code <= S2N_TLS_MAX_FRAG_LEN_4096; mfl_code++) { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, mfl_code)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Preference should be ignored as the TlS Maximum Fragment Length Extension is Set */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, mfl_code_to_length[mfl_code]); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, mfl_code); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Client requests invalid maximum fragment length */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_FAILURE(s2n_config_send_max_fragment_length(client_config, 5)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* check that max_fragment_length did not get set due to invalid mfl_code */ + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Server ignores client's request of S2N_TLS_MAX_FRAG_LEN_2048 maximum fragment length when accept_mfl is not set*/ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_2048)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* check that max_fragment_length did not get set since accept_mfl is not set */ + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_recv_test.c b/tests/unit/s2n_client_hello_recv_test.c index 4afcecb8702..efb758ca87e 100644 --- a/tests/unit/s2n_client_hello_recv_test.c +++ b/tests/unit/s2n_client_hello_recv_test.c @@ -1,562 +1,562 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_sslv2_client_hello.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_client_hello.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -int main(int argc, char **argv) -{ - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - struct s2n_stuffer *hello_stuffer = NULL; - struct s2n_config *tls12_config = NULL; - struct s2n_config *tls13_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char *cert_chain = NULL; - char *tls13_cert_chain = NULL; - char *private_key = NULL; - char *tls13_private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(tls12_config = s2n_config_new()); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_config, "test_all_tls12")); - - EXPECT_NOT_NULL(tls13_cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(tls13_private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(tls13_config = s2n_config_new()); - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "test_all")); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, tls13_chain_and_key)); - - /* These tests verify the logic behind the setting of these three connection fields: - server_protocol_version, client_protocol_version, and actual_protocol version. */ - - /* Test we can successfully receive an sslv2 client hello and set a - * tls12 connection */ - { - /* "enable" tls13, to test under default s2n-tls behavior */ - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - /* Record version and protocol version are in the header for SSLv2 */ - server_conn->client_hello.sslv2 = true; - server_conn->client_hello.legacy_version = S2N_TLS12; - - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - struct s2n_blob client_hello = { - .data = sslv2_client_hello, - .size = sizeof(sslv2_client_hello), - .allocated = 0, - .growable = 0 - }; - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.sslv2, true); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.callback_invoked, 1); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server_conn), S2N_SSLv2); - - s2n_connection_free(server_conn); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* Test that a tls12 client legacy version and tls12 server version - will successfully set a tls12 connection, since tls13 is not enabled. */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - }; - - /* Test that a tls12 client legacy version and tls12 server version - will successfully set a tls12 connection, even when tls13 is enabled. */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test that a tls11 client legacy version and tls12 server version - will successfully set a tls11 connection. */ - for (uint8_t i = 0; i < 2; i++) { - if (i == 1) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - } - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - client_conn->client_protocol_version = S2N_TLS11; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS11); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS11); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS11); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS11); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - /* Test that a tls12 client and tls13 server will successfully - set a tls12 connection. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - /* Test that an erroneous client legacy version and tls13 server version - will still successfully set a tls13 connection, when real client version is tls13. */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - hello_stuffer = &client_conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ - uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - incorrect_protocol_version[0] = S2N_TLS13 / 10; - incorrect_protocol_version[1] = S2N_TLS13 % 10; - EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test that a tls12 client legacy version and tls13 server version - will still successfully set a tls13 connection, if possible. */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - /* Test that an erroneous(tls13) client legacy version and tls13 server version - will still successfully set a tls12 connection, if tls12 is the true client version. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - hello_stuffer = &client_conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ - uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - incorrect_protocol_version[0] = S2N_TLS13 / 10; - incorrect_protocol_version[1] = S2N_TLS13 % 10; - EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->server_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* s2n receiving a client hello will error when parsing an empty cipher suite */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - - hello_stuffer = &client_conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - uint8_t empty_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - - /* Move write_cursor to cipher_suite position */ - EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN + 1)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, empty_cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); - - s2n_connection_free(server_conn); - s2n_connection_free(client_conn); - }; - - /* s2n receiving a sslv2 client hello will error when parsing an empty cipher suite */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); - - /* Record version and protocol version are in the header for SSLv2 */ - server_conn->client_hello.sslv2 = true; - server_conn->client_hello.legacy_version = S2N_TLS12; - - /* Writing a sslv2 client hello with a length 0 cipher suite list */ - uint8_t sslv2_client_hello[] = { - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x20, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - struct s2n_blob client_hello = { - .data = sslv2_client_hello, - .size = sizeof(sslv2_client_hello), - .allocated = 0, - .growable = 0 - }; - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); - - s2n_connection_free(server_conn); - }; - - /* Test that S2N will accept a ClientHello with legacy_session_id set when running with QUIC. - * Since this requirement is a SHOULD, we're accepting it for non-compliant endpoints. - * https://tools.ietf.org/html/draft-ietf-quic-tls-32#section-8.4*/ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - const size_t test_session_id_len = 10; - - struct s2n_config *quic_config = NULL; - EXPECT_NOT_NULL(quic_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(quic_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(quic_config, "default_tls13")); - - /* Succeeds without a session id */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - s2n_connection_free(client_conn); - s2n_connection_free(server_conn); - }; - - /* Also, succeeds with a session id */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); - - /* Directly set session id, which is not set by default when using QUIC */ - client_conn->session_id_len = test_session_id_len; - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - s2n_connection_free(client_conn); - s2n_connection_free(server_conn); - }; - - s2n_config_free(quic_config); - } - - /* Test that the server will not choose a signature algorithm or certificate if using PSKs */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - struct s2n_psk chosen_psk = { 0 }; - chosen_psk.hmac_alg = S2N_HMAC_SHA256; - server_conn->psk_params.chosen_psk = &chosen_psk; - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->iana_value, 0); - EXPECT_NULL(server_conn->handshake_params.our_chain_and_key); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test that curve selection will be NIST P-256 when tls12 client does not send curve extension. - * - *= https://www.rfc-editor.org/rfc/rfc4492#section-4 - *= type=test - *# A client that proposes ECC cipher suites may choose not to include these extensions. - *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. - */ - { - S2N_BLOB_FROM_HEX(tls12_client_hello_no_curves, - /* clang-format off */ - "030307de81928fe1" "7cba77904c2798da" "2521a76b013a16e4" "21ade32208f658d4" "327d000048000400" - "05000a0016002f00" "3300350039003c00" "3d0067006b009c00" "9d009e009fc009c0" "0ac011c012c013c0" - "14c023c024c027c0" "28c02bc02cc02fc0" "30cca8cca9ccaaff" "04ff0800ff010000" "30000d0016001404" - "0105010601030104" "0305030603030302" "010203000b000201" "00fe01000c000a00" "17000d0013000100" - "0a" /* clang-format on */); - - /* The above code is generated the following code, - disabling s2n_client_supported_groups_extension - from client_hello_extensions (s2n_extension_type_lists.c) - and exporting the resulting client_conn->handshake.io.blob - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); - s2n_connection_free(client_conn); - */ - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); - - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &tls12_client_hello_no_curves)); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - /* ensure negotiated_curve == secp256r1 for maximum client compatibility */ - EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, &s2n_ecc_curve_secp256r1); - - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - s2n_connection_free(server_conn); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* Test: Parse fragmented sslv2 client hello. - * - * Even if the sslv2 client hello is sent in one packet, there is no requirement - * that our first call to conn->recv returns the whole message. sslv2 uses separate - * record parsing code, so we need to ensure that those paths can handle partial reads. - */ - { - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_config)); - - struct s2n_stuffer server_in = { 0 }; - uint8_t sslv2_client_hello_bytes[] = { - SSLv2_CLIENT_HELLO_HEADER, - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - EXPECT_SUCCESS(s2n_blob_init(&server_in.blob, - sslv2_client_hello_bytes, sizeof(sslv2_client_hello_bytes))); - EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&server_in, server)); - - /* Read message one byte at a time */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - for (size_t i = 0; i < sizeof(sslv2_client_hello_bytes); i++) { - EXPECT_ERROR_WITH_ERRNO( - s2n_negotiate_until_message(server, &blocked, SERVER_HELLO), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&server_in, 1)); - } - - /* Successfully read the full message */ - EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); - EXPECT_EQUAL(server->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->client_hello.legacy_version, S2N_TLS12); - EXPECT_TRUE(server->client_hello.sslv2); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), S2N_SSLv2); - }; - - s2n_config_free(tls12_config); - s2n_config_free(tls13_config); - s2n_cert_chain_and_key_free(chain_and_key); - free(cert_chain); - free(private_key); - s2n_cert_chain_and_key_free(tls13_chain_and_key); - free(tls13_cert_chain); - free(tls13_private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + struct s2n_stuffer *hello_stuffer = NULL; + struct s2n_config *tls12_config = NULL; + struct s2n_config *tls13_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char *cert_chain = NULL; + char *tls13_cert_chain = NULL; + char *private_key = NULL; + char *tls13_private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls12_config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_config, "test_all_tls12")); + + EXPECT_NOT_NULL(tls13_cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls13_private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls13_config = s2n_config_new()); + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "test_all")); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, tls13_chain_and_key)); + + /* These tests verify the logic behind the setting of these three connection fields: + server_protocol_version, client_protocol_version, and actual_protocol version. */ + + /* Test we can successfully receive an sslv2 client hello and set a + * tls12 connection */ + { + /* "enable" tls13, to test under default s2n-tls behavior */ + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello.sslv2 = true; + server_conn->client_hello.legacy_version = S2N_TLS12; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { + .data = sslv2_client_hello, + .size = sizeof(sslv2_client_hello), + .allocated = 0, + .growable = 0 + }; + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.sslv2, true); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.callback_invoked, 1); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server_conn), S2N_SSLv2); + + s2n_connection_free(server_conn); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Test that a tls12 client legacy version and tls12 server version + will successfully set a tls12 connection, since tls13 is not enabled. */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + }; + + /* Test that a tls12 client legacy version and tls12 server version + will successfully set a tls12 connection, even when tls13 is enabled. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test that a tls11 client legacy version and tls12 server version + will successfully set a tls11 connection. */ + for (uint8_t i = 0; i < 2; i++) { + if (i == 1) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + client_conn->client_protocol_version = S2N_TLS11; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS11); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS11); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS11); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS11); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + /* Test that a tls12 client and tls13 server will successfully + set a tls12 connection. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that an erroneous client legacy version and tls13 server version + will still successfully set a tls13 connection, when real client version is tls13. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ + uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + incorrect_protocol_version[0] = S2N_TLS13 / 10; + incorrect_protocol_version[1] = S2N_TLS13 % 10; + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test that a tls12 client legacy version and tls13 server version + will still successfully set a tls13 connection, if possible. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that an erroneous(tls13) client legacy version and tls13 server version + will still successfully set a tls12 connection, if tls12 is the true client version. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ + uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + incorrect_protocol_version[0] = S2N_TLS13 / 10; + incorrect_protocol_version[1] = S2N_TLS13 % 10; + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS13); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* s2n receiving a client hello will error when parsing an empty cipher suite */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + uint8_t empty_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + + /* Move write_cursor to cipher_suite position */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN + 1)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, empty_cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + }; + + /* s2n receiving a sslv2 client hello will error when parsing an empty cipher suite */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello.sslv2 = true; + server_conn->client_hello.legacy_version = S2N_TLS12; + + /* Writing a sslv2 client hello with a length 0 cipher suite list */ + uint8_t sslv2_client_hello[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x20, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { + .data = sslv2_client_hello, + .size = sizeof(sslv2_client_hello), + .allocated = 0, + .growable = 0 + }; + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(server_conn); + }; + + /* Test that S2N will accept a ClientHello with legacy_session_id set when running with QUIC. + * Since this requirement is a SHOULD, we're accepting it for non-compliant endpoints. + * https://tools.ietf.org/html/draft-ietf-quic-tls-32#section-8.4*/ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + const size_t test_session_id_len = 10; + + struct s2n_config *quic_config = NULL; + EXPECT_NOT_NULL(quic_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(quic_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(quic_config, "default_tls13")); + + /* Succeeds without a session id */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + /* Also, succeeds with a session id */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + /* Directly set session id, which is not set by default when using QUIC */ + client_conn->session_id_len = test_session_id_len; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + s2n_config_free(quic_config); + } + + /* Test that the server will not choose a signature algorithm or certificate if using PSKs */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + struct s2n_psk chosen_psk = { 0 }; + chosen_psk.hmac_alg = S2N_HMAC_SHA256; + server_conn->psk_params.chosen_psk = &chosen_psk; + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->iana_value, 0); + EXPECT_NULL(server_conn->handshake_params.our_chain_and_key); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that curve selection will be NIST P-256 when tls12 client does not send curve extension. + * + *= https://www.rfc-editor.org/rfc/rfc4492#section-4 + *= type=test + *# A client that proposes ECC cipher suites may choose not to include these extensions. + *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. + */ + { + S2N_BLOB_FROM_HEX(tls12_client_hello_no_curves, + /* clang-format off */ + "030307de81928fe1" "7cba77904c2798da" "2521a76b013a16e4" "21ade32208f658d4" "327d000048000400" + "05000a0016002f00" "3300350039003c00" "3d0067006b009c00" "9d009e009fc009c0" "0ac011c012c013c0" + "14c023c024c027c0" "28c02bc02cc02fc0" "30cca8cca9ccaaff" "04ff0800ff010000" "30000d0016001404" + "0105010601030104" "0305030603030302" "010203000b000201" "00fe01000c000a00" "17000d0013000100" + "0a" /* clang-format on */); + + /* The above code is generated the following code, + disabling s2n_client_supported_groups_extension + from client_hello_extensions (s2n_extension_type_lists.c) + and exporting the resulting client_conn->handshake.io.blob + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + s2n_connection_free(client_conn); + */ + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &tls12_client_hello_no_curves)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* ensure negotiated_curve == secp256r1 for maximum client compatibility */ + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, &s2n_ecc_curve_secp256r1); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + s2n_connection_free(server_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test: Parse fragmented sslv2 client hello. + * + * Even if the sslv2 client hello is sent in one packet, there is no requirement + * that our first call to conn->recv returns the whole message. sslv2 uses separate + * record parsing code, so we need to ensure that those paths can handle partial reads. + */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_config)); + + struct s2n_stuffer server_in = { 0 }; + uint8_t sslv2_client_hello_bytes[] = { + SSLv2_CLIENT_HELLO_HEADER, + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + EXPECT_SUCCESS(s2n_blob_init(&server_in.blob, + sslv2_client_hello_bytes, sizeof(sslv2_client_hello_bytes))); + EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&server_in, server)); + + /* Read message one byte at a time */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + for (size_t i = 0; i < sizeof(sslv2_client_hello_bytes); i++) { + EXPECT_ERROR_WITH_ERRNO( + s2n_negotiate_until_message(server, &blocked, SERVER_HELLO), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&server_in, 1)); + } + + /* Successfully read the full message */ + EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); + EXPECT_EQUAL(server->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->client_hello.legacy_version, S2N_TLS12); + EXPECT_TRUE(server->client_hello.sslv2); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), S2N_SSLv2); + }; + + s2n_config_free(tls12_config); + s2n_config_free(tls13_config); + s2n_cert_chain_and_key_free(chain_and_key); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(tls13_chain_and_key); + free(tls13_cert_chain); + free(tls13_private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_retry_test.c b/tests/unit/s2n_client_hello_retry_test.c index f64223f484b..00634be07bf 100644 --- a/tests/unit/s2n_client_hello_retry_test.c +++ b/tests/unit/s2n_client_hello_retry_test.c @@ -1,2011 +1,2011 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_pq.h" -#include "s2n.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_client_renegotiation_info.h" -#include "tls/extensions/s2n_cookie.h" -#include "tls/extensions/s2n_extension_type_lists.h" -#include "tls/extensions/s2n_server_supported_versions.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" - -/* This include is required to access static function s2n_server_hello_parse */ -#include "error/s2n_errno.h" -#include "tls/extensions/s2n_early_data_indication.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/s2n_server_hello.c" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define HELLO_RETRY_MSG_NO 1 -#define SERVER_HELLO_MSG_NO 5 - -int s2n_parse_client_hello(struct s2n_connection *conn); - -static int s2n_client_hello_cb_with_get_server_name(struct s2n_connection *conn, void *ctx) -{ - const char *expected_server_name = (const char *) ctx; - const char *actual_server_name = s2n_get_server_name(conn); - POSIX_ENSURE_EQ(strcmp(expected_server_name, actual_server_name), 0); - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Test s2n_server_hello_retry_recv */ - { - /* s2n_server_hello_retry_recv must fail when a keyshare for a matching curve was already present */ - { - struct s2n_config *config = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - const struct s2n_ecc_preferences *ecc_pref = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - EXPECT_NOT_NULL(ecc_pref); - - conn->actual_protocol_version = S2N_TLS13; - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - - EXPECT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* s2n_server_hello_retry_recv must fail for a connection with actual protocol version less than TLS13 */ - { - struct s2n_config *config = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - conn->actual_protocol_version = S2N_TLS12; - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test ECC success case for s2n_server_hello_retry_recv */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server sends HelloRetryMessage */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - /* Read the message off the wire */ - EXPECT_SUCCESS(s2n_server_hello_parse(client_conn)); - client_conn->actual_protocol_version_established = 1; - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(client_conn)); - /* Client receives the HelloRetryRequest mesage */ - EXPECT_SUCCESS(s2n_server_hello_retry_recv(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - { - const struct s2n_security_policy test_security_policy = { - .minimum_protocol_version = S2N_SSLv3, - .cipher_preferences = &cipher_preferences_test_all_tls13, - .kem_preferences = &kem_preferences_all, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20200310, - }; - - if (!s2n_pq_is_enabled()) { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->actual_protocol_version = S2N_TLS13; - conn->security_policy_override = &test_security_policy; - - const struct s2n_kem_preferences *kem_pref = NULL; - POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); - EXPECT_NOT_NULL(kem_pref); - - conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; - EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } else { - /* s2n_server_hello_retry_recv must fail when a keyshare for a matching PQ KEM was already present */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->actual_protocol_version = S2N_TLS13; - conn->security_policy_override = &test_security_policy; - - const struct s2n_kem_preferences *kem_pref = NULL; - POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); - EXPECT_NOT_NULL(kem_pref); - - const struct s2n_kem_group *kem_group = s2n_kem_preferences_get_highest_priority_group(kem_pref); - EXPECT_NOT_NULL(kem_group); - - conn->kex_params.server_kem_group_params.kem_group = kem_group; - EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); - - struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params; - client_params->kem_group = kem_group; - client_params->kem_params.kem = kem_group->kem; - client_params->ecc_params.negotiated_curve = kem_group->curve; - - EXPECT_NULL(client_params->ecc_params.evp_pkey); - EXPECT_NULL(client_params->kem_params.private_key.data); - - kem_public_key_size public_key_size = kem_group->kem->public_key_length; - EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, public_key_size)); - - EXPECT_OK(s2n_kem_generate_keypair(&client_params->kem_params)); - EXPECT_NOT_NULL(client_params->kem_params.private_key.data); - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); - EXPECT_NOT_NULL(client_params->ecc_params.evp_pkey); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_free(&client_params->kem_params.public_key)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - /* Test failure if exactly one of {named_curve, kem_group} isn't non-null */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->actual_protocol_version = S2N_TLS13; - conn->security_policy_override = &test_security_policy; - - conn->kex_params.server_kem_group_params.kem_group = &s2n_x25519_mlkem_768; - conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - conn->kex_params.server_kem_group_params.kem_group = NULL; - conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - /* Test PQ KEM success case for s2n_server_hello_retry_recv. */ - /* Need at least two KEM's to test fallback */ - uint32_t available_groups = 0; - EXPECT_OK(s2n_kem_preferences_groups_available(test_security_policy.kem_preferences, &available_groups)); - if (available_groups >= 2) { - struct s2n_config *config = NULL; - struct s2n_connection *conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - conn->security_policy_override = &test_security_policy; - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); - - /* Client sends ClientHello containing key share for X25519MLKEM768 - * (but indicates support for SecP256r1MLKEM768 in supported_groups) */ - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - conn->session_id_len = 0; /* Wipe the session id to match the HRR hex */ - - /* Server responds with HRR indicating SecP256r1MLKEM768 as choice for negotiation; - * the last 6 bytes (0033 0002 11EB) are the key share extension with SecP256r1MLKEM768 */ - DEFER_CLEANUP(struct s2n_stuffer hrr = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_stuffer_alloc_from_hex(&hrr, - "0303CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C00130200000C002B000203040033000211EB")); - - EXPECT_SUCCESS(s2n_stuffer_copy(&hrr, &conn->handshake.io, s2n_stuffer_data_available(&hrr))); - conn->handshake.message_number = HELLO_RETRY_MSG_NO; - /* Read the message off the wire */ - EXPECT_SUCCESS(s2n_server_hello_parse(conn)); - conn->actual_protocol_version_established = 1; - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - /* Client receives the HelloRetryRequest message */ - EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - } - } - }; - }; - - /* Verify that the hash transcript recreation function is called correctly, - * within the s2n_server_hello_retry_send and s2n_server_hello_retry_recv functions. - * The hash transcript recreation function, if called correctly takes the existing ClientHello1 - * hash, and generates a synthetic message. This test verifies that transcript hash recreated is the same - * on both the server and client side. */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - - /* Server sends HelloRetryRequest message, note that s2n_server_hello_retry_recreate_transcript - * is called within the s2n_server_hello_retry_send function */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - s2n_tls13_connection_keys(server_keys, server_conn); - uint8_t hash_digest_length = server_keys.size; - - /* Obtain the transcript hash recreated within the HelloRetryRequest message */ - DEFER_CLEANUP(struct s2n_hash_state server_hash = { 0 }, s2n_hash_free); - uint8_t server_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD(s2n_hash_new(&server_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(server_conn, server_keys.hash_algorithm, &server_hash)); - POSIX_GUARD(s2n_hash_digest(&server_hash, server_digest_out, hash_digest_length)); - - struct s2n_blob server_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&server_blob, server_digest_out, hash_digest_length)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - /* Client receives the HelloRetryRequest mesage, note that s2n_server_hello_retry_recreate_transcript - * is called within the s2n_server_hello_recv function */ - EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); - - s2n_tls13_connection_keys(client_keys, client_conn); - hash_digest_length = client_keys.size; - - /* Obtain the transcript hash recreated within ClientHello2 message */ - DEFER_CLEANUP(struct s2n_hash_state client_hash = { 0 }, s2n_hash_free); - uint8_t client_digest_out[S2N_MAX_DIGEST_LEN]; - POSIX_GUARD(s2n_hash_new(&client_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(client_conn, client_keys.hash_algorithm, &client_hash)); - POSIX_GUARD(s2n_hash_digest(&client_hash, client_digest_out, hash_digest_length)); - - struct s2n_blob client_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&client_blob, client_digest_out, hash_digest_length)); - - /* Test that the transcript hash recreated MUST be the same on the server and client side */ - S2N_BLOB_EXPECT_EQUAL(client_blob, server_blob); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - /** - * Self-Talk test: the client initiates a handshake with an unknown keyshare. - * The server sends a HelloRetryRequest that requires the client to generate a - * key share on the server negotiated curve. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Otherwise, the client MUST process all extensions in the - *# HelloRetryRequest and send a second updated ClientHello. - **/ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - server_conn->x509_validator.skip_cert_validation = 1; - client_conn->x509_validator.skip_cert_validation = 1; - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /** - * Self-Talk test: the client initiates a handshake with an X25519 share. - * The server, however does not support x25519 and prefers P-256. - * The server then sends a HelloRetryRequest that requires the - * client to generate a key share on the P-256 curve. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1 - *= type=test - *# If the server selects an (EC)DHE group and the client did not offer a - *# compatible "key_share" extension in the initial ClientHello, the - *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message. - **/ - if (s2n_is_evp_apis_supported()) { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); /* contains x25519 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190802")); /* does not contain x25519 */ - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - server_conn->x509_validator.skip_cert_validation = 1; - client_conn->x509_validator.skip_cert_validation = 1; - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - /* Ensure the handshake included a hello retry request */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - /** - * Ensure the client aborts the handshake if more than one - * HelloRetryRequest is received - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# If a client receives a second - *# HelloRetryRequest in the same connection (i.e., where the ClientHello - *# was itself in response to a HelloRetryRequest), it MUST abort the - *# handshake with an "unexpected_message" alert. - **/ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server HelloRetryRequest 1 */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); - - /* ClientHello 2 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - /* Server HelloRetryRequest 2 */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - /** - * Ensure that s2n_random_value_is_hello_retry returns true for hello - * retry random values, and false otherwise - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 - *= type=test - *# Upon receiving a message with type server_hello, implementations MUST - *# first examine the Random value and, if it matches this value, process - *# it as described in Section 4.1.4). - **/ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - const uint8_t not_hello_retry_request_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, not_hello_retry_request_random, - S2N_TLS_RANDOM_DATA_LEN); - - EXPECT_FAILURE_WITH_ERRNO(s2n_random_value_is_hello_retry(conn), S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_random_value_is_hello_retry(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Upon receiving - *# the ServerHello, clients MUST check that the cipher suite supplied in - *# the ServerHello is the same as that in the HelloRetryRequest and - *# otherwise abort the handshake with an "illegal_parameter" alert. - **/ - { - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - /* A Hello Retry Request has been processed */ - EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); - client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->handshake.handshake_type |= NEGOTIATED; - client_conn->handshake.handshake_type |= FULL_HANDSHAKE; - client_conn->handshake.message_number = SERVER_HELLO_MSG_NO; - - /* Server Hello with cipher suite that does not match Hello Retry Request cipher suite */ - server_conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; - EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_CIPHER_NOT_SUPPORTED); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* - * Self-Talk - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *= type=test - *# The client will also send a - *# ClientHello when the server has responded to its ClientHello with a - *# HelloRetryRequest. In that case, the client MUST send the same - *# ClientHello without modification - */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* Sanity Check: The server accepts an unchanged ClientHello */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Finish handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - }; - - /* Test: The server rejects a second ClientHello with a changed legacy version */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Change the legacy version. */ - client_conn->client_protocol_version = S2N_TLS11; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: The server rejects a second ClientHello with changed client random */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Change client random */ - client_conn->client_hello.random[0]++; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: Outside of testing, the server accepts a second ClientHello with a changed client random */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Change client random */ - client_conn->client_hello.random[0]++; - - /* Expect success if we pretend that this isn't a unit test */ - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - } - - /* Test: The server accepts a second ClientHello with a changed session ID */ - for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path. */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Change session id */ - client_conn->session_id[0]++; - - if (test_in_test_mode) { - /* Ensure that validation fails in test mode to prevent regressions. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - } else { - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - } - }; - - /* Test: The server accepts a second ClientHello with a changed cipher suite list */ - for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - struct s2n_security_policy test_policy = security_policy_test_tls13_retry; - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384 - }; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = s2n_array_len(test_cipher_suites), - .suites = test_cipher_suites, - }; - test_policy.cipher_preferences = &test_cipher_preferences; - - /* Force the HRR path. */ - client_conn->security_policy_override = &test_policy; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Modify a cipher suite. */ - test_cipher_suites[1] = &s2n_tls13_chacha20_poly1305_sha256; - - if (test_in_test_mode) { - /* Ensure that validation fails in test mode to prevent regressions. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - } else { - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - } - }; - - /* Test: Ensure that the connection fails if the cipher suite list changes such that the - * server cannot negotiate its original selection from the first ClientHello - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Servers MUST ensure that they negotiate the - *# same cipher suite when receiving a conformant updated ClientHello (if - *# the server selects the cipher suite as the first step in the - *# negotiation, then this will happen automatically). - **/ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - struct s2n_security_policy test_policy = security_policy_test_tls13_retry; - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384 - }; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = s2n_array_len(test_cipher_suites), - .suites = test_cipher_suites, - }; - test_policy.cipher_preferences = &test_cipher_preferences; - - /* Force the HRR path. */ - client_conn->security_policy_override = &test_policy; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Replace the most preferred cipher suite, forcing the server to select a different - * cipher suite when processing the second ClientHello. - */ - test_cipher_suites[0] = &s2n_tls13_chacha20_poly1305_sha256; - - /* Test mode is disabled to skip the failing ClientHello comparison check due to a - * changed cipher suite list. - */ - EXPECT_SUCCESS(s2n_in_unit_test_set(false)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - EXPECT_SUCCESS(s2n_in_unit_test_set(true)); - }; - - /* The server rejects a second ClientHello with a changed compression methods field */ - for (uint8_t test_compression_method = 0; test_compression_method <= 1; test_compression_method++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path. */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Send the second ClientHello. */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - struct s2n_stuffer client_hello_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_init_written(&client_hello_stuffer, &server_conn->handshake.io.blob)); - - /* Read up to the single null compression method byte */ - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_RANDOM_DATA_LEN)); - uint8_t session_id_len = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &session_id_len)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, session_id_len)); - uint16_t cipher_suites_len = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(&client_hello_stuffer, &cipher_suites_len)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, cipher_suites_len)); - uint8_t compression_methods_len = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &compression_methods_len)); - EXPECT_EQUAL(compression_methods_len, 1); - uint32_t compression_method_pos = client_hello_stuffer.read_cursor; - - /* Overwrite the compression method in the second ClientHello. */ - EXPECT_SUCCESS(s2n_stuffer_rewrite(&client_hello_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_hello_stuffer, compression_method_pos)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_hello_stuffer, test_compression_method)); - - if (test_compression_method == 0) { - /* A second ClientHello with a compression method of 0 shouldn't be different from - * the first ClientHello, so validation should succeed. - */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); - } - }; - - /* The server accepts a second ClientHello with a changed supported versions extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - struct s2n_security_policy test_policy = security_policy_test_tls13_retry; - test_policy.minimum_protocol_version = S2N_TLS10; - - /* Force the HRR path. */ - client_conn->security_policy_override = &test_policy; - - /* Skip to after the first ClientHello is received. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - ssize_t first_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS); - - /* Skip to before the client sends the second ClientHello. */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Increase the minimum protocol version for the security policy to send fewer - * supported versions in the second ClientHello. - */ - test_policy.minimum_protocol_version = S2N_TLS11; - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Ensure that the supported versions extension changed. */ - ssize_t second_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS); - EXPECT_TRUE(first_supported_versions_length != second_supported_versions_length); - } - - /* Test: The server rejects a supported versions extension with < TLS 1.3 in the second ClientHello */ - for (uint8_t test_version = S2N_TLS10; test_version <= S2N_TLS13; test_version++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path. */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before the client sends the second ClientHello. */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Send the second ClientHello. */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Parse the ClientHello, but don't process the extensions yet. */ - EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); - server_conn->client_hello.parsed = true; - - uint8_t extension_data[3] = { 0 }; - struct s2n_blob extension_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); - struct s2n_stuffer extension_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); - - /* Overwrite the received supported versions extension with only the test version. */ - uint8_t extension_length = 2; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, extension_length)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version / 10)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version % 10)); - - struct s2n_client_hello *second_client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(second_client_hello); - s2n_extension_type_id supported_versions_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); - s2n_parsed_extension *extension = &second_client_hello->extensions.parsed_extensions[supported_versions_id]; - extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; - extension->extension = extension_blob; - - int ret = s2n_client_hello_recv(server_conn); - if (test_version == S2N_TLS13) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - } - - /* Test: The server rejects a second ClientHello with a changed extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Change server name */ - client_conn->server_name[0]++; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: The server rejects a second ClientHello with a removed extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Clear server name. - * Without a server name, we don't send the server name extension. */ - client_conn->server_name[0] = '\0'; - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* Test: The server rejects a second ClientHello with an added extension */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Add a server name. - * Without a server name, we don't send the server name extension. */ - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - - /* Expect failure because second client hello doesn't match */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /* - * Test: If the initial ClientHello includes all extensions, so does the second ClientHello. - * - * This includes TLS1.2 extensions, since the ClientHello is sent before - * the client knows what version the server will negotiate. - * - * We have to test with all extensions to ensure that an s2n server will never reject - * an s2n client's second ClientHello. - * - * TLS1.2 and TLS1.3 session tickets are mutually exclusive and use different - * extensions, so we test once with each. - */ - for (size_t tls13_tickets = 0; tls13_tickets < 2; tls13_tickets++) { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry_with_pq; - - /* Setup all extensions */ - uint8_t apn[] = "https"; - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "AWS-CRT-SDK-TLSv1.2-2025-PQ")); - EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_4096)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_append_protocol_preference(client_conn, apn, sizeof(apn))); - EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); - client_conn->config->npn_supported = true; - if (tls13_tickets) { - EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); - } - EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); - /* Need to enable quic on both sides so they can communicate */ - EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); - - /* Send and receive ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* All ClientHello extensions must be present (except very specific exceptions) */ - s2n_extension_type_list *extensions = NULL; - EXPECT_SUCCESS(s2n_extension_type_list_get(S2N_EXTENSION_LIST_CLIENT_HELLO, &extensions)); - for (size_t i = 0; i < extensions->count; i++) { - uint16_t iana = extensions->extension_types[i]->iana_value; - - /* The cookie is a special case and only appears AFTER the retry */ - if (iana == TLS_EXTENSION_COOKIE) { - continue; - } - - /* PQ TLS 1.2 extension is not enabled */ - if (iana == TLS_EXTENSION_PQ_KEM_PARAMETERS) { - continue; - } - - /* TLS1.2 session tickets and TLS1.3 session tickets are mutually exclusive */ - if (tls13_tickets && iana == TLS_EXTENSION_SESSION_TICKET) { - continue; - } else if (!tls13_tickets - && (iana == TLS_EXTENSION_PRE_SHARED_KEY - || iana == TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES - || iana == TLS_EXTENSION_EARLY_DATA)) { - continue; - } - - /* No extension is sent for an initial handshake, - * and TLS1.3 doesn't support renegotiation handshakes. - */ - if (iana == TLS_EXTENSION_RENEGOTIATION_INFO) { - continue; - } - - bool extension_exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server_conn->client_hello, - iana, &extension_exists)); - EXPECT_TRUE(extension_exists); - } - - /* Expect successful retry */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - - /* Processing an extension early does not affect the extension matching check. - * - * This test exists because of a previously released bug. Triggering the bug - * required a specific series of events: - * - The first ClientHello is received. - * - The extensions are parsed. - * - The ClientHello callback triggers. - * - The customer's ClientHello callback implementation calls the s2n_get_server_name API. - * - To retrieve the server name, s2n_get_server_name processes the server name extension early. - * - The server name extension is wiped. Before the "processed" flag, we wiped extensions - * to mark that they had been processed. - * - The server sends a HelloRetryRequest, triggering a retry. - * - The second ClientHello is received. - * - The extensions are parsed. - * - The customer's ClientHello callback does NOT trigger this time. The callback only - * triggers after the first ClientHello. - * - Therefore, s2n_get_server_name is not called and the server name extension is not - * processed early or wiped. - * - The first ClientHello is compared to the second ClientHello. The second ClientHello - * appears to contain a server name extension not present in the first ClientHello. - * - The handshake fails because the ClientHellos must match. - */ - { - char server_name[] = "test server name"; - - DEFER_CLEANUP(struct s2n_config *config_with_cb = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cb, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_cb, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_with_cb)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cb)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cb)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Setup server name and client hello callback */ - EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_cb, - s2n_client_hello_cb_with_get_server_name, server_name)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Handshake should complete as expected */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(strcmp(s2n_get_server_name(server_conn), server_name), 0); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - }; - } - - /** - * Ensure all hello retry extensions sent by the server will have first - * been sent by the client. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any - *# extensions that were not first offered by the client in its - *# ClientHello, with the exception of optionally the "cookie" (see - *# Section 4.2.2) extension. - **/ - { - s2n_extension_type_list *hello_retry_extension_types = 0; - POSIX_GUARD(s2n_extension_type_list_get(S2N_EXTENSION_LIST_HELLO_RETRY_REQUEST, &hello_retry_extension_types)); - - for (int i = 0; i < hello_retry_extension_types->count; ++i) { - const s2n_extension_type *const extension_type = hello_retry_extension_types->extension_types[i]; - - /* with the exception of optionally the "cookie" extension. */ - if (extension_type->iana_value == TLS_EXTENSION_COOKIE) { - continue; - } - - EXPECT_TRUE(extension_type->is_response); - } - }; - - /** - * Ensure each of the following are checked: legacy_version, - * legacy_session_id_echo, cipher_suite, and - * legacy_compression_method - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version, legacy_session_id_echo, cipher_suite, and - *# legacy_compression_method as specified in Section 4.1.3 and then - *# process the extensions, starting with determining the version using - *# "supported_versions". - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* The client MUST check the legacy_version */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Force the server to send an erroneous legacy protocol version in the HelloRetryRequest message */ - server_conn->actual_protocol_version = S2N_TLS11; - - /* Server sends HelloRetryRequest */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_INVALID_HELLO_RETRY); - }; - - /* The client MUST check the legacy_session_id_echo */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Set a session id that's different from the client hello */ - POSIX_CHECKED_MEMSET(&server_conn->session_id, 0, S2N_TLS_SESSION_ID_MAX_LEN); - - /* Server sends HelloRetryRequest */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /** - * The client MUST check the cipher_suite - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# A client which receives a cipher suite that was not offered MUST - *# abort the handshake. - **/ - { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Receive ClientHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* - * Pick a cipher that wasn't offered in the CH, and should cause the - * handshake to abort. - */ - server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - /* Finish handshake */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CIPHER_NOT_SUPPORTED); - }; - - /* The client MUST check the legacy_compression_method */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server sends HelloRetryRequest */ - POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_server_hello_write_message(server_conn)); - - /* Overwrite compression method to 1 */ - struct s2n_stuffer *io = &server_conn->handshake.io; - io->write_cursor -= 1; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 1)); - - /* Write the extensions */ - EXPECT_SUCCESS(s2n_server_extensions_send(server_conn, &server_conn->handshake.io)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_BAD_MESSAGE); - }; - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# The server's extensions MUST contain "supported_versions". - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - POSIX_GUARD(s2n_server_hello_write_message(server_conn)); - struct s2n_stuffer_reservation total_extensions_size = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint16(&server_conn->handshake.io, &total_extensions_size)); - - /* Only send key share extension - exclude supported_versions */ - s2n_extension_send(&s2n_server_key_share_extension, server_conn, &server_conn->handshake.io); - - POSIX_GUARD(s2n_stuffer_write_vector_size(&total_extensions_size)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_MISSING_EXTENSION); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# Servers MUST ensure that they negotiate the - *# same cipher suite when receiving a conformant updated ClientHello (if - *# the server selects the cipher suite as the first step in the - *# negotiation, then this will happen automatically). - **/ - { - /* Create a custom security policy so it can be changed mid-handshake */ - struct s2n_cipher_suite *test_cipher_suites[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384 - }; - struct s2n_cipher_preferences test_cipher_preferences = { - .count = s2n_array_len(test_cipher_suites), - .suites = test_cipher_suites, - }; - struct s2n_security_policy security_policy_test_tls13_retry_temp = { - .minimum_protocol_version = S2N_TLS10, - .cipher_preferences = &test_cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20200207, - .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, - .ecc_preferences = &ecc_preferences_for_retry, - }; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Receive ClientHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* Rearrange the cipher preference order so that a different cipher will be - * picked by the server */ - server_conn->config->security_policy->cipher_preferences->suites[0] = &s2n_tls13_aes_256_gcm_sha384; - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /** - * Ensure that the client aborts the handshake if selected_version - * differs in the received server hellos - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *= type=test - *# The value of selected_version in the HelloRetryRequest - *# "supported_versions" extension MUST be retained in the ServerHello, - *# and a client MUST abort the handshake with an "illegal_parameter" - *# alert if the value changes. - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send ClientHello */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - - /* Receive ClientHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - - /* Skip to before server sends ServerHello */ - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - - /* Change the server_protocol_version so the value found in the ServerHello - * differs from the value found in the HelloRetryRequest */ - server_conn->server_protocol_version = S2N_TLS13 + 10; - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_BAD_MESSAGE); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 - *= type=test - *# Upon receipt of this extension in a HelloRetryRequest, the client - *# MUST verify that (1) the selected_group field corresponds to a group - *# which was provided in the "supported_groups" extension in the - *# original ClientHello - **/ - { - /* Create a custom security policy without secp521r1 */ - const struct s2n_ecc_named_curve *const test_ecc_pref_list_for_retry[] = { - &s2n_ecc_curve_secp256r1, - &s2n_ecc_curve_secp384r1, - }; - const struct s2n_ecc_preferences test_ecc_preferences_for_retry = { - .count = s2n_array_len(test_ecc_pref_list_for_retry), - .ecc_curves = test_ecc_pref_list_for_retry, - }; - struct s2n_security_policy security_policy_test_tls13_retry_temp = { - .minimum_protocol_version = S2N_TLS10, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20200207, - .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, - .ecc_preferences = &test_ecc_preferences_for_retry, - }; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; - - /* ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Server receives ClientHello 1 */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - - /* Server sends HelloRetryRequest */ - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - - /* Set the curve to secp521r1, which was not provided in supported_groups */ - client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; - - /* Client receives HelloRetryRequest */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), - S2N_ERR_INVALID_HELLO_RETRY); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 - *= type=test - *# If using (EC)DHE key establishment and a HelloRetryRequest containing a - *# "key_share" extension was received by the client, the client MUST - *# verify that the selected NamedGroup in the ServerHello is the same as - *# that in the HelloRetryRequest. If this check fails, the client MUST - *# abort the handshake with an "illegal_parameter" alert. - **/ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Skip to before client receives ServerHello 2 */ - s2n_blocked_status blocked = 0; - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, ENCRYPTED_EXTENSIONS)); - - /* Set the negotiated curve to something other than what was sent in the HRR */ - client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; - - /* Client receives ServerHello 2 */ - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(client_conn, &blocked, ENCRYPTED_EXTENSIONS), - S2N_ERR_BAD_MESSAGE); - }; - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_pq.h" +#include "s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_renegotiation_info.h" +#include "tls/extensions/s2n_cookie.h" +#include "tls/extensions/s2n_extension_type_lists.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +/* This include is required to access static function s2n_server_hello_parse */ +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_early_data_indication.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_server_hello.c" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define HELLO_RETRY_MSG_NO 1 +#define SERVER_HELLO_MSG_NO 5 + +int s2n_parse_client_hello(struct s2n_connection *conn); + +static int s2n_client_hello_cb_with_get_server_name(struct s2n_connection *conn, void *ctx) +{ + const char *expected_server_name = (const char *) ctx; + const char *actual_server_name = s2n_get_server_name(conn); + POSIX_ENSURE_EQ(strcmp(expected_server_name, actual_server_name), 0); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_server_hello_retry_recv */ + { + /* s2n_server_hello_retry_recv must fail when a keyshare for a matching curve was already present */ + { + struct s2n_config *config = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->actual_protocol_version = S2N_TLS13; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + EXPECT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_server_hello_retry_recv must fail for a connection with actual protocol version less than TLS13 */ + { + struct s2n_config *config = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test ECC success case for s2n_server_hello_retry_recv */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryMessage */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Read the message off the wire */ + EXPECT_SUCCESS(s2n_server_hello_parse(client_conn)); + client_conn->actual_protocol_version_established = 1; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(client_conn)); + /* Client receives the HelloRetryRequest mesage */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + { + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &kem_preferences_all, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + if (!s2n_pq_is_enabled()) { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } else { + /* s2n_server_hello_retry_recv must fail when a keyshare for a matching PQ KEM was already present */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + const struct s2n_kem_group *kem_group = s2n_kem_preferences_get_highest_priority_group(kem_pref); + EXPECT_NOT_NULL(kem_group); + + conn->kex_params.server_kem_group_params.kem_group = kem_group; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params; + client_params->kem_group = kem_group; + client_params->kem_params.kem = kem_group->kem; + client_params->ecc_params.negotiated_curve = kem_group->curve; + + EXPECT_NULL(client_params->ecc_params.evp_pkey); + EXPECT_NULL(client_params->kem_params.private_key.data); + + kem_public_key_size public_key_size = kem_group->kem->public_key_length; + EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, public_key_size)); + + EXPECT_OK(s2n_kem_generate_keypair(&client_params->kem_params)); + EXPECT_NOT_NULL(client_params->kem_params.private_key.data); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); + EXPECT_NOT_NULL(client_params->ecc_params.evp_pkey); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_free(&client_params->kem_params.public_key)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test failure if exactly one of {named_curve, kem_group} isn't non-null */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + conn->kex_params.server_kem_group_params.kem_group = &s2n_x25519_mlkem_768; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + conn->kex_params.server_kem_group_params.kem_group = NULL; + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test PQ KEM success case for s2n_server_hello_retry_recv. */ + /* Need at least two KEM's to test fallback */ + uint32_t available_groups = 0; + EXPECT_OK(s2n_kem_preferences_groups_available(test_security_policy.kem_preferences, &available_groups)); + if (available_groups >= 2) { + struct s2n_config *config = NULL; + struct s2n_connection *conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->security_policy_override = &test_security_policy; + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); + + /* Client sends ClientHello containing key share for X25519MLKEM768 + * (but indicates support for SecP256r1MLKEM768 in supported_groups) */ + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + conn->session_id_len = 0; /* Wipe the session id to match the HRR hex */ + + /* Server responds with HRR indicating SecP256r1MLKEM768 as choice for negotiation; + * the last 6 bytes (0033 0002 11EB) are the key share extension with SecP256r1MLKEM768 */ + DEFER_CLEANUP(struct s2n_stuffer hrr = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_stuffer_alloc_from_hex(&hrr, + "0303CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C00130200000C002B000203040033000211EB")); + + EXPECT_SUCCESS(s2n_stuffer_copy(&hrr, &conn->handshake.io, s2n_stuffer_data_available(&hrr))); + conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Read the message off the wire */ + EXPECT_SUCCESS(s2n_server_hello_parse(conn)); + conn->actual_protocol_version_established = 1; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + /* Client receives the HelloRetryRequest message */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + } + } + }; + }; + + /* Verify that the hash transcript recreation function is called correctly, + * within the s2n_server_hello_retry_send and s2n_server_hello_retry_recv functions. + * The hash transcript recreation function, if called correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. This test verifies that transcript hash recreated is the same + * on both the server and client side. */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + /* Server sends HelloRetryRequest message, note that s2n_server_hello_retry_recreate_transcript + * is called within the s2n_server_hello_retry_send function */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + s2n_tls13_connection_keys(server_keys, server_conn); + uint8_t hash_digest_length = server_keys.size; + + /* Obtain the transcript hash recreated within the HelloRetryRequest message */ + DEFER_CLEANUP(struct s2n_hash_state server_hash = { 0 }, s2n_hash_free); + uint8_t server_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_new(&server_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(server_conn, server_keys.hash_algorithm, &server_hash)); + POSIX_GUARD(s2n_hash_digest(&server_hash, server_digest_out, hash_digest_length)); + + struct s2n_blob server_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&server_blob, server_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Client receives the HelloRetryRequest mesage, note that s2n_server_hello_retry_recreate_transcript + * is called within the s2n_server_hello_recv function */ + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + s2n_tls13_connection_keys(client_keys, client_conn); + hash_digest_length = client_keys.size; + + /* Obtain the transcript hash recreated within ClientHello2 message */ + DEFER_CLEANUP(struct s2n_hash_state client_hash = { 0 }, s2n_hash_free); + uint8_t client_digest_out[S2N_MAX_DIGEST_LEN]; + POSIX_GUARD(s2n_hash_new(&client_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(client_conn, client_keys.hash_algorithm, &client_hash)); + POSIX_GUARD(s2n_hash_digest(&client_hash, client_digest_out, hash_digest_length)); + + struct s2n_blob client_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&client_blob, client_digest_out, hash_digest_length)); + + /* Test that the transcript hash recreated MUST be the same on the server and client side */ + S2N_BLOB_EXPECT_EQUAL(client_blob, server_blob); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /** + * Self-Talk test: the client initiates a handshake with an unknown keyshare. + * The server sends a HelloRetryRequest that requires the client to generate a + * key share on the server negotiated curve. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Otherwise, the client MUST process all extensions in the + *# HelloRetryRequest and send a second updated ClientHello. + **/ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->x509_validator.skip_cert_validation = 1; + client_conn->x509_validator.skip_cert_validation = 1; + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + * Self-Talk test: the client initiates a handshake with an X25519 share. + * The server, however does not support x25519 and prefers P-256. + * The server then sends a HelloRetryRequest that requires the + * client to generate a key share on the P-256 curve. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1 + *= type=test + *# If the server selects an (EC)DHE group and the client did not offer a + *# compatible "key_share" extension in the initial ClientHello, the + *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message. + **/ + if (s2n_is_evp_apis_supported()) { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); /* contains x25519 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190802")); /* does not contain x25519 */ + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->x509_validator.skip_cert_validation = 1; + client_conn->x509_validator.skip_cert_validation = 1; + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Ensure the handshake included a hello retry request */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /** + * Ensure the client aborts the handshake if more than one + * HelloRetryRequest is received + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# If a client receives a second + *# HelloRetryRequest in the same connection (i.e., where the ClientHello + *# was itself in response to a HelloRetryRequest), it MUST abort the + *# handshake with an "unexpected_message" alert. + **/ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server HelloRetryRequest 1 */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* ClientHello 2 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* Server HelloRetryRequest 2 */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /** + * Ensure that s2n_random_value_is_hello_retry returns true for hello + * retry random values, and false otherwise + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 + *= type=test + *# Upon receiving a message with type server_hello, implementations MUST + *# first examine the Random value and, if it matches this value, process + *# it as described in Section 4.1.4). + **/ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + const uint8_t not_hello_retry_request_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, not_hello_retry_request_random, + S2N_TLS_RANDOM_DATA_LEN); + + EXPECT_FAILURE_WITH_ERRNO(s2n_random_value_is_hello_retry(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_random_value_is_hello_retry(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Upon receiving + *# the ServerHello, clients MUST check that the cipher suite supplied in + *# the ServerHello is the same as that in the HelloRetryRequest and + *# otherwise abort the handshake with an "illegal_parameter" alert. + **/ + { + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* A Hello Retry Request has been processed */ + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->handshake.handshake_type |= NEGOTIATED; + client_conn->handshake.handshake_type |= FULL_HANDSHAKE; + client_conn->handshake.message_number = SERVER_HELLO_MSG_NO; + + /* Server Hello with cipher suite that does not match Hello Retry Request cipher suite */ + server_conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* + * Self-Talk + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *= type=test + *# The client will also send a + *# ClientHello when the server has responded to its ClientHello with a + *# HelloRetryRequest. In that case, the client MUST send the same + *# ClientHello without modification + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* Sanity Check: The server accepts an unchanged ClientHello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Finish handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test: The server rejects a second ClientHello with a changed legacy version */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change the legacy version. */ + client_conn->client_protocol_version = S2N_TLS11; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with changed client random */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change client random */ + client_conn->client_hello.random[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: Outside of testing, the server accepts a second ClientHello with a changed client random */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change client random */ + client_conn->client_hello.random[0]++; + + /* Expect success if we pretend that this isn't a unit test */ + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + + /* Test: The server accepts a second ClientHello with a changed session ID */ + for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path. */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change session id */ + client_conn->session_id[0]++; + + if (test_in_test_mode) { + /* Ensure that validation fails in test mode to prevent regressions. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } else { + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + }; + + /* Test: The server accepts a second ClientHello with a changed cipher suite list */ + for (size_t test_in_test_mode = 0; test_in_test_mode <= 1; test_in_test_mode++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_security_policy test_policy = security_policy_test_tls13_retry; + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + test_policy.cipher_preferences = &test_cipher_preferences; + + /* Force the HRR path. */ + client_conn->security_policy_override = &test_policy; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Modify a cipher suite. */ + test_cipher_suites[1] = &s2n_tls13_chacha20_poly1305_sha256; + + if (test_in_test_mode) { + /* Ensure that validation fails in test mode to prevent regressions. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } else { + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + }; + + /* Test: Ensure that the connection fails if the cipher suite list changes such that the + * server cannot negotiate its original selection from the first ClientHello + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_security_policy test_policy = security_policy_test_tls13_retry; + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + test_policy.cipher_preferences = &test_cipher_preferences; + + /* Force the HRR path. */ + client_conn->security_policy_override = &test_policy; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Replace the most preferred cipher suite, forcing the server to select a different + * cipher suite when processing the second ClientHello. + */ + test_cipher_suites[0] = &s2n_tls13_chacha20_poly1305_sha256; + + /* Test mode is disabled to skip the failing ClientHello comparison check due to a + * changed cipher suite list. + */ + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + }; + + /* The server rejects a second ClientHello with a changed compression methods field */ + for (uint8_t test_compression_method = 0; test_compression_method <= 1; test_compression_method++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path. */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Send the second ClientHello. */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + struct s2n_stuffer client_hello_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init_written(&client_hello_stuffer, &server_conn->handshake.io.blob)); + + /* Read up to the single null compression method byte */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, S2N_TLS_RANDOM_DATA_LEN)); + uint8_t session_id_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &session_id_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, session_id_len)); + uint16_t cipher_suites_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&client_hello_stuffer, &cipher_suites_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_hello_stuffer, cipher_suites_len)); + uint8_t compression_methods_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_hello_stuffer, &compression_methods_len)); + EXPECT_EQUAL(compression_methods_len, 1); + uint32_t compression_method_pos = client_hello_stuffer.read_cursor; + + /* Overwrite the compression method in the second ClientHello. */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(&client_hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_hello_stuffer, compression_method_pos)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_hello_stuffer, test_compression_method)); + + if (test_compression_method == 0) { + /* A second ClientHello with a compression method of 0 shouldn't be different from + * the first ClientHello, so validation should succeed. + */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + } + }; + + /* The server accepts a second ClientHello with a changed supported versions extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_security_policy test_policy = security_policy_test_tls13_retry; + test_policy.minimum_protocol_version = S2N_TLS10; + + /* Force the HRR path. */ + client_conn->security_policy_override = &test_policy; + + /* Skip to after the first ClientHello is received. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + ssize_t first_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS); + + /* Skip to before the client sends the second ClientHello. */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Increase the minimum protocol version for the security policy to send fewer + * supported versions in the second ClientHello. + */ + test_policy.minimum_protocol_version = S2N_TLS11; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Ensure that the supported versions extension changed. */ + ssize_t second_supported_versions_length = s2n_client_hello_get_extension_length(client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS); + EXPECT_TRUE(first_supported_versions_length != second_supported_versions_length); + } + + /* Test: The server rejects a supported versions extension with < TLS 1.3 in the second ClientHello */ + for (uint8_t test_version = S2N_TLS10; test_version <= S2N_TLS13; test_version++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path. */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before the client sends the second ClientHello. */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Send the second ClientHello. */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Parse the ClientHello, but don't process the extensions yet. */ + EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); + server_conn->client_hello.parsed = true; + + uint8_t extension_data[3] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + + /* Overwrite the received supported versions extension with only the test version. */ + uint8_t extension_length = 2; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, extension_length)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension_stuffer, test_version % 10)); + + struct s2n_client_hello *second_client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(second_client_hello); + s2n_extension_type_id supported_versions_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + s2n_parsed_extension *extension = &second_client_hello->extensions.parsed_extensions[supported_versions_id]; + extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; + extension->extension = extension_blob; + + int ret = s2n_client_hello_recv(server_conn); + if (test_version == S2N_TLS13) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + } + + /* Test: The server rejects a second ClientHello with a changed extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change server name */ + client_conn->server_name[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with a removed extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Clear server name. + * Without a server name, we don't send the server name extension. */ + client_conn->server_name[0] = '\0'; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with an added extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Add a server name. + * Without a server name, we don't send the server name extension. */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* + * Test: If the initial ClientHello includes all extensions, so does the second ClientHello. + * + * This includes TLS1.2 extensions, since the ClientHello is sent before + * the client knows what version the server will negotiate. + * + * We have to test with all extensions to ensure that an s2n server will never reject + * an s2n client's second ClientHello. + * + * TLS1.2 and TLS1.3 session tickets are mutually exclusive and use different + * extensions, so we test once with each. + */ + for (size_t tls13_tickets = 0; tls13_tickets < 2; tls13_tickets++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_with_pq; + + /* Setup all extensions */ + uint8_t apn[] = "https"; + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "AWS-CRT-SDK-TLSv1.2-2025-PQ")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_4096)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(client_conn, apn, sizeof(apn))); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + client_conn->config->npn_supported = true; + if (tls13_tickets) { + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + } + EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); + /* Need to enable quic on both sides so they can communicate */ + EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); + + /* Send and receive ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* All ClientHello extensions must be present (except very specific exceptions) */ + s2n_extension_type_list *extensions = NULL; + EXPECT_SUCCESS(s2n_extension_type_list_get(S2N_EXTENSION_LIST_CLIENT_HELLO, &extensions)); + for (size_t i = 0; i < extensions->count; i++) { + uint16_t iana = extensions->extension_types[i]->iana_value; + + /* The cookie is a special case and only appears AFTER the retry */ + if (iana == TLS_EXTENSION_COOKIE) { + continue; + } + + /* PQ TLS 1.2 extension is not enabled */ + if (iana == TLS_EXTENSION_PQ_KEM_PARAMETERS) { + continue; + } + + /* TLS1.2 session tickets and TLS1.3 session tickets are mutually exclusive */ + if (tls13_tickets && iana == TLS_EXTENSION_SESSION_TICKET) { + continue; + } else if (!tls13_tickets + && (iana == TLS_EXTENSION_PRE_SHARED_KEY + || iana == TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES + || iana == TLS_EXTENSION_EARLY_DATA)) { + continue; + } + + /* No extension is sent for an initial handshake, + * and TLS1.3 doesn't support renegotiation handshakes. + */ + if (iana == TLS_EXTENSION_RENEGOTIATION_INFO) { + continue; + } + + bool extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server_conn->client_hello, + iana, &extension_exists)); + EXPECT_TRUE(extension_exists); + } + + /* Expect successful retry */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + /* Processing an extension early does not affect the extension matching check. + * + * This test exists because of a previously released bug. Triggering the bug + * required a specific series of events: + * - The first ClientHello is received. + * - The extensions are parsed. + * - The ClientHello callback triggers. + * - The customer's ClientHello callback implementation calls the s2n_get_server_name API. + * - To retrieve the server name, s2n_get_server_name processes the server name extension early. + * - The server name extension is wiped. Before the "processed" flag, we wiped extensions + * to mark that they had been processed. + * - The server sends a HelloRetryRequest, triggering a retry. + * - The second ClientHello is received. + * - The extensions are parsed. + * - The customer's ClientHello callback does NOT trigger this time. The callback only + * triggers after the first ClientHello. + * - Therefore, s2n_get_server_name is not called and the server name extension is not + * processed early or wiped. + * - The first ClientHello is compared to the second ClientHello. The second ClientHello + * appears to contain a server name extension not present in the first ClientHello. + * - The handshake fails because the ClientHellos must match. + */ + { + char server_name[] = "test server name"; + + DEFER_CLEANUP(struct s2n_config *config_with_cb = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cb, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_cb, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_with_cb)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cb)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cb)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Setup server name and client hello callback */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_cb, + s2n_client_hello_cb_with_get_server_name, server_name)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Handshake should complete as expected */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(strcmp(s2n_get_server_name(server_conn), server_name), 0); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + }; + } + + /** + * Ensure all hello retry extensions sent by the server will have first + * been sent by the client. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + { + s2n_extension_type_list *hello_retry_extension_types = 0; + POSIX_GUARD(s2n_extension_type_list_get(S2N_EXTENSION_LIST_HELLO_RETRY_REQUEST, &hello_retry_extension_types)); + + for (int i = 0; i < hello_retry_extension_types->count; ++i) { + const s2n_extension_type *const extension_type = hello_retry_extension_types->extension_types[i]; + + /* with the exception of optionally the "cookie" extension. */ + if (extension_type->iana_value == TLS_EXTENSION_COOKIE) { + continue; + } + + EXPECT_TRUE(extension_type->is_response); + } + }; + + /** + * Ensure each of the following are checked: legacy_version, + * legacy_session_id_echo, cipher_suite, and + * legacy_compression_method + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo, cipher_suite, and + *# legacy_compression_method as specified in Section 4.1.3 and then + *# process the extensions, starting with determining the version using + *# "supported_versions". + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* The client MUST check the legacy_version */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Force the server to send an erroneous legacy protocol version in the HelloRetryRequest message */ + server_conn->actual_protocol_version = S2N_TLS11; + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_INVALID_HELLO_RETRY); + }; + + /* The client MUST check the legacy_session_id_echo */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Set a session id that's different from the client hello */ + POSIX_CHECKED_MEMSET(&server_conn->session_id, 0, S2N_TLS_SESSION_ID_MAX_LEN); + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + * The client MUST check the cipher_suite + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# A client which receives a cipher suite that was not offered MUST + *# abort the handshake. + **/ + { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* + * Pick a cipher that wasn't offered in the CH, and should cause the + * handshake to abort. + */ + server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + /* Finish handshake */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CIPHER_NOT_SUPPORTED); + }; + + /* The client MUST check the legacy_compression_method */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_server_hello_write_message(server_conn)); + + /* Overwrite compression method to 1 */ + struct s2n_stuffer *io = &server_conn->handshake.io; + io->write_cursor -= 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 1)); + + /* Write the extensions */ + EXPECT_SUCCESS(s2n_server_extensions_send(server_conn, &server_conn->handshake.io)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_BAD_MESSAGE); + }; + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# The server's extensions MUST contain "supported_versions". + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + POSIX_GUARD(s2n_server_hello_write_message(server_conn)); + struct s2n_stuffer_reservation total_extensions_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(&server_conn->handshake.io, &total_extensions_size)); + + /* Only send key share extension - exclude supported_versions */ + s2n_extension_send(&s2n_server_key_share_extension, server_conn, &server_conn->handshake.io); + + POSIX_GUARD(s2n_stuffer_write_vector_size(&total_extensions_size)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_MISSING_EXTENSION); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + { + /* Create a custom security policy so it can be changed mid-handshake */ + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + struct s2n_security_policy security_policy_test_tls13_retry_temp = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &test_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &ecc_preferences_for_retry, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* Rearrange the cipher preference order so that a different cipher will be + * picked by the server */ + server_conn->config->security_policy->cipher_preferences->suites[0] = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + * Ensure that the client aborts the handshake if selected_version + * differs in the received server hellos + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *= type=test + *# The value of selected_version in the HelloRetryRequest + *# "supported_versions" extension MUST be retained in the ServerHello, + *# and a client MUST abort the handshake with an "illegal_parameter" + *# alert if the value changes. + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* Skip to before server sends ServerHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change the server_protocol_version so the value found in the ServerHello + * differs from the value found in the HelloRetryRequest */ + server_conn->server_protocol_version = S2N_TLS13 + 10; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 + *= type=test + *# Upon receipt of this extension in a HelloRetryRequest, the client + *# MUST verify that (1) the selected_group field corresponds to a group + *# which was provided in the "supported_groups" extension in the + *# original ClientHello + **/ + { + /* Create a custom security policy without secp521r1 */ + const struct s2n_ecc_named_curve *const test_ecc_pref_list_for_retry[] = { + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_secp384r1, + }; + const struct s2n_ecc_preferences test_ecc_preferences_for_retry = { + .count = s2n_array_len(test_ecc_pref_list_for_retry), + .ecc_curves = test_ecc_pref_list_for_retry, + }; + struct s2n_security_policy security_policy_test_tls13_retry_temp = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &test_ecc_preferences_for_retry, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Set the curve to secp521r1, which was not provided in supported_groups */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_INVALID_HELLO_RETRY); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 + *= type=test + *# If using (EC)DHE key establishment and a HelloRetryRequest containing a + *# "key_share" extension was received by the client, the client MUST + *# verify that the selected NamedGroup in the ServerHello is the same as + *# that in the HelloRetryRequest. If this check fails, the client MUST + *# abort the handshake with an "illegal_parameter" alert. + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before client receives ServerHello 2 */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, ENCRYPTED_EXTENSIONS)); + + /* Set the negotiated curve to something other than what was sent in the HRR */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + /* Client receives ServerHello 2 */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(client_conn, &blocked, ENCRYPTED_EXTENSIONS), + S2N_ERR_BAD_MESSAGE); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_test.c b/tests/unit/s2n_client_hello_test.c index fe2a477a1d0..f3efe3a2dba 100644 --- a/tests/unit/s2n_client_hello_test.c +++ b/tests/unit/s2n_client_hello_test.c @@ -1,2146 +1,2146 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_client_hello.h" - -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_sslv2_client_hello.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -#define LENGTH_TO_SESSION_ID (S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN) -#define TLS12_LENGTH_TO_CIPHER_LIST (LENGTH_TO_SESSION_ID + 1) -#define TLS13_LENGTH_TO_CIPHER_LIST (TLS12_LENGTH_TO_CIPHER_LIST + S2N_TLS_SESSION_ID_MAX_LEN) - -#define COMPRESSION_METHODS 0x00, 0x01, 0x02, 0x03, 0x04 -#define COMPRESSION_METHODS_LEN 0x05 - -int s2n_parse_client_hello(struct s2n_connection *conn); -S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, - struct s2n_blob *raw_extensions, struct s2n_blob *extension); - -int main(int argc, char **argv) -{ - struct s2n_cert_chain_and_key *chain_and_key = NULL, *ecdsa_chain_and_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Test s2n_client_hello_get_extension_by_id */ - { - /* Test with invalid parsed extensions */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - s2n_tls_extension_type test_extension_type = S2N_EXTENSION_SERVER_NAME; - - s2n_extension_type_id test_extension_type_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_extension_type, &test_extension_type_id)); - - uint8_t data[] = "data"; - s2n_parsed_extension *parsed_extension = &conn->client_hello.extensions.parsed_extensions[test_extension_type_id]; - parsed_extension->extension_type = test_extension_type; - parsed_extension->extension.data = data; - parsed_extension->extension.size = sizeof(data); - - /* Succeeds with correct extension type */ - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, - test_extension_type, data, sizeof(data)), - sizeof(data)); - - /* Fails with wrong extension type */ - parsed_extension->extension_type = test_extension_type + 1; - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, - test_extension_type, data, sizeof(data)), - 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test s2n_client_hello_has_extension */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - uint8_t data[] = { - /* arbitrary extension with 2 data */ - 0xFF, 0x00, /* extension type */ - 0x00, 0x02, /* extension payload length */ - 0xAB, 0xCD, /* extension payload */ - /* Encrypt then mac extension without data */ - 0x00, 0x16, - 0x00, 0x00 - }; - - struct s2n_blob *raw_extension = &conn->client_hello.extensions.raw; - raw_extension->data = data; - raw_extension->size = sizeof(data); - - /* Succeeds on an unsupported extension with no payload */ - bool exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0x0016, &exists)); - EXPECT_TRUE(exists); - - /* Succeeds on an unsupported extension with payload */ - exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFF00, &exists)); - EXPECT_TRUE(exists); - - /* Succeeds with an invalid extension */ - exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFFFF, &exists)); - EXPECT_FALSE(exists); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test s2n_client_hello_has_extension with a zero-length extension */ - for (int send_sct = 0; send_sct <= 1; send_sct++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - /* The SCT extension is zero-length. */ - if (send_sct) { - EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST)); - } - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, - s2n_stuffer_data_available(&client->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); - EXPECT_NOT_NULL(client_hello); - - s2n_parsed_extension *sct_extension = NULL; - int ret = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &client_hello->extensions, - &sct_extension); - - if (send_sct) { - /* Ensure that the extension was received. */ - EXPECT_SUCCESS(ret); - POSIX_ENSURE_REF(sct_extension); - - /* Ensure that the extension is zero-length. */ - EXPECT_EQUAL(sct_extension->extension.size, 0); - } else { - /* The extension shouldn't have been received because it wasn't requested. */ - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); - } - } - - /* Test s2n_client_hello_get_raw_extension */ - { - uint8_t data[] = { - /* arbitrary extension with 2 data */ - 0xFF, 0x00, /* extension type */ - 0x00, 0x02, /* extension payload length */ - 0xAB, 0xCD, /* extension payload */ - /* NPN extension without data */ - 0x33, 0x74, - 0x00, 0x00 - }; - struct s2n_blob raw_extension = { - .data = data, - .size = sizeof(data), - }; - - struct s2n_blob extension = { 0 }; - /* Succeeds with extension exists without payload */ - EXPECT_OK(s2n_client_hello_get_raw_extension(0x3374, &raw_extension, &extension)); - EXPECT_EQUAL(extension.size, 0); - EXPECT_NOT_NULL(extension.data); - - /* Succeeds with extension exists with payload */ - extension = (struct s2n_blob){ 0 }; - EXPECT_OK(s2n_client_hello_get_raw_extension(0xFF00, &raw_extension, &extension)); - EXPECT_EQUAL(extension.size, 2); - EXPECT_NOT_NULL(extension.data); - EXPECT_BYTEARRAY_EQUAL(extension.data, &data[4], 2); - - /* Failed with extension not exist */ - extension = (struct s2n_blob){ 0 }; - EXPECT_OK(s2n_client_hello_get_raw_extension(0xFFFF, &raw_extension, &extension)); - EXPECT_EQUAL(extension.size, 0); - EXPECT_NULL(extension.data); - }; - - /* Test setting cert chain on recv */ - { - s2n_enable_tls13_in_test(); - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - - /* TLS13 fails to parse client hello when no certs set */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->client_protocol_version = conn->server_protocol_version; - conn->actual_protocol_version = conn->client_protocol_version; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(conn), S2N_ERR_NO_VALID_SIGNATURE_SCHEME); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - - /* TLS13 successfully sets certs */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->client_protocol_version = conn->server_protocol_version; - conn->actual_protocol_version = conn->client_protocol_version; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); - EXPECT_SUCCESS(s2n_client_hello_recv(conn)); - - EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - s2n_disable_tls13_in_test(); - }; - - /* Test getting supported versions from the client hello */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - /* TLS13 has supported versions in the client hello */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - uint8_t supported_versions[256] = { 0 }; - uint8_t size_of_version_list = 0; - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, - &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); - size_of_version_list = supported_versions[0]; - /* No supported versions before the handshake is received */ - EXPECT_EQUAL(0, size_of_version_list); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, - S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); - size_of_version_list = supported_versions[0]; - EXPECT_TRUE(size_of_version_list > 0); - bool found_tls13 = false; - const uint8_t tls13_bytes[] = { 0x03, 0x04 }; - const size_t supported_version_size = sizeof(tls13_bytes); - for (uint16_t offset = 1; offset < size_of_version_list; offset += supported_version_size) { - if (memcmp(tls13_bytes, &supported_versions[offset], supported_version_size) == 0) { - found_tls13 = true; - } - } - EXPECT_TRUE(found_tls13); - }; - s2n_disable_tls13_in_test(); - }; - - /* Test generating session id */ - { - const uint8_t test_session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 7 }; - - /* Use session id if already generated */ - for (uint8_t i = S2N_TLS10; i <= S2N_TLS13; i++) { - if (i >= S2N_TLS13) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - } - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - conn->actual_protocol_version = i; - - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - EXPECT_MEMCPY_SUCCESS(conn->session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - - uint8_t *session_id = NULL; - EXPECT_NOT_NULL(session_id = s2n_stuffer_raw_read(hello_stuffer, S2N_TLS_SESSION_ID_MAX_LEN)); - EXPECT_BYTEARRAY_EQUAL(session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* With TLS1.3 */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Generate a session id by default */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Do NOT generate a session id if middlebox compatibility mode is disabled. - * For now, middlebox compatibility mode is only disabled by QUIC. - */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_enable_quic(config)); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Generate a session id if trying to resume a handshake.io; - conn->resume_protocol_version = S2N_TLS12; - EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); - EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - }; - - /* Fail if we need to generate a session id to resume a resume_protocol_version = S2N_TLS12; - EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); - EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); - conn->quic_enabled = true; - - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), - S2N_ERR_UNSUPPORTED_WITH_QUIC); - }; - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* With TLS1.2 */ - { - /* Do NOT generate a session id by default */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Generate a session id if using tickets */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); - - uint8_t session_id_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); - EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - }; - }; - - /* Test cipher suites list */ - { - /* When TLS 1.3 NOT supported */ - { - /* TLS 1.3 cipher suites NOT written by client by default */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); - - uint16_t list_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); - EXPECT_NOT_EQUAL(list_length, 0); - - uint8_t first_cipher_byte = 0; - for (int i = 0; i < list_length; i++) { - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); - EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS 1.3 cipher suites NOT written by client even if included in security policy */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); - - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); - - uint16_t list_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); - EXPECT_NOT_EQUAL(list_length, 0); - - uint8_t first_cipher_byte = 0; - for (int i = 0; i < list_length; i++) { - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); - EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* When TLS 1.3 supported */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - s2n_config_set_session_tickets_onoff(config, 0); - - /* TLS 1.3 cipher suites written by client */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_stuffer *hello_stuffer = &conn->handshake.io; - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - - EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS13_LENGTH_TO_CIPHER_LIST)); - - uint16_t list_length = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); - EXPECT_NOT_EQUAL(list_length, 0); - - uint8_t first_cipher_byte = 0; - int tls13_ciphers_found = 0; - for (int i = 0; i < list_length; i++) { - EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); - if (first_cipher_byte == 0x13) { - tls13_ciphers_found++; - } - EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); - } - EXPECT_NOT_EQUAL(tls13_ciphers_found, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* TLS_EMPTY_RENEGOTIATION_INFO_SCSV included if TLS1.2 ciphers included - * - *= https://www.rfc-editor.org/rfc/rfc5746#3.4 - *= type=test - *# o The client MUST include either an empty "renegotiation_info" - *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling - *# cipher suite value in the ClientHello. - */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - const uint8_t empty_renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; - - struct { - const char *security_policy; - bool expect_renegotiation_info; - } test_cases[] = { - { .security_policy = "test_all_tls13", .expect_renegotiation_info = false }, - { .security_policy = "default_tls13", .expect_renegotiation_info = true }, - /* 20240501 can only negotiate up to tls12 */ - { .security_policy = "20240501", .expect_renegotiation_info = true }, - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_parse_client_hello(conn)); - - struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; - EXPECT_TRUE(cipher_suites->size > 0); - - uint8_t *iana = cipher_suites->data; - bool found_renegotiation_info = false; - for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { - if (memcmp(iana + j, empty_renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN) == 0) { - found_renegotiation_info = true; - } - } - - EXPECT_EQUAL(found_renegotiation_info, test_cases[i].expect_renegotiation_info); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - } - - /* TLS1.2 cipher suites not written if QUIC enabled */ - { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - bool quic_enabled[] = { false, s2n_is_tls13_fully_supported() }; - - /* TLS 1.2 cipher suites only written if QUIC not enabled */ - for (size_t i = 0; i < s2n_array_len(quic_enabled); i++) { - config->quic_enabled = quic_enabled[i]; - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_SUCCESS(s2n_parse_client_hello(conn)); - - struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; - EXPECT_TRUE(cipher_suites->size > 0); - - bool tls12_cipher_found = false; - uint8_t *iana = cipher_suites->data; - for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { - /* All TLS1.3 cipher suites have IANAs starting with 0x13 */ - if (iana[j] != 0x13) { - tls12_cipher_found = true; - } - } - - /* TLS1.2 and QUIC are mutually exclusive */ - EXPECT_TRUE(tls12_cipher_found != quic_enabled[i]); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Error if no cipher suites written. */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - struct s2n_cipher_suite cipher_suite = s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - struct s2n_cipher_suite *cipher_suites = &cipher_suite; - struct s2n_cipher_preferences cipher_preferences = { - .suites = &cipher_suites, - .count = 1, - }; - struct s2n_security_policy policy = *conn->config->security_policy; - policy.cipher_preferences = &cipher_preferences; - conn->security_policy_override = &policy; - - /* Fails with no cipher suites available / written */ - cipher_suite.available = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), - S2N_ERR_INVALID_CIPHER_PREFERENCES); - - /* Succeeds with one cipher suite available / written */ - /* cppcheck-suppress redundantAssignment */ - cipher_suite.available = true; - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - }; - }; - - /* Test that negotiating TLS1.2 with QUIC-enabled server fails */ - if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_enable_quic(config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - - /* Succeeds when negotiating TLS1.3 */ - if (s2n_is_tls13_fully_supported()) { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, - &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - } - - /* Fails when negotiating TLS1.2 */ - { - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all_tls12")); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, - &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - - /* Test that cipher suites enforce proper highest supported versions. - * Eg. server configs TLS 1.2 only ciphers should never negotiate TLS 1.3 - */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - - { - /* TLS 1.3 client cipher preference uses TLS13 version */ - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - { - /* TLS 1.2 client cipher preference uses TLS12 version */ - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20240501")); - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(conn)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - { - /* TLS 1.3 client cipher preference uses TLS13 version */ - struct s2n_connection *client_conn = NULL, *server_conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - - POSIX_GUARD(s2n_connection_get_security_policy(client_conn, &security_policy)); - EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); - - /* Server configured with TLS 1.2 negotiates TLS12 version */ - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - struct s2n_config *server_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); - - POSIX_GUARD(s2n_connection_get_security_policy(server_conn, &security_policy)); - EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); - - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); - - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* SSlv2 client hello */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - int sslv2_client_hello_len = sizeof(sslv2_client_hello); - - uint8_t sslv2_client_hello_header[] = { - SSLv2_CLIENT_HELLO_HEADER, - }; - - int sslv2_client_hello_header_len = sizeof(sslv2_client_hello_header); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* The security policy does not need to support SSLv2. - * - * s2n-tls does NOT support SSLv2. However, it does accept ClientHellos in the SSLv2 - * format but advertising higher protocol versions. Clients use this strategy to - * communicate with servers in a backwards-compatible way. - * - * Our test SSLv2 ClientHello advertises TLS1.2. - * So the security policy only needs to support TLS1.2. - * (and at least one of the ciphers in the hard coded sslv2 client hello) - */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); - - /* Send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello_header, sslv2_client_hello_header_len), sslv2_client_hello_header_len); - EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello, sslv2_client_hello_len), sslv2_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_TRUE(IS_NEGOTIATED(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - - /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ - EXPECT_EQUAL(client_hello, &server_conn->client_hello); - - uint8_t *collected_client_hello = client_hello->raw_message.data; - uint16_t collected_client_hello_len = client_hello->raw_message.size; - - /* Verify correctly identified as SSLv2 */ - EXPECT_TRUE(client_hello->sslv2); - - /* Verify collected client hello message length */ - EXPECT_EQUAL(collected_client_hello_len, sslv2_client_hello_len); - - /* Verify the collected client hello matches what was sent */ - EXPECT_BYTEARRAY_EQUAL(collected_client_hello, sslv2_client_hello, sslv2_client_hello_len); - - /* Verify s2n_client_hello_get_raw_message_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sslv2_client_hello_len); - - uint8_t expected_cs[] = { - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - }; - - /* Verify collected cipher_suites size correct */ - EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); - - /* Verify collected cipher_suites correct */ - EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); - - /* Verify s2n_client_hello_get_cipher_suites_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); - - /* Verify collected extensions size correct */ - EXPECT_EQUAL(client_hello->extensions.raw.size, 0); - - /* Verify s2n_client_hello_get_extensions_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), 0); - - /* Verify s2n_client_hello_get_session_id_length correct */ - uint32_t ch_session_id_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); - EXPECT_EQUAL(ch_session_id_length, 0); - - /* Free all handshake data */ - EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); - - /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - - /* Wipe connection */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ - EXPECT_EQUAL(client_hello->cipher_suites.size, 0); - EXPECT_NULL(client_hello->cipher_suites.data); - EXPECT_EQUAL(client_hello->extensions.raw.size, 0); - EXPECT_NULL(client_hello->extensions.raw.data); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Minimal TLS 1.2 client hello. */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - uint8_t *sent_client_hello = NULL; - uint8_t *expected_client_hello = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x08, - /* Server names len */ - 0x00, - 0x06, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - }; - - uint8_t server_name_extension[] = { - /* Server names len */ - 0x00, - 0x06, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - }; - int server_name_extension_len = sizeof(server_name_extension); - - size_t client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_prefix[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int client_hello_prefix_len = sizeof(client_hello_prefix); - int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (sent_client_hello_len >> 16) & 0xff, - (sent_client_hello_len >> 8) & 0xff, - (sent_client_hello_len & 0xff), - }; - int message_len = sizeof(message_header) + sent_client_hello_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); - EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); - EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Verify s2n_connection_get_client_hello returns null if client hello not yet processed */ - EXPECT_NULL(s2n_connection_get_client_hello(server_conn)); - - uint8_t *ext_data = NULL; - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); - /* Verify we don't get extension and it's length when client hello is not yet processed */ - EXPECT_FAILURE(s2n_client_hello_get_extension_length(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME)); - EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len)); - free(ext_data); - ext_data = NULL; - - /* Send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - - /* Verify correctly identified as NOT sslv2 */ - EXPECT_FALSE(client_hello->sslv2); - - /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ - EXPECT_EQUAL(client_hello, &server_conn->client_hello); - - uint8_t *collected_client_hello = client_hello->raw_message.data; - uint16_t collected_client_hello_len = client_hello->raw_message.size; - - /* Verify collected client hello message length */ - EXPECT_EQUAL(collected_client_hello_len, sent_client_hello_len); - - /* Verify the collected client hello has client random zero-ed out */ - uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; - uint8_t expected_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_BYTEARRAY_EQUAL(collected_client_hello + client_random_offset, expected_client_random, S2N_TLS_RANDOM_DATA_LEN); - - /* Verify the collected client hello matches what was sent except for the zero-ed client random */ - EXPECT_NOT_NULL(expected_client_hello = malloc(sent_client_hello_len)); - EXPECT_MEMCPY_SUCCESS(expected_client_hello, sent_client_hello, sent_client_hello_len); - POSIX_CHECKED_MEMSET(expected_client_hello + client_random_offset, 0, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); - - /* Verify s2n_client_hello_get_raw_message_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sent_client_hello_len); - - uint8_t *raw_ch_out = NULL; - - /* Verify s2n_client_hello_get_raw_message retrieves the full message when its len <= max_len */ - EXPECT_TRUE(collected_client_hello_len < S2N_LARGE_RECORD_LENGTH); - EXPECT_NOT_NULL(raw_ch_out = malloc(S2N_LARGE_RECORD_LENGTH)); - EXPECT_EQUAL(sent_client_hello_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, S2N_LARGE_RECORD_LENGTH)); - EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, sent_client_hello_len); - free(raw_ch_out); - raw_ch_out = NULL; - - /* Verify s2n_client_hello_get_raw_message retrieves truncated message when its len > max_len */ - EXPECT_TRUE(collected_client_hello_len > 0); - uint32_t max_len = collected_client_hello_len - 1; - EXPECT_NOT_NULL(raw_ch_out = malloc(max_len)); - EXPECT_EQUAL(max_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, max_len)); - EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, max_len); - free(raw_ch_out); - raw_ch_out = NULL; - - uint8_t expected_cs[] = { 0x00, 0x3C }; - - /* Verify collected cipher_suites size correct */ - EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); - - /* Verify collected cipher_suites correct */ - EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); - - /* Verify s2n_client_hello_get_cipher_suites_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); - - /* Verify s2n_client_hello_get_cipher_suites correct */ - uint8_t *cs_out = NULL; - - /* Verify s2n_client_hello_get_cipher_suites retrieves the full cipher_suites when its len <= max_len */ - EXPECT_TRUE(client_hello->cipher_suites.size < S2N_LARGE_RECORD_LENGTH); - EXPECT_NOT_NULL(cs_out = malloc(S2N_LARGE_RECORD_LENGTH)); - EXPECT_EQUAL(sizeof(expected_cs), s2n_client_hello_get_cipher_suites(client_hello, cs_out, S2N_LARGE_RECORD_LENGTH)); - EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, sizeof(expected_cs)); - free(cs_out); - cs_out = NULL; - - /* Verify s2n_client_hello_get_cipher_suites retrieves truncated message when cipher_suites len > max_len */ - max_len = sizeof(expected_cs) - 1; - EXPECT_TRUE(max_len > 0); - - EXPECT_NOT_NULL(cs_out = malloc(max_len)); - EXPECT_EQUAL(max_len, s2n_client_hello_get_cipher_suites(client_hello, cs_out, max_len)); - EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, max_len); - free(cs_out); - cs_out = NULL; - - /* Verify collected extensions size correct */ - EXPECT_EQUAL(client_hello->extensions.raw.size, client_extensions_len); - - /* Verify collected extensions correct */ - EXPECT_BYTEARRAY_EQUAL(client_hello->extensions.raw.data, client_extensions, client_extensions_len); - - /* Verify s2n_client_hello_get_extensions_length correct */ - EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), client_extensions_len); - - /* Verify s2n_client_hello_get_extensions correct */ - uint8_t *extensions_out = NULL; - - /* Verify s2n_client_hello_get_extensions retrieves the full cipher_suites when its len <= max_len */ - EXPECT_TRUE(client_hello->extensions.raw.size < S2N_LARGE_RECORD_LENGTH); - EXPECT_NOT_NULL(extensions_out = malloc(S2N_LARGE_RECORD_LENGTH)); - EXPECT_EQUAL(client_extensions_len, s2n_client_hello_get_extensions(client_hello, extensions_out, S2N_LARGE_RECORD_LENGTH)); - EXPECT_BYTEARRAY_EQUAL(extensions_out, client_extensions, client_extensions_len); - free(extensions_out); - extensions_out = NULL; - - /* Verify s2n_client_hello_get_extensions retrieves truncated message when cipher_suites len > max_len */ - max_len = client_extensions_len - 1; - EXPECT_TRUE(max_len > 0); - - EXPECT_NOT_NULL(extensions_out = malloc(max_len)); - EXPECT_EQUAL(max_len, s2n_client_hello_get_extensions(client_hello, extensions_out, max_len)); - EXPECT_BYTEARRAY_EQUAL(extensions_out, client_hello->extensions.raw.data, max_len); - free(extensions_out); - extensions_out = NULL; - - /* Verify server name extension and it's length are returned correctly */ - EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_SERVER_NAME), server_name_extension_len); - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len), server_name_extension_len); - EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len); - free(ext_data); - ext_data = NULL; - - /* Verify server name extension is truncated if extension_size > max_len */ - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len - 1)); - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len - 1), server_name_extension_len - 1); - EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len - 1); - free(ext_data); - ext_data = NULL; - - /* Verify get extension and it's length calls for a non-existing extension type */ - EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY), 0); - EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); - EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, ext_data, server_name_extension_len), 0); - EXPECT_EQUAL(s2n_errno, S2N_ERR_EXTENSION_NOT_RECEIVED); - free(ext_data); - ext_data = NULL; - - /* Verify server name extension exists */ - bool extension_exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_SERVER_NAME, &extension_exists)); - EXPECT_TRUE(extension_exists); - - /* Verify expected result for non-existing extension */ - extension_exists = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &extension_exists)); - EXPECT_FALSE(extension_exists); - - /* Verify s2n_client_hello_get_session_id is what we received in ClientHello */ - uint8_t expected_ch_session_id[] = { ZERO_TO_THIRTY_ONE }; - uint8_t ch_session_id[sizeof(expected_ch_session_id)]; - uint32_t ch_session_id_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); - EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); - EXPECT_SUCCESS(s2n_client_hello_get_session_id(client_hello, ch_session_id, &ch_session_id_length, sizeof(ch_session_id))); - EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); - EXPECT_BYTEARRAY_EQUAL(ch_session_id, expected_ch_session_id, sizeof(expected_ch_session_id)); - - /* Verify s2n_connection_get_session_id is different from the one we received in ClientHello, as we generated a new one in ServerHello */ - uint8_t conn_session_id[sizeof(expected_ch_session_id)]; - EXPECT_EQUAL(s2n_connection_get_session_id_length(server_conn), sizeof(conn_session_id)); - EXPECT_SUCCESS(s2n_connection_get_session_id(server_conn, conn_session_id, sizeof(conn_session_id))); - EXPECT_BYTEARRAY_NOT_EQUAL(conn_session_id, ch_session_id, sizeof(expected_ch_session_id)); - - /* Free all handshake data */ - EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); - - /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - - /* Wipe connection */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ - EXPECT_NULL(client_hello->raw_message.data); - EXPECT_EQUAL(client_hello->raw_message.size, 0); - - /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ - EXPECT_EQUAL(client_hello->cipher_suites.size, 0); - EXPECT_NULL(client_hello->cipher_suites.data); - EXPECT_EQUAL(client_hello->extensions.raw.size, 0); - EXPECT_NULL(client_hello->extensions.raw.data); - - /* Verify the connection is successfully reused after connection_wipe */ - - /* Re-configure connection */ - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - /* Recreate config */ - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Re-send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - - /* Verify the collected client hello on the reused connection matches the expected client hello */ - client_hello = s2n_connection_get_client_hello(server_conn); - collected_client_hello = client_hello->raw_message.data; - EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - free(expected_client_hello); - free(sent_client_hello); - }; - - /* Client hello api with NULL inputs */ - { - uint32_t len = 128; - uint8_t *out = NULL; - EXPECT_NOT_NULL(out = malloc(len)); - - EXPECT_FAILURE(s2n_client_hello_get_raw_message_length(NULL)); - EXPECT_FAILURE(s2n_client_hello_get_raw_message(NULL, out, len)); - EXPECT_FAILURE(s2n_client_hello_get_cipher_suites_length(NULL)); - EXPECT_FAILURE(s2n_client_hello_get_cipher_suites(NULL, out, len)); - EXPECT_FAILURE(s2n_client_hello_get_extensions_length(NULL)); - EXPECT_FAILURE(s2n_client_hello_get_extensions(NULL, out, len)); - EXPECT_FAILURE(s2n_client_hello_get_extension_length(NULL, S2N_EXTENSION_SERVER_NAME)); - EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(NULL, S2N_EXTENSION_SERVER_NAME, out, len)); - free(out); - out = NULL; - - bool exists = false; - EXPECT_FAILURE(s2n_client_hello_has_extension(NULL, S2N_EXTENSION_SERVER_NAME, &exists)); - EXPECT_FALSE(exists); - }; - - /* test_weird_client_hello_version() */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - uint8_t *sent_client_hello = NULL; - - uint8_t client_extensions[] = { - /* Extension type TLS_EXTENSION_SERVER_NAME */ - 0x00, - 0x00, - /* Extension size */ - 0x00, - 0x08, - /* Server names len */ - 0x00, - 0x06, - /* First server name type - host name */ - 0x00, - /* First server name len */ - 0x00, - 0x03, - /* First server name, matches sent_server_name */ - 's', - 'v', - 'r', - }; - - int client_extensions_len = sizeof(client_extensions); - uint8_t client_hello_prefix[] = { - /* Protocol version TLS ??? */ - 0xFF, - 0xFF, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, - 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (client_extensions_len >> 8) & 0xff, - (client_extensions_len & 0xff), - }; - int client_hello_prefix_len = sizeof(client_hello_prefix); - int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (sent_client_hello_len >> 16) & 0xff, - (sent_client_hello_len >> 8) & 0xff, - (sent_client_hello_len & 0xff), - }; - int message_len = sizeof(message_header) + sent_client_hello_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); - EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); - EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello message */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); - - /* Verify that the sent client hello message is accepted */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); - /* Client sent an invalid legacy protocol version. We should still have negotiate the maximum value(TLS1.2) */ - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - s2n_connection_free(server_conn); - s2n_config_free(server_config); - free(sent_client_hello); - }; - - { - struct s2n_cipher_suite *client_cipher_suites[] = { - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - }; - - struct s2n_cipher_preferences client_cipher_preferences = { - .count = s2n_array_len(client_cipher_suites), - .suites = client_cipher_suites, - }; - - const struct s2n_signature_scheme *const client_sig_scheme_pref_list[] = { - &s2n_rsa_pkcs1_sha1, - - /* Intentionally do not send and ECDSA SignatureScheme in the Client Hello. This is malformed since the - * Client's only Ciphersuite uses ECDSA, meaning that technically the Server could reject it, but there are - * some clients that send this form of malformed Client Hello's in the wild. So ensure we are compatible - * with them by assuming that the Client does support ECDSA, even though it's missing from the ClientHello. - */ - - /* &s2n_ecdsa_sha1, */ - }; - - struct s2n_signature_preferences client_signature_preferences = { - .count = s2n_array_len(client_sig_scheme_pref_list), - .signature_schemes = client_sig_scheme_pref_list, - }; - - struct s2n_security_policy client_security_policy = { - .minimum_protocol_version = S2N_TLS10, - .cipher_preferences = &client_cipher_preferences, - .kem_preferences = &kem_preferences_null, - .signature_preferences = &client_signature_preferences, - .ecc_preferences = &s2n_ecc_preferences_20140601, - }; - - EXPECT_TRUE(client_cipher_suites[0]->available); - - struct s2n_cert_chain_and_key *ecdsa_cert_chain = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Create Configs */ - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert_chain)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - server_config->security_policy = &security_policy_20190214; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - client_config->security_policy = &client_security_policy; - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Create connection */ - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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)); - - /* We have to update the client's security policy after it sends the ClientHello. - * The client sends all signature algorithms in its security policy, and - * won't accept any signature algorithm it receives that's not in its security policy. - * So we need to change the security policy between sending and receiving. - */ - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); - client_config->security_policy = &security_policy_20190214; - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(server_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); - EXPECT_EQUAL(client_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); - EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); - EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); - - /* Free the data */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* s2n_client_hello_recv should fail when reading an SSLv2 client hello during a hello retry handshake */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - /* Handshake is hello retry and TLS1.3 was negotiated */ - server_conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_handshake_type_set_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); - - /* Second client hello has version SSLv2 */ - server_conn->client_hello.sslv2 = true; - - /* Mock having some data in the client hello */ - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&server_conn->handshake.io, 100)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_parse_client_hello(server_conn), S2N_ERR_BAD_MESSAGE); - }; - - /* Test s2n_client_hello_parse_message - * - * Comparing ClientHellos produced by connection IO parsing vs - * produced by s2n_client_hello_parse_message is difficult, but we can - * use JA3 fingerprints as an approximation. See s2n_fingerprint_ja3_test.c - */ - { - /* 20240501 can only negotiate tls12 */ - const char *security_policies[] = { "20240501", "default_tls13", "test_all" }; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - /* Test: Can parse ClientHellos sent by the s2n client */ - for (size_t i = 0; i < s2n_array_len(security_policies); i++) { - const char *security_policy = security_policies[i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policy)); - - EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); - - uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); - EXPECT_NOT_EQUAL(raw_size, 0); - uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); - EXPECT_NOT_NULL(raw); - - DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); - EXPECT_NOT_NULL(client_hello = s2n_client_hello_parse_message(raw, raw_size)); - EXPECT_TRUE(client_hello->alloced); - }; - - /* Test: Rejects invalid ClientHellos - * - * This test is important to verify that no memory is leaked when parsing fails. - */ - { - struct s2n_client_hello *client_hello = NULL; - - uint8_t wrong_message_type[50] = { 0x02, 0x00, 0x00, 1 }; - client_hello = s2n_client_hello_parse_message(wrong_message_type, sizeof(wrong_message_type)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - - uint8_t wrong_message_size[50] = { 0x01, 0x00, 0x00, UINT8_MAX }; - client_hello = s2n_client_hello_parse_message(wrong_message_size, sizeof(wrong_message_size)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - - uint8_t too_short[5] = { 0x01, 0x00, 0x00, 1 }; - client_hello = s2n_client_hello_parse_message(too_short, sizeof(too_short)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_STUFFER_OUT_OF_DATA); - - uint8_t all_zeroes[50] = { 0x01, 0x00, 0x00, 46 }; - client_hello = s2n_client_hello_parse_message(all_zeroes, sizeof(all_zeroes)); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - }; - - /* Test: Rejects SSLv2 */ - { - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_HEADER, - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - /* Try parsing variations on the complete record vs just the message. - * The sslv2 record header is technically the first two bytes, - * but s2n-tls usually starts parsing after the first five bytes. - */ - for (size_t i = 0; i <= S2N_TLS_RECORD_HEADER_LENGTH; i++) { - struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( - sslv2_client_hello + i, sizeof(sslv2_client_hello) - i); - EXPECT_NULL(client_hello); - EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); - } - - /* Sanity check: s2n accepts the test sslv2 message via the connection */ - { - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); - - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->header_in, - sslv2_client_hello, S2N_TLS_RECORD_HEADER_LENGTH)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->in, - sslv2_client_hello + S2N_TLS_RECORD_HEADER_LENGTH, - sizeof(sslv2_client_hello) - S2N_TLS_RECORD_HEADER_LENGTH)); - - EXPECT_FALSE(server->client_hello.sslv2); - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); - EXPECT_TRUE(server->client_hello.sslv2); - EXPECT_FALSE(server->client_hello.alloced); - } - }; - }; - - /* Test s2n_client_hello_free */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(NULL), S2N_ERR_NULL); - - /* Test: Accepts but ignores NULL / already freed */ - { - struct s2n_client_hello *client_hello = NULL; - for (size_t i = 0; i < 3; i++) { - EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); - EXPECT_NULL(client_hello); - } - }; - - /* Test: Errors on client hello associated with a connection */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, - s2n_stuffer_data_available(&client->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); - EXPECT_NOT_NULL(client_hello); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(&client_hello), S2N_ERR_INVALID_ARGUMENT); - EXPECT_NOT_NULL(s2n_connection_get_client_hello(server)); - EXPECT_NOT_EQUAL(server->client_hello.raw_message.size, 0); - }; - - /* Test: Frees client hello from raw message */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client)); - - EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_client_hello_send(client)); - EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); - - uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); - EXPECT_NOT_EQUAL(raw_size, 0); - uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); - EXPECT_NOT_NULL(raw); - - struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( - raw, raw_size); - EXPECT_NOT_NULL(client_hello); - - for (size_t i = 0; i < 3; i++) { - EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); - EXPECT_NULL(client_hello); - } - }; - }; - - /* s2n_client_hello_get_compression_methods */ - { - /* Safety */ - { - uint32_t length = 0; - struct s2n_client_hello client_hello = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(NULL, &length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(&client_hello, NULL), S2N_ERR_NULL); - - uint8_t list = 0; - uint32_t list_length = 0; - uint32_t out_length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(NULL, &list, list_length, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, NULL, list_length, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, NULL), S2N_ERR_NULL); - - /* User did not provide a large enough buffer to write the compression methods */ - uint8_t data[] = { 1, 2, 3, 4, 5 }; - EXPECT_SUCCESS(s2n_blob_init(&client_hello.compression_methods, data, sizeof(data))); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, &out_length), - S2N_ERR_INSUFFICIENT_MEM_SIZE); - }; - - /* Retrieves the compression methods list */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); - EXPECT_EQUAL(length, 1); - uint8_t list = 0; - uint32_t out_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, &list, sizeof(list), &out_length)); - EXPECT_EQUAL(out_length, 1); - }; - - /* Retrieves compression methods list longer than one byte */ - { - /* Compression methods were deprecated in TLS13 and s2n has never - * supported them. However, it is conceivable that a client could send - * us a list that contains more than the "null" byte. Therefore, we construct - * a fake Client Hello that contains a longer list of compression methods - * for testing. - */ - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, 0x02, - /* Cipher suite - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */ - 0xC0, 0x2F, - COMPRESSION_METHODS_LEN, - COMPRESSION_METHODS, - /* Extensions len */ - 0x00, 0x00 - }; - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_conn->handshake.io, client_hello_message, sizeof(client_hello_message))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); - EXPECT_EQUAL(length, COMPRESSION_METHODS_LEN); - uint8_t list[5] = { 0 }; - uint32_t out_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, list, sizeof(list), &out_length)); - EXPECT_EQUAL(out_length, COMPRESSION_METHODS_LEN); - - uint8_t compression_data[] = { COMPRESSION_METHODS }; - EXPECT_BYTEARRAY_EQUAL(list, compression_data, out_length); - } - }; - - /* s2n_client_hello_get_legacy_protocol_version */ - { - /* Safety */ - { - uint8_t out = 0; - struct s2n_client_hello client_hello = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(NULL, &out), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(&client_hello, NULL), S2N_ERR_NULL); - }; - - /* Retrieves the Client Hello protocol version */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint8_t version = 0; - EXPECT_SUCCESS(s2n_client_hello_get_legacy_protocol_version(client_hello, &version)); - EXPECT_EQUAL(version, S2N_TLS12); - }; - }; - - /* s2n_client_hello_get_legacy_record_version */ - { - /* Safety */ - { - uint8_t out = 0; - struct s2n_client_hello client_hello = { 0 }; - - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(NULL, &out), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(&client_hello, NULL), S2N_ERR_NULL); - }; - - /* Retrieves record version */ - { - uint8_t out = 0; - struct s2n_client_hello client_hello = { 0 }; - client_hello.legacy_record_version = S2N_TLS12; - client_hello.record_version_recorded = 1; - EXPECT_SUCCESS(s2n_client_hello_get_legacy_record_version(&client_hello, &out)); - EXPECT_EQUAL(out, S2N_TLS12); - }; - }; - - /* s2n_client_hello_get_server_name() */ - { - /* Safety */ - { - struct s2n_client_hello ch = { 0 }; - uint16_t length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(NULL, &length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(&ch, NULL), S2N_ERR_NULL); - - uint8_t buffer = 0; - uint16_t out_length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(NULL, &buffer, 0, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, NULL, 0, &out_length), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, &buffer, 0, NULL), S2N_ERR_NULL); - }; - - /* Retrieves the first entry in the server_name extension */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - const char *test_server_name = "test server name!"; - EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); - - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); - EXPECT_NOT_NULL(client_hello); - - uint16_t length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_server_name_length(client_hello, &length)); - EXPECT_EQUAL(strlen(test_server_name), length); - uint8_t buffer[20] = { 0 }; - uint16_t out_length = 0; - EXPECT_SUCCESS(s2n_client_hello_get_server_name(client_hello, buffer, sizeof(buffer), &out_length)); - EXPECT_EQUAL(length, out_length); - - EXPECT_BYTEARRAY_EQUAL(buffer, test_server_name, out_length); - - /* Check error occurs if buffer is too small to hold server name */ - uint8_t small_buf[2] = { 0 }; - out_length = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(client_hello, small_buf, sizeof(small_buf), &out_length), S2N_ERR_SAFETY); - }; - }; - - /* Test: large Client Hellos */ - { - const uint16_t cipher_suites_max_length = (1 << 16) - 2; - const uint16_t num_of_cipher_suites_to_drop = 150; - - /** - * S2N-TLS automatically includes TLS_EMPTY_RENEGOTIATION_INFO_SCSV in TLS 1.2 ClientHello, - * so we subtract 1 from the maximum number of cipher suites to reserve space for it. - */ - const uint16_t max_cipher_suite_count = (cipher_suites_max_length / S2N_TLS_CIPHER_SUITE_LEN) - 1; - - /* Drop 150 cipher suites from max, so that the total handshake message length won't exceed 64KB */ - const uint16_t reduced_cipher_suite_count = max_cipher_suite_count - num_of_cipher_suites_to_drop; - - uint16_t cipher_suites_counts[] = { reduced_cipher_suite_count, max_cipher_suite_count }; - - for (size_t i = 0; i < s2n_array_len(cipher_suites_counts); i++) { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* We need to generate a large Client Hello. - * We do this by manipulating the number of cipher suites, - * which is the easiest way to make Client Hello large. - */ - uint16_t cipher_suites_count = cipher_suites_counts[i]; - struct s2n_cipher_suite *test_cipher_suites[UINT16_MAX] = { 0 }; - for (size_t j = 0; j < cipher_suites_count; j++) { - test_cipher_suites[j] = &s2n_rsa_with_aes_128_gcm_sha256; - } - const struct s2n_cipher_preferences test_cipher_suites_preferences = { - .count = cipher_suites_count, - .suites = test_cipher_suites, - }; - const struct s2n_security_policy *default_policy = NULL; - /* 20240501 is a policy that can only negotiate tls12 */ - EXPECT_SUCCESS(s2n_find_security_policy_from_version("20240501", &default_policy)); - struct s2n_security_policy test_security_policy = *default_policy; - test_security_policy.cipher_preferences = &test_cipher_suites_preferences; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); - client->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - server->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - - /** - * Write Client Hello into io_pair.server_in. - * The server_in buffer contains the Client Hello message plus a 5-byte record header. - */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client, &blocked), S2N_ERR_IO_BLOCKED); - - /* Add one extra cipher suite length to account for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ - uint16_t cipher_suites_length = (cipher_suites_count + 1) * S2N_TLS_CIPHER_SUITE_LEN; - - if (cipher_suites_length < cipher_suites_max_length) { - /** - * The Client Hello message size should be less than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, even with - * the five byte record header. - */ - EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - } else { - /** - * When using maximum cipher suites, the Client Hello message size exceeds - * S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH by more than five byte. - * - * Hence, if server_in's available data is greater than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, - * then the Client Hello itself exceeds the maximum allowed size, even after accounting for the record header. - */ - EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), S2N_ERR_BAD_MESSAGE); - } - } - } - - /* Test s2n_client_hello_get_random */ - { - /* Safety */ - { - uint8_t out[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - struct s2n_client_hello client_hello = { 0 }; - - EXPECT_FAILURE_WITH_ERRNO( - s2n_client_hello_get_random(NULL, out, sizeof(out)), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO( - s2n_client_hello_get_random(&client_hello, NULL, sizeof(out)), - S2N_ERR_NULL); - - /* Buffer size must be large enough to hold the full client random */ - uint8_t small_buffer[S2N_TLS_RANDOM_DATA_LEN - 1] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO( - s2n_client_hello_get_random(&client_hello, small_buffer, sizeof(small_buffer)), - S2N_ERR_INSUFFICIENT_MEM_SIZE); - }; - - /* Retrieves the client random and zeroes it from the raw message */ - { - /* Construct a minimal ClientHello with a custom random value */ - uint8_t custom_random[S2N_TLS_RANDOM_DATA_LEN] = { ZERO_TO_THIRTY_ONE }; - - /* clang-format off */ - uint8_t raw_client_hello[] = { - /* message type */ - TLS_CLIENT_HELLO, - /* message size */ - 0x00, 0x00, 43, - /* protocol version - TLS1.2 */ - 0x03, 0x03, - /* random - custom value */ - ZERO_TO_THIRTY_ONE, - /* session id */ - 0x00, - /* cipher suites */ - 0x00, 0x02, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - /* legacy compression methods */ - 0x01, 0x00, - /* extensions - empty */ - 0x00, 0x00, - }; - /* clang-format on */ - - DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); - client_hello = s2n_client_hello_parse_message(raw_client_hello, sizeof(raw_client_hello)); - EXPECT_NOT_NULL(client_hello); - - /* Exact-size buffer: retrieve the client random */ - uint8_t retrieved_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_client_hello_get_random( - client_hello, retrieved_random, S2N_TLS_RANDOM_DATA_LEN)); - EXPECT_BYTEARRAY_EQUAL(retrieved_random, custom_random, S2N_TLS_RANDOM_DATA_LEN); - - /* Buffer size > 32 bytes must also succeed and only write the first 32 bytes */ - { - uint8_t large_buffer[S2N_TLS_RANDOM_DATA_LEN + 10] = { 0 }; - EXPECT_SUCCESS(s2n_client_hello_get_random( - client_hello, large_buffer, sizeof(large_buffer))); - - /* First 32 bytes match the client random */ - EXPECT_BYTEARRAY_EQUAL(large_buffer, custom_random, S2N_TLS_RANDOM_DATA_LEN); - - /* Tail remains untouched */ - uint8_t zero_tail[10] = { 0 }; - EXPECT_BYTEARRAY_EQUAL( - large_buffer + S2N_TLS_RANDOM_DATA_LEN, - zero_tail, - sizeof(zero_tail)); - } - - /* The raw message should have the random zeroed out after retrieval */ - uint8_t *raw_message = client_hello->raw_message.data; - uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; - uint8_t zeroed_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_BYTEARRAY_EQUAL( - raw_message + client_random_offset, - zeroed_random, - S2N_TLS_RANDOM_DATA_LEN); - }; - }; - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_client_hello.h" + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +#define LENGTH_TO_SESSION_ID (S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN) +#define TLS12_LENGTH_TO_CIPHER_LIST (LENGTH_TO_SESSION_ID + 1) +#define TLS13_LENGTH_TO_CIPHER_LIST (TLS12_LENGTH_TO_CIPHER_LIST + S2N_TLS_SESSION_ID_MAX_LEN) + +#define COMPRESSION_METHODS 0x00, 0x01, 0x02, 0x03, 0x04 +#define COMPRESSION_METHODS_LEN 0x05 + +int s2n_parse_client_hello(struct s2n_connection *conn); +S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, + struct s2n_blob *raw_extensions, struct s2n_blob *extension); + +int main(int argc, char **argv) +{ + struct s2n_cert_chain_and_key *chain_and_key = NULL, *ecdsa_chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Test s2n_client_hello_get_extension_by_id */ + { + /* Test with invalid parsed extensions */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + s2n_tls_extension_type test_extension_type = S2N_EXTENSION_SERVER_NAME; + + s2n_extension_type_id test_extension_type_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_extension_type, &test_extension_type_id)); + + uint8_t data[] = "data"; + s2n_parsed_extension *parsed_extension = &conn->client_hello.extensions.parsed_extensions[test_extension_type_id]; + parsed_extension->extension_type = test_extension_type; + parsed_extension->extension.data = data; + parsed_extension->extension.size = sizeof(data); + + /* Succeeds with correct extension type */ + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, + test_extension_type, data, sizeof(data)), + sizeof(data)); + + /* Fails with wrong extension type */ + parsed_extension->extension_type = test_extension_type + 1; + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, + test_extension_type, data, sizeof(data)), + 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_client_hello_has_extension */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + uint8_t data[] = { + /* arbitrary extension with 2 data */ + 0xFF, 0x00, /* extension type */ + 0x00, 0x02, /* extension payload length */ + 0xAB, 0xCD, /* extension payload */ + /* Encrypt then mac extension without data */ + 0x00, 0x16, + 0x00, 0x00 + }; + + struct s2n_blob *raw_extension = &conn->client_hello.extensions.raw; + raw_extension->data = data; + raw_extension->size = sizeof(data); + + /* Succeeds on an unsupported extension with no payload */ + bool exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0x0016, &exists)); + EXPECT_TRUE(exists); + + /* Succeeds on an unsupported extension with payload */ + exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFF00, &exists)); + EXPECT_TRUE(exists); + + /* Succeeds with an invalid extension */ + exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFFFF, &exists)); + EXPECT_FALSE(exists); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_client_hello_has_extension with a zero-length extension */ + for (int send_sct = 0; send_sct <= 1; send_sct++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* The SCT extension is zero-length. */ + if (send_sct) { + EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST)); + } + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + s2n_parsed_extension *sct_extension = NULL; + int ret = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &client_hello->extensions, + &sct_extension); + + if (send_sct) { + /* Ensure that the extension was received. */ + EXPECT_SUCCESS(ret); + POSIX_ENSURE_REF(sct_extension); + + /* Ensure that the extension is zero-length. */ + EXPECT_EQUAL(sct_extension->extension.size, 0); + } else { + /* The extension shouldn't have been received because it wasn't requested. */ + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); + } + } + + /* Test s2n_client_hello_get_raw_extension */ + { + uint8_t data[] = { + /* arbitrary extension with 2 data */ + 0xFF, 0x00, /* extension type */ + 0x00, 0x02, /* extension payload length */ + 0xAB, 0xCD, /* extension payload */ + /* NPN extension without data */ + 0x33, 0x74, + 0x00, 0x00 + }; + struct s2n_blob raw_extension = { + .data = data, + .size = sizeof(data), + }; + + struct s2n_blob extension = { 0 }; + /* Succeeds with extension exists without payload */ + EXPECT_OK(s2n_client_hello_get_raw_extension(0x3374, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 0); + EXPECT_NOT_NULL(extension.data); + + /* Succeeds with extension exists with payload */ + extension = (struct s2n_blob){ 0 }; + EXPECT_OK(s2n_client_hello_get_raw_extension(0xFF00, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 2); + EXPECT_NOT_NULL(extension.data); + EXPECT_BYTEARRAY_EQUAL(extension.data, &data[4], 2); + + /* Failed with extension not exist */ + extension = (struct s2n_blob){ 0 }; + EXPECT_OK(s2n_client_hello_get_raw_extension(0xFFFF, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 0); + EXPECT_NULL(extension.data); + }; + + /* Test setting cert chain on recv */ + { + s2n_enable_tls13_in_test(); + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* TLS13 fails to parse client hello when no certs set */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->client_protocol_version = conn->server_protocol_version; + conn->actual_protocol_version = conn->client_protocol_version; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(conn), S2N_ERR_NO_VALID_SIGNATURE_SCHEME); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + /* TLS13 successfully sets certs */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->client_protocol_version = conn->server_protocol_version; + conn->actual_protocol_version = conn->client_protocol_version; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); + EXPECT_SUCCESS(s2n_client_hello_recv(conn)); + + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + s2n_disable_tls13_in_test(); + }; + + /* Test getting supported versions from the client hello */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + /* TLS13 has supported versions in the client hello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + uint8_t supported_versions[256] = { 0 }; + uint8_t size_of_version_list = 0; + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); + size_of_version_list = supported_versions[0]; + /* No supported versions before the handshake is received */ + EXPECT_EQUAL(0, size_of_version_list); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); + size_of_version_list = supported_versions[0]; + EXPECT_TRUE(size_of_version_list > 0); + bool found_tls13 = false; + const uint8_t tls13_bytes[] = { 0x03, 0x04 }; + const size_t supported_version_size = sizeof(tls13_bytes); + for (uint16_t offset = 1; offset < size_of_version_list; offset += supported_version_size) { + if (memcmp(tls13_bytes, &supported_versions[offset], supported_version_size) == 0) { + found_tls13 = true; + } + } + EXPECT_TRUE(found_tls13); + }; + s2n_disable_tls13_in_test(); + }; + + /* Test generating session id */ + { + const uint8_t test_session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 7 }; + + /* Use session id if already generated */ + for (uint8_t i = S2N_TLS10; i <= S2N_TLS13; i++) { + if (i >= S2N_TLS13) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + conn->actual_protocol_version = i; + + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + EXPECT_MEMCPY_SUCCESS(conn->session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + uint8_t *session_id = NULL; + EXPECT_NOT_NULL(session_id = s2n_stuffer_raw_read(hello_stuffer, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_BYTEARRAY_EQUAL(session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* With TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Generate a session id by default */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Do NOT generate a session id if middlebox compatibility mode is disabled. + * For now, middlebox compatibility mode is only disabled by QUIC. + */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Generate a session id if trying to resume a handshake.io; + conn->resume_protocol_version = S2N_TLS12; + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + }; + + /* Fail if we need to generate a session id to resume a resume_protocol_version = S2N_TLS12; + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); + conn->quic_enabled = true; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), + S2N_ERR_UNSUPPORTED_WITH_QUIC); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* With TLS1.2 */ + { + /* Do NOT generate a session id by default */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Generate a session id if using tickets */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + }; + + /* Test cipher suites list */ + { + /* When TLS 1.3 NOT supported */ + { + /* TLS 1.3 cipher suites NOT written by client by default */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS 1.3 cipher suites NOT written by client even if included in security policy */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* When TLS 1.3 supported */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + s2n_config_set_session_tickets_onoff(config, 0); + + /* TLS 1.3 cipher suites written by client */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS13_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte = 0; + int tls13_ciphers_found = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + if (first_cipher_byte == 0x13) { + tls13_ciphers_found++; + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + EXPECT_NOT_EQUAL(tls13_ciphers_found, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* TLS_EMPTY_RENEGOTIATION_INFO_SCSV included if TLS1.2 ciphers included + * + *= https://www.rfc-editor.org/rfc/rfc5746#3.4 + *= type=test + *# o The client MUST include either an empty "renegotiation_info" + *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling + *# cipher suite value in the ClientHello. + */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + const uint8_t empty_renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; + + struct { + const char *security_policy; + bool expect_renegotiation_info; + } test_cases[] = { + { .security_policy = "test_all_tls13", .expect_renegotiation_info = false }, + { .security_policy = "default_tls13", .expect_renegotiation_info = true }, + /* 20240501 can only negotiate up to tls12 */ + { .security_policy = "20240501", .expect_renegotiation_info = true }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; + EXPECT_TRUE(cipher_suites->size > 0); + + uint8_t *iana = cipher_suites->data; + bool found_renegotiation_info = false; + for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { + if (memcmp(iana + j, empty_renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN) == 0) { + found_renegotiation_info = true; + } + } + + EXPECT_EQUAL(found_renegotiation_info, test_cases[i].expect_renegotiation_info); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + } + + /* TLS1.2 cipher suites not written if QUIC enabled */ + { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + bool quic_enabled[] = { false, s2n_is_tls13_fully_supported() }; + + /* TLS 1.2 cipher suites only written if QUIC not enabled */ + for (size_t i = 0; i < s2n_array_len(quic_enabled); i++) { + config->quic_enabled = quic_enabled[i]; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; + EXPECT_TRUE(cipher_suites->size > 0); + + bool tls12_cipher_found = false; + uint8_t *iana = cipher_suites->data; + for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { + /* All TLS1.3 cipher suites have IANAs starting with 0x13 */ + if (iana[j] != 0x13) { + tls12_cipher_found = true; + } + } + + /* TLS1.2 and QUIC are mutually exclusive */ + EXPECT_TRUE(tls12_cipher_found != quic_enabled[i]); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Error if no cipher suites written. */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + struct s2n_cipher_suite cipher_suite = s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + struct s2n_cipher_suite *cipher_suites = &cipher_suite; + struct s2n_cipher_preferences cipher_preferences = { + .suites = &cipher_suites, + .count = 1, + }; + struct s2n_security_policy policy = *conn->config->security_policy; + policy.cipher_preferences = &cipher_preferences; + conn->security_policy_override = &policy; + + /* Fails with no cipher suites available / written */ + cipher_suite.available = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), + S2N_ERR_INVALID_CIPHER_PREFERENCES); + + /* Succeeds with one cipher suite available / written */ + /* cppcheck-suppress redundantAssignment */ + cipher_suite.available = true; + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + }; + }; + + /* Test that negotiating TLS1.2 with QUIC-enabled server fails */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + /* Succeeds when negotiating TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Fails when negotiating TLS1.2 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all_tls12")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Test that cipher suites enforce proper highest supported versions. + * Eg. server configs TLS 1.2 only ciphers should never negotiate TLS 1.3 + */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + { + /* TLS 1.3 client cipher preference uses TLS13 version */ + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* TLS 1.2 client cipher preference uses TLS12 version */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20240501")); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* TLS 1.3 client cipher preference uses TLS13 version */ + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + POSIX_GUARD(s2n_connection_get_security_policy(client_conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_hello.legacy_version, S2N_TLS12); + + /* Server configured with TLS 1.2 negotiates TLS12 version */ + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + struct s2n_config *server_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + + POSIX_GUARD(s2n_connection_get_security_policy(server_conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello.legacy_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* SSlv2 client hello */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + int sslv2_client_hello_len = sizeof(sslv2_client_hello); + + uint8_t sslv2_client_hello_header[] = { + SSLv2_CLIENT_HELLO_HEADER, + }; + + int sslv2_client_hello_header_len = sizeof(sslv2_client_hello_header); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* The security policy does not need to support SSLv2. + * + * s2n-tls does NOT support SSLv2. However, it does accept ClientHellos in the SSLv2 + * format but advertising higher protocol versions. Clients use this strategy to + * communicate with servers in a backwards-compatible way. + * + * Our test SSLv2 ClientHello advertises TLS1.2. + * So the security policy only needs to support TLS1.2. + * (and at least one of the ciphers in the hard coded sslv2 client hello) + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello_header, sslv2_client_hello_header_len), sslv2_client_hello_header_len); + EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello, sslv2_client_hello_len), sslv2_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + + /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ + EXPECT_EQUAL(client_hello, &server_conn->client_hello); + + uint8_t *collected_client_hello = client_hello->raw_message.data; + uint16_t collected_client_hello_len = client_hello->raw_message.size; + + /* Verify correctly identified as SSLv2 */ + EXPECT_TRUE(client_hello->sslv2); + + /* Verify collected client hello message length */ + EXPECT_EQUAL(collected_client_hello_len, sslv2_client_hello_len); + + /* Verify the collected client hello matches what was sent */ + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, sslv2_client_hello, sslv2_client_hello_len); + + /* Verify s2n_client_hello_get_raw_message_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sslv2_client_hello_len); + + uint8_t expected_cs[] = { + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + }; + + /* Verify collected cipher_suites size correct */ + EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); + + /* Verify collected cipher_suites correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); + + /* Verify collected extensions size correct */ + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + + /* Verify s2n_client_hello_get_extensions_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), 0); + + /* Verify s2n_client_hello_get_session_id_length correct */ + uint32_t ch_session_id_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); + EXPECT_EQUAL(ch_session_id_length, 0); + + /* Free all handshake data */ + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ + EXPECT_EQUAL(client_hello->cipher_suites.size, 0); + EXPECT_NULL(client_hello->cipher_suites.data); + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + EXPECT_NULL(client_hello->extensions.raw.data); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Minimal TLS 1.2 client hello. */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + uint8_t *sent_client_hello = NULL; + uint8_t *expected_client_hello = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x08, + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + + uint8_t server_name_extension[] = { + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + int server_name_extension_len = sizeof(server_name_extension); + + size_t client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_prefix[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int client_hello_prefix_len = sizeof(client_hello_prefix); + int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (sent_client_hello_len >> 16) & 0xff, + (sent_client_hello_len >> 8) & 0xff, + (sent_client_hello_len & 0xff), + }; + int message_len = sizeof(message_header) + sent_client_hello_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); + EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Verify s2n_connection_get_client_hello returns null if client hello not yet processed */ + EXPECT_NULL(s2n_connection_get_client_hello(server_conn)); + + uint8_t *ext_data = NULL; + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + /* Verify we don't get extension and it's length when client hello is not yet processed */ + EXPECT_FAILURE(s2n_client_hello_get_extension_length(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME)); + EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len)); + free(ext_data); + ext_data = NULL; + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + + /* Verify correctly identified as NOT sslv2 */ + EXPECT_FALSE(client_hello->sslv2); + + /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ + EXPECT_EQUAL(client_hello, &server_conn->client_hello); + + uint8_t *collected_client_hello = client_hello->raw_message.data; + uint16_t collected_client_hello_len = client_hello->raw_message.size; + + /* Verify collected client hello message length */ + EXPECT_EQUAL(collected_client_hello_len, sent_client_hello_len); + + /* Verify the collected client hello has client random zero-ed out */ + uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; + uint8_t expected_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_BYTEARRAY_EQUAL(collected_client_hello + client_random_offset, expected_client_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Verify the collected client hello matches what was sent except for the zero-ed client random */ + EXPECT_NOT_NULL(expected_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(expected_client_hello, sent_client_hello, sent_client_hello_len); + POSIX_CHECKED_MEMSET(expected_client_hello + client_random_offset, 0, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); + + /* Verify s2n_client_hello_get_raw_message_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sent_client_hello_len); + + uint8_t *raw_ch_out = NULL; + + /* Verify s2n_client_hello_get_raw_message retrieves the full message when its len <= max_len */ + EXPECT_TRUE(collected_client_hello_len < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(raw_ch_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(sent_client_hello_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, sent_client_hello_len); + free(raw_ch_out); + raw_ch_out = NULL; + + /* Verify s2n_client_hello_get_raw_message retrieves truncated message when its len > max_len */ + EXPECT_TRUE(collected_client_hello_len > 0); + uint32_t max_len = collected_client_hello_len - 1; + EXPECT_NOT_NULL(raw_ch_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, max_len); + free(raw_ch_out); + raw_ch_out = NULL; + + uint8_t expected_cs[] = { 0x00, 0x3C }; + + /* Verify collected cipher_suites size correct */ + EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); + + /* Verify collected cipher_suites correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites correct */ + uint8_t *cs_out = NULL; + + /* Verify s2n_client_hello_get_cipher_suites retrieves the full cipher_suites when its len <= max_len */ + EXPECT_TRUE(client_hello->cipher_suites.size < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(cs_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(sizeof(expected_cs), s2n_client_hello_get_cipher_suites(client_hello, cs_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, sizeof(expected_cs)); + free(cs_out); + cs_out = NULL; + + /* Verify s2n_client_hello_get_cipher_suites retrieves truncated message when cipher_suites len > max_len */ + max_len = sizeof(expected_cs) - 1; + EXPECT_TRUE(max_len > 0); + + EXPECT_NOT_NULL(cs_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_cipher_suites(client_hello, cs_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, max_len); + free(cs_out); + cs_out = NULL; + + /* Verify collected extensions size correct */ + EXPECT_EQUAL(client_hello->extensions.raw.size, client_extensions_len); + + /* Verify collected extensions correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->extensions.raw.data, client_extensions, client_extensions_len); + + /* Verify s2n_client_hello_get_extensions_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), client_extensions_len); + + /* Verify s2n_client_hello_get_extensions correct */ + uint8_t *extensions_out = NULL; + + /* Verify s2n_client_hello_get_extensions retrieves the full cipher_suites when its len <= max_len */ + EXPECT_TRUE(client_hello->extensions.raw.size < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(extensions_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(client_extensions_len, s2n_client_hello_get_extensions(client_hello, extensions_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(extensions_out, client_extensions, client_extensions_len); + free(extensions_out); + extensions_out = NULL; + + /* Verify s2n_client_hello_get_extensions retrieves truncated message when cipher_suites len > max_len */ + max_len = client_extensions_len - 1; + EXPECT_TRUE(max_len > 0); + + EXPECT_NOT_NULL(extensions_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_extensions(client_hello, extensions_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(extensions_out, client_hello->extensions.raw.data, max_len); + free(extensions_out); + extensions_out = NULL; + + /* Verify server name extension and it's length are returned correctly */ + EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_SERVER_NAME), server_name_extension_len); + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len), server_name_extension_len); + EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len); + free(ext_data); + ext_data = NULL; + + /* Verify server name extension is truncated if extension_size > max_len */ + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len - 1)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len - 1), server_name_extension_len - 1); + EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len - 1); + free(ext_data); + ext_data = NULL; + + /* Verify get extension and it's length calls for a non-existing extension type */ + EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY), 0); + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, ext_data, server_name_extension_len), 0); + EXPECT_EQUAL(s2n_errno, S2N_ERR_EXTENSION_NOT_RECEIVED); + free(ext_data); + ext_data = NULL; + + /* Verify server name extension exists */ + bool extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_SERVER_NAME, &extension_exists)); + EXPECT_TRUE(extension_exists); + + /* Verify expected result for non-existing extension */ + extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &extension_exists)); + EXPECT_FALSE(extension_exists); + + /* Verify s2n_client_hello_get_session_id is what we received in ClientHello */ + uint8_t expected_ch_session_id[] = { ZERO_TO_THIRTY_ONE }; + uint8_t ch_session_id[sizeof(expected_ch_session_id)]; + uint32_t ch_session_id_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); + EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); + EXPECT_SUCCESS(s2n_client_hello_get_session_id(client_hello, ch_session_id, &ch_session_id_length, sizeof(ch_session_id))); + EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); + EXPECT_BYTEARRAY_EQUAL(ch_session_id, expected_ch_session_id, sizeof(expected_ch_session_id)); + + /* Verify s2n_connection_get_session_id is different from the one we received in ClientHello, as we generated a new one in ServerHello */ + uint8_t conn_session_id[sizeof(expected_ch_session_id)]; + EXPECT_EQUAL(s2n_connection_get_session_id_length(server_conn), sizeof(conn_session_id)); + EXPECT_SUCCESS(s2n_connection_get_session_id(server_conn, conn_session_id, sizeof(conn_session_id))); + EXPECT_BYTEARRAY_NOT_EQUAL(conn_session_id, ch_session_id, sizeof(expected_ch_session_id)); + + /* Free all handshake data */ + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ + EXPECT_EQUAL(client_hello->cipher_suites.size, 0); + EXPECT_NULL(client_hello->cipher_suites.data); + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + EXPECT_NULL(client_hello->extensions.raw.data); + + /* Verify the connection is successfully reused after connection_wipe */ + + /* Re-configure connection */ + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Recreate config */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Re-send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + /* Verify the collected client hello on the reused connection matches the expected client hello */ + client_hello = s2n_connection_get_client_hello(server_conn); + collected_client_hello = client_hello->raw_message.data; + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + free(expected_client_hello); + free(sent_client_hello); + }; + + /* Client hello api with NULL inputs */ + { + uint32_t len = 128; + uint8_t *out = NULL; + EXPECT_NOT_NULL(out = malloc(len)); + + EXPECT_FAILURE(s2n_client_hello_get_raw_message_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_raw_message(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_cipher_suites_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_cipher_suites(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_extensions_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_extensions(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_extension_length(NULL, S2N_EXTENSION_SERVER_NAME)); + EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(NULL, S2N_EXTENSION_SERVER_NAME, out, len)); + free(out); + out = NULL; + + bool exists = false; + EXPECT_FAILURE(s2n_client_hello_has_extension(NULL, S2N_EXTENSION_SERVER_NAME, &exists)); + EXPECT_FALSE(exists); + }; + + /* test_weird_client_hello_version() */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + uint8_t *sent_client_hello = NULL; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x08, + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_prefix[] = { + /* Protocol version TLS ??? */ + 0xFF, + 0xFF, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int client_hello_prefix_len = sizeof(client_hello_prefix); + int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (sent_client_hello_len >> 16) & 0xff, + (sent_client_hello_len >> 8) & 0xff, + (sent_client_hello_len & 0xff), + }; + int message_len = sizeof(message_header) + sent_client_hello_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); + EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + /* Client sent an invalid legacy protocol version. We should still have negotiate the maximum value(TLS1.2) */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + s2n_connection_free(server_conn); + s2n_config_free(server_config); + free(sent_client_hello); + }; + + { + struct s2n_cipher_suite *client_cipher_suites[] = { + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + }; + + struct s2n_cipher_preferences client_cipher_preferences = { + .count = s2n_array_len(client_cipher_suites), + .suites = client_cipher_suites, + }; + + const struct s2n_signature_scheme *const client_sig_scheme_pref_list[] = { + &s2n_rsa_pkcs1_sha1, + + /* Intentionally do not send and ECDSA SignatureScheme in the Client Hello. This is malformed since the + * Client's only Ciphersuite uses ECDSA, meaning that technically the Server could reject it, but there are + * some clients that send this form of malformed Client Hello's in the wild. So ensure we are compatible + * with them by assuming that the Client does support ECDSA, even though it's missing from the ClientHello. + */ + + /* &s2n_ecdsa_sha1, */ + }; + + struct s2n_signature_preferences client_signature_preferences = { + .count = s2n_array_len(client_sig_scheme_pref_list), + .signature_schemes = client_sig_scheme_pref_list, + }; + + struct s2n_security_policy client_security_policy = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &client_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &client_signature_preferences, + .ecc_preferences = &s2n_ecc_preferences_20140601, + }; + + EXPECT_TRUE(client_cipher_suites[0]->available); + + struct s2n_cert_chain_and_key *ecdsa_cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Create Configs */ + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert_chain)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + server_config->security_policy = &security_policy_20190214; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + client_config->security_policy = &client_security_policy; + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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)); + + /* We have to update the client's security policy after it sends the ClientHello. + * The client sends all signature algorithms in its security policy, and + * won't accept any signature algorithm it receives that's not in its security policy. + * So we need to change the security policy between sending and receiving. + */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); + client_config->security_policy = &security_policy_20190214; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + EXPECT_EQUAL(client_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); + EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); + EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* s2n_client_hello_recv should fail when reading an SSLv2 client hello during a hello retry handshake */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Handshake is hello retry and TLS1.3 was negotiated */ + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); + + /* Second client hello has version SSLv2 */ + server_conn->client_hello.sslv2 = true; + + /* Mock having some data in the client hello */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&server_conn->handshake.io, 100)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_parse_client_hello(server_conn), S2N_ERR_BAD_MESSAGE); + }; + + /* Test s2n_client_hello_parse_message + * + * Comparing ClientHellos produced by connection IO parsing vs + * produced by s2n_client_hello_parse_message is difficult, but we can + * use JA3 fingerprints as an approximation. See s2n_fingerprint_ja3_test.c + */ + { + /* 20240501 can only negotiate tls12 */ + const char *security_policies[] = { "20240501", "default_tls13", "test_all" }; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Test: Can parse ClientHellos sent by the s2n client */ + for (size_t i = 0; i < s2n_array_len(security_policies); i++) { + const char *security_policy = security_policies[i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policy)); + + EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); + + uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); + EXPECT_NOT_EQUAL(raw_size, 0); + uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); + EXPECT_NOT_NULL(raw); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + EXPECT_NOT_NULL(client_hello = s2n_client_hello_parse_message(raw, raw_size)); + EXPECT_TRUE(client_hello->alloced); + }; + + /* Test: Rejects invalid ClientHellos + * + * This test is important to verify that no memory is leaked when parsing fails. + */ + { + struct s2n_client_hello *client_hello = NULL; + + uint8_t wrong_message_type[50] = { 0x02, 0x00, 0x00, 1 }; + client_hello = s2n_client_hello_parse_message(wrong_message_type, sizeof(wrong_message_type)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + + uint8_t wrong_message_size[50] = { 0x01, 0x00, 0x00, UINT8_MAX }; + client_hello = s2n_client_hello_parse_message(wrong_message_size, sizeof(wrong_message_size)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + + uint8_t too_short[5] = { 0x01, 0x00, 0x00, 1 }; + client_hello = s2n_client_hello_parse_message(too_short, sizeof(too_short)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_STUFFER_OUT_OF_DATA); + + uint8_t all_zeroes[50] = { 0x01, 0x00, 0x00, 46 }; + client_hello = s2n_client_hello_parse_message(all_zeroes, sizeof(all_zeroes)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + }; + + /* Test: Rejects SSLv2 */ + { + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_HEADER, + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + /* Try parsing variations on the complete record vs just the message. + * The sslv2 record header is technically the first two bytes, + * but s2n-tls usually starts parsing after the first five bytes. + */ + for (size_t i = 0; i <= S2N_TLS_RECORD_HEADER_LENGTH; i++) { + struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( + sslv2_client_hello + i, sizeof(sslv2_client_hello) - i); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + } + + /* Sanity check: s2n accepts the test sslv2 message via the connection */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->header_in, + sslv2_client_hello, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->in, + sslv2_client_hello + S2N_TLS_RECORD_HEADER_LENGTH, + sizeof(sslv2_client_hello) - S2N_TLS_RECORD_HEADER_LENGTH)); + + EXPECT_FALSE(server->client_hello.sslv2); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); + EXPECT_TRUE(server->client_hello.sslv2); + EXPECT_FALSE(server->client_hello.alloced); + } + }; + }; + + /* Test s2n_client_hello_free */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(NULL), S2N_ERR_NULL); + + /* Test: Accepts but ignores NULL / already freed */ + { + struct s2n_client_hello *client_hello = NULL; + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); + EXPECT_NULL(client_hello); + } + }; + + /* Test: Errors on client hello associated with a connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(&client_hello), S2N_ERR_INVALID_ARGUMENT); + EXPECT_NOT_NULL(s2n_connection_get_client_hello(server)); + EXPECT_NOT_EQUAL(server->client_hello.raw_message.size, 0); + }; + + /* Test: Frees client hello from raw message */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client)); + + EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); + + uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); + EXPECT_NOT_EQUAL(raw_size, 0); + uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); + EXPECT_NOT_NULL(raw); + + struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( + raw, raw_size); + EXPECT_NOT_NULL(client_hello); + + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); + EXPECT_NULL(client_hello); + } + }; + }; + + /* s2n_client_hello_get_compression_methods */ + { + /* Safety */ + { + uint32_t length = 0; + struct s2n_client_hello client_hello = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(NULL, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods_length(&client_hello, NULL), S2N_ERR_NULL); + + uint8_t list = 0; + uint32_t list_length = 0; + uint32_t out_length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(NULL, &list, list_length, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, NULL, list_length, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, NULL), S2N_ERR_NULL); + + /* User did not provide a large enough buffer to write the compression methods */ + uint8_t data[] = { 1, 2, 3, 4, 5 }; + EXPECT_SUCCESS(s2n_blob_init(&client_hello.compression_methods, data, sizeof(data))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_compression_methods(&client_hello, &list, list_length, &out_length), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Retrieves the compression methods list */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); + EXPECT_EQUAL(length, 1); + uint8_t list = 0; + uint32_t out_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, &list, sizeof(list), &out_length)); + EXPECT_EQUAL(out_length, 1); + }; + + /* Retrieves compression methods list longer than one byte */ + { + /* Compression methods were deprecated in TLS13 and s2n has never + * supported them. However, it is conceivable that a client could send + * us a list that contains more than the "null" byte. Therefore, we construct + * a fake Client Hello that contains a longer list of compression methods + * for testing. + */ + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, 0x02, + /* Cipher suite - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 */ + 0xC0, 0x2F, + COMPRESSION_METHODS_LEN, + COMPRESSION_METHODS, + /* Extensions len */ + 0x00, 0x00 + }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_conn->handshake.io, client_hello_message, sizeof(client_hello_message))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods_length(client_hello, &length)); + EXPECT_EQUAL(length, COMPRESSION_METHODS_LEN); + uint8_t list[5] = { 0 }; + uint32_t out_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_compression_methods(client_hello, list, sizeof(list), &out_length)); + EXPECT_EQUAL(out_length, COMPRESSION_METHODS_LEN); + + uint8_t compression_data[] = { COMPRESSION_METHODS }; + EXPECT_BYTEARRAY_EQUAL(list, compression_data, out_length); + } + }; + + /* s2n_client_hello_get_legacy_protocol_version */ + { + /* Safety */ + { + uint8_t out = 0; + struct s2n_client_hello client_hello = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(NULL, &out), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_protocol_version(&client_hello, NULL), S2N_ERR_NULL); + }; + + /* Retrieves the Client Hello protocol version */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint8_t version = 0; + EXPECT_SUCCESS(s2n_client_hello_get_legacy_protocol_version(client_hello, &version)); + EXPECT_EQUAL(version, S2N_TLS12); + }; + }; + + /* s2n_client_hello_get_legacy_record_version */ + { + /* Safety */ + { + uint8_t out = 0; + struct s2n_client_hello client_hello = { 0 }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(NULL, &out), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_legacy_record_version(&client_hello, NULL), S2N_ERR_NULL); + }; + + /* Retrieves record version */ + { + uint8_t out = 0; + struct s2n_client_hello client_hello = { 0 }; + client_hello.legacy_record_version = S2N_TLS12; + client_hello.record_version_recorded = 1; + EXPECT_SUCCESS(s2n_client_hello_get_legacy_record_version(&client_hello, &out)); + EXPECT_EQUAL(out, S2N_TLS12); + }; + }; + + /* s2n_client_hello_get_server_name() */ + { + /* Safety */ + { + struct s2n_client_hello ch = { 0 }; + uint16_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(NULL, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name_length(&ch, NULL), S2N_ERR_NULL); + + uint8_t buffer = 0; + uint16_t out_length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(NULL, &buffer, 0, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, NULL, 0, &out_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(&ch, &buffer, 0, NULL), S2N_ERR_NULL); + }; + + /* Retrieves the first entry in the server_name extension */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + const char *test_server_name = "test server name!"; + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + EXPECT_NOT_NULL(client_hello); + + uint16_t length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_server_name_length(client_hello, &length)); + EXPECT_EQUAL(strlen(test_server_name), length); + uint8_t buffer[20] = { 0 }; + uint16_t out_length = 0; + EXPECT_SUCCESS(s2n_client_hello_get_server_name(client_hello, buffer, sizeof(buffer), &out_length)); + EXPECT_EQUAL(length, out_length); + + EXPECT_BYTEARRAY_EQUAL(buffer, test_server_name, out_length); + + /* Check error occurs if buffer is too small to hold server name */ + uint8_t small_buf[2] = { 0 }; + out_length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_get_server_name(client_hello, small_buf, sizeof(small_buf), &out_length), S2N_ERR_SAFETY); + }; + }; + + /* Test: large Client Hellos */ + { + const uint16_t cipher_suites_max_length = (1 << 16) - 2; + const uint16_t num_of_cipher_suites_to_drop = 150; + + /** + * S2N-TLS automatically includes TLS_EMPTY_RENEGOTIATION_INFO_SCSV in TLS 1.2 ClientHello, + * so we subtract 1 from the maximum number of cipher suites to reserve space for it. + */ + const uint16_t max_cipher_suite_count = (cipher_suites_max_length / S2N_TLS_CIPHER_SUITE_LEN) - 1; + + /* Drop 150 cipher suites from max, so that the total handshake message length won't exceed 64KB */ + const uint16_t reduced_cipher_suite_count = max_cipher_suite_count - num_of_cipher_suites_to_drop; + + uint16_t cipher_suites_counts[] = { reduced_cipher_suite_count, max_cipher_suite_count }; + + for (size_t i = 0; i < s2n_array_len(cipher_suites_counts); i++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* We need to generate a large Client Hello. + * We do this by manipulating the number of cipher suites, + * which is the easiest way to make Client Hello large. + */ + uint16_t cipher_suites_count = cipher_suites_counts[i]; + struct s2n_cipher_suite *test_cipher_suites[UINT16_MAX] = { 0 }; + for (size_t j = 0; j < cipher_suites_count; j++) { + test_cipher_suites[j] = &s2n_rsa_with_aes_128_gcm_sha256; + } + const struct s2n_cipher_preferences test_cipher_suites_preferences = { + .count = cipher_suites_count, + .suites = test_cipher_suites, + }; + const struct s2n_security_policy *default_policy = NULL; + /* 20240501 is a policy that can only negotiate tls12 */ + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20240501", &default_policy)); + struct s2n_security_policy test_security_policy = *default_policy; + test_security_policy.cipher_preferences = &test_cipher_suites_preferences; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + client->security_policy_override = &test_security_policy; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + server->security_policy_override = &test_security_policy; + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + /** + * Write Client Hello into io_pair.server_in. + * The server_in buffer contains the Client Hello message plus a 5-byte record header. + */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client, &blocked), S2N_ERR_IO_BLOCKED); + + /* Add one extra cipher suite length to account for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ + uint16_t cipher_suites_length = (cipher_suites_count + 1) * S2N_TLS_CIPHER_SUITE_LEN; + + if (cipher_suites_length < cipher_suites_max_length) { + /** + * The Client Hello message size should be less than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, even with + * the five byte record header. + */ + EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + } else { + /** + * When using maximum cipher suites, the Client Hello message size exceeds + * S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH by more than five byte. + * + * Hence, if server_in's available data is greater than S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, + * then the Client Hello itself exceeds the maximum allowed size, even after accounting for the record header. + */ + EXPECT_TRUE(s2n_stuffer_data_available(&io_pair.server_in) > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH); + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), S2N_ERR_BAD_MESSAGE); + } + } + } + + /* Test s2n_client_hello_get_random */ + { + /* Safety */ + { + uint8_t out[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + struct s2n_client_hello client_hello = { 0 }; + + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_random(NULL, out, sizeof(out)), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_random(&client_hello, NULL, sizeof(out)), + S2N_ERR_NULL); + + /* Buffer size must be large enough to hold the full client random */ + uint8_t small_buffer[S2N_TLS_RANDOM_DATA_LEN - 1] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_random(&client_hello, small_buffer, sizeof(small_buffer)), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Retrieves the client random and zeroes it from the raw message */ + { + /* Construct a minimal ClientHello with a custom random value */ + uint8_t custom_random[S2N_TLS_RANDOM_DATA_LEN] = { ZERO_TO_THIRTY_ONE }; + + /* clang-format off */ + uint8_t raw_client_hello[] = { + /* message type */ + TLS_CLIENT_HELLO, + /* message size */ + 0x00, 0x00, 43, + /* protocol version - TLS1.2 */ + 0x03, 0x03, + /* random - custom value */ + ZERO_TO_THIRTY_ONE, + /* session id */ + 0x00, + /* cipher suites */ + 0x00, 0x02, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + /* legacy compression methods */ + 0x01, 0x00, + /* extensions - empty */ + 0x00, 0x00, + }; + /* clang-format on */ + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + client_hello = s2n_client_hello_parse_message(raw_client_hello, sizeof(raw_client_hello)); + EXPECT_NOT_NULL(client_hello); + + /* Exact-size buffer: retrieve the client random */ + uint8_t retrieved_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_client_hello_get_random( + client_hello, retrieved_random, S2N_TLS_RANDOM_DATA_LEN)); + EXPECT_BYTEARRAY_EQUAL(retrieved_random, custom_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Buffer size > 32 bytes must also succeed and only write the first 32 bytes */ + { + uint8_t large_buffer[S2N_TLS_RANDOM_DATA_LEN + 10] = { 0 }; + EXPECT_SUCCESS(s2n_client_hello_get_random( + client_hello, large_buffer, sizeof(large_buffer))); + + /* First 32 bytes match the client random */ + EXPECT_BYTEARRAY_EQUAL(large_buffer, custom_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Tail remains untouched */ + uint8_t zero_tail[10] = { 0 }; + EXPECT_BYTEARRAY_EQUAL( + large_buffer + S2N_TLS_RANDOM_DATA_LEN, + zero_tail, + sizeof(zero_tail)); + } + + /* The raw message should have the random zeroed out after retrieval */ + uint8_t *raw_message = client_hello->raw_message.data; + uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; + uint8_t zeroed_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_BYTEARRAY_EQUAL( + raw_message + client_random_offset, + zeroed_random, + S2N_TLS_RANDOM_DATA_LEN); + }; + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + END_TEST(); +} diff --git a/tests/unit/s2n_client_record_version_test.c b/tests/unit/s2n_client_record_version_test.c index 12a9a88cef1..fa875e218ec 100644 --- a/tests/unit/s2n_client_record_version_test.c +++ b/tests/unit/s2n_client_record_version_test.c @@ -1,316 +1,316 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Server negotiates TLS1.2 */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - int body_len = sizeof(server_hello_message); - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Read ClientHello s2n wrote */ - uint8_t buf[1024]; - size_t buf_occupied = 0; - - /* we need only first 10 bytes to get to ClientHello protocol version */ - while (buf_occupied < 10) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 10 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Record Type is Handshake */ - EXPECT_EQUAL(buf[0], 0x16); - /* Protocol version is TLS1.0 */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x01); - /* Handshake Type is ClientHello */ - EXPECT_EQUAL(buf[5], 0x01); - /* Handshake Protocol Version is TLS1.2 */ - EXPECT_EQUAL(buf[9], 0x03); - EXPECT_EQUAL(buf[10], 0x03); - - /* Read the rest of the pipe */ - while (1) { - ssize_t n = read(io_pair.server, buf, sizeof(buf)); - - if (n > 0) { - continue; - } - - EXPECT_EQUAL(n, -1); - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - } - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Verify that protocol versions are TLS1.2 now */ - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS12); - - /* Now lets shutdown the connection and verify that alert is sent in record with protocol version TLS1.2 */ - EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); - - /* Receive the next record from client and ensure that record protocol version is TLS1.2 */ - buf_occupied = 0; - /* We need only first 5 bytes to get to record protocol version */ - while (buf_occupied < 5) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 5 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Protocol version is TLS1.2 now */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x03); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Server negotiates SSLv3 */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_hello_message[] = { - /* Protocol version SSLv3 */ - 0x03, 0x00, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - DES-CBC3-SHA */ - 0x00, 0x0A, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - int body_len = sizeof(server_hello_message); - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version SSLv3 */ - 0x03, - 0x00, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Read ClientHello s2n wrote */ - uint8_t buf[1024]; - size_t buf_occupied = 0; - - /* we need only first 10 bytes to get to ClientHello protocol version */ - while (buf_occupied < 10) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 10 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Record Type is Handshake */ - EXPECT_EQUAL(buf[0], 0x16); - /* Protocol version is TLS1.0 */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x01); - /* Handshake Type is ClientHello */ - EXPECT_EQUAL(buf[5], 0x01); - /* Handshake Protocol Version is TLS1.2 */ - EXPECT_EQUAL(buf[9], 0x03); - EXPECT_EQUAL(buf[10], 0x03); - - /* Read the rest of the pipe */ - while (1) { - ssize_t n = read(io_pair.server, buf, sizeof(buf)); - - if (n > 0) { - continue; - } - - EXPECT_EQUAL(n, -1); - if (errno == EAGAIN || errno == EWOULDBLOCK) { - break; - } - } - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Verify that protocol versions are SSLv3 with the exeption of client which supports TLS1.2 */ - EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); - EXPECT_EQUAL(client_conn->server_protocol_version, S2N_SSLv3); - - /* Now lets shutdown the connection and verify that alert is sent in record with protocol version SSLv3 */ - EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); - - /* Receive the next record from client and ensure that record protocol version is SSLv3 */ - buf_occupied = 0; - /* We need only first 5 bytes to get to record protocol version */ - while (buf_occupied < 5) { - ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); - - /* We should be able to read 5 bytes without blocking */ - EXPECT_TRUE(n > 0); - buf_occupied += n; - } - /* Protocol version is SSLv3 now */ - EXPECT_EQUAL(buf[1], 0x03); - EXPECT_EQUAL(buf[2], 0x00); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Server negotiates TLS1.2 */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Read ClientHello s2n wrote */ + uint8_t buf[1024]; + size_t buf_occupied = 0; + + /* we need only first 10 bytes to get to ClientHello protocol version */ + while (buf_occupied < 10) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 10 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Record Type is Handshake */ + EXPECT_EQUAL(buf[0], 0x16); + /* Protocol version is TLS1.0 */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x01); + /* Handshake Type is ClientHello */ + EXPECT_EQUAL(buf[5], 0x01); + /* Handshake Protocol Version is TLS1.2 */ + EXPECT_EQUAL(buf[9], 0x03); + EXPECT_EQUAL(buf[10], 0x03); + + /* Read the rest of the pipe */ + while (1) { + ssize_t n = read(io_pair.server, buf, sizeof(buf)); + + if (n > 0) { + continue; + } + + EXPECT_EQUAL(n, -1); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + } + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Verify that protocol versions are TLS1.2 now */ + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS12); + + /* Now lets shutdown the connection and verify that alert is sent in record with protocol version TLS1.2 */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); + + /* Receive the next record from client and ensure that record protocol version is TLS1.2 */ + buf_occupied = 0; + /* We need only first 5 bytes to get to record protocol version */ + while (buf_occupied < 5) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 5 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Protocol version is TLS1.2 now */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x03); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Server negotiates SSLv3 */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version SSLv3 */ + 0x03, 0x00, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - DES-CBC3-SHA */ + 0x00, 0x0A, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version SSLv3 */ + 0x03, + 0x00, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Read ClientHello s2n wrote */ + uint8_t buf[1024]; + size_t buf_occupied = 0; + + /* we need only first 10 bytes to get to ClientHello protocol version */ + while (buf_occupied < 10) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 10 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Record Type is Handshake */ + EXPECT_EQUAL(buf[0], 0x16); + /* Protocol version is TLS1.0 */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x01); + /* Handshake Type is ClientHello */ + EXPECT_EQUAL(buf[5], 0x01); + /* Handshake Protocol Version is TLS1.2 */ + EXPECT_EQUAL(buf[9], 0x03); + EXPECT_EQUAL(buf[10], 0x03); + + /* Read the rest of the pipe */ + while (1) { + ssize_t n = read(io_pair.server, buf, sizeof(buf)); + + if (n > 0) { + continue; + } + + EXPECT_EQUAL(n, -1); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + } + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Verify that protocol versions are SSLv3 with the exeption of client which supports TLS1.2 */ + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_SSLv3); + + /* Now lets shutdown the connection and verify that alert is sent in record with protocol version SSLv3 */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); + + /* Receive the next record from client and ensure that record protocol version is SSLv3 */ + buf_occupied = 0; + /* We need only first 5 bytes to get to record protocol version */ + while (buf_occupied < 5) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 5 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Protocol version is SSLv3 now */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x00); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_client_secure_renegotiation_test.c b/tests/unit/s2n_client_secure_renegotiation_test.c index 10922261afc..76427096456 100644 --- a/tests/unit/s2n_client_secure_renegotiation_test.c +++ b/tests/unit/s2n_client_secure_renegotiation_test.c @@ -1,310 +1,310 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Success: server sends an empty initial renegotiation_info */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x01, - /* renegotiated_connection len */ - 0x00, - }; - int server_extensions_len = sizeof(server_extensions); - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (server_extensions_len >> 8) & 0xff, - (server_extensions_len & 0xff), - }; - int body_len = sizeof(server_hello_message) + server_extensions_len; - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into server hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Secure renegotiation is set */ - EXPECT_EQUAL(client_conn->secure_renegotiation, 1); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Success: server doesn't send an renegotiation_info extension */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - int body_len = sizeof(server_hello_message); - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into server hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - - /* Verify that we proceed with handshake */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Secure renegotiation is not set, as server doesn't support it */ - EXPECT_EQUAL(client_conn->secure_renegotiation, 0); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Failure: server sends a non-empty initial renegotiation_info */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status client_blocked; - - uint8_t server_extensions[] = { - /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ - 0xff, - 0x01, - /* Extension size */ - 0x00, - 0x21, - /* renegotiated_connection len */ - 0x20, - /* fake renegotiated_connection */ - ZERO_TO_THIRTY_ONE, - }; - int server_extensions_len = sizeof(server_extensions); - uint8_t server_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Server random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, - 0x3C, - /* Compression method - none */ - 0x00, - /* Extensions len */ - (server_extensions_len >> 8) & 0xff, - (server_extensions_len & 0xff), - }; - int body_len = sizeof(server_hello_message) + server_extensions_len; - uint8_t message_header[] = { - /* Handshake message type SERVER HELLO */ - 0x02, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - int message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Send the client hello */ - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); - - /* Write the server hello */ - EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); - EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); - - /* Verify that we fail for non-empty renegotiated_connection */ - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Success: server sends an empty initial renegotiation_info */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x01, + /* renegotiated_connection len */ + 0x00, + }; + int server_extensions_len = sizeof(server_extensions); + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (server_extensions_len >> 8) & 0xff, + (server_extensions_len & 0xff), + }; + int body_len = sizeof(server_hello_message) + server_extensions_len; + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into server hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Secure renegotiation is set */ + EXPECT_EQUAL(client_conn->secure_renegotiation, 1); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Success: server doesn't send an renegotiation_info extension */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into server hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Secure renegotiation is not set, as server doesn't support it */ + EXPECT_EQUAL(client_conn->secure_renegotiation, 0); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Failure: server sends a non-empty initial renegotiation_info */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status client_blocked; + + uint8_t server_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x21, + /* renegotiated_connection len */ + 0x20, + /* fake renegotiated_connection */ + ZERO_TO_THIRTY_ONE, + }; + int server_extensions_len = sizeof(server_extensions); + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (server_extensions_len >> 8) & 0xff, + (server_extensions_len & 0xff), + }; + int body_len = sizeof(server_hello_message) + server_extensions_len; + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); + + /* Verify that we fail for non-empty renegotiated_connection */ + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_config_test.c b/tests/unit/s2n_config_test.c index 06731b7a554..5af481bf6d9 100644 --- a/tests/unit/s2n_config_test.c +++ b/tests/unit/s2n_config_test.c @@ -1,1385 +1,1385 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_config.h" - -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_openssl.h" -#include "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_client_supported_groups.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_record.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "unstable/npn.h" -#include "utils/s2n_map.h" -#include "utils/s2n_mem.h" - -#define S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT 30 - -/* forward declaration */ -int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair); - -static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, - struct s2n_offered_psk_list *psk_identity_list) -{ - return S2N_SUCCESS; -} - -static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) -{ - return S2N_SUCCESS; -} - -static int s2n_test_crl_lookup_cb(struct s2n_crl_lookup *lookup, void *context) -{ - return S2N_SUCCESS; -} - -static int s2n_test_cert_validation_cb(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *context) -{ - return S2N_SUCCESS; -} - -static int s2n_test_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - return S2N_SUCCESS; -} - -/* A malloc callback that always fails. Used by the regression test - * to force s2n_map_add to fail during cert map construction. - */ -static int s2n_test_failing_malloc_cb(void **ptr, uint32_t requested, uint32_t *allocated) -{ - *ptr = NULL; - *allocated = 0; - POSIX_BAIL(S2N_ERR_ALLOC); -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; - - const struct s2n_security_policy *default_security_policy = NULL, *tls13_security_policy = NULL, *fips_security_policy = NULL; - EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); - EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); - EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); - - char cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert, S2N_MAX_TEST_PEM_SIZE)); - char key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, key, S2N_MAX_TEST_PEM_SIZE)); - - /* Test: s2n_config_new and tls13_default_config match */ - { - struct s2n_config *config = NULL, *default_config = NULL; - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(default_config = s2n_fetch_default_config()); - - /* s2n_config_new() matches s2n_fetch_default_config() */ - if (default_config->security_policy != config->security_policy) { - /* one possible cause for this is attempting to includes s2n_config.c - * for access to internal `static` functions. This causes two copies - * of the default config to be created. The default_config in *this* - * unit of translation doesn't get properly initialized. */ - const char *default_policy = NULL; - const char *new_policy = NULL; - EXPECT_OK(s2n_security_policy_get_version(default_config->security_policy, &default_policy)); - EXPECT_OK(s2n_security_policy_get_version(config->security_policy, &new_policy)); - printf("default policy is %s but new policy is %s\n", default_policy, new_policy); - } - EXPECT_EQUAL(default_config->security_policy, config->security_policy); - EXPECT_EQUAL(default_config->security_policy->signature_preferences, config->security_policy->signature_preferences); - EXPECT_EQUAL(default_config->client_cert_auth_type, config->client_cert_auth_type); - - /* Calling s2n_fetch_default_config() repeatedly returns the same object */ - EXPECT_EQUAL(default_config, s2n_fetch_default_config()); - - /* TLS1.3 default does not match non-TLS1.3 default */ - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_EQUAL(default_config, s2n_fetch_default_config()); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Connections created with default configs */ - { - /* For TLS1.2 */ - if (!s2n_is_in_fips_mode()) { - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); - - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, default_security_policy); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* For TLS1.3 */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); - - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, tls13_security_policy); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - /* For fips */ - if (s2n_is_in_fips_mode()) { - struct s2n_connection *conn = NULL; - const struct s2n_security_policy *security_policy = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); - - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, fips_security_policy); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - }; - - /* Test for s2n_config_new() and tls 1.3 behavior */ - { - if (!s2n_is_in_fips_mode()) { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_EQUAL(config->security_policy, default_security_policy); - EXPECT_SUCCESS(s2n_config_free(config)); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_EQUAL(config->security_policy, tls13_security_policy); - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - }; - - /* Test setting the callback to select PSK identity */ - { - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - uint8_t context = 13; - - /* Safety check */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_psk_selection_callback( - NULL, s2n_test_select_psk_identity_callback, &context), - S2N_ERR_NULL); - EXPECT_NULL(config->psk_selection_cb); - EXPECT_NULL(config->psk_selection_ctx); - - EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &context)); - EXPECT_EQUAL(config->psk_selection_cb, s2n_test_select_psk_identity_callback); - EXPECT_EQUAL(config->psk_selection_ctx, &context); - - EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, NULL, NULL)); - EXPECT_NULL(config->psk_selection_cb); - EXPECT_NULL(config->psk_selection_ctx); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /*Test s2n_connection_set_config */ - { - /* Test that tickets_to_send is set correctly */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - struct s2n_config *config = NULL; - uint8_t num_tickets = 1; - - EXPECT_NOT_NULL(config = s2n_config_new()); - - config->initial_tickets_to_send = num_tickets; - - EXPECT_EQUAL(conn->tickets_to_send, 0); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->tickets_to_send, num_tickets); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test that PSK type is set correctly */ - { - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); - - /* Overrides connection value */ - { - conn->config = NULL; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); - EXPECT_FALSE(conn->psk_mode_overridden); - }; - - /* Does not override connection value if conn->override_psk_mode set */ - { - conn->config = NULL; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - conn->psk_mode_overridden = true; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); - EXPECT_TRUE(conn->psk_mode_overridden); - conn->psk_mode_overridden = false; - }; - - /* Does not override connection value if PSKs already set */ - { - conn->config = NULL; - DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); - EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); - EXPECT_FALSE(conn->psk_mode_overridden); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - }; - - /* s2n_config_set_session_tickets_onoff */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, true), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, false), S2N_ERR_NULL); - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); - EXPECT_TRUE(config->use_tickets); - EXPECT_EQUAL(config->initial_tickets_to_send, 1); - - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); - EXPECT_FALSE(config->use_tickets); - EXPECT_EQUAL(config->initial_tickets_to_send, 1); - - config->initial_tickets_to_send = 10; - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); - EXPECT_TRUE(config->use_tickets); - EXPECT_EQUAL(config->initial_tickets_to_send, 10); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* s2n_config_set_context */ - /* s2n_config_get_context */ - { - uint8_t context = 42; - uint8_t other = 123; - void *returned_context = NULL; - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); - EXPECT_NULL(returned_context); - - EXPECT_SUCCESS(s2n_config_set_ctx(config, &context)); - EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); - EXPECT_NOT_NULL(returned_context); - - EXPECT_EQUAL(*((uint8_t *) returned_context), context); - EXPECT_NOT_EQUAL(*((uint8_t *) returned_context), other); - - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test s2n_config_set_extension_data */ - { - uint8_t extension_data[] = "extension data"; - - /* Test s2n_config_set_extension_data can be called for owned cert chains */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - - EXPECT_SUCCESS(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, - extension_data, sizeof(extension_data))); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, sizeof(extension_data)); - }; - - /* Test s2n_config_set_extension_data can't be called for unowned cert chains */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, - extension_data, sizeof(extension_data)), - S2N_ERR_CERT_OWNERSHIP); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, 0); - EXPECT_EQUAL(chain->ocsp_status.size, 0); - }; - }; - - /* Test s2n_config_free_cert_chain_and_key */ - { - /* Chain owned by application */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* No-op for application-owned chains */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Still no-op if called again */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - }; - - /* Chain owned by application and freed too early: - * This is arguably incorrect behavior, but did not cause errors in the past. - * We should continue to ensure it doesn't cause any errors. - */ - { - struct s2n_cert_chain_and_key *chain = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* Free the chain early */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain)); - - /* No-op for application-owned chains */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - /* No-op if called again */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - }; - - /* Chain owned by library */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* No-op if called again */ - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NULL(s2n_config_get_single_default_cert(config)); - }; - - /* Switch from library-owned certs to application-owned certs */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - EXPECT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); - - /* Now add an application-owned chain */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); - }; - }; - - /* Test s2n_config_set_cert_chain_and_key_defaults */ - { - /* Succeeds if chains owned by app */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_1 = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_1, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_2 = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_2, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_1)); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_1); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &chain_2, 1)); - EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_2); - EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); - }; - - /* Fails if chains owned by library */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); - EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_chain_and_key_defaults( - config, &chain, 1), - S2N_ERR_CERT_OWNERSHIP); - }; - }; - - /* Test s2n_config_set_send_buffer_size */ - { - /* Safety */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_EQUAL(config->send_buffer_size_override, 0); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(NULL, S2N_MIN_SEND_BUFFER_SIZE), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(config, 0), S2N_ERR_INVALID_ARGUMENT); - EXPECT_EQUAL(config->send_buffer_size_override, 0); - }; - - /* Default applied to connection */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_EQUAL(config->send_buffer_size_override, 0); - EXPECT_FALSE(conn->multirecord_send); - }; - - /* Custom applied to connection */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, S2N_MIN_SEND_BUFFER_SIZE)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_EQUAL(config->send_buffer_size_override, S2N_MIN_SEND_BUFFER_SIZE); - EXPECT_TRUE(conn->multirecord_send); - }; - }; - - /* Test s2n_config_set_verify_after_sign */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_FALSE(config->verify_after_sign); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(NULL, S2N_VERIFY_AFTER_SIGN_ENABLED), S2N_ERR_NULL); - - /* Invalid mode */ - config->verify_after_sign = true; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); - EXPECT_TRUE(config->verify_after_sign); - config->verify_after_sign = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FALSE(config->verify_after_sign); - - /* Set and unset */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - EXPECT_TRUE(config->verify_after_sign); - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_DISABLED)); - EXPECT_FALSE(config->verify_after_sign); - }; - - /* Test s2n_config_set_renegotiate_request_cb */ - { - uint8_t context = 0; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Unset by default */ - EXPECT_EQUAL(config->renegotiate_request_cb, NULL); - EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_renegotiate_request_cb(NULL, s2n_test_reneg_req_cb, &context), S2N_ERR_NULL); - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, &context)); - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, NULL)); - - /* Set */ - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, &context)); - EXPECT_EQUAL(config->renegotiate_request_cb, s2n_test_reneg_req_cb); - EXPECT_EQUAL(config->renegotiate_request_ctx, &context); - - /* Unset */ - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, NULL)); - EXPECT_EQUAL(config->renegotiate_request_cb, NULL); - EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); - }; - - /* Test s2n_config_set_npn */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_FALSE(config->npn_supported); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_npn(NULL, true), S2N_ERR_NULL); - - /* Set and unset */ - EXPECT_SUCCESS(s2n_config_set_npn(config, true)); - EXPECT_TRUE(config->npn_supported); - EXPECT_SUCCESS(s2n_config_set_npn(config, false)); - EXPECT_FALSE(config->npn_supported); - }; - - /* Test s2n_config_set_crl_lookup_cb */ - { - uint8_t context = 0; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Unset by default */ - EXPECT_EQUAL(config->crl_lookup_cb, NULL); - EXPECT_EQUAL(config->crl_lookup_ctx, NULL); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_crl_lookup_cb(NULL, s2n_test_crl_lookup_cb, &context), S2N_ERR_NULL); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, &context)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, NULL)); - - /* Set */ - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, &context)); - EXPECT_EQUAL(config->crl_lookup_cb, s2n_test_crl_lookup_cb); - EXPECT_EQUAL(config->crl_lookup_ctx, &context); - - /* Unset */ - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, NULL)); - EXPECT_EQUAL(config->crl_lookup_cb, NULL); - EXPECT_EQUAL(config->crl_lookup_ctx, NULL); - }; - - /* Test s2n_config_set_cert_validation_cb */ - { - uint8_t context = 0; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Unset by default */ - EXPECT_EQUAL(config->cert_validation_cb, NULL); - EXPECT_EQUAL(config->cert_validation_ctx, NULL); - - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_validation_cb(NULL, s2n_test_cert_validation_cb, &context), - S2N_ERR_NULL); - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, &context)); - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, NULL)); - - /* Set */ - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, &context)); - EXPECT_EQUAL(config->cert_validation_cb, s2n_test_cert_validation_cb); - EXPECT_EQUAL(config->cert_validation_ctx, &context); - - /* Unset */ - EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, NULL)); - EXPECT_EQUAL(config->cert_validation_cb, NULL); - EXPECT_EQUAL(config->cert_validation_ctx, NULL); - }; - - /* Test s2n_config_set_status_request_type */ - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - s2n_mode mode = modes[mode_i]; - - if (!s2n_x509_ocsp_stapling_supported()) { - break; - } - - /* request_ocsp_status should be false by default */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_FALSE(config->ocsp_status_requested_by_user); - EXPECT_FALSE(config->ocsp_status_requested_by_s2n); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FALSE(conn->request_ocsp_status); - }; - - /* request_ocsp_status should be true if set via s2n_config_set_status_request_type */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_TRUE(config->ocsp_status_requested_by_user); - EXPECT_FALSE(config->ocsp_status_requested_by_s2n); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_TRUE(conn->request_ocsp_status); - }; - - /* ocsp_status_requested_by_s2n can be set in s2n_config_set_verification_ca_location. For - * backwards compatibility, this should tell clients to request OCSP stapling. However, this - * API should not tell servers to request OCSP stapling. - */ - for (int api_configuration_i = 0; api_configuration_i < 3; api_configuration_i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - switch (api_configuration_i) { - case 0: - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - break; - case 1: - /* If a user intentionally disables OCSP stapling, s2n_config_set_verification_ca_location - * should not re-enable it for servers. - */ - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - break; - default: - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - break; - } - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - if (mode == S2N_CLIENT) { - EXPECT_TRUE(conn->request_ocsp_status); - } else { - EXPECT_FALSE(conn->request_ocsp_status); - } - }; - - /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_OCSP should enable OCSP - * status requests, regardless of s2n_config_set_verification_ca_location. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_TRUE(conn->request_ocsp_status); - }; - - /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_NONE should disable OCSP - * status requests, regardless of s2n_config_set_verification_ca_location. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FALSE(conn->request_ocsp_status); - }; - }; - - /* Test s2n_config_add_cert_chain */ - { - uint32_t pem_len = 0; - uint8_t pem_bytes[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - pem_bytes, &pem_len, sizeof(pem_bytes))); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain(config, pem_bytes, pem_len)); - EXPECT_TRUE(config->no_signing_key); - EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); - - struct s2n_cert_chain_and_key *chain = s2n_config_get_single_default_cert(config); - POSIX_ENSURE_REF(chain); - EXPECT_FAILURE(s2n_pkey_check_key_exists(chain->private_key)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_NO_PRIVATE_KEY); - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_fn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - }; - - /* Test loading system certs */ - { - /* s2n_config_load_system_certs safety */ - { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(NULL), S2N_ERR_NULL); - } - - /* s2n_config_new_minimal should not load system certs */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_NULL(config->trust_store.trust_store); - EXPECT_FALSE(config->trust_store.loaded_system_certs); - - /* System certs can be loaded onto the minimal config */ - EXPECT_SUCCESS(s2n_config_load_system_certs(config)); - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - - /* Attempting to load system certs multiple times on the same config should error */ - for (int i = 0; i < 20; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - } - - /* s2n_config_new should load system certs */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - - /* Attempting to load system certs multiple times on the same config should error */ - for (int i = 0; i < 20; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - } - - /* The default config should load system certs */ - { - struct s2n_config *config = s2n_fetch_default_config(); - EXPECT_NOT_NULL(config); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - - /* System certs can be loaded again after wiping the trust store */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - for (int i = 0; i < 20; i++) { - /* System certs were already loaded, so an attempt to load them should fail */ - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - - EXPECT_SUCCESS(s2n_config_wipe_trust_store(config)); - - /* The trust store is cleared after a wipe, so it should be possible to load system certs again */ - EXPECT_FALSE(config->trust_store.loaded_system_certs); - EXPECT_SUCCESS(s2n_config_load_system_certs(config)); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - } - } - - /* Ensure that system certs are properly loaded into the X509_STORE. - * - * The API used to retrieve the contents of an X509_STORE, X509_STORE_get0_objects, - * wasn't added until OpenSSL 1.1.0. - */ -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_NULL(config->trust_store.trust_store); - EXPECT_FALSE(config->trust_store.loaded_system_certs); - - /* Initialize the X509_STORE by adding a cert */ - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&config->trust_store, S2N_RSA_PSS_2048_SHA256_CA_CERT, NULL)); - EXPECT_NOT_NULL(config->trust_store.trust_store); - EXPECT_FALSE(config->trust_store.loaded_system_certs); - - /* The X509_STORE should only contain the single cert that was added. */ - STACK_OF(X509_OBJECT) *x509_store_contents = X509_STORE_get0_objects(config->trust_store.trust_store); - EXPECT_NOT_NULL(x509_store_contents); - int initial_contents_count = sk_X509_OBJECT_num(x509_store_contents); - EXPECT_EQUAL(initial_contents_count, 1); - - /* Override the system cert file to guarantee that a system cert will be loaded */ - EXPECT_SUCCESS(setenv("SSL_CERT_FILE", S2N_SHA1_ROOT_SIGNATURE_CA_CERT, 1)); - - /* Load the system cert into the store */ - EXPECT_SUCCESS(s2n_config_load_system_certs(config)); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - int system_certs_contents_count = sk_X509_OBJECT_num(x509_store_contents); - - /* LibreSSL doesn't use the SSL_CERT_FILE environment variable to set the system cert location, - * so we don't know how many system certs will be loaded, if any. - */ - if (!s2n_libcrypto_is_libressl()) { - EXPECT_EQUAL(system_certs_contents_count, initial_contents_count + 1); - } - - /* Additional calls to s2n_config_load_default_certs should not add additional certs to the store */ - for (int i = 0; i < 20; i++) { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); - EXPECT_TRUE(config->trust_store.loaded_system_certs); - int additional_call_contents_count = sk_X509_OBJECT_num(x509_store_contents); - EXPECT_TRUE(additional_call_contents_count == system_certs_contents_count); - } - - EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); - } -#endif - - /* Self-talk tests */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Ensure a handshake succeeds with a minimal server config and no mutual auth */ - { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); - 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_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_FALSE(server_config->trust_store.loaded_system_certs); - EXPECT_NULL(server_config->trust_store.trust_store); - - EXPECT_FALSE(client_config->trust_store.loaded_system_certs); - EXPECT_NOT_NULL(client_config->trust_store.trust_store); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - - /* Ensure a handshake fails gracefully with an uninitialized trust store */ - { - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); - 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_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); - EXPECT_NOT_NULL(client_config); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_FALSE(server_config->trust_store.loaded_system_certs); - EXPECT_NULL(server_config->trust_store.trust_store); - - EXPECT_FALSE(client_config->trust_store.loaded_system_certs); - EXPECT_NULL(client_config->trust_store.trust_store); - - /* The client should fail to validate the server's certificate without an initialized trust store */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_UNTRUSTED); - } - } - } - - /* s2n_config_disable_x509_time_verification tests */ - { - /* Safety */ - EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_time_verification(NULL), S2N_ERR_NULL); - - /* Ensure s2n_config_disable_x509_time_verification sets the proper state */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_EQUAL(config->disable_x509_time_validation, false); - - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - EXPECT_EQUAL(config->disable_x509_time_validation, true); - } - } - - /* Test s2n_config_get_supported_groups */ - { - /* Safety */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; - uint16_t supported_groups_count = 0; - - int ret = s2n_config_get_supported_groups(NULL, supported_groups, s2n_array_len(supported_groups), - &supported_groups_count); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); - EXPECT_EQUAL(supported_groups_count, 0); - - ret = s2n_config_get_supported_groups(config, NULL, s2n_array_len(supported_groups), &supported_groups_count); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); - EXPECT_EQUAL(supported_groups_count, 0); - - ret = s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), NULL); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); - EXPECT_EQUAL(supported_groups_count, 0); - } - - /* Error if the provided supported groups array is too small */ - { - /* Test a policy with and without PQ kem groups */ - const char *policies[] = { - "20170210", - "PQ-TLS-1-2-2024-10-07", - }; - - for (size_t i = 0; i < s2n_array_len(policies); i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, policies[i])); - - uint32_t policy_groups_count = 0; - EXPECT_OK(s2n_kem_preferences_groups_available(config->security_policy->kem_preferences, - &policy_groups_count)); - policy_groups_count += config->security_policy->ecc_preferences->count; - EXPECT_TRUE(policy_groups_count > 0); - - uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; - uint16_t supported_groups_count = 11; - for (size_t invalid_count = 0; invalid_count < policy_groups_count; invalid_count++) { - int ret = s2n_config_get_supported_groups(config, supported_groups, invalid_count, - &supported_groups_count); - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INSUFFICIENT_MEM_SIZE); - EXPECT_EQUAL(supported_groups_count, 0); - } - - EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, policy_groups_count, - &supported_groups_count)); - EXPECT_EQUAL(supported_groups_count, policy_groups_count); - } - } - - /* The groups produced by s2n_config_get_supported_groups should match the groups produced - * by a connection that's configured to send its entire list of supported groups - */ - for (size_t policy_idx = 0; security_policy_selection[policy_idx].version != NULL; policy_idx++) { - const struct s2n_security_policy *security_policy = security_policy_selection[policy_idx].security_policy; - EXPECT_NOT_NULL(security_policy); - - uint32_t expected_groups_count = 0; - EXPECT_OK(s2n_kem_preferences_groups_available(security_policy->kem_preferences, &expected_groups_count)); - expected_groups_count += security_policy->ecc_preferences->count; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = security_policy; - - uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; - uint16_t supported_groups_count = 0; - EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), - &supported_groups_count)); - EXPECT_EQUAL(supported_groups_count, expected_groups_count); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - /* PQ kem groups aren't sent in the supported groups extension before TLS 1.3. */ - conn->actual_protocol_version = S2N_TLS13; - - DEFER_CLEANUP(struct s2n_stuffer extension_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension_stuffer, 0)); - EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &extension_stuffer)); - - uint16_t extension_groups_count = 0; - EXPECT_OK(s2n_supported_groups_parse_count(&extension_stuffer, &extension_groups_count)); - EXPECT_EQUAL(extension_groups_count, expected_groups_count); - - for (size_t i = 0; i < supported_groups_count; i++) { - /* s2n_stuffer_read_uint16 is used to read each of the supported groups in - * network-order endianness. - */ - uint16_t group_iana = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &group_iana)); - - EXPECT_EQUAL(group_iana, supported_groups[i]); - } - } - } - - /* Test s2n_config_validate_loaded_certificates */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *invalid_cert = NULL, s2n_cert_chain_and_key_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *valid_cert = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&invalid_cert, "ec", "ecdsa", "p384", "sha256")); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&valid_cert, "ec", "ecdsa", "p384", "sha384")); - - struct s2n_security_policy rfc9151_applied_locally = security_policy_20250429; - rfc9151_applied_locally.certificate_preferences_apply_locally = true; - - /* rfc9151 doesn't allow SHA256 signatures, but does allow SHA384 signatures, - * so ecdsa_p384_sha256 is invalid and ecdsa_p384_sha384 is valid */ - - /* valid certs are accepted */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); - EXPECT_OK(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally)); - }; - - /* when cert preferences don't apply locally, invalid certs are accepted */ - { - struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; - non_local_rfc9151.certificate_preferences_apply_locally = false; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, invalid_cert)); - EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); - }; - - /* Certs in an s2n_config are stored in default_certs_by_type, domain_name_to_cert_map, or - * both. We want to ensure that the s2n_config_validate_loaded_certificates method will - * validate certs in both locations. - */ - - /* certs in default_certs_by_type are validated */ - { - /* s2n_config_set_cert_chain_and_key_defaults populates default_certs_by_type - * but doesn't populate domain_name_to_cert_map - */ - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &invalid_cert, 1)); - - /* domain certs is empty */ - uint32_t domain_certs_count = 0; - EXPECT_OK(s2n_map_size(config->domain_name_to_cert_map, &domain_certs_count)); - EXPECT_EQUAL(domain_certs_count, 0); - - /* certs in default_certs_by_type are validated */ - EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - }; - - /* certs in the domain map are validated */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_build_domain_name_to_cert_map(config, invalid_cert)); - - /* default_certs_by_type is empty. */ - EXPECT_EQUAL(s2n_config_get_num_default_certs(config), 0); - - /* certs in domain_map are validated */ - EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - }; - - /* when cert preferences don't apply locally, certs in domain map are not iterated over - * - * Some customers load large numbers of certificates, so even iterating - * over every certificate without performing any validation is expensive. - */ - { - struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; - - /* Assert that the security policy WOULD apply, - * if certificate_preferences_apply_locally was true. - */ - EXPECT_NOT_NULL(non_local_rfc9151.certificate_key_preferences); - EXPECT_NOT_NULL(non_local_rfc9151.certificate_signature_preferences); - EXPECT_TRUE(non_local_rfc9151.certificate_key_preferences->count > 0); - EXPECT_TRUE(non_local_rfc9151.certificate_signature_preferences->count > 0); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); - - /* Invalidate the domain map so that attempting to use it will trigger - * an error. We want to ensure that we DON'T use it. - * Iterating over a map requires that map to be immutable / complete. - */ - EXPECT_OK(s2n_map_unlock(config->domain_name_to_cert_map)); - - /* Control case: if local validation needed, attempt to use invalid domain map */ - non_local_rfc9151.certificate_preferences_apply_locally = true; - EXPECT_ERROR_WITH_ERRNO( - s2n_config_validate_loaded_certificates(config, &non_local_rfc9151), - S2N_ERR_MAP_MUTABLE); - - /* Test case: if no local validation needed, do not use invalid domain map */ - non_local_rfc9151.certificate_preferences_apply_locally = false; - EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); - }; - }; - - /* Checks that servers don't use a config before the client hello callback is executed. - * - * We want to assert that a config is never used by a server until the client hello callback - * is called, given that users have the ability to swap out the config during this callback. - */ - { - DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_client_config); - /* Security policy that only supports TLS12 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); - - DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(tls13_client_config); - /* Security policy that supports TLS13 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "20240503")); - - struct s2n_config *config_arr[] = { tls12_client_config, tls13_client_config }; - - /* Checks that the handshake gets as far as the client hello callback with a NULL config */ - for (size_t i = 0; i < s2n_array_len(config_arr); i++) { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_arr[i])); - - /* Server config pointer is explicitly set to NULL */ - server_conn->config = NULL; - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &test_io)); - - /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the - * client hello callback. Therefore, we can assert that if we hit this error, we - * have gotten as far as the client hello callback without dereferencing the config. - */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); - } - } - - /* Checks that servers don't use a config before the client hello callback is executed on a - * SSLv2-formatted client hello. - * - * Parsing SSLv2 hellos uses a different code path and needs to be tested separately. - */ - { - uint8_t sslv2_client_hello[] = { - SSLv2_CLIENT_HELLO_PREFIX, - SSLv2_CLIENT_HELLO_CIPHER_SUITES, - SSLv2_CLIENT_HELLO_CHALLENGE, - }; - - struct s2n_blob client_hello = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&client_hello, sslv2_client_hello, sizeof(sslv2_client_hello))); - - /* Checks that the handshake gets as far as the client hello callback with a NULL config */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - server_conn->config = NULL; - - /* Record version and protocol version are in the header for SSLv2 */ - server_conn->client_hello.sslv2 = true; - server_conn->client_hello.legacy_version = S2N_TLS12; - - /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the - * client hello callback. Therefore, we can assert that if we hit this error, we - * have gotten as far as the client hello callback without dereferencing the config. - */ - EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); - EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); - } - } - - /* s2n_config_set_subscriber */ - { - /* Safety */ - uint64_t fake_subscriber = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (void *) &fake_subscriber), S2N_ERR_NULL); - }; - - /* s2n_config_set_handshake_event */ - { - /* Safety */ - uint64_t fake_callback = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (s2n_event_on_handshake_cb) &fake_callback), S2N_ERR_NULL); - }; - - /* Test: domain_name_to_cert_map remains usable after s2n_map_add fails - * - * Regression test: s2n_config_update_domain_name_to_cert_map - * calls s2n_map_unlock -> s2n_map_add -> s2n_map_complete. If s2n_map_add - * fails (e.g. OOM), the map was left in a mutable (!immutable) state. - * All subsequent s2n_map_lookup calls, used on every ClientHello for - * SNI-based cert selection, would then fail with S2N_ERR_MAP_MUTABLE, - * silently disabling SNI matching for the config's lifetime. - * - * The fix ensures s2n_map_complete is always called after unlock, - * even when add fails. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* The map starts empty and immutable. Verify a lookup succeeds. */ - { - struct s2n_blob lookup_name = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&lookup_name, - (uint8_t *) "test.example.com", strlen("test.example.com"))); - struct s2n_blob lookup_value = { 0 }; - bool found = false; - EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, - &lookup_name, &lookup_value, &found)); - EXPECT_FALSE(found); - } - - /* Create a cert chain to attempt adding. */ - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Install a malloc callback that always fails so s2n_map_add will - * fail during s2n_dup inside the unlock -> add -> complete path. - */ - s2n_mem_init_callback saved_init_cb = NULL; - s2n_mem_cleanup_callback saved_cleanup_cb = NULL; - s2n_mem_malloc_callback saved_malloc_cb = NULL; - s2n_mem_free_callback saved_free_cb = NULL; - EXPECT_OK(s2n_mem_get_callbacks(&saved_init_cb, &saved_cleanup_cb, - &saved_malloc_cb, &saved_free_cb)); - EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, - s2n_test_failing_malloc_cb, saved_free_cb)); - - /* The cert's SANs/CNs are not in the empty map, so the update - * function will take the !key_found branch: unlock -> add (FAIL). - * With the fix, complete is still called before returning the error. - */ - EXPECT_FAILURE(s2n_config_build_domain_name_to_cert_map(config, chain)); - - /* Restore the real malloc callback before any further allocations. */ - EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, - saved_malloc_cb, saved_free_cb)); - - /* The regression check: the map must still be immutable so that - * lookups succeed. Before the fix, this would fail with - * S2N_ERR_MAP_MUTABLE because s2n_map_complete was never called - * after the failed add. - */ - { - struct s2n_blob lookup_name = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&lookup_name, - (uint8_t *) "test.example.com", strlen("test.example.com"))); - struct s2n_blob lookup_value = { 0 }; - bool found = false; - EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, - &lookup_name, &lookup_value, &found)); - /* Lookup should succeed (not error) even though the key isn't found. */ - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_config.h" + +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_openssl.h" +#include "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_record.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "unstable/npn.h" +#include "utils/s2n_map.h" +#include "utils/s2n_mem.h" + +#define S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT 30 + +/* forward declaration */ +int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair); + +static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_identity_list) +{ + return S2N_SUCCESS; +} + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) +{ + return S2N_SUCCESS; +} + +static int s2n_test_crl_lookup_cb(struct s2n_crl_lookup *lookup, void *context) +{ + return S2N_SUCCESS; +} + +static int s2n_test_cert_validation_cb(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *context) +{ + return S2N_SUCCESS; +} + +static int s2n_test_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + return S2N_SUCCESS; +} + +/* A malloc callback that always fails. Used by the regression test + * to force s2n_map_add to fail during cert map construction. + */ +static int s2n_test_failing_malloc_cb(void **ptr, uint32_t requested, uint32_t *allocated) +{ + *ptr = NULL; + *allocated = 0; + POSIX_BAIL(S2N_ERR_ALLOC); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + const struct s2n_security_policy *default_security_policy = NULL, *tls13_security_policy = NULL, *fips_security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); + + char cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert, S2N_MAX_TEST_PEM_SIZE)); + char key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, key, S2N_MAX_TEST_PEM_SIZE)); + + /* Test: s2n_config_new and tls13_default_config match */ + { + struct s2n_config *config = NULL, *default_config = NULL; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(default_config = s2n_fetch_default_config()); + + /* s2n_config_new() matches s2n_fetch_default_config() */ + if (default_config->security_policy != config->security_policy) { + /* one possible cause for this is attempting to includes s2n_config.c + * for access to internal `static` functions. This causes two copies + * of the default config to be created. The default_config in *this* + * unit of translation doesn't get properly initialized. */ + const char *default_policy = NULL; + const char *new_policy = NULL; + EXPECT_OK(s2n_security_policy_get_version(default_config->security_policy, &default_policy)); + EXPECT_OK(s2n_security_policy_get_version(config->security_policy, &new_policy)); + printf("default policy is %s but new policy is %s\n", default_policy, new_policy); + } + EXPECT_EQUAL(default_config->security_policy, config->security_policy); + EXPECT_EQUAL(default_config->security_policy->signature_preferences, config->security_policy->signature_preferences); + EXPECT_EQUAL(default_config->client_cert_auth_type, config->client_cert_auth_type); + + /* Calling s2n_fetch_default_config() repeatedly returns the same object */ + EXPECT_EQUAL(default_config, s2n_fetch_default_config()); + + /* TLS1.3 default does not match non-TLS1.3 default */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_EQUAL(default_config, s2n_fetch_default_config()); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Connections created with default configs */ + { + /* For TLS1.2 */ + if (!s2n_is_in_fips_mode()) { + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, default_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* For TLS1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, tls13_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* For fips */ + if (s2n_is_in_fips_mode()) { + struct s2n_connection *conn = NULL; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, fips_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test for s2n_config_new() and tls 1.3 behavior */ + { + if (!s2n_is_in_fips_mode()) { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->security_policy, default_security_policy); + EXPECT_SUCCESS(s2n_config_free(config)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->security_policy, tls13_security_policy); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test setting the callback to select PSK identity */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + uint8_t context = 13; + + /* Safety check */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_psk_selection_callback( + NULL, s2n_test_select_psk_identity_callback, &context), + S2N_ERR_NULL); + EXPECT_NULL(config->psk_selection_cb); + EXPECT_NULL(config->psk_selection_ctx); + + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &context)); + EXPECT_EQUAL(config->psk_selection_cb, s2n_test_select_psk_identity_callback); + EXPECT_EQUAL(config->psk_selection_ctx, &context); + + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, NULL, NULL)); + EXPECT_NULL(config->psk_selection_cb); + EXPECT_NULL(config->psk_selection_ctx); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /*Test s2n_connection_set_config */ + { + /* Test that tickets_to_send is set correctly */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_config *config = NULL; + uint8_t num_tickets = 1; + + EXPECT_NOT_NULL(config = s2n_config_new()); + + config->initial_tickets_to_send = num_tickets; + + EXPECT_EQUAL(conn->tickets_to_send, 0); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->tickets_to_send, num_tickets); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that PSK type is set correctly */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); + + /* Overrides connection value */ + { + conn->config = NULL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_FALSE(conn->psk_mode_overridden); + }; + + /* Does not override connection value if conn->override_psk_mode set */ + { + conn->config = NULL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + conn->psk_mode_overridden = true; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_TRUE(conn->psk_mode_overridden); + conn->psk_mode_overridden = false; + }; + + /* Does not override connection value if PSKs already set */ + { + conn->config = NULL; + DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_FALSE(conn->psk_mode_overridden); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_config_set_session_tickets_onoff */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, true), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, false), S2N_ERR_NULL); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 1); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + EXPECT_FALSE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 1); + + config->initial_tickets_to_send = 10; + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 10); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* s2n_config_set_context */ + /* s2n_config_get_context */ + { + uint8_t context = 42; + uint8_t other = 123; + void *returned_context = NULL; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); + EXPECT_NULL(returned_context); + + EXPECT_SUCCESS(s2n_config_set_ctx(config, &context)); + EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); + EXPECT_NOT_NULL(returned_context); + + EXPECT_EQUAL(*((uint8_t *) returned_context), context); + EXPECT_NOT_EQUAL(*((uint8_t *) returned_context), other); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_config_set_extension_data */ + { + uint8_t extension_data[] = "extension data"; + + /* Test s2n_config_set_extension_data can be called for owned cert chains */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + + EXPECT_SUCCESS(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, + extension_data, sizeof(extension_data))); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, sizeof(extension_data)); + }; + + /* Test s2n_config_set_extension_data can't be called for unowned cert chains */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, + extension_data, sizeof(extension_data)), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, 0); + EXPECT_EQUAL(chain->ocsp_status.size, 0); + }; + }; + + /* Test s2n_config_free_cert_chain_and_key */ + { + /* Chain owned by application */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* No-op for application-owned chains */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Still no-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Chain owned by application and freed too early: + * This is arguably incorrect behavior, but did not cause errors in the past. + * We should continue to ensure it doesn't cause any errors. + */ + { + struct s2n_cert_chain_and_key *chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Free the chain early */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain)); + + /* No-op for application-owned chains */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* No-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Chain owned by library */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* No-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Switch from library-owned certs to application-owned certs */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Now add an application-owned chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + }; + }; + + /* Test s2n_config_set_cert_chain_and_key_defaults */ + { + /* Succeeds if chains owned by app */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_1 = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_1, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_2 = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_2, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_1)); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_1); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &chain_2, 1)); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_2); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + }; + + /* Fails if chains owned by library */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_chain_and_key_defaults( + config, &chain, 1), + S2N_ERR_CERT_OWNERSHIP); + }; + }; + + /* Test s2n_config_set_send_buffer_size */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_EQUAL(config->send_buffer_size_override, 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(NULL, S2N_MIN_SEND_BUFFER_SIZE), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(config, 0), S2N_ERR_INVALID_ARGUMENT); + EXPECT_EQUAL(config->send_buffer_size_override, 0); + }; + + /* Default applied to connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_EQUAL(config->send_buffer_size_override, 0); + EXPECT_FALSE(conn->multirecord_send); + }; + + /* Custom applied to connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, S2N_MIN_SEND_BUFFER_SIZE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_EQUAL(config->send_buffer_size_override, S2N_MIN_SEND_BUFFER_SIZE); + EXPECT_TRUE(conn->multirecord_send); + }; + }; + + /* Test s2n_config_set_verify_after_sign */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->verify_after_sign); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(NULL, S2N_VERIFY_AFTER_SIGN_ENABLED), S2N_ERR_NULL); + + /* Invalid mode */ + config->verify_after_sign = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); + EXPECT_TRUE(config->verify_after_sign); + config->verify_after_sign = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FALSE(config->verify_after_sign); + + /* Set and unset */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_TRUE(config->verify_after_sign); + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_DISABLED)); + EXPECT_FALSE(config->verify_after_sign); + }; + + /* Test s2n_config_set_renegotiate_request_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->renegotiate_request_cb, NULL); + EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_renegotiate_request_cb(NULL, s2n_test_reneg_req_cb, &context), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, &context)); + EXPECT_EQUAL(config->renegotiate_request_cb, s2n_test_reneg_req_cb); + EXPECT_EQUAL(config->renegotiate_request_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->renegotiate_request_cb, NULL); + EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); + }; + + /* Test s2n_config_set_npn */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->npn_supported); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_npn(NULL, true), S2N_ERR_NULL); + + /* Set and unset */ + EXPECT_SUCCESS(s2n_config_set_npn(config, true)); + EXPECT_TRUE(config->npn_supported); + EXPECT_SUCCESS(s2n_config_set_npn(config, false)); + EXPECT_FALSE(config->npn_supported); + }; + + /* Test s2n_config_set_crl_lookup_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->crl_lookup_cb, NULL); + EXPECT_EQUAL(config->crl_lookup_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_crl_lookup_cb(NULL, s2n_test_crl_lookup_cb, &context), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, &context)); + EXPECT_EQUAL(config->crl_lookup_cb, s2n_test_crl_lookup_cb); + EXPECT_EQUAL(config->crl_lookup_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->crl_lookup_cb, NULL); + EXPECT_EQUAL(config->crl_lookup_ctx, NULL); + }; + + /* Test s2n_config_set_cert_validation_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->cert_validation_cb, NULL); + EXPECT_EQUAL(config->cert_validation_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_validation_cb(NULL, s2n_test_cert_validation_cb, &context), + S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, &context)); + EXPECT_EQUAL(config->cert_validation_cb, s2n_test_cert_validation_cb); + EXPECT_EQUAL(config->cert_validation_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->cert_validation_cb, NULL); + EXPECT_EQUAL(config->cert_validation_ctx, NULL); + }; + + /* Test s2n_config_set_status_request_type */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + s2n_mode mode = modes[mode_i]; + + if (!s2n_x509_ocsp_stapling_supported()) { + break; + } + + /* request_ocsp_status should be false by default */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->ocsp_status_requested_by_user); + EXPECT_FALSE(config->ocsp_status_requested_by_s2n); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(conn->request_ocsp_status); + }; + + /* request_ocsp_status should be true if set via s2n_config_set_status_request_type */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_TRUE(config->ocsp_status_requested_by_user); + EXPECT_FALSE(config->ocsp_status_requested_by_s2n); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(conn->request_ocsp_status); + }; + + /* ocsp_status_requested_by_s2n can be set in s2n_config_set_verification_ca_location. For + * backwards compatibility, this should tell clients to request OCSP stapling. However, this + * API should not tell servers to request OCSP stapling. + */ + for (int api_configuration_i = 0; api_configuration_i < 3; api_configuration_i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + switch (api_configuration_i) { + case 0: + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + case 1: + /* If a user intentionally disables OCSP stapling, s2n_config_set_verification_ca_location + * should not re-enable it for servers. + */ + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + default: + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + if (mode == S2N_CLIENT) { + EXPECT_TRUE(conn->request_ocsp_status); + } else { + EXPECT_FALSE(conn->request_ocsp_status); + } + }; + + /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_OCSP should enable OCSP + * status requests, regardless of s2n_config_set_verification_ca_location. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(conn->request_ocsp_status); + }; + + /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_NONE should disable OCSP + * status requests, regardless of s2n_config_set_verification_ca_location. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(conn->request_ocsp_status); + }; + }; + + /* Test s2n_config_add_cert_chain */ + { + uint32_t pem_len = 0; + uint8_t pem_bytes[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + pem_bytes, &pem_len, sizeof(pem_bytes))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain(config, pem_bytes, pem_len)); + EXPECT_TRUE(config->no_signing_key); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + struct s2n_cert_chain_and_key *chain = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(chain); + EXPECT_FAILURE(s2n_pkey_check_key_exists(chain->private_key)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_NO_PRIVATE_KEY); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_fn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + }; + + /* Test loading system certs */ + { + /* s2n_config_load_system_certs safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(NULL), S2N_ERR_NULL); + } + + /* s2n_config_new_minimal should not load system certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* System certs can be loaded onto the minimal config */ + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + + /* Attempting to load system certs multiple times on the same config should error */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* s2n_config_new should load system certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + + /* Attempting to load system certs multiple times on the same config should error */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* The default config should load system certs */ + { + struct s2n_config *config = s2n_fetch_default_config(); + EXPECT_NOT_NULL(config); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + + /* System certs can be loaded again after wiping the trust store */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + for (int i = 0; i < 20; i++) { + /* System certs were already loaded, so an attempt to load them should fail */ + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(config)); + + /* The trust store is cleared after a wipe, so it should be possible to load system certs again */ + EXPECT_FALSE(config->trust_store.loaded_system_certs); + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* Ensure that system certs are properly loaded into the X509_STORE. + * + * The API used to retrieve the contents of an X509_STORE, X509_STORE_get0_objects, + * wasn't added until OpenSSL 1.1.0. + */ +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* Initialize the X509_STORE by adding a cert */ + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&config->trust_store, S2N_RSA_PSS_2048_SHA256_CA_CERT, NULL)); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* The X509_STORE should only contain the single cert that was added. */ + STACK_OF(X509_OBJECT) *x509_store_contents = X509_STORE_get0_objects(config->trust_store.trust_store); + EXPECT_NOT_NULL(x509_store_contents); + int initial_contents_count = sk_X509_OBJECT_num(x509_store_contents); + EXPECT_EQUAL(initial_contents_count, 1); + + /* Override the system cert file to guarantee that a system cert will be loaded */ + EXPECT_SUCCESS(setenv("SSL_CERT_FILE", S2N_SHA1_ROOT_SIGNATURE_CA_CERT, 1)); + + /* Load the system cert into the store */ + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + int system_certs_contents_count = sk_X509_OBJECT_num(x509_store_contents); + + /* LibreSSL doesn't use the SSL_CERT_FILE environment variable to set the system cert location, + * so we don't know how many system certs will be loaded, if any. + */ + if (!s2n_libcrypto_is_libressl()) { + EXPECT_EQUAL(system_certs_contents_count, initial_contents_count + 1); + } + + /* Additional calls to s2n_config_load_default_certs should not add additional certs to the store */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + int additional_call_contents_count = sk_X509_OBJECT_num(x509_store_contents); + EXPECT_TRUE(additional_call_contents_count == system_certs_contents_count); + } + + EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); + } +#endif + + /* Self-talk tests */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Ensure a handshake succeeds with a minimal server config and no mutual auth */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + 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_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_FALSE(server_config->trust_store.loaded_system_certs); + EXPECT_NULL(server_config->trust_store.trust_store); + + EXPECT_FALSE(client_config->trust_store.loaded_system_certs); + EXPECT_NOT_NULL(client_config->trust_store.trust_store); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + /* Ensure a handshake fails gracefully with an uninitialized trust store */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + 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_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_NOT_NULL(client_config); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_FALSE(server_config->trust_store.loaded_system_certs); + EXPECT_NULL(server_config->trust_store.trust_store); + + EXPECT_FALSE(client_config->trust_store.loaded_system_certs); + EXPECT_NULL(client_config->trust_store.trust_store); + + /* The client should fail to validate the server's certificate without an initialized trust store */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_UNTRUSTED); + } + } + } + + /* s2n_config_disable_x509_time_verification tests */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_time_verification(NULL), S2N_ERR_NULL); + + /* Ensure s2n_config_disable_x509_time_verification sets the proper state */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->disable_x509_time_validation, false); + + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + EXPECT_EQUAL(config->disable_x509_time_validation, true); + } + } + + /* Test s2n_config_get_supported_groups */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + + int ret = s2n_config_get_supported_groups(NULL, supported_groups, s2n_array_len(supported_groups), + &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + + ret = s2n_config_get_supported_groups(config, NULL, s2n_array_len(supported_groups), &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + + ret = s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), NULL); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + } + + /* Error if the provided supported groups array is too small */ + { + /* Test a policy with and without PQ kem groups */ + const char *policies[] = { + "20170210", + "PQ-TLS-1-2-2024-10-07", + }; + + for (size_t i = 0; i < s2n_array_len(policies); i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, policies[i])); + + uint32_t policy_groups_count = 0; + EXPECT_OK(s2n_kem_preferences_groups_available(config->security_policy->kem_preferences, + &policy_groups_count)); + policy_groups_count += config->security_policy->ecc_preferences->count; + EXPECT_TRUE(policy_groups_count > 0); + + uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; + uint16_t supported_groups_count = 11; + for (size_t invalid_count = 0; invalid_count < policy_groups_count; invalid_count++) { + int ret = s2n_config_get_supported_groups(config, supported_groups, invalid_count, + &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_EQUAL(supported_groups_count, 0); + } + + EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, policy_groups_count, + &supported_groups_count)); + EXPECT_EQUAL(supported_groups_count, policy_groups_count); + } + } + + /* The groups produced by s2n_config_get_supported_groups should match the groups produced + * by a connection that's configured to send its entire list of supported groups + */ + for (size_t policy_idx = 0; security_policy_selection[policy_idx].version != NULL; policy_idx++) { + const struct s2n_security_policy *security_policy = security_policy_selection[policy_idx].security_policy; + EXPECT_NOT_NULL(security_policy); + + uint32_t expected_groups_count = 0; + EXPECT_OK(s2n_kem_preferences_groups_available(security_policy->kem_preferences, &expected_groups_count)); + expected_groups_count += security_policy->ecc_preferences->count; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = security_policy; + + uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), + &supported_groups_count)); + EXPECT_EQUAL(supported_groups_count, expected_groups_count); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + /* PQ kem groups aren't sent in the supported groups extension before TLS 1.3. */ + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer extension_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension_stuffer, 0)); + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &extension_stuffer)); + + uint16_t extension_groups_count = 0; + EXPECT_OK(s2n_supported_groups_parse_count(&extension_stuffer, &extension_groups_count)); + EXPECT_EQUAL(extension_groups_count, expected_groups_count); + + for (size_t i = 0; i < supported_groups_count; i++) { + /* s2n_stuffer_read_uint16 is used to read each of the supported groups in + * network-order endianness. + */ + uint16_t group_iana = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &group_iana)); + + EXPECT_EQUAL(group_iana, supported_groups[i]); + } + } + } + + /* Test s2n_config_validate_loaded_certificates */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *invalid_cert = NULL, s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *valid_cert = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&invalid_cert, "ec", "ecdsa", "p384", "sha256")); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&valid_cert, "ec", "ecdsa", "p384", "sha384")); + + struct s2n_security_policy rfc9151_applied_locally = security_policy_20250429; + rfc9151_applied_locally.certificate_preferences_apply_locally = true; + + /* rfc9151 doesn't allow SHA256 signatures, but does allow SHA384 signatures, + * so ecdsa_p384_sha256 is invalid and ecdsa_p384_sha384 is valid */ + + /* valid certs are accepted */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); + EXPECT_OK(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally)); + }; + + /* when cert preferences don't apply locally, invalid certs are accepted */ + { + struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; + non_local_rfc9151.certificate_preferences_apply_locally = false; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, invalid_cert)); + EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); + }; + + /* Certs in an s2n_config are stored in default_certs_by_type, domain_name_to_cert_map, or + * both. We want to ensure that the s2n_config_validate_loaded_certificates method will + * validate certs in both locations. + */ + + /* certs in default_certs_by_type are validated */ + { + /* s2n_config_set_cert_chain_and_key_defaults populates default_certs_by_type + * but doesn't populate domain_name_to_cert_map + */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &invalid_cert, 1)); + + /* domain certs is empty */ + uint32_t domain_certs_count = 0; + EXPECT_OK(s2n_map_size(config->domain_name_to_cert_map, &domain_certs_count)); + EXPECT_EQUAL(domain_certs_count, 0); + + /* certs in default_certs_by_type are validated */ + EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + }; + + /* certs in the domain map are validated */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_build_domain_name_to_cert_map(config, invalid_cert)); + + /* default_certs_by_type is empty. */ + EXPECT_EQUAL(s2n_config_get_num_default_certs(config), 0); + + /* certs in domain_map are validated */ + EXPECT_ERROR_WITH_ERRNO(s2n_config_validate_loaded_certificates(config, &rfc9151_applied_locally), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + }; + + /* when cert preferences don't apply locally, certs in domain map are not iterated over + * + * Some customers load large numbers of certificates, so even iterating + * over every certificate without performing any validation is expensive. + */ + { + struct s2n_security_policy non_local_rfc9151 = security_policy_20250429; + + /* Assert that the security policy WOULD apply, + * if certificate_preferences_apply_locally was true. + */ + EXPECT_NOT_NULL(non_local_rfc9151.certificate_key_preferences); + EXPECT_NOT_NULL(non_local_rfc9151.certificate_signature_preferences); + EXPECT_TRUE(non_local_rfc9151.certificate_key_preferences->count > 0); + EXPECT_TRUE(non_local_rfc9151.certificate_signature_preferences->count > 0); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, valid_cert)); + + /* Invalidate the domain map so that attempting to use it will trigger + * an error. We want to ensure that we DON'T use it. + * Iterating over a map requires that map to be immutable / complete. + */ + EXPECT_OK(s2n_map_unlock(config->domain_name_to_cert_map)); + + /* Control case: if local validation needed, attempt to use invalid domain map */ + non_local_rfc9151.certificate_preferences_apply_locally = true; + EXPECT_ERROR_WITH_ERRNO( + s2n_config_validate_loaded_certificates(config, &non_local_rfc9151), + S2N_ERR_MAP_MUTABLE); + + /* Test case: if no local validation needed, do not use invalid domain map */ + non_local_rfc9151.certificate_preferences_apply_locally = false; + EXPECT_OK(s2n_config_validate_loaded_certificates(config, &non_local_rfc9151)); + }; + }; + + /* Checks that servers don't use a config before the client hello callback is executed. + * + * We want to assert that a config is never used by a server until the client hello callback + * is called, given that users have the ability to swap out the config during this callback. + */ + { + DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_client_config); + /* Security policy that only supports TLS12 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); + + DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(tls13_client_config); + /* Security policy that supports TLS13 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "20240503")); + + struct s2n_config *config_arr[] = { tls12_client_config, tls13_client_config }; + + /* Checks that the handshake gets as far as the client hello callback with a NULL config */ + for (size_t i = 0; i < s2n_array_len(config_arr); i++) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_arr[i])); + + /* Server config pointer is explicitly set to NULL */ + server_conn->config = NULL; + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &test_io)); + + /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the + * client hello callback. Therefore, we can assert that if we hit this error, we + * have gotten as far as the client hello callback without dereferencing the config. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); + } + } + + /* Checks that servers don't use a config before the client hello callback is executed on a + * SSLv2-formatted client hello. + * + * Parsing SSLv2 hellos uses a different code path and needs to be tested separately. + */ + { + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&client_hello, sslv2_client_hello, sizeof(sslv2_client_hello))); + + /* Checks that the handshake gets as far as the client hello callback with a NULL config */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->config = NULL; + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello.sslv2 = true; + server_conn->client_hello.legacy_version = S2N_TLS12; + + /* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the + * client hello callback. Therefore, we can assert that if we hit this error, we + * have gotten as far as the client hello callback without dereferencing the config. + */ + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); + } + } + + /* s2n_config_set_subscriber */ + { + /* Safety */ + uint64_t fake_subscriber = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (void *) &fake_subscriber), S2N_ERR_NULL); + }; + + /* s2n_config_set_handshake_event */ + { + /* Safety */ + uint64_t fake_callback = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_subscriber(NULL, (s2n_event_on_handshake_cb) &fake_callback), S2N_ERR_NULL); + }; + + /* Test: domain_name_to_cert_map remains usable after s2n_map_add fails + * + * Regression test: s2n_config_update_domain_name_to_cert_map + * calls s2n_map_unlock -> s2n_map_add -> s2n_map_complete. If s2n_map_add + * fails (e.g. OOM), the map was left in a mutable (!immutable) state. + * All subsequent s2n_map_lookup calls, used on every ClientHello for + * SNI-based cert selection, would then fail with S2N_ERR_MAP_MUTABLE, + * silently disabling SNI matching for the config's lifetime. + * + * The fix ensures s2n_map_complete is always called after unlock, + * even when add fails. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* The map starts empty and immutable. Verify a lookup succeeds. */ + { + struct s2n_blob lookup_name = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&lookup_name, + (uint8_t *) "test.example.com", strlen("test.example.com"))); + struct s2n_blob lookup_value = { 0 }; + bool found = false; + EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, + &lookup_name, &lookup_value, &found)); + EXPECT_FALSE(found); + } + + /* Create a cert chain to attempt adding. */ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Install a malloc callback that always fails so s2n_map_add will + * fail during s2n_dup inside the unlock -> add -> complete path. + */ + s2n_mem_init_callback saved_init_cb = NULL; + s2n_mem_cleanup_callback saved_cleanup_cb = NULL; + s2n_mem_malloc_callback saved_malloc_cb = NULL; + s2n_mem_free_callback saved_free_cb = NULL; + EXPECT_OK(s2n_mem_get_callbacks(&saved_init_cb, &saved_cleanup_cb, + &saved_malloc_cb, &saved_free_cb)); + EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, + s2n_test_failing_malloc_cb, saved_free_cb)); + + /* The cert's SANs/CNs are not in the empty map, so the update + * function will take the !key_found branch: unlock -> add (FAIL). + * With the fix, complete is still called before returning the error. + */ + EXPECT_FAILURE(s2n_config_build_domain_name_to_cert_map(config, chain)); + + /* Restore the real malloc callback before any further allocations. */ + EXPECT_OK(s2n_mem_override_callbacks(saved_init_cb, saved_cleanup_cb, + saved_malloc_cb, saved_free_cb)); + + /* The regression check: the map must still be immutable so that + * lookups succeed. Before the fix, this would fail with + * S2N_ERR_MAP_MUTABLE because s2n_map_complete was never called + * after the failed add. + */ + { + struct s2n_blob lookup_name = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&lookup_name, + (uint8_t *) "test.example.com", strlen("test.example.com"))); + struct s2n_blob lookup_value = { 0 }; + bool found = false; + EXPECT_OK(s2n_map_lookup(config->domain_name_to_cert_map, + &lookup_name, &lookup_value, &found)); + /* Lookup should succeed (not error) even though the key isn't found. */ + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_protocol_versions_test.c b/tests/unit/s2n_connection_protocol_versions_test.c index fa931cdf3ac..9e06822105d 100644 --- a/tests/unit/s2n_connection_protocol_versions_test.c +++ b/tests/unit/s2n_connection_protocol_versions_test.c @@ -1,391 +1,391 @@ -#include "utils/s2n_prelude.h" -/* -* 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 "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" - -static S2N_RESULT s2n_write_test_supported_versions_extension(struct s2n_blob *supported_versions_blob, uint8_t version, - uint8_t extension_length) -{ - RESULT_ENSURE_REF(supported_versions_blob); - - struct s2n_stuffer supported_versions_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&supported_versions_stuffer, supported_versions_blob)); - - /* Write the length byte. */ - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, extension_length)); - /* Write the supported version. */ - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version / 10)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version % 10)); - - return S2N_RESULT_OK; -} - -struct s2n_overwrite_client_hello_ctx { - uint8_t client_hello_version; - uint8_t client_supported_version; - uint8_t extension_length; - - uint8_t supported_versions_data[3]; - int invoked_count; -}; - -static int s2n_overwrite_client_hello_cb(struct s2n_connection *conn, void *ctx) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(ctx); - - struct s2n_overwrite_client_hello_ctx *context = (struct s2n_overwrite_client_hello_ctx *) ctx; - context->invoked_count += 1; - - if (context->extension_length) { - struct s2n_blob supported_versions_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&supported_versions_blob, context->supported_versions_data, - sizeof(context->supported_versions_data))); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); - EXPECT_NOT_NULL(client_hello); - - s2n_extension_type_id supported_versions_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); - s2n_parsed_extension *extension = &client_hello->extensions.parsed_extensions[supported_versions_id]; - - EXPECT_OK(s2n_write_test_supported_versions_extension(&supported_versions_blob, - context->client_supported_version, context->extension_length)); - - extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; - extension->extension = supported_versions_blob; - } - - /* The client version fields are set when parsing the client hello before the client hello - * callback is invoked. The version fields are overridden to emulate receiving a client hello - * with a different version. - */ - if (context->client_hello_version) { - conn->client_hello.legacy_version = context->client_hello_version; - conn->client_protocol_version = context->client_hello_version; - } - - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Safety */ - { - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_protocol_version(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_hello_version(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_server_protocol_version(NULL), S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_actual_protocol_version(NULL), S2N_ERR_NULL); - } - - /* Test protocol version getters on the server when a supported versions extension is received */ - for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - for (uint8_t client_supported_version = S2N_SSLv3; client_supported_version <= S2N_TLS13; client_supported_version++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - if (server_version == S2N_TLS12) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - } else if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - } else { - continue; - } - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - .client_supported_version = client_supported_version, - .extension_length = 2, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); - - /* The reported client protocol version should always match the version specified - * in the supported versions extension, even for TLS 1.2 servers which don't - * process the extension for version selection. - */ - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_supported_version); - - uint8_t actual_protocol_version = s2n_connection_get_actual_protocol_version(server); - if (server_version == S2N_TLS12) { - /* For backwards compatibility, TLS 1.2 servers always use the client hello - * version to determine the client's maximum version, even if a supported - * versions extension was received. - */ - EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_hello_version)); - } else { - /* TLS 1.3 servers always use the version in the supported versions extension, - * regardless of the client hello version. - */ - EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_supported_version)); - } - } - } - } - - /* Test protocol version getters on the server when a supported versions extension isn't received */ - for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - - /* A TLS 1.2 security policy is set to prevent the client from sending a supported - * versions extension. - */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - if (server_version == S2N_TLS12) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_tls12")); - } else if (s2n_is_tls13_fully_supported()) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - } else { - continue; - } - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension wasn't received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_FALSE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), client_hello_version); - } - } - - /* Test protocol version getters on the client */ - for (uint8_t server_version = S2N_SSLv3; server_version <= S2N_TLS13; server_version++) { - if (server_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { - continue; - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - server->server_protocol_version = server_version; - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(client), server_version); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(client), s2n_get_highest_fully_supported_tls_version()); - EXPECT_EQUAL(s2n_connection_get_client_hello_version(client), S2N_TLS12); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(client), server_version); - } - - /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version - * if a malformed supported versions extension was received - */ - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - .client_supported_version = S2N_TLS13, - /* Write an invalid length */ - .extension_length = 11, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); - } - - /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version - * if an invalid supported version is received - */ - for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - - struct s2n_overwrite_client_hello_ctx context = { - .client_hello_version = client_hello_version, - /* Write an invalid version */ - .client_supported_version = S2N_TLS13 + 10, - .extension_length = 2, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); - } - - /* Ensure that TLS 1.3 servers report an unknown protocol version if a supported versions - * extension can't be processed - */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); - - struct s2n_overwrite_client_hello_ctx context = { - .client_supported_version = S2N_TLS13, - /* Write an invalid length */ - .extension_length = 11, - }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), - S2N_ERR_BAD_MESSAGE); - EXPECT_EQUAL(context.invoked_count, 1); - - /* Ensure that a supported versions extension was received. */ - bool supported_versions_received = false; - EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, - &supported_versions_received)); - EXPECT_TRUE(supported_versions_received); - - EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), S2N_TLS13); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), s2n_unknown_protocol_version); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), s2n_unknown_protocol_version); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static S2N_RESULT s2n_write_test_supported_versions_extension(struct s2n_blob *supported_versions_blob, uint8_t version, + uint8_t extension_length) +{ + RESULT_ENSURE_REF(supported_versions_blob); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&supported_versions_stuffer, supported_versions_blob)); + + /* Write the length byte. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, extension_length)); + /* Write the supported version. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version / 10)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version % 10)); + + return S2N_RESULT_OK; +} + +struct s2n_overwrite_client_hello_ctx { + uint8_t client_hello_version; + uint8_t client_supported_version; + uint8_t extension_length; + + uint8_t supported_versions_data[3]; + int invoked_count; +}; + +static int s2n_overwrite_client_hello_cb(struct s2n_connection *conn, void *ctx) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ctx); + + struct s2n_overwrite_client_hello_ctx *context = (struct s2n_overwrite_client_hello_ctx *) ctx; + context->invoked_count += 1; + + if (context->extension_length) { + struct s2n_blob supported_versions_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&supported_versions_blob, context->supported_versions_data, + sizeof(context->supported_versions_data))); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + EXPECT_NOT_NULL(client_hello); + + s2n_extension_type_id supported_versions_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + s2n_parsed_extension *extension = &client_hello->extensions.parsed_extensions[supported_versions_id]; + + EXPECT_OK(s2n_write_test_supported_versions_extension(&supported_versions_blob, + context->client_supported_version, context->extension_length)); + + extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; + extension->extension = supported_versions_blob; + } + + /* The client version fields are set when parsing the client hello before the client hello + * callback is invoked. The version fields are overridden to emulate receiving a client hello + * with a different version. + */ + if (context->client_hello_version) { + conn->client_hello.legacy_version = context->client_hello_version; + conn->client_protocol_version = context->client_hello_version; + } + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_hello_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_server_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_actual_protocol_version(NULL), S2N_ERR_NULL); + } + + /* Test protocol version getters on the server when a supported versions extension is received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + for (uint8_t client_supported_version = S2N_SSLv3; client_supported_version <= S2N_TLS13; client_supported_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = client_supported_version, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + + /* The reported client protocol version should always match the version specified + * in the supported versions extension, even for TLS 1.2 servers which don't + * process the extension for version selection. + */ + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_supported_version); + + uint8_t actual_protocol_version = s2n_connection_get_actual_protocol_version(server); + if (server_version == S2N_TLS12) { + /* For backwards compatibility, TLS 1.2 servers always use the client hello + * version to determine the client's maximum version, even if a supported + * versions extension was received. + */ + EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_hello_version)); + } else { + /* TLS 1.3 servers always use the version in the supported versions extension, + * regardless of the client hello version. + */ + EXPECT_EQUAL(actual_protocol_version, S2N_MIN(server_version, client_supported_version)); + } + } + } + } + + /* Test protocol version getters on the server when a supported versions extension isn't received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + + /* A TLS 1.2 security policy is set to prevent the client from sending a supported + * versions extension. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension wasn't received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_FALSE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), client_hello_version); + } + } + + /* Test protocol version getters on the client */ + for (uint8_t server_version = S2N_SSLv3; server_version <= S2N_TLS13; server_version++) { + if (server_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { + continue; + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + server->server_protocol_version = server_version; + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(client), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(client), s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(client), S2N_TLS12); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(client), server_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if a malformed supported versions extension was received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if an invalid supported version is received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + /* Write an invalid version */ + .client_supported_version = S2N_TLS13 + 10, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.3 servers report an unknown protocol version if a supported versions + * extension can't be processed + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), S2N_TLS13); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), s2n_unknown_protocol_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), s2n_unknown_protocol_version); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_cookie_test.c b/tests/unit/s2n_cookie_test.c index 132acda036a..6fb3837ff43 100644 --- a/tests/unit/s2n_cookie_test.c +++ b/tests/unit/s2n_cookie_test.c @@ -1,377 +1,377 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_cookie.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_handshake_type.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -#define TEST_COOKIE_COUNT 5 - -int main() -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - uint16_t test_cookie_sizes[TEST_COOKIE_COUNT] = { 1, UINT8_MAX, UINT8_MAX + 1, UINT16_MAX - 1, UINT16_MAX }; - struct s2n_blob test_cookies[TEST_COOKIE_COUNT] = { 0 }; - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - EXPECT_SUCCESS(s2n_alloc(&test_cookies[i], test_cookie_sizes[i])); - EXPECT_OK(s2n_get_public_random_data(&test_cookies[i])); - } - - /** - * Test: client only sends extension if cookie present - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.2 - *= type=test - *# - Including a "cookie" extension if one was provided in the - *# HelloRetryRequest. - **/ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - - /* Not sent without a cookie */ - EXPECT_FALSE(s2n_client_cookie_extension.should_send(client_conn)); - - /* Sent with a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &client_conn->cookie)); - EXPECT_TRUE(s2n_client_cookie_extension.should_send(client_conn)); - }; - - /* Test: server only sends extension if cookie present - * (cookie will never be present in production) - */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - /* Not sent without a cookie */ - EXPECT_FALSE(s2n_server_cookie_extension.should_send(server_conn)); - - /* Sent with a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); - EXPECT_TRUE(s2n_server_cookie_extension.should_send(server_conn)); - }; - - /* Test: client can parse server cookie extension */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_stuffer server_extension = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_extension, 0)); - - /* Server sends extension with test cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - EXPECT_SUCCESS(s2n_server_cookie_extension.send(server_conn, &server_extension)); - - /* Client doesn't parse extension if no retry */ - EXPECT_FAILURE_WITH_ERRNO(s2n_server_cookie_extension.recv(client_conn, &server_extension), - S2N_ERR_UNSUPPORTED_EXTENSION); - - /* Client parses extension if retry */ - client_conn->handshake.handshake_type = HELLO_RETRY_REQUEST; - EXPECT_SUCCESS(s2n_server_cookie_extension.recv(client_conn, &server_extension)); - S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); - } - - /* Test: client sends correctly formatted extension */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_stuffer client_extension = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_extension, 0)); - - /* Client sends extension with test cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &client_conn->cookie)); - EXPECT_SUCCESS(s2n_client_cookie_extension.send(client_conn, &client_extension)); - - /* Sanity check: Server rejects incorrectly sized cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - server_conn->cookie.size--; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), - S2N_ERR_BAD_MESSAGE); - EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); - EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); - - /* Sanity check: Server rejects incorrect cookie data */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - server_conn->cookie.data[0] = server_conn->cookie.data[0] + 1; - EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), - S2N_ERR_BAD_MESSAGE); - EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); - EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); - - /* Server accepts correct cookie data */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - EXPECT_SUCCESS(s2n_client_cookie_extension.recv(server_conn, &client_extension)); - } - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* Although the cookie is *technically* allowed to be UINT16_MAX, - * in reality it has to share a uint16_t extensions list length - * with other extensions. - * - * So for the self-talk tests, reduce the size of any large test cookies. - */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - test_cookies[i].size = S2N_MIN(test_cookies[i].size, UINT16_MAX / 2); - } - - /* Sanity check: server fails if client does not provide expected cookie */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Force the server to send a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); - - /* Begin negotiating handshake. - * The first negotiate_until blocks because the client is looking for a SERVER_HELLO, - * not the HELLO_RETRY_MESSAGE. This is fine; it's in the right place in the handshake. - */ - EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, HELLO_RETRY_MSG), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_HELLO)); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - - /* Verify HRR path */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - - /* At this point, the server has already sent its HRR request with a cookie. - * The client has stored the server's cookie, but not responded. - * Wipe the cookie on the client, preventing it from sending the response. - */ - EXPECT_NOT_EQUAL(client_conn->cookie.size, 0); - EXPECT_SUCCESS(s2n_free(&client_conn->cookie)); - - /* Continue negotiating. We should fail because of the "missing" cookie. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_MISSING_EXTENSION); - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Self-Talk: Server does NOT use cookies */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify HRR path */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - - /* Verify no cookies */ - EXPECT_EQUAL(client_conn->cookie.size, 0); - EXPECT_EQUAL(server_conn->cookie.size, 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Self-Talk: Server does use cookies - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.2 - *= type=test - *# When sending a HelloRetryRequest, the server MAY provide a "cookie" - *# extension to the client (this is an exception to the usual rule that - *# the only extensions that may be sent are those that appear in the - *# ClientHello). When sending the new ClientHello, the client MUST copy - *# the contents of the extension received in the HelloRetryRequest into - *# a "cookie" extension in the new ClientHello. - */ - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Force the server to send a cookie */ - EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify HRR path */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - - /* Verify cookies */ - S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); - S2N_BLOB_EXPECT_EQUAL(test_cookies[i], server_conn->cookie); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - /* Self-Talk: Full connection lifecycle with cookies - * We try the handshake multiple times with different possible call patterns. - */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* We need an arbitrary combination of conditions, - * but consistent across test runs. - */ - srand(0); - - for (size_t i = 0; i < 250; i++) { - int r = rand(); - bool hrr = (r % 2) == 0; - bool cookie = (r % 3) == 0; - size_t cookie_i = i % TEST_COOKIE_COUNT; - bool free_handshake = (r % 7) == 0; - - /* Verify calls to s2n_connection_wipe are safe */ - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the HRR path */ - if (hrr) { - client_conn->security_policy_override = &security_policy_test_tls13_retry; - } - - /* Force the server to send a cookie */ - if (cookie) { - EXPECT_SUCCESS(s2n_dup(&test_cookies[cookie_i], &server_conn->cookie)); - } - - /* Negotiate handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify HRR path */ - if (hrr) { - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_handshake(client_conn)); - } else { - EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_FALSE(s2n_is_hello_retry_handshake(client_conn)); - } - - /* Verify cookie data */ - if (hrr && cookie) { - S2N_BLOB_EXPECT_EQUAL(test_cookies[cookie_i], client_conn->cookie); - } else { - EXPECT_EQUAL(client_conn->cookie.size, 0); - } - - if (free_handshake) { - EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); - EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); - } - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - }; - - for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { - EXPECT_SUCCESS(s2n_free(&test_cookies[i])); - } - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_cookie.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake_type.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +#define TEST_COOKIE_COUNT 5 + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + uint16_t test_cookie_sizes[TEST_COOKIE_COUNT] = { 1, UINT8_MAX, UINT8_MAX + 1, UINT16_MAX - 1, UINT16_MAX }; + struct s2n_blob test_cookies[TEST_COOKIE_COUNT] = { 0 }; + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + EXPECT_SUCCESS(s2n_alloc(&test_cookies[i], test_cookie_sizes[i])); + EXPECT_OK(s2n_get_public_random_data(&test_cookies[i])); + } + + /** + * Test: client only sends extension if cookie present + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.2 + *= type=test + *# - Including a "cookie" extension if one was provided in the + *# HelloRetryRequest. + **/ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + /* Not sent without a cookie */ + EXPECT_FALSE(s2n_client_cookie_extension.should_send(client_conn)); + + /* Sent with a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &client_conn->cookie)); + EXPECT_TRUE(s2n_client_cookie_extension.should_send(client_conn)); + }; + + /* Test: server only sends extension if cookie present + * (cookie will never be present in production) + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Not sent without a cookie */ + EXPECT_FALSE(s2n_server_cookie_extension.should_send(server_conn)); + + /* Sent with a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); + EXPECT_TRUE(s2n_server_cookie_extension.should_send(server_conn)); + }; + + /* Test: client can parse server cookie extension */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer server_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_extension, 0)); + + /* Server sends extension with test cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + EXPECT_SUCCESS(s2n_server_cookie_extension.send(server_conn, &server_extension)); + + /* Client doesn't parse extension if no retry */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_cookie_extension.recv(client_conn, &server_extension), + S2N_ERR_UNSUPPORTED_EXTENSION); + + /* Client parses extension if retry */ + client_conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_server_cookie_extension.recv(client_conn, &server_extension)); + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); + } + + /* Test: client sends correctly formatted extension */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer client_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_extension, 0)); + + /* Client sends extension with test cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &client_conn->cookie)); + EXPECT_SUCCESS(s2n_client_cookie_extension.send(client_conn, &client_extension)); + + /* Sanity check: Server rejects incorrectly sized cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + server_conn->cookie.size--; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); + EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); + + /* Sanity check: Server rejects incorrect cookie data */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + server_conn->cookie.data[0] = server_conn->cookie.data[0] + 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); + EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); + + /* Server accepts correct cookie data */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + EXPECT_SUCCESS(s2n_client_cookie_extension.recv(server_conn, &client_extension)); + } + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* Although the cookie is *technically* allowed to be UINT16_MAX, + * in reality it has to share a uint16_t extensions list length + * with other extensions. + * + * So for the self-talk tests, reduce the size of any large test cookies. + */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + test_cookies[i].size = S2N_MIN(test_cookies[i].size, UINT16_MAX / 2); + } + + /* Sanity check: server fails if client does not provide expected cookie */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Force the server to send a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); + + /* Begin negotiating handshake. + * The first negotiate_until blocks because the client is looking for a SERVER_HELLO, + * not the HELLO_RETRY_MESSAGE. This is fine; it's in the right place in the handshake. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, HELLO_RETRY_MSG), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* At this point, the server has already sent its HRR request with a cookie. + * The client has stored the server's cookie, but not responded. + * Wipe the cookie on the client, preventing it from sending the response. + */ + EXPECT_NOT_EQUAL(client_conn->cookie.size, 0); + EXPECT_SUCCESS(s2n_free(&client_conn->cookie)); + + /* Continue negotiating. We should fail because of the "missing" cookie. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Self-Talk: Server does NOT use cookies */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* Verify no cookies */ + EXPECT_EQUAL(client_conn->cookie.size, 0); + EXPECT_EQUAL(server_conn->cookie.size, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Self-Talk: Server does use cookies + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.2 + *= type=test + *# When sending a HelloRetryRequest, the server MAY provide a "cookie" + *# extension to the client (this is an exception to the usual rule that + *# the only extensions that may be sent are those that appear in the + *# ClientHello). When sending the new ClientHello, the client MUST copy + *# the contents of the extension received in the HelloRetryRequest into + *# a "cookie" extension in the new ClientHello. + */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Force the server to send a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* Verify cookies */ + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], server_conn->cookie); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /* Self-Talk: Full connection lifecycle with cookies + * We try the handshake multiple times with different possible call patterns. + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* We need an arbitrary combination of conditions, + * but consistent across test runs. + */ + srand(0); + + for (size_t i = 0; i < 250; i++) { + int r = rand(); + bool hrr = (r % 2) == 0; + bool cookie = (r % 3) == 0; + size_t cookie_i = i % TEST_COOKIE_COUNT; + bool free_handshake = (r % 7) == 0; + + /* Verify calls to s2n_connection_wipe are safe */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + if (hrr) { + client_conn->security_policy_override = &security_policy_test_tls13_retry; + } + + /* Force the server to send a cookie */ + if (cookie) { + EXPECT_SUCCESS(s2n_dup(&test_cookies[cookie_i], &server_conn->cookie)); + } + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + if (hrr) { + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(client_conn)); + } else { + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_FALSE(s2n_is_hello_retry_handshake(client_conn)); + } + + /* Verify cookie data */ + if (hrr && cookie) { + S2N_BLOB_EXPECT_EQUAL(test_cookies[cookie_i], client_conn->cookie); + } else { + EXPECT_EQUAL(client_conn->cookie.size, 0); + } + + if (free_handshake) { + EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + }; + + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + EXPECT_SUCCESS(s2n_free(&test_cookies[i])); + } + END_TEST(); +} diff --git a/tests/unit/s2n_crl_test.c b/tests/unit/s2n_crl_test.c index 6ad12d9b63b..ac904b41726 100644 --- a/tests/unit/s2n_crl_test.c +++ b/tests/unit/s2n_crl_test.c @@ -1,942 +1,942 @@ -#include "utils/s2n_prelude.h" -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "tls/s2n_crl.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -#define S2N_CRL_ROOT_CERT "../pems/crl/root_cert.pem" -#define S2N_CRL_NONE_REVOKED_CERT_CHAIN "../pems/crl/none_revoked_cert_chain.pem" -#define S2N_CRL_NONE_REVOKED_KEY "../pems/crl/none_revoked_key.pem" -#define S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN "../pems/crl/intermediate_revoked_cert_chain.pem" -#define S2N_CRL_INTERMEDIATE_REVOKED_KEY "../pems/crl/intermediate_revoked_key.pem" -#define S2N_CRL_LEAF_REVOKED_CERT_CHAIN "../pems/crl/leaf_revoked_cert_chain.pem" -#define S2N_CRL_LEAF_REVOKED_KEY "../pems/crl/leaf_revoked_key.pem" -#define S2N_CRL_ALL_REVOKED_CERT_CHAIN "../pems/crl/all_revoked_cert_chain.pem" -#define S2N_CRL_ALL_REVOKED_KEY "../pems/crl/all_revoked_key.pem" -#define S2N_CRL_ROOT_CRL "../pems/crl/root_crl.pem" -#define S2N_CRL_INTERMEDIATE_CRL "../pems/crl/intermediate_crl.pem" -#define S2N_CRL_INTERMEDIATE_REVOKED_CRL "../pems/crl/intermediate_revoked_crl.pem" -#define S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL "../pems/crl/intermediate_invalid_this_update_crl.pem" -#define S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL "../pems/crl/intermediate_invalid_next_update_crl.pem" - -#define CRL_TEST_CHAIN_LEN 2 - -struct crl_lookup_data { - struct s2n_crl *crls[5]; - X509 *certs[5]; - uint8_t callback_invoked_count; -}; - -static int crl_lookup_test_callback(struct s2n_crl_lookup *lookup, void *context) -{ - struct crl_lookup_data *crl_data = (struct crl_lookup_data *) context; - crl_data->callback_invoked_count += 1; - crl_data->certs[lookup->cert_idx] = lookup->cert; - - struct s2n_crl *crl = crl_data->crls[lookup->cert_idx]; - if (crl == NULL) { - POSIX_GUARD(s2n_crl_lookup_ignore(lookup)); - } else { - POSIX_GUARD(s2n_crl_lookup_set(lookup, crl)); - } - - return 0; -} - -static int crl_lookup_noop(struct s2n_crl_lookup *lookup, void *context) -{ - return 0; -} - -static int crl_lookup_callback_fail(struct s2n_crl_lookup *lookup, void *context) -{ - return 1; -} - -static uint8_t verify_host_always_allow(const char *host_name, size_t host_name_len, void *data) -{ - return 1; -} - -static struct s2n_crl *load_test_crl(const char *pem_path) -{ - uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t pem_len = 0; - PTR_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, crl_pem, &pem_len, S2N_MAX_TEST_PEM_SIZE)); - DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); - PTR_ENSURE_REF(crl); - PTR_GUARD_POSIX(s2n_crl_load_pem(crl, crl_pem, pem_len)); - - struct s2n_crl *crl_ret = crl; - ZERO_TO_DISABLE_DEFER_CLEANUP(crl); - - return crl_ret; -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* s2n_crl_new allocates and frees a s2n_crl */ - { - struct s2n_crl *crl = s2n_crl_new(); - EXPECT_NOT_NULL(crl); - - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - - /* Multiple calls to free succeed */ - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - }; - - /* s2n_crl_new allocates and frees a s2n_crl with an internal X509_CRL set */ - { - struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL); - EXPECT_NOT_NULL(crl); - EXPECT_NOT_NULL(crl->crl); - - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - - /* Multiple calls to free succeed */ - EXPECT_SUCCESS(s2n_crl_free(&crl)); - EXPECT_NULL(crl); - }; - - /* Ensure s2n_crl_load_pem produces a valid X509_CRL internally */ - { - DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); - EXPECT_NOT_NULL(crl); - EXPECT_NOT_NULL(crl->crl); - - /* Make sure an OpenSSL operation succeeds on the internal X509_CRL */ - X509_NAME *crl_name = X509_CRL_get_issuer(crl->crl); - POSIX_ENSURE_REF(crl_name); - }; - - /* s2n_crl_load_pem fails if provided a bad pem */ - { - uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t crl_pem_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_ROOT_CRL, crl_pem, &crl_pem_len, S2N_MAX_TEST_PEM_SIZE)); - DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); - EXPECT_NOT_NULL(crl); - EXPECT_SUCCESS(s2n_crl_load_pem(crl, crl_pem, crl_pem_len)); - - /* Change a random byte in the pem to make it invalid */ - crl_pem[50] = 1; - - DEFER_CLEANUP(struct s2n_crl *invalid_crl = s2n_crl_new(), s2n_crl_free); - EXPECT_NOT_NULL(invalid_crl); - EXPECT_FAILURE_WITH_ERRNO(s2n_crl_load_pem(invalid_crl, crl_pem, crl_pem_len), - S2N_ERR_INVALID_PEM); - }; - - /* CRL issuer hash is retrieved successfully */ - { - DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); - EXPECT_NOT_NULL(crl); - - uint64_t hash = 0; - EXPECT_SUCCESS(s2n_crl_get_issuer_hash(crl, &hash)); - EXPECT_TRUE(hash != 0); - }; - - DEFER_CLEANUP(struct s2n_crl *root_crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); - EXPECT_NOT_NULL(root_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_crl = load_test_crl(S2N_CRL_INTERMEDIATE_CRL), s2n_crl_free); - EXPECT_NOT_NULL(intermediate_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_revoked_crl = load_test_crl(S2N_CRL_INTERMEDIATE_REVOKED_CRL), s2n_crl_free); - EXPECT_NOT_NULL(intermediate_revoked_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_this_update_crl = - load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL), - s2n_crl_free); - EXPECT_NOT_NULL(intermediate_invalid_this_update_crl); - - DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_next_update_crl = - load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL), - s2n_crl_free); - EXPECT_NOT_NULL(intermediate_invalid_next_update_crl); - - /* Save a list of received X509s for s2n_crl_lookup tests */ - struct crl_lookup_data received_lookup_data = { 0 }; - DEFER_CLEANUP(struct s2n_x509_validator received_lookup_data_validator, s2n_x509_validator_wipe); - - /* CRL validation succeeds for unrevoked certificate chain */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - EXPECT_SUCCESS(s2n_x509_validator_init(&received_lookup_data_validator, &trust_store, 0)); - - received_lookup_data.crls[0] = intermediate_crl; - received_lookup_data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &received_lookup_data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&received_lookup_data_validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out)); - EXPECT_TRUE(received_lookup_data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - - /* Ensure all certificates were received in the callback */ - for (int i = 0; i < CRL_TEST_CHAIN_LEN; i++) { - EXPECT_NOT_NULL(received_lookup_data.certs[i]); - } - }; - - /* CRL validation errors when a leaf certificate is revoked */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation errors when an intermediate certificate is revoked */ - for (int i = 0; i < 2; i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_revoked_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - if (i == 0) { - /* Ensure CRL validation fails when only the intermediate certificate is revoked */ - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - } else if (i == 1) { - /* Ensure CRL validation fails when both the intermediate and leaf certificates are revoked */ - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_ALL_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - } - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - } - - /* CRL validation fails when a certificate is rejected from the callback */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CRL_LOOKUP_FAILED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation succeeds for unrevoked certificate chain when extraneous certificate is rejected */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - - /* Reject the extraneous cert */ - data.crls[2] = NULL; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE * 2]; - uint32_t pem_len_1 = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_NONE_REVOKED_CERT_CHAIN, cert_chain_pem, &pem_len_1, - S2N_MAX_TEST_PEM_SIZE)); - - /* Add an arbitrary cert to the chain that won't be included in the chain of trust */ - uint32_t pem_len_2 = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_SHA256_CLIENT_CERT, cert_chain_pem + pem_len_1, - &pem_len_2, S2N_MAX_TEST_PEM_SIZE)); - - uint32_t cert_chain_len = pem_len_1 + pem_len_2; - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(connection, cert_chain_pem, cert_chain_len, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - EXPECT_TRUE(data.callback_invoked_count == 3); - }; - - /* s2n_x509_validator_validate_cert_chain blocks until all CRL callbacks respond */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_noop, NULL)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - /* Blocks if no response received from callbacks */ - for (int i = 0; i < 10; i++) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_ASYNC_BLOCKED); - } - - /* Continues to block if only one callback has sent a response */ - struct s2n_crl_lookup *lookup = NULL; - EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 0, (void **) &lookup)); - EXPECT_NOT_NULL(lookup); - EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, root_crl)); - for (int i = 0; i < 10; ++i) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_ASYNC_BLOCKED); - } - - /* Unblocks when all callbacks send a response */ - lookup = NULL; - EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 1, (void **) &lookup)); - EXPECT_NOT_NULL(lookup); - EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, intermediate_crl)); - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - }; - - /* CRL validation fails when a callback returns unsuccessfully */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_callback_fail, NULL)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, - chain_len, &pkey_type, &public_key_out), - S2N_ERR_CANCELLED); - }; - - /* CRL validation succeeds for a CRL with an invalid thisUpdate date */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_this_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid thisUpdate date */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_this_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation succeeds for a CRL with an invalid nextUpdate date */ - for (int disable_time_validation = 0; disable_time_validation <= 1; disable_time_validation += 1) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_next_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - /* Ensure that validation succeeds for a CRL with an invalid nextUpdate field when time - * validation is disabled. - */ - if (disable_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - } - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid nextUpdate date */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char root_cert[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_invalid_next_update_crl; - data.crls[1] = root_crl; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_REVOKED); - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: server certificate is not revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: server certificate is revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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, config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_REVOKED); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: client certificate is not revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, - S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Self-talk: client certificate is revoked */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, - S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - struct crl_lookup_data data = { 0 }; - data.crls[0] = intermediate_crl; - data.crls[1] = root_crl; - EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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_REVOKED); - - EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); - }; - - /* Calling s2n_crl_lookup return functions correctly set context fields */ - { - struct s2n_crl_lookup lookup = { 0 }; - - lookup.status = AWAITING_RESPONSE; - EXPECT_SUCCESS(s2n_crl_lookup_set(&lookup, root_crl)); - EXPECT_TRUE(lookup.status == FINISHED); - EXPECT_NOT_NULL(lookup.crl); - - lookup.status = AWAITING_RESPONSE; - EXPECT_SUCCESS(s2n_crl_lookup_ignore(&lookup)); - EXPECT_TRUE(lookup.status == FINISHED); - EXPECT_NULL(lookup.crl); - }; - - /* Certificate issuer hash is retrieved successfully */ - { - struct s2n_crl_lookup lookup = { 0 }; - EXPECT_NOT_NULL(received_lookup_data.certs[0]); - lookup.cert = received_lookup_data.certs[0]; - - uint64_t hash = 0; - EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&lookup, &hash)); - EXPECT_TRUE(hash != 0); - }; - - /* Retrieved hash values for certificates match CRL hashes */ - { - /* The hash of the leaf certificate matches the hash of the intermediate CRL */ - - struct s2n_crl_lookup leaf_lookup = { 0 }; - EXPECT_NOT_NULL(received_lookup_data.certs[0]); - leaf_lookup.cert = received_lookup_data.certs[0]; - - uint64_t leaf_cert_hash = 0; - EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&leaf_lookup, &leaf_cert_hash)); - EXPECT_TRUE(leaf_cert_hash != 0); - - uint64_t intermediate_crl_hash = 0; - EXPECT_SUCCESS(s2n_crl_get_issuer_hash(intermediate_crl, &intermediate_crl_hash)); - EXPECT_TRUE(intermediate_crl_hash != 0); - - EXPECT_TRUE(leaf_cert_hash == intermediate_crl_hash); - - /* The hash of the intermediate certificate matches the hash of the root CRL */ - - struct s2n_crl_lookup intermediate_lookup = { 0 }; - EXPECT_NOT_NULL(received_lookup_data.certs[1]); - intermediate_lookup.cert = received_lookup_data.certs[1]; - - uint64_t intermediate_cert_hash = 0; - EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&intermediate_lookup, &intermediate_cert_hash)); - EXPECT_TRUE(intermediate_cert_hash != 0); - - uint64_t root_crl_hash = 0; - EXPECT_SUCCESS(s2n_crl_get_issuer_hash(root_crl, &root_crl_hash)); - EXPECT_TRUE(root_crl_hash != 0); - - EXPECT_TRUE(intermediate_cert_hash == root_crl_hash); - - /* If the certificate and CRL were issued by different CAs, their hashes should not match */ - EXPECT_TRUE(leaf_cert_hash != root_crl_hash); - }; - - /* s2n_crl_validate_active tests */ - { - /* Succeeds for valid CRL */ - EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_crl)); - - /* Succeeds for expired CRL */ - EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_invalid_next_update_crl)); - - /* Fails for CRL that is not yet valid */ - EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_active(intermediate_invalid_this_update_crl), - S2N_ERR_CRL_NOT_YET_VALID); - }; - - /* s2n_crl_validate_not_expired tests */ - { - /* Succeeds for valid CRL */ - EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_crl)); - - /* Succeeds for CRL that is not yet valid */ - EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_invalid_this_update_crl)); - - /* Fails for expired CRL */ - EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_not_expired(intermediate_invalid_next_update_crl), - S2N_ERR_CRL_EXPIRED); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "tls/s2n_crl.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define S2N_CRL_ROOT_CERT "../pems/crl/root_cert.pem" +#define S2N_CRL_NONE_REVOKED_CERT_CHAIN "../pems/crl/none_revoked_cert_chain.pem" +#define S2N_CRL_NONE_REVOKED_KEY "../pems/crl/none_revoked_key.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN "../pems/crl/intermediate_revoked_cert_chain.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_KEY "../pems/crl/intermediate_revoked_key.pem" +#define S2N_CRL_LEAF_REVOKED_CERT_CHAIN "../pems/crl/leaf_revoked_cert_chain.pem" +#define S2N_CRL_LEAF_REVOKED_KEY "../pems/crl/leaf_revoked_key.pem" +#define S2N_CRL_ALL_REVOKED_CERT_CHAIN "../pems/crl/all_revoked_cert_chain.pem" +#define S2N_CRL_ALL_REVOKED_KEY "../pems/crl/all_revoked_key.pem" +#define S2N_CRL_ROOT_CRL "../pems/crl/root_crl.pem" +#define S2N_CRL_INTERMEDIATE_CRL "../pems/crl/intermediate_crl.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_CRL "../pems/crl/intermediate_revoked_crl.pem" +#define S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL "../pems/crl/intermediate_invalid_this_update_crl.pem" +#define S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL "../pems/crl/intermediate_invalid_next_update_crl.pem" + +#define CRL_TEST_CHAIN_LEN 2 + +struct crl_lookup_data { + struct s2n_crl *crls[5]; + X509 *certs[5]; + uint8_t callback_invoked_count; +}; + +static int crl_lookup_test_callback(struct s2n_crl_lookup *lookup, void *context) +{ + struct crl_lookup_data *crl_data = (struct crl_lookup_data *) context; + crl_data->callback_invoked_count += 1; + crl_data->certs[lookup->cert_idx] = lookup->cert; + + struct s2n_crl *crl = crl_data->crls[lookup->cert_idx]; + if (crl == NULL) { + POSIX_GUARD(s2n_crl_lookup_ignore(lookup)); + } else { + POSIX_GUARD(s2n_crl_lookup_set(lookup, crl)); + } + + return 0; +} + +static int crl_lookup_noop(struct s2n_crl_lookup *lookup, void *context) +{ + return 0; +} + +static int crl_lookup_callback_fail(struct s2n_crl_lookup *lookup, void *context) +{ + return 1; +} + +static uint8_t verify_host_always_allow(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +static struct s2n_crl *load_test_crl(const char *pem_path) +{ + uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t pem_len = 0; + PTR_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, crl_pem, &pem_len, S2N_MAX_TEST_PEM_SIZE)); + DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); + PTR_ENSURE_REF(crl); + PTR_GUARD_POSIX(s2n_crl_load_pem(crl, crl_pem, pem_len)); + + struct s2n_crl *crl_ret = crl; + ZERO_TO_DISABLE_DEFER_CLEANUP(crl); + + return crl_ret; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* s2n_crl_new allocates and frees a s2n_crl */ + { + struct s2n_crl *crl = s2n_crl_new(); + EXPECT_NOT_NULL(crl); + + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + + /* Multiple calls to free succeed */ + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + }; + + /* s2n_crl_new allocates and frees a s2n_crl with an internal X509_CRL set */ + { + struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL); + EXPECT_NOT_NULL(crl); + EXPECT_NOT_NULL(crl->crl); + + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + + /* Multiple calls to free succeed */ + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + }; + + /* Ensure s2n_crl_load_pem produces a valid X509_CRL internally */ + { + DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(crl); + EXPECT_NOT_NULL(crl->crl); + + /* Make sure an OpenSSL operation succeeds on the internal X509_CRL */ + X509_NAME *crl_name = X509_CRL_get_issuer(crl->crl); + POSIX_ENSURE_REF(crl_name); + }; + + /* s2n_crl_load_pem fails if provided a bad pem */ + { + uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t crl_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_ROOT_CRL, crl_pem, &crl_pem_len, S2N_MAX_TEST_PEM_SIZE)); + DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); + EXPECT_NOT_NULL(crl); + EXPECT_SUCCESS(s2n_crl_load_pem(crl, crl_pem, crl_pem_len)); + + /* Change a random byte in the pem to make it invalid */ + crl_pem[50] = 1; + + DEFER_CLEANUP(struct s2n_crl *invalid_crl = s2n_crl_new(), s2n_crl_free); + EXPECT_NOT_NULL(invalid_crl); + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_load_pem(invalid_crl, crl_pem, crl_pem_len), + S2N_ERR_INVALID_PEM); + }; + + /* CRL issuer hash is retrieved successfully */ + { + DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(crl); + + uint64_t hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(crl, &hash)); + EXPECT_TRUE(hash != 0); + }; + + DEFER_CLEANUP(struct s2n_crl *root_crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(root_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_crl = load_test_crl(S2N_CRL_INTERMEDIATE_CRL), s2n_crl_free); + EXPECT_NOT_NULL(intermediate_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_revoked_crl = load_test_crl(S2N_CRL_INTERMEDIATE_REVOKED_CRL), s2n_crl_free); + EXPECT_NOT_NULL(intermediate_revoked_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_this_update_crl = + load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL), + s2n_crl_free); + EXPECT_NOT_NULL(intermediate_invalid_this_update_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_next_update_crl = + load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL), + s2n_crl_free); + EXPECT_NOT_NULL(intermediate_invalid_next_update_crl); + + /* Save a list of received X509s for s2n_crl_lookup tests */ + struct crl_lookup_data received_lookup_data = { 0 }; + DEFER_CLEANUP(struct s2n_x509_validator received_lookup_data_validator, s2n_x509_validator_wipe); + + /* CRL validation succeeds for unrevoked certificate chain */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + EXPECT_SUCCESS(s2n_x509_validator_init(&received_lookup_data_validator, &trust_store, 0)); + + received_lookup_data.crls[0] = intermediate_crl; + received_lookup_data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &received_lookup_data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&received_lookup_data_validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out)); + EXPECT_TRUE(received_lookup_data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + + /* Ensure all certificates were received in the callback */ + for (int i = 0; i < CRL_TEST_CHAIN_LEN; i++) { + EXPECT_NOT_NULL(received_lookup_data.certs[i]); + } + }; + + /* CRL validation errors when a leaf certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation errors when an intermediate certificate is revoked */ + for (int i = 0; i < 2; i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_revoked_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + if (i == 0) { + /* Ensure CRL validation fails when only the intermediate certificate is revoked */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + } else if (i == 1) { + /* Ensure CRL validation fails when both the intermediate and leaf certificates are revoked */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_ALL_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + } + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + } + + /* CRL validation fails when a certificate is rejected from the callback */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CRL_LOOKUP_FAILED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation succeeds for unrevoked certificate chain when extraneous certificate is rejected */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + + /* Reject the extraneous cert */ + data.crls[2] = NULL; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE * 2]; + uint32_t pem_len_1 = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_NONE_REVOKED_CERT_CHAIN, cert_chain_pem, &pem_len_1, + S2N_MAX_TEST_PEM_SIZE)); + + /* Add an arbitrary cert to the chain that won't be included in the chain of trust */ + uint32_t pem_len_2 = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_SHA256_CLIENT_CERT, cert_chain_pem + pem_len_1, + &pem_len_2, S2N_MAX_TEST_PEM_SIZE)); + + uint32_t cert_chain_len = pem_len_1 + pem_len_2; + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(connection, cert_chain_pem, cert_chain_len, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == 3); + }; + + /* s2n_x509_validator_validate_cert_chain blocks until all CRL callbacks respond */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_noop, NULL)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + /* Blocks if no response received from callbacks */ + for (int i = 0; i < 10; i++) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_ASYNC_BLOCKED); + } + + /* Continues to block if only one callback has sent a response */ + struct s2n_crl_lookup *lookup = NULL; + EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 0, (void **) &lookup)); + EXPECT_NOT_NULL(lookup); + EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, root_crl)); + for (int i = 0; i < 10; ++i) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_ASYNC_BLOCKED); + } + + /* Unblocks when all callbacks send a response */ + lookup = NULL; + EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 1, (void **) &lookup)); + EXPECT_NOT_NULL(lookup); + EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, intermediate_crl)); + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + }; + + /* CRL validation fails when a callback returns unsuccessfully */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_callback_fail, NULL)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CANCELLED); + }; + + /* CRL validation succeeds for a CRL with an invalid thisUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_this_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid thisUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_this_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation succeeds for a CRL with an invalid nextUpdate date */ + for (int disable_time_validation = 0; disable_time_validation <= 1; disable_time_validation += 1) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_next_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + /* Ensure that validation succeeds for a CRL with an invalid nextUpdate field when time + * validation is disabled. + */ + if (disable_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid nextUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_next_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: server certificate is not revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: server certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_REVOKED); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: client certificate is not revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: client certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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_REVOKED); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Calling s2n_crl_lookup return functions correctly set context fields */ + { + struct s2n_crl_lookup lookup = { 0 }; + + lookup.status = AWAITING_RESPONSE; + EXPECT_SUCCESS(s2n_crl_lookup_set(&lookup, root_crl)); + EXPECT_TRUE(lookup.status == FINISHED); + EXPECT_NOT_NULL(lookup.crl); + + lookup.status = AWAITING_RESPONSE; + EXPECT_SUCCESS(s2n_crl_lookup_ignore(&lookup)); + EXPECT_TRUE(lookup.status == FINISHED); + EXPECT_NULL(lookup.crl); + }; + + /* Certificate issuer hash is retrieved successfully */ + { + struct s2n_crl_lookup lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[0]); + lookup.cert = received_lookup_data.certs[0]; + + uint64_t hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&lookup, &hash)); + EXPECT_TRUE(hash != 0); + }; + + /* Retrieved hash values for certificates match CRL hashes */ + { + /* The hash of the leaf certificate matches the hash of the intermediate CRL */ + + struct s2n_crl_lookup leaf_lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[0]); + leaf_lookup.cert = received_lookup_data.certs[0]; + + uint64_t leaf_cert_hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&leaf_lookup, &leaf_cert_hash)); + EXPECT_TRUE(leaf_cert_hash != 0); + + uint64_t intermediate_crl_hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(intermediate_crl, &intermediate_crl_hash)); + EXPECT_TRUE(intermediate_crl_hash != 0); + + EXPECT_TRUE(leaf_cert_hash == intermediate_crl_hash); + + /* The hash of the intermediate certificate matches the hash of the root CRL */ + + struct s2n_crl_lookup intermediate_lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[1]); + intermediate_lookup.cert = received_lookup_data.certs[1]; + + uint64_t intermediate_cert_hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&intermediate_lookup, &intermediate_cert_hash)); + EXPECT_TRUE(intermediate_cert_hash != 0); + + uint64_t root_crl_hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(root_crl, &root_crl_hash)); + EXPECT_TRUE(root_crl_hash != 0); + + EXPECT_TRUE(intermediate_cert_hash == root_crl_hash); + + /* If the certificate and CRL were issued by different CAs, their hashes should not match */ + EXPECT_TRUE(leaf_cert_hash != root_crl_hash); + }; + + /* s2n_crl_validate_active tests */ + { + /* Succeeds for valid CRL */ + EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_crl)); + + /* Succeeds for expired CRL */ + EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_invalid_next_update_crl)); + + /* Fails for CRL that is not yet valid */ + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_active(intermediate_invalid_this_update_crl), + S2N_ERR_CRL_NOT_YET_VALID); + }; + + /* s2n_crl_validate_not_expired tests */ + { + /* Succeeds for valid CRL */ + EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_crl)); + + /* Succeeds for CRL that is not yet valid */ + EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_invalid_this_update_crl)); + + /* Fails for expired CRL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_not_expired(intermediate_invalid_next_update_crl), + S2N_ERR_CRL_EXPIRED); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_drain_alert_test.c b/tests/unit/s2n_drain_alert_test.c index f56c46b221f..34f231bf80c 100644 --- a/tests/unit/s2n_drain_alert_test.c +++ b/tests/unit/s2n_drain_alert_test.c @@ -1,141 +1,141 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - -#define INTERNAL_ERROR_ALERT_HEX 0x50 - -/* This test simulates a client sends a TLS alert record and closes its socket immediately after the ClientHello. - * We want to validate that s2n informs the caller of the alert instead of an I/O error. Both errors result - * in a failed handshake, but the alert is generally more useful. - */ - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - uint8_t client_hello_message[] = { - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* Client random */ - ZERO_TO_THIRTY_ONE, - /* SessionID len - 32 bytes */ - 0x20, - /* Session ID */ - ZERO_TO_THIRTY_ONE, - /* Cipher suites len */ - 0x00, 0x02, - /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ - 0x00, 0x3C, - /* Compression methods len */ - 0x01, - /* Compression method - none */ - 0x00, - /* Extensions len */ - 0x00, 0x00 - }; - size_t body_len = sizeof(client_hello_message); - uint8_t message_header[] = { - /* Handshake message type CLIENT HELLO */ - 0x01, - /* Body len */ - (body_len >> 16) & 0xff, - (body_len >> 8) & 0xff, - (body_len & 0xff), - }; - size_t message_len = sizeof(message_header) + body_len; - uint8_t record_header[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Message len */ - (message_len >> 8) & 0xff, - (message_len & 0xff), - }; - - uint8_t alert_record[] = { - /* Record type ALERT */ - 0x15, - /* Protocol version TLS 1.2 */ - 0x03, - 0x03, - /* Length */ - 0x00, - 0x02, - /* Fatal alert "internal_error" */ - 0x02, - INTERNAL_ERROR_ALERT_HEX, - }; - - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - s2n_blocked_status server_blocked; - char *cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); - char *private_key = malloc(S2N_MAX_TEST_PEM_SIZE); - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Security policy must allow cipher suite hard coded into client hello */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Send the client hello */ - EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); - EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); - EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); - - /* Send an alert from client to server */ - EXPECT_EQUAL(write(io_pair.client, alert_record, sizeof(alert_record)), sizeof(alert_record)); - - /* Close the client read/write end */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - /* Expect the server to fail due to an incoming alert. We should not fail due to an I/O error(EPIPE). */ - s2n_negotiate(server_conn, &server_blocked); - EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_ALERT); - EXPECT_EQUAL(s2n_connection_get_alert(server_conn), INTERNAL_ERROR_ALERT_HEX); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - free(cert_chain); - free(private_key); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +#define INTERNAL_ERROR_ALERT_HEX 0x50 + +/* This test simulates a client sends a TLS alert record and closes its socket immediately after the ClientHello. + * We want to validate that s2n informs the caller of the alert instead of an I/O error. Both errors result + * in a failed handshake, but the alert is generally more useful. + */ + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + size_t body_len = sizeof(client_hello_message); + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + size_t message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + uint8_t alert_record[] = { + /* Record type ALERT */ + 0x15, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Length */ + 0x00, + 0x02, + /* Fatal alert "internal_error" */ + 0x02, + INTERNAL_ERROR_ALERT_HEX, + }; + + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + s2n_blocked_status server_blocked; + char *cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + char *private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + + /* Send an alert from client to server */ + EXPECT_EQUAL(write(io_pair.client, alert_record, sizeof(alert_record)), sizeof(alert_record)); + + /* Close the client read/write end */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + /* Expect the server to fail due to an incoming alert. We should not fail due to an I/O error(EPIPE). */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_ALERT); + EXPECT_EQUAL(s2n_connection_get_alert(server_conn), INTERNAL_ERROR_ALERT_HEX); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + free(cert_chain); + free(private_key); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_invariant_test.c b/tests/unit/s2n_handshake_invariant_test.c index a3de6282309..108dc37a560 100644 --- a/tests/unit/s2n_handshake_invariant_test.c +++ b/tests/unit/s2n_handshake_invariant_test.c @@ -1,89 +1,89 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* A full record that has an "APPLICATION_DATA" inside according to s2n's - * handshake message_types. - */ -uint8_t record[] = { - /* Record type HANDSHAKE */ - 0x16, - /* Protocol version TLS 1.2 */ - 0x03, 0x03, - /* record len */ - 0x00, 0x05, - /* Type(s2n has this as expected message type for APPLICATION_DATA handler. - * This is not a standardized value, just something s2n has hardcoded as a placeholder - * For the APPLICATON_DATA state in the state machine. - */ - 0x00, - /* Len */ - 0x00, 0x00, 0x01, - /* Data */ - 0x00 -}; -static int amt_written = 0; - -int s2n_app_data_in_handshake_record_recv_fn(void *io_context, uint8_t *buf, uint32_t len) -{ - int amt_left = sizeof(record) - amt_written; - int to_write = S2N_MIN(len, amt_left); - POSIX_CHECKED_MEMCPY(buf, record + amt_written, to_write); - amt_written += to_write; - return to_write; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - /* Initialize *some* handshake type. Not terribly relevant for this test. */ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; - /* Fast forward the handshake state machine to the end of this "handshake_type". - * APPLICATION_DATA is the 11th state for "NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY". - */ - conn->handshake.message_number = 10; - conn->actual_protocol_version = S2N_TLS12; - /* Provide the crafted record to s2n's I/O */ - s2n_connection_set_recv_cb(conn, s2n_app_data_in_handshake_record_recv_fn); - - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - s2n_connection_free(conn); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* A full record that has an "APPLICATION_DATA" inside according to s2n's + * handshake message_types. + */ +uint8_t record[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* record len */ + 0x00, 0x05, + /* Type(s2n has this as expected message type for APPLICATION_DATA handler. + * This is not a standardized value, just something s2n has hardcoded as a placeholder + * For the APPLICATON_DATA state in the state machine. + */ + 0x00, + /* Len */ + 0x00, 0x00, 0x01, + /* Data */ + 0x00 +}; +static int amt_written = 0; + +int s2n_app_data_in_handshake_record_recv_fn(void *io_context, uint8_t *buf, uint32_t len) +{ + int amt_left = sizeof(record) - amt_written; + int to_write = S2N_MIN(len, amt_left); + POSIX_CHECKED_MEMCPY(buf, record + amt_written, to_write); + amt_written += to_write; + return to_write; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Initialize *some* handshake type. Not terribly relevant for this test. */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; + /* Fast forward the handshake state machine to the end of this "handshake_type". + * APPLICATION_DATA is the 11th state for "NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY". + */ + conn->handshake.message_number = 10; + conn->actual_protocol_version = S2N_TLS12; + /* Provide the crafted record to s2n's I/O */ + s2n_connection_set_recv_cb(conn, s2n_app_data_in_handshake_record_recv_fn); + + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(conn); + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_test.c b/tests/unit/s2n_handshake_test.c index 966fa91b5ad..2e8b25da5d7 100644 --- a/tests/unit/s2n_handshake_test.c +++ b/tests/unit/s2n_handshake_test.c @@ -1,554 +1,554 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_handshake.h" - -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_mldsa.h" -#include "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -enum test_type { - TEST_TYPE_START, - TEST_TYPE_SYNC = TEST_TYPE_START, - TEST_TYPE_ASYNC, - TEST_TYPE_END -} test_type; - -struct s2n_async_pkey_op *pkey_op = NULL; -int async_pkey_op_called = 0; -int async_pkey_op_performed = 0; - -static uint8_t s2n_trust_all_verify_host(const char *host_name, size_t len, void *data) -{ - return 1; -} - -static int handle_async(struct s2n_connection *server_conn) -{ - s2n_blocked_status server_blocked; - - /* Test that handshake can't proceed until async pkey op is complete */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &server_blocked), S2N_ERR_ASYNC_BLOCKED); - - /* Check that we have pkey_op */ - EXPECT_NOT_NULL(pkey_op); - - /* Test that not performed pkey can't be applied */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), - S2N_ERR_ASYNC_NOT_PERFORMED); - - /* Extract pkey */ - struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(server_conn); - EXPECT_NOT_NULL(chain_and_key); - - s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); - EXPECT_NOT_NULL(pkey); - - /* Test that we can perform pkey operation only once */ - EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_perform(pkey_op, pkey), S2N_ERR_ASYNC_ALREADY_PERFORMED); - - /* Test that pkey op can't be applied to connection other than original one */ - struct s2n_connection *server_conn2 = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn2); - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn2), - S2N_ERR_ASYNC_WRONG_CONNECTION); - EXPECT_SUCCESS(s2n_connection_free(server_conn2)); - - /* Test that pkey op can be applied to original connection */ - EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, server_conn)); - - /* Test that pkey op can't be applied to original connection more than once */ - EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), - S2N_ERR_ASYNC_ALREADY_APPLIED); - - /* Free the pkey op */ - EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); - pkey_op = NULL; - - async_pkey_op_performed++; - - return 0; -} - -static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn) -{ - s2n_blocked_status server_blocked; - s2n_blocked_status client_blocked; - - int tries = 0; - do { - int client_rc = s2n_negotiate(client_conn, &client_blocked); - if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return -1; - } - - int server_rc = s2n_negotiate(server_conn, &server_blocked); - if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { - return -1; - } - - if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { - /* Only can happen in async tests */ - EXPECT_EQUAL(test_type, TEST_TYPE_ASYNC); - EXPECT_SUCCESS(handle_async(server_conn)); - } - - EXPECT_NOT_EQUAL(++tries, 5); - } while (client_blocked || server_blocked); - - POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - return S2N_SUCCESS; -} - -int test_cipher_preferences(struct s2n_config *server_config, struct s2n_config *client_config, - struct s2n_cert_chain_and_key *expected_cert_chain, s2n_signature_algorithm sig_alg) -{ - const struct s2n_security_policy *security_policy = server_config->security_policy; - EXPECT_NOT_NULL(security_policy); - - if (s2n_is_in_fips_mode()) { - /* Override default client config ciphers when in FIPS mode to ensure all FIPS - * default ciphers are tested. - */ - client_config->security_policy = security_policy; - } - - const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; - EXPECT_NOT_NULL(cipher_preferences); - - /* Verify that a handshake succeeds for every available cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_suite *expected_cipher = cipher_preferences->suites[cipher_idx]; - uint8_t expect_failure = 0; - - /* Expect failure if the libcrypto we're building with can't support the cipher */ - if (!expected_cipher->available) { - expect_failure = 1; - } - - s2n_signature_algorithm expected_sig_alg = sig_alg; - /* Expect no server signature algorithm if RSA kex */ - if (expected_cipher->key_exchange_alg == &s2n_rsa) { - expected_sig_alg = S2N_SIGNATURE_ANONYMOUS; - } - - TEST_DEBUG_PRINT("Testing %s in %s mode, expect_failure=%d\n", expected_cipher->name, - test_type == TEST_TYPE_SYNC ? "synchronous" : "asynchronous", expect_failure); - - struct s2n_security_policy server_security_policy; - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(client_conn); - EXPECT_NOT_NULL(server_conn); - - /* Craft a cipher preference with a cipher_idx cipher and assign it to server */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - server_cipher_preferences.suites = &expected_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - server_conn->security_policy_override = &server_security_policy; - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair; - 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_config(client_conn, client_config)); - - EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Reset counters */ - async_pkey_op_called = 0; - async_pkey_op_performed = 0; - - if (!expect_failure) { - POSIX_GUARD(try_handshake(server_conn, client_conn)); - - EXPECT_STRING_EQUAL(s2n_connection_get_cipher(server_conn), expected_cipher->name); - - EXPECT_EQUAL(server_conn->handshake_params.our_chain_and_key, expected_cert_chain); - EXPECT_NOT_NULL(server_conn->handshake_params.server_cert_sig_scheme); - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, expected_sig_alg); - - EXPECT_TRUE(IS_NEGOTIATED(server_conn)); - EXPECT_TRUE(IS_NEGOTIATED(client_conn)); - - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); - EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); - - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_EQUAL(async_pkey_op_called, 1); - EXPECT_EQUAL(async_pkey_op_performed, 1); - } else { - EXPECT_EQUAL(async_pkey_op_called, 0); - EXPECT_EQUAL(async_pkey_op_performed, 0); - } - } else { - POSIX_ENSURE_EQ(try_handshake(server_conn, client_conn), -1); - EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); - EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); - EXPECT_EQUAL(async_pkey_op_called, 0); - EXPECT_EQUAL(async_pkey_op_performed, 0); - } - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - return 0; -} - -int async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) -{ - /* Just store the op, we will process it later */ - pkey_op = op; - async_pkey_op_called++; - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - for (test_type = TEST_TYPE_START; test_type < TEST_TYPE_END; test_type++) { - /* Test: RSA cert */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* We need a security policy that only supports RSA certificates for auth */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20170210")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Test: RSA (TLS 1.2) key exchanges with TLS 1.3 client */ - { - if (!s2n_is_in_fips_mode()) { - /* Enable TLS 1.3 for the client */ - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* Configures server with maximum version 1.2 with only RSA key exchange ciphersuites */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_rsa_kex")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* RSA encrypted premaster secret key exchange requires client versions - * to be set and read correctly, this test covers the behavior with a 1.3 client */ - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - } - }; - - /* Test: ECDSA cert */ - { - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_ecdsa")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_ecdsa")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_ECDSA)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Test: RSA cert with RSA PSS signatures */ - if (s2n_is_rsa_pss_signing_supported()) { - const struct s2n_signature_scheme *const rsa_pss_rsae_sig_schemes[] = { - /* RSA PSS */ - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_rsae_sha384, - &s2n_rsa_pss_rsae_sha512, - }; - - struct s2n_signature_preferences sig_prefs = { - .count = 3, - .signature_schemes = rsa_pss_rsae_sig_schemes, - }; - - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - /* We need a security policy that only supports RSA certificates for auth */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); - - struct s2n_security_policy security_policy = { - .minimum_protocol_version = server_config->security_policy->minimum_protocol_version, - .cipher_preferences = server_config->security_policy->cipher_preferences, - .kem_preferences = server_config->security_policy->kem_preferences, - .signature_preferences = &sig_prefs, - .ecc_preferences = server_config->security_policy->ecc_preferences, - }; - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - server_config->security_policy = &security_policy; - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - client_config->check_ocsp = 0; - client_config->disable_x509_validation = 1; - client_config->security_policy = &security_policy; - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA_PSS_RSAE)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - - /* Test: RSA_PSS cert with RSA_PSS signatures */ - if (s2n_is_rsa_pss_certs_supported()) { - s2n_enable_tls13_in_test(); - - struct s2n_config *server_config = NULL, *client_config = NULL; - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20200207")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - /* Enable signature validation */ - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); - } - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20200207")); - client_config->check_ocsp = 0; - client_config->disable_x509_validation = 1; - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_RSA_PSS_PSS)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - s2n_disable_tls13_in_test(); - } - - s2n_reset_tls13_in_test(); - - /* Test: ML-DSA cert */ - if (s2n_mldsa_is_supported()) { - for (size_t verify = 0; verify <= 1; verify++) { - for (size_t client_auth = 0; client_auth <= 1; client_auth++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_MLDSA87_CERT, S2N_MLDSA87_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( - server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, - s2n_trust_all_verify_host, NULL)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, - S2N_MLDSA87_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( - client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(client_config, - s2n_trust_all_verify_host, NULL)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, - S2N_MLDSA87_CERT, NULL)); - - /* ML-DSA is only usable with TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, - "test_all_tls13")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, - "test_all_tls13")); - - /* The hashing path for verify_after_sign is subtly different. - * Make sure we test both paths. - */ - if (verify) { - EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, - S2N_VERIFY_AFTER_SIGN_ENABLED)); - } - - /* Test both the async and sync paths */ - if (test_type == TEST_TYPE_ASYNC) { - EXPECT_SUCCESS(s2n_config_set_async_pkey_callback( - server_config, async_pkey_fn)); - } - - /* Also test the client side use of ML-DSA */ - if (client_auth) { - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, - S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, - S2N_CERT_AUTH_REQUIRED)); - } - - EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, - chain_and_key, S2N_SIGNATURE_MLDSA)); - } - } - } - } - - /* Ensure that a handshake can be performed after all file descriptors are closed */ - { - /* A fork is created to ensure that closing file descriptors (like stdout) won't impact - * other tests. - */ - pid_t pid = fork(); - if (pid == 0) { - long max_file_descriptors = sysconf(_SC_OPEN_MAX); - for (long fd = 0; fd < max_file_descriptors; fd++) { - EXPECT_TRUE(fd <= INT_MAX); - close((int) fd); - } - - /* use a nested scope to force the DEFER_CLEANUP statements to - * execute before the exit() call. - */ - { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_NOT_NULL(chain_and_key); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - EXPECT_SUCCESS(test_cipher_preferences(config, config, chain_and_key, S2N_SIGNATURE_RSA)); - } - - exit(EXIT_SUCCESS); - } - - int status = 0; - EXPECT_EQUAL(waitpid(pid, &status, 0), pid); - EXPECT_EQUAL(status, EXIT_SUCCESS); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_handshake.h" + +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_mldsa.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +enum test_type { + TEST_TYPE_START, + TEST_TYPE_SYNC = TEST_TYPE_START, + TEST_TYPE_ASYNC, + TEST_TYPE_END +} test_type; + +struct s2n_async_pkey_op *pkey_op = NULL; +int async_pkey_op_called = 0; +int async_pkey_op_performed = 0; + +static uint8_t s2n_trust_all_verify_host(const char *host_name, size_t len, void *data) +{ + return 1; +} + +static int handle_async(struct s2n_connection *server_conn) +{ + s2n_blocked_status server_blocked; + + /* Test that handshake can't proceed until async pkey op is complete */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &server_blocked), S2N_ERR_ASYNC_BLOCKED); + + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Test that not performed pkey can't be applied */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), + S2N_ERR_ASYNC_NOT_PERFORMED); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(server_conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Test that we can perform pkey operation only once */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_perform(pkey_op, pkey), S2N_ERR_ASYNC_ALREADY_PERFORMED); + + /* Test that pkey op can't be applied to connection other than original one */ + struct s2n_connection *server_conn2 = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn2); + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn2), + S2N_ERR_ASYNC_WRONG_CONNECTION); + EXPECT_SUCCESS(s2n_connection_free(server_conn2)); + + /* Test that pkey op can be applied to original connection */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, server_conn)); + + /* Test that pkey op can't be applied to original connection more than once */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), + S2N_ERR_ASYNC_ALREADY_APPLIED); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + async_pkey_op_performed++; + + return 0; +} + +static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn) +{ + s2n_blocked_status server_blocked; + s2n_blocked_status client_blocked; + + int tries = 0; + do { + int client_rc = s2n_negotiate(client_conn, &client_blocked); + if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return -1; + } + + int server_rc = s2n_negotiate(server_conn, &server_blocked); + if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return -1; + } + + if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { + /* Only can happen in async tests */ + EXPECT_EQUAL(test_type, TEST_TYPE_ASYNC); + EXPECT_SUCCESS(handle_async(server_conn)); + } + + EXPECT_NOT_EQUAL(++tries, 5); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + return S2N_SUCCESS; +} + +int test_cipher_preferences(struct s2n_config *server_config, struct s2n_config *client_config, + struct s2n_cert_chain_and_key *expected_cert_chain, s2n_signature_algorithm sig_alg) +{ + const struct s2n_security_policy *security_policy = server_config->security_policy; + EXPECT_NOT_NULL(security_policy); + + if (s2n_is_in_fips_mode()) { + /* Override default client config ciphers when in FIPS mode to ensure all FIPS + * default ciphers are tested. + */ + client_config->security_policy = security_policy; + } + + const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; + EXPECT_NOT_NULL(cipher_preferences); + + /* Verify that a handshake succeeds for every available cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_suite *expected_cipher = cipher_preferences->suites[cipher_idx]; + uint8_t expect_failure = 0; + + /* Expect failure if the libcrypto we're building with can't support the cipher */ + if (!expected_cipher->available) { + expect_failure = 1; + } + + s2n_signature_algorithm expected_sig_alg = sig_alg; + /* Expect no server signature algorithm if RSA kex */ + if (expected_cipher->key_exchange_alg == &s2n_rsa) { + expected_sig_alg = S2N_SIGNATURE_ANONYMOUS; + } + + TEST_DEBUG_PRINT("Testing %s in %s mode, expect_failure=%d\n", expected_cipher->name, + test_type == TEST_TYPE_SYNC ? "synchronous" : "asynchronous", expect_failure); + + struct s2n_security_policy server_security_policy; + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + /* Craft a cipher preference with a cipher_idx cipher and assign it to server */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + server_cipher_preferences.suites = &expected_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + server_conn->security_policy_override = &server_security_policy; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + 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_config(client_conn, client_config)); + + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Reset counters */ + async_pkey_op_called = 0; + async_pkey_op_performed = 0; + + if (!expect_failure) { + POSIX_GUARD(try_handshake(server_conn, client_conn)); + + EXPECT_STRING_EQUAL(s2n_connection_get_cipher(server_conn), expected_cipher->name); + + EXPECT_EQUAL(server_conn->handshake_params.our_chain_and_key, expected_cert_chain); + EXPECT_NOT_NULL(server_conn->handshake_params.server_cert_sig_scheme); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, expected_sig_alg); + + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); + EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); + + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_EQUAL(async_pkey_op_called, 1); + EXPECT_EQUAL(async_pkey_op_performed, 1); + } else { + EXPECT_EQUAL(async_pkey_op_called, 0); + EXPECT_EQUAL(async_pkey_op_performed, 0); + } + } else { + POSIX_ENSURE_EQ(try_handshake(server_conn, client_conn), -1); + EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); + EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); + EXPECT_EQUAL(async_pkey_op_called, 0); + EXPECT_EQUAL(async_pkey_op_performed, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + return 0; +} + +int async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + /* Just store the op, we will process it later */ + pkey_op = op; + async_pkey_op_called++; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + for (test_type = TEST_TYPE_START; test_type < TEST_TYPE_END; test_type++) { + /* Test: RSA cert */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* We need a security policy that only supports RSA certificates for auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20170210")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test: RSA (TLS 1.2) key exchanges with TLS 1.3 client */ + { + if (!s2n_is_in_fips_mode()) { + /* Enable TLS 1.3 for the client */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Configures server with maximum version 1.2 with only RSA key exchange ciphersuites */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_rsa_kex")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* RSA encrypted premaster secret key exchange requires client versions + * to be set and read correctly, this test covers the behavior with a 1.3 client */ + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test: ECDSA cert */ + { + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_ecdsa")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_ecdsa")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_ECDSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test: RSA cert with RSA PSS signatures */ + if (s2n_is_rsa_pss_signing_supported()) { + const struct s2n_signature_scheme *const rsa_pss_rsae_sig_schemes[] = { + /* RSA PSS */ + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + }; + + struct s2n_signature_preferences sig_prefs = { + .count = 3, + .signature_schemes = rsa_pss_rsae_sig_schemes, + }; + + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* We need a security policy that only supports RSA certificates for auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + + struct s2n_security_policy security_policy = { + .minimum_protocol_version = server_config->security_policy->minimum_protocol_version, + .cipher_preferences = server_config->security_policy->cipher_preferences, + .kem_preferences = server_config->security_policy->kem_preferences, + .signature_preferences = &sig_prefs, + .ecc_preferences = server_config->security_policy->ecc_preferences, + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + server_config->security_policy = &security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + client_config->check_ocsp = 0; + client_config->disable_x509_validation = 1; + client_config->security_policy = &security_policy; + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA_PSS_RSAE)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: RSA_PSS cert with RSA_PSS signatures */ + if (s2n_is_rsa_pss_certs_supported()) { + s2n_enable_tls13_in_test(); + + struct s2n_config *server_config = NULL, *client_config = NULL; + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20200207")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20200207")); + client_config->check_ocsp = 0; + client_config->disable_x509_validation = 1; + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA_PSS_PSS)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + s2n_disable_tls13_in_test(); + } + + s2n_reset_tls13_in_test(); + + /* Test: ML-DSA cert */ + if (s2n_mldsa_is_supported()) { + for (size_t verify = 0; verify <= 1; verify++) { + for (size_t client_auth = 0; client_auth <= 1; client_auth++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_MLDSA87_CERT, S2N_MLDSA87_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( + server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, + s2n_trust_all_verify_host, NULL)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, + S2N_MLDSA87_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( + client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(client_config, + s2n_trust_all_verify_host, NULL)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, + S2N_MLDSA87_CERT, NULL)); + + /* ML-DSA is only usable with TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, + "test_all_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, + "test_all_tls13")); + + /* The hashing path for verify_after_sign is subtly different. + * Make sure we test both paths. + */ + if (verify) { + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, + S2N_VERIFY_AFTER_SIGN_ENABLED)); + } + + /* Test both the async and sync paths */ + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback( + server_config, async_pkey_fn)); + } + + /* Also test the client side use of ML-DSA */ + if (client_auth) { + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, + S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, + S2N_CERT_AUTH_REQUIRED)); + } + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_MLDSA)); + } + } + } + } + + /* Ensure that a handshake can be performed after all file descriptors are closed */ + { + /* A fork is created to ensure that closing file descriptors (like stdout) won't impact + * other tests. + */ + pid_t pid = fork(); + if (pid == 0) { + long max_file_descriptors = sysconf(_SC_OPEN_MAX); + for (long fd = 0; fd < max_file_descriptors; fd++) { + EXPECT_TRUE(fd <= INT_MAX); + close((int) fd); + } + + /* use a nested scope to force the DEFER_CLEANUP statements to + * execute before the exit() call. + */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_NOT_NULL(chain_and_key); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + EXPECT_SUCCESS(test_cipher_preferences(config, config, chain_and_key, S2N_SIGNATURE_RSA)); + } + + exit(EXIT_SUCCESS); + } + + int status = 0; + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, EXIT_SUCCESS); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_type_test.c b/tests/unit/s2n_handshake_type_test.c index a69a6e318b4..983b87f3f8f 100644 --- a/tests/unit/s2n_handshake_type_test.c +++ b/tests/unit/s2n_handshake_type_test.c @@ -1,276 +1,276 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "s2n_test.h" -#include "tls/s2n_connection.h" - -#define S2N_FIRST_COMMON_HANDSHAKE_FLAG NEGOTIATED -#define S2N_LAST_COMMON_HANDSHAKE_FLAG NO_CLIENT_CERT -#define S2N_FIRST_TLS12_HANDSHAKE_FLAG TLS12_PERFECT_FORWARD_SECRECY -#define S2N_LAST_TLS12_HANDSHAKE_FLAG WITH_SESSION_TICKET -#define S2N_FIRST_TLS13_HANDSHAKE_FLAG HELLO_RETRY_REQUEST -#define S2N_LAST_TLS13_HANDSHAKE_FLAG EARLY_CLIENT_CCS - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* Sanity check test setup */ - EXPECT_EQUAL(S2N_FIRST_COMMON_HANDSHAKE_FLAG, 1); - EXPECT_TRUE(S2N_FIRST_COMMON_HANDSHAKE_FLAG < S2N_LAST_COMMON_HANDSHAKE_FLAG); - EXPECT_EQUAL(S2N_FIRST_TLS12_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); - EXPECT_TRUE(S2N_FIRST_TLS12_HANDSHAKE_FLAG < S2N_LAST_TLS12_HANDSHAKE_FLAG); - EXPECT_EQUAL(S2N_FIRST_TLS13_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); - EXPECT_TRUE(S2N_FIRST_TLS13_HANDSHAKE_FLAG < S2N_LAST_TLS13_HANDSHAKE_FLAG); - EXPECT_EQUAL(S2N_MAX((uint32_t) S2N_LAST_TLS12_HANDSHAKE_FLAG, (uint32_t) S2N_LAST_TLS13_HANDSHAKE_FLAG), S2N_HANDSHAKES_COUNT / 2); - - /* Test s2n_handshake_type_reset */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->handshake.handshake_type = 0x12AB; - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_OK(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_OK(s2n_handshake_type_set_flag(conn, 0xFFFF)); - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_OK(s2n_handshake_type_set_flag(conn, 0)); - EXPECT_OK(s2n_handshake_type_reset(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test s2n_handshake_type_set_flag */ - { - /* Safety */ - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_flag(NULL, 0), S2N_ERR_NULL); - - /* Sets all common flags */ - for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - EXPECT_OK(s2n_handshake_type_reset(conn)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_check_flag */ - { - /* Safety */ - EXPECT_FALSE(s2n_handshake_type_check_flag(NULL, 0)); - - /* Check when common flags set */ - for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - /* All flags set */ - { - conn->handshake.handshake_type = 0xFFFF; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - }; - - /* No flags set */ - { - conn->handshake.handshake_type = 0; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); - }; - - /* One flag set */ - { - conn->handshake.handshake_type = flag; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_set_tls12_flag */ - { - /* Safety */ - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(NULL, 0), S2N_ERR_NULL); - - /* Sets all TLS1.2 flags */ - for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_handshake_type_set_tls12_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - EXPECT_OK(s2n_handshake_type_reset(conn)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_check_tls12_flag */ - { - /* Safety */ - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(NULL, 0)); - - /* Check when common flags set */ - for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - /* All flags set */ - { - conn->handshake.handshake_type = 0xFFFF; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - }; - - /* No flags set */ - { - conn->handshake.handshake_type = 0; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - }; - - /* One flag set */ - { - conn->handshake.handshake_type = flag; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_set_tls13_flag */ - { - /* Safety */ - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(NULL, 0), S2N_ERR_NULL); - - /* Sets all TLS1.3 flags */ - for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_handshake_type_set_tls13_flag(conn, flag)); - EXPECT_EQUAL(conn->handshake.handshake_type, flag); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - /* Test s2n_handshake_type_check_tls13_flag */ - { - /* Safety */ - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(NULL, 0)); - - /* Check when common flags set */ - for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - - /* All flags set */ - { - conn->handshake.handshake_type = 0xFFFF; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); - }; - - /* No flags set */ - { - conn->handshake.handshake_type = 0; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - }; - - /* One flag set */ - { - conn->handshake.handshake_type = flag; - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); - }; - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "s2n_test.h" +#include "tls/s2n_connection.h" + +#define S2N_FIRST_COMMON_HANDSHAKE_FLAG NEGOTIATED +#define S2N_LAST_COMMON_HANDSHAKE_FLAG NO_CLIENT_CERT +#define S2N_FIRST_TLS12_HANDSHAKE_FLAG TLS12_PERFECT_FORWARD_SECRECY +#define S2N_LAST_TLS12_HANDSHAKE_FLAG WITH_SESSION_TICKET +#define S2N_FIRST_TLS13_HANDSHAKE_FLAG HELLO_RETRY_REQUEST +#define S2N_LAST_TLS13_HANDSHAKE_FLAG EARLY_CLIENT_CCS + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Sanity check test setup */ + EXPECT_EQUAL(S2N_FIRST_COMMON_HANDSHAKE_FLAG, 1); + EXPECT_TRUE(S2N_FIRST_COMMON_HANDSHAKE_FLAG < S2N_LAST_COMMON_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_FIRST_TLS12_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); + EXPECT_TRUE(S2N_FIRST_TLS12_HANDSHAKE_FLAG < S2N_LAST_TLS12_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_FIRST_TLS13_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); + EXPECT_TRUE(S2N_FIRST_TLS13_HANDSHAKE_FLAG < S2N_LAST_TLS13_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_MAX((uint32_t) S2N_LAST_TLS12_HANDSHAKE_FLAG, (uint32_t) S2N_LAST_TLS13_HANDSHAKE_FLAG), S2N_HANDSHAKES_COUNT / 2); + + /* Test s2n_handshake_type_reset */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->handshake.handshake_type = 0x12AB; + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, 0xFFFF)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, 0)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_handshake_type_set_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all common flags */ + for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_OK(s2n_handshake_type_reset(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_set_tls12_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all TLS1.2 flags */ + for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_handshake_type_set_tls12_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_OK(s2n_handshake_type_reset(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_tls12_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_set_tls13_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all TLS1.3 flags */ + for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_tls13_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_tls13_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_key_update_threads_test.c b/tests/unit/s2n_key_update_threads_test.c index b46e32e7cbb..0cb56c01c3b 100644 --- a/tests/unit/s2n_key_update_threads_test.c +++ b/tests/unit/s2n_key_update_threads_test.c @@ -1,269 +1,269 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_sequence.h" -#include "s2n_test.h" -#include "testlib/s2n_examples.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_random.h" - -#define S2N_TEST_BUFFER_SIZE 1000 -#define S2N_TEST_ENCRYPTION_LIMIT 3 -#define S2N_TEST_KEY_UPDATE_COUNT 25 -#define S2N_TEST_RECORD_COUNT (S2N_TEST_ENCRYPTION_LIMIT * S2N_TEST_KEY_UPDATE_COUNT) -#define S2N_TEST_BYTES_TO_SEND (S2N_DEFAULT_FRAGMENT_LENGTH * S2N_TEST_RECORD_COUNT) - -#define S2N_CIPHER_SUITE_WITH_LIMIT(name, source, limit) \ - struct s2n_cipher_suite name = *(source); \ - struct s2n_record_algorithm _##name##_record_alg = *name.record_alg; \ - _##name##_record_alg.encryption_limit = limit; \ - name.record_alg = &_##name##_record_alg; - -S2N_RESULT s2n_set_key_update_request_for_testing(s2n_peer_key_update request); - -static void *s2n_send_random_data(void *arg) -{ - struct s2n_connection *conn = (struct s2n_connection *) arg; - - uint8_t buffer[S2N_TEST_BUFFER_SIZE] = "hello world"; - - size_t bytes_to_send = S2N_TEST_BYTES_TO_SEND; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - while (bytes_to_send) { - int r = s2n_send(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_send), &blocked); - if (r >= 0) { - bytes_to_send -= r; - } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Send error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); - return NULL; - } - } - return conn; -} - -static void *s2n_recv_random_data(void *arg) -{ - struct s2n_connection *conn = (struct s2n_connection *) arg; - - uint8_t buffer[S2N_TEST_BUFFER_SIZE] = { 0 }; - - size_t bytes_to_read = S2N_TEST_BYTES_TO_SEND; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - while (bytes_to_read) { - int r = s2n_recv(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_read), &blocked); - if (r >= 0) { - bytes_to_read -= r; - } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - fprintf(stderr, "Recv error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); - return NULL; - } - } - return conn; -} - -static S2N_RESULT s2n_send_and_recv_random_data(struct s2n_connection *conn) -{ - /* - * This test is intended to find concurrency issues when sending and receiving - * KeyUpdates, so we need to run the reader and writer in separate threads. - */ - - pthread_t reader = 0; - RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_recv_random_data, (void *) conn), 0); - - pthread_t writer = 0; - RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_send_random_data, (void *) conn), 0); - - void *reader_return = NULL; - RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); - RESULT_ENSURE_REF(reader_return); - - void *writer_return = NULL; - RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); - RESULT_ENSURE_REF(writer_return); - - RESULT_ENSURE_GT(conn->wire_bytes_out, S2N_TEST_BYTES_TO_SEND); - RESULT_ENSURE_GT(conn->wire_bytes_in, S2N_TEST_BYTES_TO_SEND); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_sanity_check_key_updates_sent(struct s2n_connection *conn) -{ - struct s2n_blob seq_num_blob = { 0 }; - RESULT_GUARD(s2n_connection_get_sequence_number(conn, conn->mode, &seq_num_blob)); - - uint64_t seq_num = 0; - RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &seq_num)); - RESULT_ENSURE_LTE(seq_num, conn->secure->cipher_suite->record_alg->encryption_limit); - - /* s2n-tls doesn't keep a running count of KeyUpdates, so to sanity check that - * at least one KeyUpdate occurred we have to rely on some math. - * - * wire_bytes_out represents the total bytes sent, and should therefore be - * less than or equal to (number of records sent) * (maximum size of a record). - * - * (maximum size of a record) can be calculated based on max_outgoing_fragment_length. - * We will call it max_record_size. - * - * (number of records sent) is seq_num, if no KeyUpdates were sent. seq_num - * starts at 0, is incremented by one for every record, and is reset to 0 by - * a KeyUpdate. So if no KeyUpdate occurs, seq_num represents the total number - * of records sent. - * - * If seq_num represents the total number of records sent, then wire_bytes_out - * must be less than or equal to (seq_num) * (max_record_size). - * If wire_bytes_out is instead greater than (seq_num) * (max_record_size), - * then more records were sent than seq_num accounts for. That means that seq_num - * must have been reset, which means that at least one KeyUpdate was sent. - */ - size_t max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); - RESULT_ENSURE_GT(conn->wire_bytes_out, max_record_size * seq_num); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_encryption_limits(struct s2n_connection *conn) -{ - RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); - - struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; - S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); - - conn->secure->cipher_suite = &key_limit_suite; - - RESULT_GUARD(s2n_send_and_recv_random_data(conn)); - RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); - - conn->secure->cipher_suite = original_suite; - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_peer_requests(struct s2n_connection *conn) -{ - RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); - - struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; - S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); - - conn->secure->cipher_suite = &key_limit_suite; - if (conn->mode == S2N_CLIENT) { - RESULT_GUARD(s2n_set_key_update_request_for_testing(S2N_KEY_UPDATE_REQUESTED)); - } - - RESULT_GUARD(s2n_send_and_recv_random_data(conn)); - RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); - - conn->secure->cipher_suite = original_suite; - return S2N_RESULT_OK; -} - -typedef S2N_RESULT (*s2n_test_scenario)(struct s2n_connection *conn); -static S2N_RESULT s2n_run_self_talk_test(s2n_test_scenario scenario_fn) -{ - struct s2n_cert_chain_and_key *chain_and_key = NULL; - RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - struct s2n_config *config = s2n_config_new(); - RESULT_ENSURE_REF(config); - RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); - RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); - RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); - - pid_t client_pid = fork(); - if (client_pid == 0) { - /* Suppress stdout. - * This only affects the new client process. - */ - fclose(stdout); - - struct s2n_connection *client = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); - - EXPECT_OK(scenario_fn(client)); - - EXPECT_SUCCESS(s2n_connection_free(client)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - exit(EXIT_SUCCESS); - } - - pid_t server_pid = fork(); - if (server_pid == 0) { - /* Suppress stdouts. - * This only affects the new server process. - */ - fclose(stdout); - - struct s2n_connection *server = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); - - EXPECT_OK(scenario_fn(server)); - - EXPECT_SUCCESS(s2n_connection_free(server)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - exit(EXIT_SUCCESS); - } - - int status = 0; - RESULT_ENSURE_EQ(waitpid(client_pid, &status, 0), client_pid); - RESULT_ENSURE_EQ(status, EXIT_SUCCESS); - RESULT_ENSURE_EQ(waitpid(server_pid, &status, 0), server_pid); - RESULT_ENSURE_EQ(status, EXIT_SUCCESS); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* KeyUpdate requires TLS1.3 */ - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* We're going to fork, so flush the initial test output first */ - EXPECT_EQUAL(fflush(stdout), 0); - - EXPECT_OK(s2n_run_self_talk_test(s2n_test_encryption_limits)); - EXPECT_OK(s2n_run_self_talk_test(s2n_test_peer_requests)); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_sequence.h" +#include "s2n_test.h" +#include "testlib/s2n_examples.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_BUFFER_SIZE 1000 +#define S2N_TEST_ENCRYPTION_LIMIT 3 +#define S2N_TEST_KEY_UPDATE_COUNT 25 +#define S2N_TEST_RECORD_COUNT (S2N_TEST_ENCRYPTION_LIMIT * S2N_TEST_KEY_UPDATE_COUNT) +#define S2N_TEST_BYTES_TO_SEND (S2N_DEFAULT_FRAGMENT_LENGTH * S2N_TEST_RECORD_COUNT) + +#define S2N_CIPHER_SUITE_WITH_LIMIT(name, source, limit) \ + struct s2n_cipher_suite name = *(source); \ + struct s2n_record_algorithm _##name##_record_alg = *name.record_alg; \ + _##name##_record_alg.encryption_limit = limit; \ + name.record_alg = &_##name##_record_alg; + +S2N_RESULT s2n_set_key_update_request_for_testing(s2n_peer_key_update request); + +static void *s2n_send_random_data(void *arg) +{ + struct s2n_connection *conn = (struct s2n_connection *) arg; + + uint8_t buffer[S2N_TEST_BUFFER_SIZE] = "hello world"; + + size_t bytes_to_send = S2N_TEST_BYTES_TO_SEND; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (bytes_to_send) { + int r = s2n_send(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_send), &blocked); + if (r >= 0) { + bytes_to_send -= r; + } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Send error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); + return NULL; + } + } + return conn; +} + +static void *s2n_recv_random_data(void *arg) +{ + struct s2n_connection *conn = (struct s2n_connection *) arg; + + uint8_t buffer[S2N_TEST_BUFFER_SIZE] = { 0 }; + + size_t bytes_to_read = S2N_TEST_BYTES_TO_SEND; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (bytes_to_read) { + int r = s2n_recv(conn, buffer, S2N_MIN(sizeof(buffer), bytes_to_read), &blocked); + if (r >= 0) { + bytes_to_read -= r; + } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Recv error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); + return NULL; + } + } + return conn; +} + +static S2N_RESULT s2n_send_and_recv_random_data(struct s2n_connection *conn) +{ + /* + * This test is intended to find concurrency issues when sending and receiving + * KeyUpdates, so we need to run the reader and writer in separate threads. + */ + + pthread_t reader = 0; + RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_recv_random_data, (void *) conn), 0); + + pthread_t writer = 0; + RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_send_random_data, (void *) conn), 0); + + void *reader_return = NULL; + RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); + RESULT_ENSURE_REF(reader_return); + + void *writer_return = NULL; + RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); + RESULT_ENSURE_REF(writer_return); + + RESULT_ENSURE_GT(conn->wire_bytes_out, S2N_TEST_BYTES_TO_SEND); + RESULT_ENSURE_GT(conn->wire_bytes_in, S2N_TEST_BYTES_TO_SEND); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_sanity_check_key_updates_sent(struct s2n_connection *conn) +{ + struct s2n_blob seq_num_blob = { 0 }; + RESULT_GUARD(s2n_connection_get_sequence_number(conn, conn->mode, &seq_num_blob)); + + uint64_t seq_num = 0; + RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &seq_num)); + RESULT_ENSURE_LTE(seq_num, conn->secure->cipher_suite->record_alg->encryption_limit); + + /* s2n-tls doesn't keep a running count of KeyUpdates, so to sanity check that + * at least one KeyUpdate occurred we have to rely on some math. + * + * wire_bytes_out represents the total bytes sent, and should therefore be + * less than or equal to (number of records sent) * (maximum size of a record). + * + * (maximum size of a record) can be calculated based on max_outgoing_fragment_length. + * We will call it max_record_size. + * + * (number of records sent) is seq_num, if no KeyUpdates were sent. seq_num + * starts at 0, is incremented by one for every record, and is reset to 0 by + * a KeyUpdate. So if no KeyUpdate occurs, seq_num represents the total number + * of records sent. + * + * If seq_num represents the total number of records sent, then wire_bytes_out + * must be less than or equal to (seq_num) * (max_record_size). + * If wire_bytes_out is instead greater than (seq_num) * (max_record_size), + * then more records were sent than seq_num accounts for. That means that seq_num + * must have been reset, which means that at least one KeyUpdate was sent. + */ + size_t max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); + RESULT_ENSURE_GT(conn->wire_bytes_out, max_record_size * seq_num); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_encryption_limits(struct s2n_connection *conn) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; + S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); + + conn->secure->cipher_suite = &key_limit_suite; + + RESULT_GUARD(s2n_send_and_recv_random_data(conn)); + RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); + + conn->secure->cipher_suite = original_suite; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_peer_requests(struct s2n_connection *conn) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; + S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); + + conn->secure->cipher_suite = &key_limit_suite; + if (conn->mode == S2N_CLIENT) { + RESULT_GUARD(s2n_set_key_update_request_for_testing(S2N_KEY_UPDATE_REQUESTED)); + } + + RESULT_GUARD(s2n_send_and_recv_random_data(conn)); + RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); + + conn->secure->cipher_suite = original_suite; + return S2N_RESULT_OK; +} + +typedef S2N_RESULT (*s2n_test_scenario)(struct s2n_connection *conn); +static S2N_RESULT s2n_run_self_talk_test(s2n_test_scenario scenario_fn) +{ + struct s2n_cert_chain_and_key *chain_and_key = NULL; + RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + RESULT_ENSURE_REF(config); + RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); + RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); + RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); + + pid_t client_pid = fork(); + if (client_pid == 0) { + /* Suppress stdout. + * This only affects the new client process. + */ + fclose(stdout); + + struct s2n_connection *client = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); + + EXPECT_OK(scenario_fn(client)); + + EXPECT_SUCCESS(s2n_connection_free(client)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + exit(EXIT_SUCCESS); + } + + pid_t server_pid = fork(); + if (server_pid == 0) { + /* Suppress stdouts. + * This only affects the new server process. + */ + fclose(stdout); + + struct s2n_connection *server = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + + EXPECT_OK(scenario_fn(server)); + + EXPECT_SUCCESS(s2n_connection_free(server)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + exit(EXIT_SUCCESS); + } + + int status = 0; + RESULT_ENSURE_EQ(waitpid(client_pid, &status, 0), client_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + RESULT_ENSURE_EQ(waitpid(server_pid, &status, 0), server_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* KeyUpdate requires TLS1.3 */ + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* We're going to fork, so flush the initial test output first */ + EXPECT_EQUAL(fflush(stdout), 0); + + EXPECT_OK(s2n_run_self_talk_test(s2n_test_encryption_limits)); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_peer_requests)); + + END_TEST(); +} diff --git a/tests/unit/s2n_mem_allocator_test.c b/tests/unit/s2n_mem_allocator_test.c index e4cf049abe2..157a5f5d78a 100644 --- a/tests/unit/s2n_mem_allocator_test.c +++ b/tests/unit/s2n_mem_allocator_test.c @@ -1,306 +1,306 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -#define SUPPORTED_CERTIFICATE_FORMATS (2) - -static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; -static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; - -#define HISTOGRAM_SIZE 100 -static uint32_t histogram_values[HISTOGRAM_SIZE] = { 0 }; -static uint32_t histogram_counts[HISTOGRAM_SIZE] = { 0 }; - -static int custom_mem_init(void) -{ - return 0; -} - -static int custom_mem_cleanup(void) -{ - return 0; -} - -static int custom_mem_malloc(void **ptr, uint32_t requested, uint32_t *allocated) -{ - int i = 0; - for (i = 0; i < HISTOGRAM_SIZE; i++) { - if (histogram_values[i] == 0) { - histogram_values[i] = requested; - } - - if (histogram_values[i] == requested) { - break; - } - } - - if (i < HISTOGRAM_SIZE) { - histogram_counts[i] += 1; - } - - *ptr = malloc(requested); - *allocated = requested; - - /* Fill the memory with non-zeroes to check that s2n handles that fine */ - memset(*ptr, 'a', requested); - - return 0; -} - -static int custom_mem_free(void *ptr, uint32_t size) -{ - free(ptr); - return 0; -} - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - char buffer[0xffff]; - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - s2n_config_disable_x509_verification(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - s2n_connection_set_config(conn, config); - conn->server_protocol_version = S2N_TLS12; - conn->client_protocol_version = S2N_TLS12; - conn->actual_protocol_version = S2N_TLS12; - - s2n_connection_set_io_pair(conn, io_pair); - - s2n_negotiate(conn, &blocked); - - s2n_connection_free_handshake(conn); - - uint16_t timeout = 1; - s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); - int i = 0; - for (i = 1; i < 0xffff - 100; i += 100) { - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - s2n_send(conn, buffer, i, &blocked); - } - - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - - /* release the buffers here to validate we can continue IO after */ - s2n_connection_release_buffers(conn); - - /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ - struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; - int r = 0; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - /* Active application bytes consumed is reset to 0 in before writing data. */ - /* Its value should equal to bytes written after writing */ - ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); - if ((uint64_t) bytes_written != conn->active_application_bytes_consumed) { - exit(0); - } - - int shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - s2n_config_free(config); - - /* Give the server a chance to a void a sigpipe */ - sleep(1); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - s2n_blocked_status blocked; - int status = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - - /* We have to set the callback before BEGIN_TEST, because s2n_init() is called - * there. - */ - int rc = s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free); - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Can't add callbacks if s2n is initialized */ - EXPECT_FAILURE(s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free)); - - EXPECT_SUCCESS(rc); - - for (size_t is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { - struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - conn->server_protocol_version = S2N_TLS12; - conn->client_protocol_version = S2N_TLS12; - conn->actual_protocol_version = S2N_TLS12; - - for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); - } - - if (is_dh_key_exchange) { - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - } - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - char buffer[0xffff]; - for (int i = 1; i < 0xffff; i += 100) { - char *ptr = buffer; - int size = i; - - do { - int bytes_read = 0; - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); - - size -= bytes_read; - ptr += bytes_read; - } while (size); - - for (int j = 0; j < i; j++) { - EXPECT_EQUAL(buffer[j], 33); - } - - /* release the buffers here to validate we can continue IO after */ - EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); - } - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - } - -#if defined(S2N_TEST_DEBUG) - /* Sort our histogram */ - uint32_t spare_value, spare_count; - for (int i = 0; i < HISTOGRAM_SIZE; i++) { - if (histogram_counts[i] == 0) { - break; - } - - for (int j = i + 1; j < HISTOGRAM_SIZE; j++) { - if (histogram_counts[j] == 0) { - break; - } - - if (histogram_values[j] < histogram_values[i]) { - spare_value = histogram_values[i]; - spare_count = histogram_counts[i]; - - histogram_values[i] = histogram_values[j]; - histogram_counts[i] = histogram_counts[j]; - - histogram_values[j] = spare_value; - histogram_counts[j] = spare_count; - } - } - } - - /* Print the histogram values */ - TEST_DEBUG_PRINT("\n\n"); - for (int i = 0; i < HISTOGRAM_SIZE; i++) { - if (histogram_values[i] == 0) { - break; - } - TEST_DEBUG_PRINT("Allocated %d bytes %d times\n", histogram_values[i], histogram_counts[i]); - } - TEST_DEBUG_PRINT("\n"); -#endif - - END_TEST(); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +#define HISTOGRAM_SIZE 100 +static uint32_t histogram_values[HISTOGRAM_SIZE] = { 0 }; +static uint32_t histogram_counts[HISTOGRAM_SIZE] = { 0 }; + +static int custom_mem_init(void) +{ + return 0; +} + +static int custom_mem_cleanup(void) +{ + return 0; +} + +static int custom_mem_malloc(void **ptr, uint32_t requested, uint32_t *allocated) +{ + int i = 0; + for (i = 0; i < HISTOGRAM_SIZE; i++) { + if (histogram_values[i] == 0) { + histogram_values[i] = requested; + } + + if (histogram_values[i] == requested) { + break; + } + } + + if (i < HISTOGRAM_SIZE) { + histogram_counts[i] += 1; + } + + *ptr = malloc(requested); + *allocated = requested; + + /* Fill the memory with non-zeroes to check that s2n handles that fine */ + memset(*ptr, 'a', requested); + + return 0; +} + +static int custom_mem_free(void *ptr, uint32_t size) +{ + free(ptr); + return 0; +} + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + char buffer[0xffff]; + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + s2n_connection_set_config(conn, config); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + s2n_connection_set_io_pair(conn, io_pair); + + s2n_negotiate(conn, &blocked); + + s2n_connection_free_handshake(conn); + + uint16_t timeout = 1; + s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); + int i = 0; + for (i = 1; i < 0xffff - 100; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + s2n_send(conn, buffer, i, &blocked); + } + + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + /* release the buffers here to validate we can continue IO after */ + s2n_connection_release_buffers(conn); + + /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ + struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; + int r = 0; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + /* Active application bytes consumed is reset to 0 in before writing data. */ + /* Its value should equal to bytes written after writing */ + ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); + if ((uint64_t) bytes_written != conn->active_application_bytes_consumed) { + exit(0); + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + + /* We have to set the callback before BEGIN_TEST, because s2n_init() is called + * there. + */ + int rc = s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Can't add callbacks if s2n is initialized */ + EXPECT_FAILURE(s2n_mem_set_callbacks(custom_mem_init, custom_mem_cleanup, custom_mem_malloc, custom_mem_free)); + + EXPECT_SUCCESS(rc); + + for (size_t is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + char buffer[0xffff]; + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + + /* release the buffers here to validate we can continue IO after */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + for (size_t cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + } + +#if defined(S2N_TEST_DEBUG) + /* Sort our histogram */ + uint32_t spare_value, spare_count; + for (int i = 0; i < HISTOGRAM_SIZE; i++) { + if (histogram_counts[i] == 0) { + break; + } + + for (int j = i + 1; j < HISTOGRAM_SIZE; j++) { + if (histogram_counts[j] == 0) { + break; + } + + if (histogram_values[j] < histogram_values[i]) { + spare_value = histogram_values[i]; + spare_count = histogram_counts[i]; + + histogram_values[i] = histogram_values[j]; + histogram_counts[i] = histogram_counts[j]; + + histogram_values[j] = spare_value; + histogram_counts[j] = spare_count; + } + } + } + + /* Print the histogram values */ + TEST_DEBUG_PRINT("\n\n"); + for (int i = 0; i < HISTOGRAM_SIZE; i++) { + if (histogram_values[i] == 0) { + break; + } + TEST_DEBUG_PRINT("Allocated %d bytes %d times\n", histogram_values[i], histogram_counts[i]); + } + TEST_DEBUG_PRINT("\n"); +#endif + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_mem_usage_test.c b/tests/unit/s2n_mem_usage_test.c index 78d73f70775..9679877252a 100644 --- a/tests/unit/s2n_mem_usage_test.c +++ b/tests/unit/s2n_mem_usage_test.c @@ -1,210 +1,210 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -#ifdef __FreeBSD__ - /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) - * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ - #undef _POSIX_C_SOURCE -/* clang-format off */ - #include - #include - /* clang-format on */ - #include -#elif defined(__OpenBSD__) - #undef _POSIX_C_SOURCE - #include -/* clang-format off */ - #include - #include - /* clang-format on */ - #include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -/* The number of connection pairs to allocate before measuring memory - * usage. The greater the value, the more accurate the end result. */ -#define MAX_CONNECTIONS 1000 - -ssize_t get_vm_data_size() -{ - long page_size = 0; - ssize_t size = 0, resident = 0, share = 0, text = 0, lib = 0, data = 0, dt = 0; - - page_size = sysconf(_SC_PAGESIZE); - if (page_size < 0) { - return -1; - } - - FILE *status_file = fopen("/proc/self/statm", "r"); - if (fscanf(status_file, "%zd %zd %zd %zd %zd %zd %zd", &size, &resident, &share, &text, &lib, &data, &dt) < 7) { - fclose(status_file); - return -1; - } - fclose(status_file); - - return data * page_size; -} - -int main(int argc, char **argv) -{ - size_t connectionsToUse = MAX_CONNECTIONS; - - char *cert_chain = NULL; - char *private_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - /* Skip the test unless specifically enabled. - * This test is too unreliable to run in all customer environments. - * We should choose specific, known builds to run this test in. - */ - const char *env_var = getenv("S2N_EXPECTED_CONNECTION_MEMORY_KB"); - if (env_var == NULL) { - END_TEST(); - } - const int expected_kbs_per_conn = atoi(env_var); - EXPECT_TRUE(expected_kbs_per_conn > 1); - - struct rlimit file_limit; - EXPECT_SUCCESS(getrlimit(RLIMIT_NOFILE, &file_limit)); - /* 4 fds per connection: {client,server} {write,read} fd - * and reserve 16 fds for libraries, stdin/stdout/stderr and so on */ - if (4 * connectionsToUse + 16 > file_limit.rlim_cur) { - connectionsToUse = S2N_MAX(1, (file_limit.rlim_cur - 16) / 4); - } - - struct s2n_connection **clients = calloc(connectionsToUse, sizeof(struct s2n_connection *)); - struct s2n_connection **servers = calloc(connectionsToUse, sizeof(struct s2n_connection *)); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_config *server_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - ssize_t vm_data_initial = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_initial, -1); - - /* Allocate all connections */ - for (size_t i = 0; i < connectionsToUse; i++) { - struct s2n_connection *client_conn = NULL; - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - clients[i] = client_conn; - - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - servers[i] = server_conn; - } - - ssize_t vm_data_after_allocation = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_allocation, -1); - - for (size_t i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connections_set_io_pair(clients[i], servers[i], &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(servers[i], clients[i])); - } - - ssize_t vm_data_after_handshakes = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_handshakes, -1); - - for (int i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connection_free_handshake(servers[i])); - EXPECT_SUCCESS(s2n_connection_free_handshake(clients[i])); - } - ssize_t vm_data_after_free_handshake = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_free_handshake, -1); - - for (int i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connection_release_buffers(servers[i])); - EXPECT_SUCCESS(s2n_connection_release_buffers(clients[i])); - } - ssize_t vm_data_after_release_buffers = get_vm_data_size(); - EXPECT_NOT_EQUAL(vm_data_after_release_buffers, -1); - - for (int i = 0; i < connectionsToUse; i++) { - EXPECT_SUCCESS(s2n_connection_free(clients[i])); - EXPECT_SUCCESS(s2n_connection_free(servers[i])); - } - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(cert_chain); - free(private_key); - free(clients); - free(servers); - - EXPECT_TRUE(vm_data_after_free_handshake <= vm_data_after_handshakes); - EXPECT_TRUE(vm_data_after_release_buffers <= vm_data_after_free_handshake); - - ssize_t handshake_diff = (vm_data_after_handshakes - vm_data_initial); - ssize_t allocation_diff = (vm_data_after_allocation - vm_data_initial); - EXPECT_TRUE(allocation_diff <= handshake_diff); - - ssize_t mem_per_conn = handshake_diff / (connectionsToUse * 2); - ssize_t kbs_per_conn = mem_per_conn / 1024; - - if (kbs_per_conn != expected_kbs_per_conn) { - printf("\nExpected KB per connection: %i\n", expected_kbs_per_conn); - printf("\nActual KB per connection: %zi\n", kbs_per_conn); - printf("This is a %.2f%% change\n", - (kbs_per_conn - expected_kbs_per_conn) * 100.0 / expected_kbs_per_conn); - - printf("\n"); - printf("VmData initial: %10zd\n", vm_data_initial); - printf("VmData after allocations: %10zd\n", vm_data_after_allocation); - printf("VmData after handshakes: %10zd\n", vm_data_after_handshakes); - printf("VmData after free handshake: %10zd\n", vm_data_after_free_handshake); - printf("VmData after release: %10zd\n", vm_data_after_release_buffers); - printf("Number of connections used: %10zu\n", connectionsToUse); - FAIL_MSG("Unexpected memory usage. If expected, update MEM_PER_CONNECTION."); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +#ifdef __FreeBSD__ + /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) + * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ + #undef _POSIX_C_SOURCE +/* clang-format off */ + #include + #include + /* clang-format on */ + #include +#elif defined(__OpenBSD__) + #undef _POSIX_C_SOURCE + #include +/* clang-format off */ + #include + #include + /* clang-format on */ + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* The number of connection pairs to allocate before measuring memory + * usage. The greater the value, the more accurate the end result. */ +#define MAX_CONNECTIONS 1000 + +ssize_t get_vm_data_size() +{ + long page_size = 0; + ssize_t size = 0, resident = 0, share = 0, text = 0, lib = 0, data = 0, dt = 0; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) { + return -1; + } + + FILE *status_file = fopen("/proc/self/statm", "r"); + if (fscanf(status_file, "%zd %zd %zd %zd %zd %zd %zd", &size, &resident, &share, &text, &lib, &data, &dt) < 7) { + fclose(status_file); + return -1; + } + fclose(status_file); + + return data * page_size; +} + +int main(int argc, char **argv) +{ + size_t connectionsToUse = MAX_CONNECTIONS; + + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Skip the test unless specifically enabled. + * This test is too unreliable to run in all customer environments. + * We should choose specific, known builds to run this test in. + */ + const char *env_var = getenv("S2N_EXPECTED_CONNECTION_MEMORY_KB"); + if (env_var == NULL) { + END_TEST(); + } + const int expected_kbs_per_conn = atoi(env_var); + EXPECT_TRUE(expected_kbs_per_conn > 1); + + struct rlimit file_limit; + EXPECT_SUCCESS(getrlimit(RLIMIT_NOFILE, &file_limit)); + /* 4 fds per connection: {client,server} {write,read} fd + * and reserve 16 fds for libraries, stdin/stdout/stderr and so on */ + if (4 * connectionsToUse + 16 > file_limit.rlim_cur) { + connectionsToUse = S2N_MAX(1, (file_limit.rlim_cur - 16) / 4); + } + + struct s2n_connection **clients = calloc(connectionsToUse, sizeof(struct s2n_connection *)); + struct s2n_connection **servers = calloc(connectionsToUse, sizeof(struct s2n_connection *)); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_config *server_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + ssize_t vm_data_initial = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_initial, -1); + + /* Allocate all connections */ + for (size_t i = 0; i < connectionsToUse; i++) { + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + clients[i] = client_conn; + + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + servers[i] = server_conn; + } + + ssize_t vm_data_after_allocation = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_allocation, -1); + + for (size_t i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connections_set_io_pair(clients[i], servers[i], &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(servers[i], clients[i])); + } + + ssize_t vm_data_after_handshakes = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_handshakes, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_free_handshake(servers[i])); + EXPECT_SUCCESS(s2n_connection_free_handshake(clients[i])); + } + ssize_t vm_data_after_free_handshake = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_free_handshake, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_release_buffers(servers[i])); + EXPECT_SUCCESS(s2n_connection_release_buffers(clients[i])); + } + ssize_t vm_data_after_release_buffers = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_release_buffers, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_free(clients[i])); + EXPECT_SUCCESS(s2n_connection_free(servers[i])); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(cert_chain); + free(private_key); + free(clients); + free(servers); + + EXPECT_TRUE(vm_data_after_free_handshake <= vm_data_after_handshakes); + EXPECT_TRUE(vm_data_after_release_buffers <= vm_data_after_free_handshake); + + ssize_t handshake_diff = (vm_data_after_handshakes - vm_data_initial); + ssize_t allocation_diff = (vm_data_after_allocation - vm_data_initial); + EXPECT_TRUE(allocation_diff <= handshake_diff); + + ssize_t mem_per_conn = handshake_diff / (connectionsToUse * 2); + ssize_t kbs_per_conn = mem_per_conn / 1024; + + if (kbs_per_conn != expected_kbs_per_conn) { + printf("\nExpected KB per connection: %i\n", expected_kbs_per_conn); + printf("\nActual KB per connection: %zi\n", kbs_per_conn); + printf("This is a %.2f%% change\n", + (kbs_per_conn - expected_kbs_per_conn) * 100.0 / expected_kbs_per_conn); + + printf("\n"); + printf("VmData initial: %10zd\n", vm_data_initial); + printf("VmData after allocations: %10zd\n", vm_data_after_allocation); + printf("VmData after handshakes: %10zd\n", vm_data_after_handshakes); + printf("VmData after free handshake: %10zd\n", vm_data_after_free_handshake); + printf("VmData after release: %10zd\n", vm_data_after_release_buffers); + printf("Number of connections used: %10zu\n", connectionsToUse); + FAIL_MSG("Unexpected memory usage. If expected, update MEM_PER_CONNECTION."); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_mutual_auth_test.c b/tests/unit/s2n_mutual_auth_test.c index b86fbd7c0b7..b36b2e384c1 100644 --- a/tests/unit/s2n_mutual_auth_test.c +++ b/tests/unit/s2n_mutual_auth_test.c @@ -1,566 +1,566 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "api/unstable/cert_authorities.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_cert_authorities.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_security_policies.h" -#include "utils/s2n_safety.h" - -DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); -DEFINE_POINTER_CLEANUP_FUNC(X509_NAME *, X509_NAME_free); - -struct host_verify_data { - uint8_t callback_invoked; - uint8_t allow; -}; - -static uint8_t verify_host_fn(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return verify_data->allow; -} - -/** - * Pretty prints a DER-encoded Distinguished Name to an s2n_blob - * - * @param der_dn The DER-encoded Distinguished Name input - * @param der_dn_len Length of the DER-encoded Distinguished Name - * @param output_blob Pointer to an s2n_blob that will contain the pretty-printed output - */ -S2N_RESULT pretty_print_dn(const uint8_t *der_dn, size_t der_dn_len, struct s2n_blob *output_blob) -{ - RESULT_ENSURE_REF(der_dn); - RESULT_ENSURE_REF(output_blob); - - DEFER_CLEANUP(BIO *bio = BIO_new(BIO_s_mem()), BIO_free_pointer); - RESULT_ENSURE_REF(bio); - - /* Parse the DER-encoded DN into an X509_NAME structure */ - DEFER_CLEANUP(X509_NAME *name = d2i_X509_NAME(NULL, &der_dn, der_dn_len), X509_NAME_free_pointer); - RESULT_ENSURE_REF(name); - - /* Pretty print the X509_NAME to the BIO */ - X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE); - - BUF_MEM *bptr; - BIO_get_mem_ptr(bio, &bptr); - - RESULT_GUARD_POSIX(s2n_realloc(output_blob, bptr->length)); - RESULT_CHECKED_MEMCPY(output_blob->data, bptr->data, bptr->length); - - return S2N_RESULT_OK; -} - -static int cert_req_cb(struct s2n_connection *conn, void *ctx_in, struct s2n_certificate_request *req) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(ctx_in); - EXPECT_NOT_NULL(req); - - struct s2n_cert_chain_and_key **ctx = ctx_in; - - struct s2n_certificate_authority_list *list = s2n_certificate_request_get_ca_list(req); - EXPECT_NOT_NULL(list); - - uint8_t *name = NULL; - uint16_t length = 0; - - if (s2n_cert_authorities_supported_from_trust_store()) { - /* Confirm the list is re-readable (by reading it 3 times) */ - for (int i = 0; i < 3; i++) { - const char *expected_subjects[] = { - "C = US, CN = leaf", - "C = US, CN = branch", - "C = US, CN = root", - }; - - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(NULL, &name, &length), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, NULL, &length), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, NULL), S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_reread(NULL), S2N_ERR_INVALID_ARGUMENT); - - for (int j = 0; j < s2n_array_len(expected_subjects); j++) { - EXPECT_TRUE(s2n_certificate_authority_list_has_next(list)); - EXPECT_SUCCESS(s2n_certificate_authority_list_next(list, &name, &length)); - - DEFER_CLEANUP(struct s2n_blob printed = { 0 }, s2n_free); - POSIX_GUARD_RESULT(pretty_print_dn(name, length, &printed)); - - const char *expected = expected_subjects[j]; - EXPECT_EQUAL(strlen(expected), printed.size); - EXPECT_EQUAL(memcmp(expected, printed.data, printed.size), 0); - } - - EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, &length), S2N_ERR_INVALID_ARGUMENT); - EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); - } - } else { - /* If certificate authorities weren't set, then there shouldn't be anything in the list. */ - EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); - EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); - EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); - } - - /* We set to null to test failure in which case setting will fail. */ - if (*ctx != NULL) { - EXPECT_SUCCESS(s2n_certificate_request_set_certificate(req, *ctx)); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_request_set_certificate(req, *ctx), - S2N_ERR_INVALID_ARGUMENT); - } - - return 0; -} - -static bool s2n_should_skip_cipher(struct s2n_cipher_suite *suite) -{ - if (!suite->available) { - /* Skip Ciphers that aren't supported with the linked libcrypto */ - return true; - } - - /* Skip Ciphers that aren't supported with the certificate chain we use in this test */ - if (suite->auth_method == S2N_AUTHENTICATION_ECDSA) { - return true; - } - - if (suite->minimum_required_tls_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { - return true; - } - - return false; -} - -int main(int argc, char **argv) -{ - struct s2n_config *config = NULL; - const struct s2n_security_policy *default_security_policy = NULL; - const struct s2n_cipher_preferences *default_cipher_preferences = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* - * Test Mutual Auth using **s2n_connection_set_client_auth_type** - */ - - EXPECT_NOT_NULL(config = s2n_config_new_minimal()); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - EXPECT_NOT_NULL(default_security_policy = config->security_policy); - EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); - - struct host_verify_data verify_data = { .allow = 1, .callback_invoked = 0 }; - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, verify_host_fn, &verify_data)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_cert_chain_and_key *cb_chain = NULL; - - /* If this isn't supported we can't really test the end-to-end flow. */ - if (s2n_cert_authorities_supported_from_trust_store()) { - EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config)); - } - - EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_request_callback(NULL, cert_req_cb, (void *) &cb_chain), - S2N_ERR_INVALID_ARGUMENT); - - /** - * Test with 3 variants: - * - * * Callback not set - * * Callback set, returns NULL - * * Callback set, returns a valid cert chain - */ - for (int cert_req_callback_mode = 0; cert_req_callback_mode < 3; cert_req_callback_mode++) { - if (cert_req_callback_mode != 0) { - EXPECT_SUCCESS(s2n_config_set_cert_request_callback(config, cert_req_cb, (void *) &cb_chain)); - } - - bool saw_tls12 = false; - bool saw_tls13 = false; - const bool expected_success = cert_req_callback_mode != 1; - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - verify_data.callback_invoked = 0; - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - if (cert_req_callback_mode == 0) { - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } else if (cert_req_callback_mode == 1) { - cb_chain = NULL; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_NO_CERT_FOUND); - } else if (cert_req_callback_mode == 2) { - cb_chain = chain_and_key; - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - - if (expected_success) { - switch (s2n_connection_get_actual_protocol_version(server_conn)) { - case S2N_TLS13: - saw_tls13 = true; - break; - case S2N_TLS12: - saw_tls12 = true; - break; - default: - /* no-op */ - break; - } - - /* Verify that both connections negotiated Mutual Auth */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - EXPECT_TRUE(verify_data.callback_invoked); - } - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - } - - if (expected_success) { - /* If not supported, don't expect to see TlS 1.3 */ - EXPECT_TRUE(saw_tls13 || !s2n_is_tls13_fully_supported()); - EXPECT_TRUE(saw_tls12); - } - - /* Reset the callback */ - config->cert_request_cb = NULL; - config->cert_request_cb_ctx = NULL; - } - - /* - * Test Mutual Auth using **s2n_config_set_client_auth_type** - */ - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated Mutual Auth */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - } - - /* - * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** - */ - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated Mutual Auth */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - } - - /* - * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** only on one side of the - * connection and verify that a connection is not established - */ - - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy server_security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - /* Craft a cipher preference with a cipher_idx cipher */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (s2n_should_skip_cipher(cur_cipher)) { - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); - server_security_policy.cipher_preferences = &server_cipher_preferences; - - config->security_policy = &server_security_policy; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* Only set S2N_CERT_AUTH_REQUIRED on the server and not the client so that the connection fails */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that NEITHER connections negotiated Mutual Auth */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - 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_ALERT(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - - /* 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); - free(private_key_pem); - free(dhparams_pem); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "api/unstable/cert_authorities.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_authorities.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); +DEFINE_POINTER_CLEANUP_FUNC(X509_NAME *, X509_NAME_free); + +struct host_verify_data { + uint8_t callback_invoked; + uint8_t allow; +}; + +static uint8_t verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return verify_data->allow; +} + +/** + * Pretty prints a DER-encoded Distinguished Name to an s2n_blob + * + * @param der_dn The DER-encoded Distinguished Name input + * @param der_dn_len Length of the DER-encoded Distinguished Name + * @param output_blob Pointer to an s2n_blob that will contain the pretty-printed output + */ +S2N_RESULT pretty_print_dn(const uint8_t *der_dn, size_t der_dn_len, struct s2n_blob *output_blob) +{ + RESULT_ENSURE_REF(der_dn); + RESULT_ENSURE_REF(output_blob); + + DEFER_CLEANUP(BIO *bio = BIO_new(BIO_s_mem()), BIO_free_pointer); + RESULT_ENSURE_REF(bio); + + /* Parse the DER-encoded DN into an X509_NAME structure */ + DEFER_CLEANUP(X509_NAME *name = d2i_X509_NAME(NULL, &der_dn, der_dn_len), X509_NAME_free_pointer); + RESULT_ENSURE_REF(name); + + /* Pretty print the X509_NAME to the BIO */ + X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE); + + BUF_MEM *bptr; + BIO_get_mem_ptr(bio, &bptr); + + RESULT_GUARD_POSIX(s2n_realloc(output_blob, bptr->length)); + RESULT_CHECKED_MEMCPY(output_blob->data, bptr->data, bptr->length); + + return S2N_RESULT_OK; +} + +static int cert_req_cb(struct s2n_connection *conn, void *ctx_in, struct s2n_certificate_request *req) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ctx_in); + EXPECT_NOT_NULL(req); + + struct s2n_cert_chain_and_key **ctx = ctx_in; + + struct s2n_certificate_authority_list *list = s2n_certificate_request_get_ca_list(req); + EXPECT_NOT_NULL(list); + + uint8_t *name = NULL; + uint16_t length = 0; + + if (s2n_cert_authorities_supported_from_trust_store()) { + /* Confirm the list is re-readable (by reading it 3 times) */ + for (int i = 0; i < 3; i++) { + const char *expected_subjects[] = { + "C = US, CN = leaf", + "C = US, CN = branch", + "C = US, CN = root", + }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(NULL, &name, &length), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, NULL, &length), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, NULL), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_reread(NULL), S2N_ERR_INVALID_ARGUMENT); + + for (int j = 0; j < s2n_array_len(expected_subjects); j++) { + EXPECT_TRUE(s2n_certificate_authority_list_has_next(list)); + EXPECT_SUCCESS(s2n_certificate_authority_list_next(list, &name, &length)); + + DEFER_CLEANUP(struct s2n_blob printed = { 0 }, s2n_free); + POSIX_GUARD_RESULT(pretty_print_dn(name, length, &printed)); + + const char *expected = expected_subjects[j]; + EXPECT_EQUAL(strlen(expected), printed.size); + EXPECT_EQUAL(memcmp(expected, printed.data, printed.size), 0); + } + + EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_authority_list_next(list, &name, &length), S2N_ERR_INVALID_ARGUMENT); + EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); + } + } else { + /* If certificate authorities weren't set, then there shouldn't be anything in the list. */ + EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); + EXPECT_SUCCESS(s2n_certificate_authority_list_reread(list)); + EXPECT_FALSE(s2n_certificate_authority_list_has_next(list)); + } + + /* We set to null to test failure in which case setting will fail. */ + if (*ctx != NULL) { + EXPECT_SUCCESS(s2n_certificate_request_set_certificate(req, *ctx)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_certificate_request_set_certificate(req, *ctx), + S2N_ERR_INVALID_ARGUMENT); + } + + return 0; +} + +static bool s2n_should_skip_cipher(struct s2n_cipher_suite *suite) +{ + if (!suite->available) { + /* Skip Ciphers that aren't supported with the linked libcrypto */ + return true; + } + + /* Skip Ciphers that aren't supported with the certificate chain we use in this test */ + if (suite->auth_method == S2N_AUTHENTICATION_ECDSA) { + return true; + } + + if (suite->minimum_required_tls_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { + return true; + } + + return false; +} + +int main(int argc, char **argv) +{ + struct s2n_config *config = NULL; + const struct s2n_security_policy *default_security_policy = NULL; + const struct s2n_cipher_preferences *default_cipher_preferences = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* + * Test Mutual Auth using **s2n_connection_set_client_auth_type** + */ + + EXPECT_NOT_NULL(config = s2n_config_new_minimal()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = config->security_policy); + EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); + + struct host_verify_data verify_data = { .allow = 1, .callback_invoked = 0 }; + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, verify_host_fn, &verify_data)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_cert_chain_and_key *cb_chain = NULL; + + /* If this isn't supported we can't really test the end-to-end flow. */ + if (s2n_cert_authorities_supported_from_trust_store()) { + EXPECT_SUCCESS(s2n_config_set_cert_authorities_from_trust_store(config)); + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_request_callback(NULL, cert_req_cb, (void *) &cb_chain), + S2N_ERR_INVALID_ARGUMENT); + + /** + * Test with 3 variants: + * + * * Callback not set + * * Callback set, returns NULL + * * Callback set, returns a valid cert chain + */ + for (int cert_req_callback_mode = 0; cert_req_callback_mode < 3; cert_req_callback_mode++) { + if (cert_req_callback_mode != 0) { + EXPECT_SUCCESS(s2n_config_set_cert_request_callback(config, cert_req_cb, (void *) &cb_chain)); + } + + bool saw_tls12 = false; + bool saw_tls13 = false; + const bool expected_success = cert_req_callback_mode != 1; + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + verify_data.callback_invoked = 0; + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + if (cert_req_callback_mode == 0) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else if (cert_req_callback_mode == 1) { + cb_chain = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_NO_CERT_FOUND); + } else if (cert_req_callback_mode == 2) { + cb_chain = chain_and_key; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + if (expected_success) { + switch (s2n_connection_get_actual_protocol_version(server_conn)) { + case S2N_TLS13: + saw_tls13 = true; + break; + case S2N_TLS12: + saw_tls12 = true; + break; + default: + /* no-op */ + break; + } + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + EXPECT_TRUE(verify_data.callback_invoked); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + if (expected_success) { + /* If not supported, don't expect to see TlS 1.3 */ + EXPECT_TRUE(saw_tls13 || !s2n_is_tls13_fully_supported()); + EXPECT_TRUE(saw_tls12); + } + + /* Reset the callback */ + config->cert_request_cb = NULL; + config->cert_request_cb_ctx = NULL; + } + + /* + * Test Mutual Auth using **s2n_config_set_client_auth_type** + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** only on one side of the + * connection and verify that a connection is not established + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (s2n_should_skip_cipher(cur_cipher)) { + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Only set S2N_CERT_AUTH_REQUIRED on the server and not the client so that the connection fails */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that NEITHER connections negotiated Mutual Auth */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + 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_ALERT(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + + /* 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); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_openssl_x509_test.c b/tests/unit/s2n_openssl_x509_test.c index 5a364cb4488..1e43c6bb893 100644 --- a/tests/unit/s2n_openssl_x509_test.c +++ b/tests/unit/s2n_openssl_x509_test.c @@ -1,239 +1,239 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_openssl_x509.h" - -#include -#include - -#include "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, - struct s2n_blob *asn1_cert); - -static S2N_RESULT s2n_test_assert_s2n_cert_info_equality(const struct s2n_cert_info *info_a, - const struct s2n_cert_info *info_b) -{ - RESULT_ENSURE_EQ(info_a->public_key_bits, info_b->public_key_bits); - RESULT_ENSURE_EQ(info_a->public_key_nid, info_b->public_key_nid); - RESULT_ENSURE_EQ(info_a->signature_nid, info_b->signature_nid); - RESULT_ENSURE_EQ(info_a->signature_digest_nid, info_b->signature_digest_nid); - RESULT_ENSURE_EQ(info_a->self_signed, info_b->self_signed); - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* A certificate with one trailing byte is parsed successfully */ - { - uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ONE_TRAILING_BYTE_CERT_BIN, cert_chain_data, - &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_blob cert_chain_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); - - struct s2n_blob cert_asn1_der = { 0 }; - EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); - - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); - } - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); - } - } - - /* A certificate with too many trailing bytes errors */ - { - uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t cert_chain_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_FOUR_TRAILING_BYTE_CERT_BIN, cert_chain_data, - &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_blob cert_chain_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); - - struct s2n_blob cert_asn1_der = { 0 }; - EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); - - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_ERROR(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); - } - { - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); - } - } - - /* s2n_openssl_x509_get_cert_info */ - struct { - const char *key_type; - const char *signature; - const char *key_size; - const char *digest; - int expected_signature_nid; - int expected_digest_nid; - int expected_public_key_nid; - int expected_public_key_bits; - } test_cases[] = { - { - .key_type = "ec", - .signature = "ecdsa", - .key_size = "p384", - .digest = "sha256", - .expected_signature_nid = NID_ecdsa_with_SHA256, - .expected_digest_nid = NID_sha256, - .expected_public_key_nid = NID_secp384r1, - .expected_public_key_bits = 384, - }, - { - .key_type = "ec", - .signature = "ecdsa", - .key_size = "p256", - .digest = "sha384", - .expected_signature_nid = NID_ecdsa_with_SHA384, - .expected_digest_nid = NID_sha384, - .expected_public_key_nid = NID_X9_62_prime256v1, - .expected_public_key_bits = 256, - }, - { - .key_type = "ec", - .signature = "ecdsa", - .key_size = "p521", - .digest = "sha512", - .expected_signature_nid = NID_ecdsa_with_SHA512, - .expected_digest_nid = NID_sha512, - .expected_public_key_nid = NID_secp521r1, - .expected_public_key_bits = 521, - }, - { - .key_type = "rsae", - .signature = "pkcs", - .key_size = "2048", - .digest = "sha1", - .expected_signature_nid = NID_sha1WithRSAEncryption, - .expected_digest_nid = NID_sha1, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 2048, - }, - { - .key_type = "rsae", - .signature = "pkcs", - .key_size = "2048", - .digest = "sha224", - .expected_signature_nid = NID_sha224WithRSAEncryption, - .expected_digest_nid = NID_sha224, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 2048, - }, - { - .key_type = "rsae", - .signature = "pkcs", - .key_size = "3072", - .digest = "sha384", - .expected_signature_nid = NID_sha384WithRSAEncryption, - .expected_digest_nid = NID_sha384, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 3072, - }, -#if RSA_PSS_CERTS_SUPPORTED - { - .key_type = "rsae", - .signature = "pss", - .key_size = "4096", - .digest = "sha384", - .expected_signature_nid = NID_rsassaPss, - .expected_digest_nid = NID_undef, - .expected_public_key_nid = NID_rsaEncryption, - .expected_public_key_bits = 4096, - }, - { - .key_type = "rsapss", - .signature = "pss", - .key_size = "2048", - .digest = "sha256", - .expected_signature_nid = NID_rsassaPss, - .expected_digest_nid = NID_undef, - .expected_public_key_nid = NID_rsassaPss, - .expected_public_key_bits = 2048, - }, -#endif - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - /* initialize variables and read in certificates */ - char pathbuffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_OK(s2n_test_cert_permutation_get_server_chain_path(&pathbuffer[0], - test_cases[i].key_type, test_cases[i].signature, test_cases[i].key_size, - test_cases[i].digest)); - EXPECT_SUCCESS(s2n_read_test_pem(pathbuffer, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(X509 *leaf = NULL, X509_free_pointer); - DEFER_CLEANUP(X509 *intermediate = NULL, X509_free_pointer); - DEFER_CLEANUP(X509 *root = NULL, X509_free_pointer); - - /* read in cert chain */ - size_t chain_len = strlen((const char *) cert_file); - BIO *cert_bio = NULL; - EXPECT_NOT_NULL(cert_bio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(cert_bio, cert_file, chain_len) > 0); - EXPECT_NOT_NULL(leaf = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); - EXPECT_NOT_NULL(intermediate = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); - EXPECT_NOT_NULL(root = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); - EXPECT_SUCCESS(BIO_free(cert_bio)); - - /* retrieve cert info from test case certificates */ - struct s2n_cert_info leaf_info = { 0 }; - struct s2n_cert_info intermediate_info = { 0 }; - struct s2n_cert_info root_info = { 0 }; - - EXPECT_OK(s2n_openssl_x509_get_cert_info(leaf, &leaf_info)); - EXPECT_OK(s2n_openssl_x509_get_cert_info(intermediate, &intermediate_info)); - EXPECT_OK(s2n_openssl_x509_get_cert_info(root, &root_info)); - - struct s2n_cert_info expected_info = { - .signature_nid = test_cases[i].expected_signature_nid, - .signature_digest_nid = test_cases[i].expected_digest_nid, - .public_key_nid = test_cases[i].expected_public_key_nid, - .public_key_bits = test_cases[i].expected_public_key_bits, - .self_signed = false, - }; - - /* assert that cert info matches expected values */ - EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&leaf_info, &expected_info)); - EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&intermediate_info, &expected_info)); - /* root should be self-signed, but otherwise equal */ - expected_info.self_signed = true; - EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&root_info, &expected_info)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_openssl_x509.h" + +#include +#include + +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, + struct s2n_blob *asn1_cert); + +static S2N_RESULT s2n_test_assert_s2n_cert_info_equality(const struct s2n_cert_info *info_a, + const struct s2n_cert_info *info_b) +{ + RESULT_ENSURE_EQ(info_a->public_key_bits, info_b->public_key_bits); + RESULT_ENSURE_EQ(info_a->public_key_nid, info_b->public_key_nid); + RESULT_ENSURE_EQ(info_a->signature_nid, info_b->signature_nid); + RESULT_ENSURE_EQ(info_a->signature_digest_nid, info_b->signature_digest_nid); + RESULT_ENSURE_EQ(info_a->self_signed, info_b->self_signed); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* A certificate with one trailing byte is parsed successfully */ + { + uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ONE_TRAILING_BYTE_CERT_BIN, cert_chain_data, + &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_blob cert_chain_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); + + struct s2n_blob cert_asn1_der = { 0 }; + EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); + + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); + } + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); + } + } + + /* A certificate with too many trailing bytes errors */ + { + uint8_t cert_chain_data[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_FOUR_TRAILING_BYTE_CERT_BIN, cert_chain_data, + &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_blob cert_chain_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&cert_chain_blob, cert_chain_data, cert_chain_len)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_init_written(&cert_chain_stuffer, &cert_chain_blob)); + + struct s2n_blob cert_asn1_der = { 0 }; + EXPECT_OK(s2n_x509_validator_read_asn1_cert(&cert_chain_stuffer, &cert_asn1_der)); + + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_ERROR(s2n_openssl_x509_parse(&cert_asn1_der, &cert)); + } + { + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse_without_length_validation(&cert_asn1_der, &cert)); + } + } + + /* s2n_openssl_x509_get_cert_info */ + struct { + const char *key_type; + const char *signature; + const char *key_size; + const char *digest; + int expected_signature_nid; + int expected_digest_nid; + int expected_public_key_nid; + int expected_public_key_bits; + } test_cases[] = { + { + .key_type = "ec", + .signature = "ecdsa", + .key_size = "p384", + .digest = "sha256", + .expected_signature_nid = NID_ecdsa_with_SHA256, + .expected_digest_nid = NID_sha256, + .expected_public_key_nid = NID_secp384r1, + .expected_public_key_bits = 384, + }, + { + .key_type = "ec", + .signature = "ecdsa", + .key_size = "p256", + .digest = "sha384", + .expected_signature_nid = NID_ecdsa_with_SHA384, + .expected_digest_nid = NID_sha384, + .expected_public_key_nid = NID_X9_62_prime256v1, + .expected_public_key_bits = 256, + }, + { + .key_type = "ec", + .signature = "ecdsa", + .key_size = "p521", + .digest = "sha512", + .expected_signature_nid = NID_ecdsa_with_SHA512, + .expected_digest_nid = NID_sha512, + .expected_public_key_nid = NID_secp521r1, + .expected_public_key_bits = 521, + }, + { + .key_type = "rsae", + .signature = "pkcs", + .key_size = "2048", + .digest = "sha1", + .expected_signature_nid = NID_sha1WithRSAEncryption, + .expected_digest_nid = NID_sha1, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 2048, + }, + { + .key_type = "rsae", + .signature = "pkcs", + .key_size = "2048", + .digest = "sha224", + .expected_signature_nid = NID_sha224WithRSAEncryption, + .expected_digest_nid = NID_sha224, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 2048, + }, + { + .key_type = "rsae", + .signature = "pkcs", + .key_size = "3072", + .digest = "sha384", + .expected_signature_nid = NID_sha384WithRSAEncryption, + .expected_digest_nid = NID_sha384, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 3072, + }, +#if RSA_PSS_CERTS_SUPPORTED + { + .key_type = "rsae", + .signature = "pss", + .key_size = "4096", + .digest = "sha384", + .expected_signature_nid = NID_rsassaPss, + .expected_digest_nid = NID_undef, + .expected_public_key_nid = NID_rsaEncryption, + .expected_public_key_bits = 4096, + }, + { + .key_type = "rsapss", + .signature = "pss", + .key_size = "2048", + .digest = "sha256", + .expected_signature_nid = NID_rsassaPss, + .expected_digest_nid = NID_undef, + .expected_public_key_nid = NID_rsassaPss, + .expected_public_key_bits = 2048, + }, +#endif + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + /* initialize variables and read in certificates */ + char pathbuffer[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_OK(s2n_test_cert_permutation_get_server_chain_path(&pathbuffer[0], + test_cases[i].key_type, test_cases[i].signature, test_cases[i].key_size, + test_cases[i].digest)); + EXPECT_SUCCESS(s2n_read_test_pem(pathbuffer, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(X509 *leaf = NULL, X509_free_pointer); + DEFER_CLEANUP(X509 *intermediate = NULL, X509_free_pointer); + DEFER_CLEANUP(X509 *root = NULL, X509_free_pointer); + + /* read in cert chain */ + size_t chain_len = strlen((const char *) cert_file); + BIO *cert_bio = NULL; + EXPECT_NOT_NULL(cert_bio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(cert_bio, cert_file, chain_len) > 0); + EXPECT_NOT_NULL(leaf = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); + EXPECT_NOT_NULL(intermediate = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); + EXPECT_NOT_NULL(root = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)); + EXPECT_SUCCESS(BIO_free(cert_bio)); + + /* retrieve cert info from test case certificates */ + struct s2n_cert_info leaf_info = { 0 }; + struct s2n_cert_info intermediate_info = { 0 }; + struct s2n_cert_info root_info = { 0 }; + + EXPECT_OK(s2n_openssl_x509_get_cert_info(leaf, &leaf_info)); + EXPECT_OK(s2n_openssl_x509_get_cert_info(intermediate, &intermediate_info)); + EXPECT_OK(s2n_openssl_x509_get_cert_info(root, &root_info)); + + struct s2n_cert_info expected_info = { + .signature_nid = test_cases[i].expected_signature_nid, + .signature_digest_nid = test_cases[i].expected_digest_nid, + .public_key_nid = test_cases[i].expected_public_key_nid, + .public_key_bits = test_cases[i].expected_public_key_bits, + .self_signed = false, + }; + + /* assert that cert info matches expected values */ + EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&leaf_info, &expected_info)); + EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&intermediate_info, &expected_info)); + /* root should be self-signed, but otherwise equal */ + expected_info.self_signed = true; + EXPECT_OK(s2n_test_assert_s2n_cert_info_equality(&root_info, &expected_info)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_optional_client_auth_test.c b/tests/unit/s2n_optional_client_auth_test.c index a2bc6be9738..964d0e96185 100644 --- a/tests/unit/s2n_optional_client_auth_test.c +++ b/tests/unit/s2n_optional_client_auth_test.c @@ -1,479 +1,479 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_security_policies.h" - -int main(int argc, char **argv) -{ - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - const struct s2n_security_policy *default_security_policy = NULL; - const struct s2n_cipher_preferences *default_cipher_preferences = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* Setup baseline server config and certs. */ - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); - EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with a valid client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server requires optional client auth and accepts the client cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_security_policy->cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated mutual auth. */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with S2N_CERT_AUTH_NONE for Server - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server does not request a Client Cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connections negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with no client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server requires optional client auth and accepts the client cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connection negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_connection_set_client_auth_type** with a valid client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - /* Server requires no client auth but the connection will. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that both connections negotiated mutual auth. */ - EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); - EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_connection_set_client_auth_type** with no client cert provided. - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); - - /* Server requires client auth but the connection will allow an empty client cert. */ - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); - - /* Verify that a handshake succeeds for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Override the config setting on the connection. */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - - /* Verify the handshake was successful. */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connection negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - /* - * Test optional client auth using **s2n_config_set_client_auth_type** with an incorrect client - * cert provided fails negotiation, allowing the user to fatally kill the handshake if they want. - * https://tools.ietf.org/html/rfc5246#section-7.4.6 - */ - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Server requires optional client auth but will reject the client cert. We need to reset the config, to turn validation back on*/ - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); - - /* Verify that a handshake fails for every cipher in the default list. */ - for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { - struct s2n_cipher_preferences server_cipher_preferences; - struct s2n_security_policy security_policy; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - /* Craft a cipher preference with a cipher_idx cipher. */ - EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); - server_cipher_preferences.count = 1; - struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; - - if (!cur_cipher->available) { - /* Skip ciphers that aren't supported with the linked libcrypto. */ - continue; - } - - server_cipher_preferences.suites = &cur_cipher; - - EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); - security_policy.cipher_preferences = &server_cipher_preferences; - - client_config->security_policy = &security_policy; - server_config->security_policy = &security_policy; - - /* Create nonblocking pipes. */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Verify the handshake failed. Blinding is disabled for the failure case to speed up tests. */ - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that neither connection negotiated mutual auth. */ - EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); - EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - } - - EXPECT_SUCCESS(s2n_config_free(client_config)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_security_policies.h" + +int main(int argc, char **argv) +{ + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + const struct s2n_security_policy *default_security_policy = NULL; + const struct s2n_cipher_preferences *default_cipher_preferences = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* Setup baseline server config and certs. */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); + EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with a valid client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth and accepts the client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_security_policy->cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated mutual auth. */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with S2N_CERT_AUTH_NONE for Server + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server does not request a Client Cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connections negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with no client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth and accepts the client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_connection_set_client_auth_type** with a valid client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + /* Server requires no client auth but the connection will. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated mutual auth. */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_connection_set_client_auth_type** with no client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + /* Server requires client auth but the connection will allow an empty client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with an incorrect client + * cert provided fails negotiation, allowing the user to fatally kill the handshake if they want. + * https://tools.ietf.org/html/rfc5246#section-7.4.6 + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth but will reject the client cert. We need to reset the config, to turn validation back on*/ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify that a handshake fails for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake failed. Blinding is disabled for the failure case to speed up tests. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_pem_rsa_dhe_test.c b/tests/unit/s2n_pem_rsa_dhe_test.c index 714bb2648f6..dd3f8c8ed16 100644 --- a/tests/unit/s2n_pem_rsa_dhe_test.c +++ b/tests/unit/s2n_pem_rsa_dhe_test.c @@ -1,205 +1,205 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_dhe.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" - -static uint8_t unmatched_private_key[] = - "-----BEGIN RSA PRIVATE KEY-----\n" - "MIIEpQIBAAKCAQEA6ddvtO6DbX5GLrWeXD2jufmR6lvYuzwpIHbDf0RQr2AT50wf\n" - "vV+sMYVa/d4g68tqtihH691tqzKevVc25LXM0a0f0NfnrNibyA1/66CwFQsyy1j2\n" - "U90BdguI/ZxqEV58ee/PzerOoS5d/rgBALzGwwYwyvlzr35KGjfRZ4XOFoToKnkN\n" - "0mR44firCvb7dJ53QRXoPbkYpQQQ3vMWMOtDZoPsvP0dJJ50B7LaFL6nbOyJmZ2T\n" - "evaAov1Nuk5QSrZf2icpuQVXxuu2xLmTNL25OUiV3t7ZjiLtrOZrmrIzv8/sLcWp\n" - "VJcFWFoKizmjvpDfD086a9c81vo4kqgD/RZ9dQIDAQABAoIBAQDBNUzJ3MxgupW4\n" - "YD2BDzjpH2jNj6fKRBHjDd3HmKVl0eeAE2iiKpt2qy2cVl0zFfaMnUmXe3PyoLeB\n" - "z76+R+v8TqPcBZgZOzuzllvcTv9N09vbIh0c+50KcMt2aDdHNJ96jIdRJzIlAM+O\n" - "9290sYU0fDfybRuFo74MXZQ6idbWyNMjXEv2oPrmvMmOHm10sz9BCXWFUpDpDFKD\n" - "8xgw1GZ1QeHITWoQXMI2uJUFFj2hYbRAl6f8cMXVjUipmMNpfJk4CbtEdSMPkjrz\n" - "XgIVrMCorCiaLuVQgSQCHB01n8ETM6R9PYSgMSg3RYYqKq4N0nvREUf4VvKV8JOY\n" - "EGzNgq3BAoGBAPvHPoCKQawLEr4jE5ITMqhZXlBXh8aU7LetjSY1BfnsiH43eg1n\n" - "186lfOX0r1I2KkVZTehZohkEo8xeEpc+XMdux1z0mX2uz1tfmHl838sRaTBs1JZ0\n" - "slkBrASfDdOnLlHCMhRmFEEnA0eelNYCiy/Ak0UX7NpuhZ3bh2jGemixAoGBAO3D\n" - "MupFbCl64AFEuLCA3wSFRhG82AScYAF1txoTM01V/kqy66j2jJsqMXkVcyBcLBkJ\n" - "5ozW4kIKkh0dYtIjDpsnXKBK/kVcvJN+60hwVnAMFFJPlRCdejkKSsSe9xod+FzU\n" - "DvwWLpLaO3sFtykGHelFbzMDB6FaZQFSfSMsNhIFAoGBAKaNafor+z9s38wpdfPG\n" - "gVc+LxakoGur7l+fDeU9ZCOs5ang1vtxOyA29sVDtIqEzDet2MygJou4NwalIFUu\n" - "ar9+t6D1KWgrsH24YivTgFNbxCLFi2ev8J7SbVFtSf8983UgKnK2CCYFQbUp4Tkk\n" - "26AOGx20svjX7cm8A/o6eZUxAoGBANtedXSnNuOSpmklMc5QKPRvzrWA6kJe0Umn\n" - "hZf+TSA2jlf3eu07BYIITPst6jnaMSms89XQUZOjUyqfuVSu2cQXbiPK7Y2rwaXI\n" - "vWbplybsTjefi6Z31ZQZReDh1pV3P3bOhUDban898RFRtauZJDHdSXrkeb7Ku1Sb\n" - "+i9glEbNAoGAJngXJZ2djyzCxYtSNyUFP8AZfmSy+vmHVCCri1FCoFfj9D1HTw4h\n" - "G0guyN3/Qm51gJVeBTOu/dtoA5JAZi6CRyNuhClLCqV+jGsUr9xyMzA+t/JHVr+0\n" - "XQhx4wqVYQs2852qaJg+wYUi50FtRT6hLyDQmg9ttvS4YIwMTdVzl2Q=\n" - "-----END RSA PRIVATE KEY-----\n"; - -int main(int argc, char **argv) -{ - struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; - struct s2n_stuffer dhparams_in = { 0 }, dhparams_out = { 0 }; - struct s2n_stuffer rsa_key_in = { 0 }, rsa_key_out = { 0 }; - struct s2n_blob b = { 0 }; - char *leaf_cert_pem = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(leaf_cert_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_out, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_out, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_LEAF_CERT, leaf_cert_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) leaf_cert_pem, strlen(leaf_cert_pem) + 1)); - EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); - - EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) private_key_pem, strlen(private_key_pem) + 1)); - EXPECT_SUCCESS(s2n_stuffer_write(&rsa_key_in, &b)); - - EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) dhparams_pem, strlen(dhparams_pem) + 1)); - EXPECT_SUCCESS(s2n_stuffer_write(&dhparams_in, &b)); - - int type = 0; - EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); - EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&rsa_key_in, &rsa_key_out, &type)); - EXPECT_SUCCESS(s2n_stuffer_dhparams_from_pem(&dhparams_in, &dhparams_out)); - EXPECT_EQUAL(type, EVP_PKEY_RSA); - - struct s2n_pkey priv_key = { 0 }; - struct s2n_pkey pub_key = { 0 }; - s2n_pkey_type pkey_type = { 0 }; - - uint32_t available_size = 0; - available_size = s2n_stuffer_data_available(&certificate_out); - EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&pub_key, &pkey_type, &b)); - - /* Test without a type hint */ - int wrong_type = 0; - EXPECT_NOT_EQUAL(wrong_type, EVP_PKEY_RSA); - - available_size = s2n_stuffer_data_available(&rsa_key_out); - EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&rsa_key_out, available_size), available_size)); - EXPECT_OK(s2n_asn1der_to_private_key(&priv_key, &b, wrong_type)); - - EXPECT_SUCCESS(s2n_pkey_match(&pub_key, &priv_key)); - - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - struct s2n_dh_params dh_params = { 0 }; - available_size = s2n_stuffer_data_available(&dhparams_out); - EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&dhparams_out, available_size), available_size)); - EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); - - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - - /* Try signing and verification with RSA */ - uint8_t inputpad[] = "Hello world!"; - struct s2n_blob signature = { 0 }; - struct s2n_hash_state tls10_one = { 0 }; - struct s2n_hash_state tls10_two = { 0 }; - struct s2n_hash_state tls12_one = { 0 }; - struct s2n_hash_state tls12_two = { 0 }; - - uint32_t maximum_signature_length = 0; - EXPECT_OK(s2n_pkey_size(&pub_key, &maximum_signature_length)); - EXPECT_SUCCESS(s2n_alloc(&signature, maximum_signature_length)); - - if (s2n_hash_is_available(S2N_HASH_MD5_SHA1)) { - /* TLS 1.0 use of RSA with DHE is not permitted when FIPS mode is set */ - EXPECT_SUCCESS(s2n_hash_new(&tls10_one)); - EXPECT_SUCCESS(s2n_hash_new(&tls10_two)); - - EXPECT_SUCCESS(s2n_hash_init(&tls10_one, S2N_HASH_MD5_SHA1)); - EXPECT_SUCCESS(s2n_hash_init(&tls10_two, S2N_HASH_MD5_SHA1)); - - EXPECT_SUCCESS(s2n_hash_update(&tls10_one, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_hash_update(&tls10_two, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls10_one, &signature)); - EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls10_two, &signature)); - - EXPECT_SUCCESS(s2n_hash_free(&tls10_one)); - EXPECT_SUCCESS(s2n_hash_free(&tls10_two)); - } - - /* TLS 1.2 use of RSA with DHE is permitted for FIPS and non-FIPS */ - EXPECT_SUCCESS(s2n_hash_new(&tls12_one)); - EXPECT_SUCCESS(s2n_hash_new(&tls12_two)); - - EXPECT_SUCCESS(s2n_hash_init(&tls12_one, S2N_HASH_SHA1)); - EXPECT_SUCCESS(s2n_hash_init(&tls12_two, S2N_HASH_SHA1)); - - EXPECT_SUCCESS(s2n_hash_update(&tls12_one, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_hash_update(&tls12_two, inputpad, sizeof(inputpad))); - EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls12_one, &signature)); - EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls12_two, &signature)); - - EXPECT_SUCCESS(s2n_hash_free(&tls12_one)); - EXPECT_SUCCESS(s2n_hash_free(&tls12_two)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - - /* Mismatched public/private key should fail */ - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, (char *) unmatched_private_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - - EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); - EXPECT_SUCCESS(s2n_pkey_free(&priv_key)); - EXPECT_SUCCESS(s2n_pkey_free(&pub_key)); - EXPECT_SUCCESS(s2n_free(&signature)); - EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); - EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); - EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_in)); - EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_out)); - EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_in)); - EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_out)); - free(cert_chain_pem); - free(leaf_cert_pem); - free(private_key_pem); - free(dhparams_pem); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_dhe.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" + +static uint8_t unmatched_private_key[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpQIBAAKCAQEA6ddvtO6DbX5GLrWeXD2jufmR6lvYuzwpIHbDf0RQr2AT50wf\n" + "vV+sMYVa/d4g68tqtihH691tqzKevVc25LXM0a0f0NfnrNibyA1/66CwFQsyy1j2\n" + "U90BdguI/ZxqEV58ee/PzerOoS5d/rgBALzGwwYwyvlzr35KGjfRZ4XOFoToKnkN\n" + "0mR44firCvb7dJ53QRXoPbkYpQQQ3vMWMOtDZoPsvP0dJJ50B7LaFL6nbOyJmZ2T\n" + "evaAov1Nuk5QSrZf2icpuQVXxuu2xLmTNL25OUiV3t7ZjiLtrOZrmrIzv8/sLcWp\n" + "VJcFWFoKizmjvpDfD086a9c81vo4kqgD/RZ9dQIDAQABAoIBAQDBNUzJ3MxgupW4\n" + "YD2BDzjpH2jNj6fKRBHjDd3HmKVl0eeAE2iiKpt2qy2cVl0zFfaMnUmXe3PyoLeB\n" + "z76+R+v8TqPcBZgZOzuzllvcTv9N09vbIh0c+50KcMt2aDdHNJ96jIdRJzIlAM+O\n" + "9290sYU0fDfybRuFo74MXZQ6idbWyNMjXEv2oPrmvMmOHm10sz9BCXWFUpDpDFKD\n" + "8xgw1GZ1QeHITWoQXMI2uJUFFj2hYbRAl6f8cMXVjUipmMNpfJk4CbtEdSMPkjrz\n" + "XgIVrMCorCiaLuVQgSQCHB01n8ETM6R9PYSgMSg3RYYqKq4N0nvREUf4VvKV8JOY\n" + "EGzNgq3BAoGBAPvHPoCKQawLEr4jE5ITMqhZXlBXh8aU7LetjSY1BfnsiH43eg1n\n" + "186lfOX0r1I2KkVZTehZohkEo8xeEpc+XMdux1z0mX2uz1tfmHl838sRaTBs1JZ0\n" + "slkBrASfDdOnLlHCMhRmFEEnA0eelNYCiy/Ak0UX7NpuhZ3bh2jGemixAoGBAO3D\n" + "MupFbCl64AFEuLCA3wSFRhG82AScYAF1txoTM01V/kqy66j2jJsqMXkVcyBcLBkJ\n" + "5ozW4kIKkh0dYtIjDpsnXKBK/kVcvJN+60hwVnAMFFJPlRCdejkKSsSe9xod+FzU\n" + "DvwWLpLaO3sFtykGHelFbzMDB6FaZQFSfSMsNhIFAoGBAKaNafor+z9s38wpdfPG\n" + "gVc+LxakoGur7l+fDeU9ZCOs5ang1vtxOyA29sVDtIqEzDet2MygJou4NwalIFUu\n" + "ar9+t6D1KWgrsH24YivTgFNbxCLFi2ev8J7SbVFtSf8983UgKnK2CCYFQbUp4Tkk\n" + "26AOGx20svjX7cm8A/o6eZUxAoGBANtedXSnNuOSpmklMc5QKPRvzrWA6kJe0Umn\n" + "hZf+TSA2jlf3eu07BYIITPst6jnaMSms89XQUZOjUyqfuVSu2cQXbiPK7Y2rwaXI\n" + "vWbplybsTjefi6Z31ZQZReDh1pV3P3bOhUDban898RFRtauZJDHdSXrkeb7Ku1Sb\n" + "+i9glEbNAoGAJngXJZ2djyzCxYtSNyUFP8AZfmSy+vmHVCCri1FCoFfj9D1HTw4h\n" + "G0guyN3/Qm51gJVeBTOu/dtoA5JAZi6CRyNuhClLCqV+jGsUr9xyMzA+t/JHVr+0\n" + "XQhx4wqVYQs2852qaJg+wYUi50FtRT6hLyDQmg9ttvS4YIwMTdVzl2Q=\n" + "-----END RSA PRIVATE KEY-----\n"; + +int main(int argc, char **argv) +{ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_stuffer dhparams_in = { 0 }, dhparams_out = { 0 }; + struct s2n_stuffer rsa_key_in = { 0 }, rsa_key_out = { 0 }; + struct s2n_blob b = { 0 }; + char *leaf_cert_pem = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_cert_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_out, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_LEAF_CERT, leaf_cert_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) leaf_cert_pem, strlen(leaf_cert_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) private_key_pem, strlen(private_key_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&rsa_key_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) dhparams_pem, strlen(dhparams_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&dhparams_in, &b)); + + int type = 0; + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&rsa_key_in, &rsa_key_out, &type)); + EXPECT_SUCCESS(s2n_stuffer_dhparams_from_pem(&dhparams_in, &dhparams_out)); + EXPECT_EQUAL(type, EVP_PKEY_RSA); + + struct s2n_pkey priv_key = { 0 }; + struct s2n_pkey pub_key = { 0 }; + s2n_pkey_type pkey_type = { 0 }; + + uint32_t available_size = 0; + available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&pub_key, &pkey_type, &b)); + + /* Test without a type hint */ + int wrong_type = 0; + EXPECT_NOT_EQUAL(wrong_type, EVP_PKEY_RSA); + + available_size = s2n_stuffer_data_available(&rsa_key_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&rsa_key_out, available_size), available_size)); + EXPECT_OK(s2n_asn1der_to_private_key(&priv_key, &b, wrong_type)); + + EXPECT_SUCCESS(s2n_pkey_match(&pub_key, &priv_key)); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_dh_params dh_params = { 0 }; + available_size = s2n_stuffer_data_available(&dhparams_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&dhparams_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + /* Try signing and verification with RSA */ + uint8_t inputpad[] = "Hello world!"; + struct s2n_blob signature = { 0 }; + struct s2n_hash_state tls10_one = { 0 }; + struct s2n_hash_state tls10_two = { 0 }; + struct s2n_hash_state tls12_one = { 0 }; + struct s2n_hash_state tls12_two = { 0 }; + + uint32_t maximum_signature_length = 0; + EXPECT_OK(s2n_pkey_size(&pub_key, &maximum_signature_length)); + EXPECT_SUCCESS(s2n_alloc(&signature, maximum_signature_length)); + + if (s2n_hash_is_available(S2N_HASH_MD5_SHA1)) { + /* TLS 1.0 use of RSA with DHE is not permitted when FIPS mode is set */ + EXPECT_SUCCESS(s2n_hash_new(&tls10_one)); + EXPECT_SUCCESS(s2n_hash_new(&tls10_two)); + + EXPECT_SUCCESS(s2n_hash_init(&tls10_one, S2N_HASH_MD5_SHA1)); + EXPECT_SUCCESS(s2n_hash_init(&tls10_two, S2N_HASH_MD5_SHA1)); + + EXPECT_SUCCESS(s2n_hash_update(&tls10_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&tls10_two, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls10_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls10_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_free(&tls10_one)); + EXPECT_SUCCESS(s2n_hash_free(&tls10_two)); + } + + /* TLS 1.2 use of RSA with DHE is permitted for FIPS and non-FIPS */ + EXPECT_SUCCESS(s2n_hash_new(&tls12_one)); + EXPECT_SUCCESS(s2n_hash_new(&tls12_two)); + + EXPECT_SUCCESS(s2n_hash_init(&tls12_one, S2N_HASH_SHA1)); + EXPECT_SUCCESS(s2n_hash_init(&tls12_two, S2N_HASH_SHA1)); + + EXPECT_SUCCESS(s2n_hash_update(&tls12_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&tls12_two, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls12_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls12_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_free(&tls12_one)); + EXPECT_SUCCESS(s2n_hash_free(&tls12_two)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Mismatched public/private key should fail */ + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, (char *) unmatched_private_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); + EXPECT_SUCCESS(s2n_pkey_free(&priv_key)); + EXPECT_SUCCESS(s2n_pkey_free(&pub_key)); + EXPECT_SUCCESS(s2n_free(&signature)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_out)); + free(cert_chain_pem); + free(leaf_cert_pem); + free(private_key_pem); + free(dhparams_pem); + + END_TEST(); +} diff --git a/tests/unit/s2n_pem_test.c b/tests/unit/s2n_pem_test.c index a05d5e31293..2a29d67f7ba 100644 --- a/tests/unit/s2n_pem_test.c +++ b/tests/unit/s2n_pem_test.c @@ -1,124 +1,124 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -/* The ECDSA private key is missing the "publicKey" field, which is optional. - * The missing field makes the cert type difficult to detect via ASN1 parsing */ -#define S2N_MISSING_ECDSA_PUB_CERT_KEY "../pems/missing_public_key_ecdsa_key.pem" -#define S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN "../pems/missing_public_key_ecdsa_cert.pem" - -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 }, - { S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN, S2N_MISSING_ECDSA_PUB_CERT_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 }, -}; - -const struct { - const char *path; - uint16_t length; -} valid_cert_chains[] = { - { .path = S2N_TEST_TRUST_STORE, .length = 179 }, -}; - -int main(int argc, char **argv) -{ - struct s2n_config *config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - for (size_t i = 0; i < s2n_array_len(valid_pem_pairs); i++) { - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(config)); - } - - for (size_t i = 0; i < s2n_array_len(invalid_pem_pairs); i++) { - EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - } - - char large_cert_chain_pem[500000] = { 0 }; - for (size_t i = 0; i < s2n_array_len(valid_cert_chains); i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(chain); - EXPECT_SUCCESS(s2n_read_test_pem(valid_cert_chains[i].path, - large_cert_chain_pem, sizeof(large_cert_chain_pem))); - - uint32_t length = 0; - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(chain, - (uint8_t *) large_cert_chain_pem, strlen(large_cert_chain_pem))); - EXPECT_SUCCESS(s2n_cert_chain_get_length(chain, &length)); - EXPECT_EQUAL(length, valid_cert_chains[i].length); - - DEFER_CLEANUP(struct s2n_config *test_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(test_config); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(test_config, large_cert_chain_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(test_config, chain)); - } - - free(cert_chain_pem); - free(private_key_pem); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +/* The ECDSA private key is missing the "publicKey" field, which is optional. + * The missing field makes the cert type difficult to detect via ASN1 parsing */ +#define S2N_MISSING_ECDSA_PUB_CERT_KEY "../pems/missing_public_key_ecdsa_key.pem" +#define S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN "../pems/missing_public_key_ecdsa_cert.pem" + +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 }, + { S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN, S2N_MISSING_ECDSA_PUB_CERT_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 }, +}; + +const struct { + const char *path; + uint16_t length; +} valid_cert_chains[] = { + { .path = S2N_TEST_TRUST_STORE, .length = 179 }, +}; + +int main(int argc, char **argv) +{ + struct s2n_config *config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + for (size_t i = 0; i < s2n_array_len(valid_pem_pairs); i++) { + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + for (size_t i = 0; i < s2n_array_len(invalid_pem_pairs); i++) { + EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + char large_cert_chain_pem[500000] = { 0 }; + for (size_t i = 0; i < s2n_array_len(valid_cert_chains); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(chain); + EXPECT_SUCCESS(s2n_read_test_pem(valid_cert_chains[i].path, + large_cert_chain_pem, sizeof(large_cert_chain_pem))); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(chain, + (uint8_t *) large_cert_chain_pem, strlen(large_cert_chain_pem))); + EXPECT_SUCCESS(s2n_cert_chain_get_length(chain, &length)); + EXPECT_EQUAL(length, valid_cert_chains[i].length); + + DEFER_CLEANUP(struct s2n_config *test_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(test_config); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(test_config, large_cert_chain_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(test_config, chain)); + } + + free(cert_chain_pem); + free(private_key_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_policy_defaults_test.c b/tests/unit/s2n_policy_defaults_test.c index 6dbdc4a86fd..ee21a75f032 100644 --- a/tests/unit/s2n_policy_defaults_test.c +++ b/tests/unit/s2n_policy_defaults_test.c @@ -1,136 +1,136 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/policy/s2n_policy_defaults.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -S2N_RESULT s2n_test_strict_compatibility(uint64_t start, uint64_t end, - struct s2n_test_cert_chain_list *cert_chains) -{ - for (size_t client_i = start; client_i <= end; client_i++) { - for (size_t server_i = start; server_i <= end; server_i++) { - for (size_t cert_i = 0; cert_i < cert_chains->count; cert_i++) { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, - cert_chains->chains[cert_i].chain)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_security_policy(client, - s2n_security_policy_get(S2N_POLICY_STRICT, client_i))); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_security_policy(server, - s2n_security_policy_get(S2N_POLICY_STRICT, server_i))); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* For now, consider a HRR a potential breaking change. - * We can revisit later if necessary. - */ - EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server)); - } - } - } - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* Test: s2n_security_policy_get */ - { - /* Policies exist only for expected policy + version combinations */ - for (size_t policy_i = 0; policy_i < UINT8_MAX; policy_i++) { - bool first_null_found = false; - uint64_t versions_found = 0; - - for (size_t version_i = 0; version_i < UINT8_MAX; version_i++) { - const struct s2n_security_policy *policy = s2n_security_policy_get(policy_i, version_i); - - /* Invalid policy or version values should be NULL */ - if (version_i == 0 || policy_i == 0) { - /* Versioning starts at 1 instead of 0. - * We may want to later assign 0 a special meaning, like "none". - */ - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - continue; - } else if (policy_i >= S2N_MAX_DEFAULT_POLICIES) { - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - continue; - } else if (version_i >= S2N_MAX_POLICY_VERSIONS) { - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - continue; - } - - if (policy) { - /* The policy exists because the version is valid. - * Versions should be contiguous. No previous gaps. - */ - EXPECT_FALSE(first_null_found); - versions_found++; - } else if (first_null_found) { - /* If we've already found the first invalid version, all later - * versions should be invalid too. - */ - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - } else { - /* We have found the first invalid version */ - first_null_found = true; - EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); - } - } - - if (policy_i > 0 && policy_i < S2N_MAX_DEFAULT_POLICIES) { - /* Each valid policy should have at least one valid version */ - EXPECT_TRUE(versions_found > 0); - /* If we did't find the last valid version, we're not testing enough */ - EXPECT_TRUE(first_null_found); - } - }; - - /* Check known good values. - * Note: don't add EVERY new version to this test. We should only test - * an interesting selection of inputs. - */ - { - EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_STRICT, S2N_STRICT_2025_08_20)); - EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_COMPATIBLE, S2N_COMPAT_2025_08_20)); - } - }; - - /* Test: S2N_POLICY_STRICT backwards compatibility promises enforced */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_test_cert_chain_list cert_chains = { 0 }, s2n_test_cert_chains_free); - EXPECT_OK(s2n_test_cert_chains_init(&cert_chains)); - - EXPECT_OK(s2n_test_strict_compatibility(1, S2N_STRICT_LATEST_1, &cert_chains)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/policy/s2n_policy_defaults.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_test_strict_compatibility(uint64_t start, uint64_t end, + struct s2n_test_cert_chain_list *cert_chains) +{ + for (size_t client_i = start; client_i <= end; client_i++) { + for (size_t server_i = start; server_i <= end; server_i++) { + for (size_t cert_i = 0; cert_i < cert_chains->count; cert_i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, + cert_chains->chains[cert_i].chain)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_security_policy(client, + s2n_security_policy_get(S2N_POLICY_STRICT, client_i))); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_security_policy(server, + s2n_security_policy_get(S2N_POLICY_STRICT, server_i))); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* For now, consider a HRR a potential breaking change. + * We can revisit later if necessary. + */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server)); + } + } + } + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: s2n_security_policy_get */ + { + /* Policies exist only for expected policy + version combinations */ + for (size_t policy_i = 0; policy_i < UINT8_MAX; policy_i++) { + bool first_null_found = false; + uint64_t versions_found = 0; + + for (size_t version_i = 0; version_i < UINT8_MAX; version_i++) { + const struct s2n_security_policy *policy = s2n_security_policy_get(policy_i, version_i); + + /* Invalid policy or version values should be NULL */ + if (version_i == 0 || policy_i == 0) { + /* Versioning starts at 1 instead of 0. + * We may want to later assign 0 a special meaning, like "none". + */ + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + continue; + } else if (policy_i >= S2N_MAX_DEFAULT_POLICIES) { + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + continue; + } else if (version_i >= S2N_MAX_POLICY_VERSIONS) { + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + continue; + } + + if (policy) { + /* The policy exists because the version is valid. + * Versions should be contiguous. No previous gaps. + */ + EXPECT_FALSE(first_null_found); + versions_found++; + } else if (first_null_found) { + /* If we've already found the first invalid version, all later + * versions should be invalid too. + */ + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + } else { + /* We have found the first invalid version */ + first_null_found = true; + EXPECT_NULL_WITH_ERRNO(policy, S2N_ERR_INVALID_SECURITY_POLICY); + } + } + + if (policy_i > 0 && policy_i < S2N_MAX_DEFAULT_POLICIES) { + /* Each valid policy should have at least one valid version */ + EXPECT_TRUE(versions_found > 0); + /* If we did't find the last valid version, we're not testing enough */ + EXPECT_TRUE(first_null_found); + } + }; + + /* Check known good values. + * Note: don't add EVERY new version to this test. We should only test + * an interesting selection of inputs. + */ + { + EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_STRICT, S2N_STRICT_2025_08_20)); + EXPECT_NOT_NULL(s2n_security_policy_get(S2N_POLICY_COMPATIBLE, S2N_COMPAT_2025_08_20)); + } + }; + + /* Test: S2N_POLICY_STRICT backwards compatibility promises enforced */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_test_cert_chain_list cert_chains = { 0 }, s2n_test_cert_chains_free); + EXPECT_OK(s2n_test_cert_chains_init(&cert_chains)); + + EXPECT_OK(s2n_test_strict_compatibility(1, S2N_STRICT_LATEST_1, &cert_chains)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_post_handshake_recv_test.c b/tests/unit/s2n_post_handshake_recv_test.c index 4113a82891a..3eb9eb5643d 100644 --- a/tests/unit/s2n_post_handshake_recv_test.c +++ b/tests/unit/s2n_post_handshake_recv_test.c @@ -1,516 +1,516 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/unstable/renegotiate.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_mem_testlib.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_key_update.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_safety.h" - -#define S2N_TEST_MESSAGE_COUNT 5 - -int s2n_key_update_write(struct s2n_blob *out); - -size_t tickets_count = 0; -static int s2n_ticket_count_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - tickets_count++; - return S2N_SUCCESS; -} - -size_t hello_request_count = 0; -static int s2n_hello_request_cb(struct s2n_connection *conn, void *ctx, s2n_renegotiate_response *response) -{ - hello_request_count++; - *response = S2N_RENEGOTIATE_IGNORE; - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_test_send_records(struct s2n_connection *conn, struct s2n_stuffer messages, uint32_t fragment_size) -{ - conn->max_outgoing_fragment_length = fragment_size; - - DEFER_CLEANUP(struct s2n_blob record_data = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_alloc(&record_data, fragment_size)); - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint32_t remaining = 0; - while ((remaining = s2n_stuffer_data_available(&messages)) > 0) { - record_data.size = S2N_MIN(record_data.size, remaining); - RESULT_GUARD_POSIX(s2n_stuffer_read(&messages, &record_data)); - RESULT_GUARD(s2n_record_write(conn, TLS_HANDSHAKE, &record_data)); - RESULT_GUARD_POSIX(s2n_flush(conn, &blocked)); - }; - - return S2N_RESULT_OK; -} - -/* - * Verify that the receiver can receive a byte sent by the sender. - * In the process, we also verify that the receiver can receive all previous - * data sent by the sender, since TCP / TLS messages have a guaranteed order. - */ -static S2N_RESULT s2n_test_basic_recv(struct s2n_connection *sender, struct s2n_connection *receiver) -{ - uint8_t app_data[1] = { 0 }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); - RESULT_GUARD_POSIX(send_ret); - RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); - - /* Reset all counters */ - RESULT_GUARD(s2n_mem_test_wipe_callbacks()); - tickets_count = 0; - hello_request_count = 0; - - int recv_ret = s2n_recv(receiver, app_data, sizeof(app_data), &blocked); - RESULT_GUARD_POSIX(recv_ret); - RESULT_ENSURE_EQ(recv_ret, sizeof(app_data)); - - return S2N_RESULT_OK; -} - -/* Like s2n_test_basic_recv, - * but we make only one byte of data available at a time. - * This forces us to call s2n_recv repeatedly and verifies that s2n_recv - * can resume across s2n_recv calls while handling fragmented post-handshake messages. - */ -static S2N_RESULT s2n_test_blocking_recv(struct s2n_connection *sender, struct s2n_connection *receiver, - struct s2n_test_io_stuffer_pair *io_pair) -{ - uint8_t app_data[1] = { 0 }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); - RESULT_GUARD_POSIX(send_ret); - RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); - - /* Reset all counters */ - RESULT_GUARD(s2n_mem_test_wipe_callbacks()); - tickets_count = 0; - hello_request_count = 0; - - /* Modify the stuffer's write_cursor to make only one byte - * of the socket / input data available at a time. - */ - struct s2n_stuffer *in = &io_pair->client_in; - if (receiver->mode == S2N_SERVER) { - in = &io_pair->server_in; - } - uint32_t *write_cursor = &in->write_cursor; - uint32_t *read_cursor = &in->read_cursor; - RESULT_ENSURE_GT(write_cursor, read_cursor); - uint32_t saved_write_cursor = *write_cursor; - RESULT_ENSURE_GT(saved_write_cursor, 0); - *write_cursor = *read_cursor + 1; - - while (s2n_recv(receiver, app_data, sizeof(app_data), &blocked) < 0) { - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - (*write_cursor)++; - RESULT_ENSURE_LTE(*write_cursor, saved_write_cursor); - } - RESULT_ENSURE_EQ(*write_cursor, saved_write_cursor); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_init_sender_and_receiver(struct s2n_config *config, - struct s2n_connection *sender, struct s2n_connection *receiver, - struct s2n_test_io_stuffer_pair *io_pair) -{ - RESULT_GUARD_POSIX(s2n_connection_set_config(sender, config)); - RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(sender, S2N_TLS13)); - RESULT_GUARD(s2n_connection_set_secrets(sender)); - RESULT_GUARD_POSIX(s2n_connection_set_blinding(sender, S2N_SELF_SERVICE_BLINDING)); - - RESULT_GUARD_POSIX(s2n_connection_set_config(receiver, config)); - RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(receiver, S2N_TLS13)); - RESULT_GUARD(s2n_connection_set_secrets(receiver)); - RESULT_GUARD_POSIX(s2n_connection_set_blinding(receiver, S2N_SELF_SERVICE_BLINDING)); - - RESULT_GUARD(s2n_io_stuffer_pair_init(io_pair)); - if (sender->mode == S2N_SERVER) { - RESULT_GUARD(s2n_connections_set_io_stuffer_pair(receiver, sender, io_pair)); - } else { - RESULT_GUARD(s2n_connections_set_io_stuffer_pair(sender, receiver, io_pair)); - } - - /* Send and receive to initialize io buffers */ - EXPECT_OK(s2n_test_basic_recv(sender, receiver)); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - const uint8_t unknown_message_type = UINT8_MAX; - const uint32_t test_large_message_size = 3001; - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_ticket_count_cb, NULL)); - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_hello_request_cb, NULL)); - - /* Some tests require sending and receiving tickets. - * Setup the config to handle tickets, but don't send any by default. - */ - uint8_t ticket_key_name[16] = "key name"; - uint8_t ticket_key[] = "key data"; - uint64_t current_time = 0; - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, sizeof(ticket_key_name), - ticket_key, sizeof(ticket_key), current_time / ONE_SEC_IN_NANOS)); - config->initial_tickets_to_send = 0; - - const uint32_t fragment_sizes[] = { - 1, - 2, - S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE, - TLS_HANDSHAKE_HEADER_LENGTH, - TLS_HANDSHAKE_HEADER_LENGTH + 1, - S2N_DEFAULT_FRAGMENT_LENGTH, - S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, - }; - const uint8_t modes[] = { S2N_CLIENT, S2N_SERVER }; - - /* Test: client and server receive small post-handshake messages (KeyUpdates) */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Write KeyUpdate message */ - struct s2n_stuffer message = { 0 }; - DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&message_blob, S2N_KEY_UPDATE_MESSAGE_SIZE)); - for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { - EXPECT_SUCCESS(s2n_key_update_write(&message_blob)); - EXPECT_SUCCESS(s2n_stuffer_init(&message, &message_blob)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, S2N_KEY_UPDATE_MESSAGE_SIZE)); - - /* The TLS1.3 RFC says "Handshake messages MUST NOT span key changes". - * Because KeyUpdate messages trigger key changes, we cannot include multiple in one record. - * We must send individual KeyUpdate messages. - */ - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - - /* Update the traffic keys for the next records */ - EXPECT_SUCCESS(s2n_update_application_traffic_keys(sender, sender->mode, SENDING)); - } - - /* - * We have no mechanism to count KeyUpdates, but we can assume they are processed - * if we successfully decrypt all records. If they were not processed, - * then we would try to use the wrong key to decrypt the next record. - */ - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_OK(s2n_test_basic_recv(sender, receiver)); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - } - - /* Test: client receives large post-handshake messages (NewSessionTickets) - * - * There is no server version of this test because there are no large post-handshake messages - * valid for the server to accept. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); - - /* Write NewSessionTicket message */ - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { - server->tickets_to_send++; - EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); - } - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_basic_recv(server, client)); - EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); - EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); - } - - /* Test: client receives large post-handshake messages of different sizes (NewSessionTickets) - * - * There is no server version of this test because there are no large post-handshake messages - * valid for the server to accept. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); - server->server_max_early_data_size = 10; - - size_t total_size = 0; - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - for (size_t i = 0; i < 3; i++) { - /* Write a basic NewSessionTicket */ - server->server_max_early_data_size_overridden = false; - server->tickets_to_send++; - EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); - size_t min_length = s2n_stuffer_data_available(&messages) - total_size; - total_size += min_length; - - /* Write a NewSesionTicket with early data enabled - * so that the early_data_indication extension is included - * and the message is therefore longer. - */ - server->server_max_early_data_size_overridden = true; - server->tickets_to_send++; - EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); - size_t max_length = s2n_stuffer_data_available(&messages) - total_size; - EXPECT_TRUE(max_length > min_length); - total_size += max_length; - } - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_basic_recv(server, client)); - EXPECT_EQUAL(tickets_count, server->tickets_to_send); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); - EXPECT_EQUAL(tickets_count, server->tickets_to_send); - } - - /* Test: server rejects known, invalid post-handshake messages (NewSessionTickets) - * - * There is no client version of this test because the client accepts all supported - * post-handshake messages. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); - - /* Send NewSessionTicket message */ - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - client->tickets_to_send = 1; - EXPECT_OK(s2n_tls13_server_nst_write(client, &messages)); - EXPECT_OK(s2n_test_send_records(client, messages, fragment_size)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); - EXPECT_EQUAL(tickets_count, 0); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - - /* Test: server rejects fragmented post-handshake message (KeyUpdate) with an invalid size - * - * This response is unique to the server because we want to prevent a malicious - * client from forcing the server to allocate large amounts of memory. - * - * While we could extend the same validation to the client, the client accepts - * a variable-sized message (NewSessionTicket) so can't really be protected. - */ - { - /* This test is only interesting if the message is fragmented */ - uint32_t fragment_size = 2; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); - - /* Write large KeyUpdate messages */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); - EXPECT_OK(s2n_test_send_records(client, message, fragment_size)); - EXPECT_SUCCESS(s2n_update_application_traffic_keys(client, client->mode, SENDING)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); - - /* No post-handshake message should trigger the server to allocate memory */ - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - }; - - /* Test: client receives empty post-handshake messages (HelloRequests) - * - * There is no server version of this test because there are no empty post-handshake messages - * valid for the server to accept. - */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); - - /* HelloRequests are ignored if secure_renegotiation isn't set */ - client->secure_renegotiation = true; - - /* Write HelloRequest messages */ - DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); - for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&messages, TLS_HELLO_REQUEST)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&messages, 0)); - } - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_basic_recv(server, client)); - EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - - EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); - EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); - EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - - /* Test: client and server reject known, invalid messages (ClientHellos) */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Send fake ClientHello messages */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, TLS_HANDSHAKE_HEADER_LENGTH)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); - - /* No post-handshake message should trigger the server to allocate memory */ - if (mode == S2N_SERVER) { - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - } - } - - /* Test: client and server reject unknown messages */ - for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { - uint32_t fragment_size = fragment_sizes[frag_i]; - - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Send unknown message */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, unknown_message_type)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - - DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); - EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); - - /* No post-handshake message should trigger the server to allocate memory */ - if (mode == S2N_SERVER) { - EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); - } - } - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *= type=test - *# - Handshake messages MUST NOT be interleaved with other record - *# types. That is, if a handshake message is split over two or more - *# records, there MUST NOT be any other records between them. - */ - for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { - uint8_t mode = modes[mode_i]; - - /* This test is only interesting if the message is fragmented */ - uint32_t fragment_size = 2; - - DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); - - /* Write a partial message */ - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, S2N_KEY_UPDATE_LENGTH)); - /* Don't write the actual message body -- we want the message to be incomplete */ - - /* Verify we can't receive the records: s2n_test_send_records does not send - * the complete handshake message, so we receive the application data sent by - * s2n_test_basic_recv in the middle of the handshake message. - */ - EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/unstable/renegotiate.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_mem_testlib.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_key_update.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_safety.h" + +#define S2N_TEST_MESSAGE_COUNT 5 + +int s2n_key_update_write(struct s2n_blob *out); + +size_t tickets_count = 0; +static int s2n_ticket_count_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + tickets_count++; + return S2N_SUCCESS; +} + +size_t hello_request_count = 0; +static int s2n_hello_request_cb(struct s2n_connection *conn, void *ctx, s2n_renegotiate_response *response) +{ + hello_request_count++; + *response = S2N_RENEGOTIATE_IGNORE; + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_test_send_records(struct s2n_connection *conn, struct s2n_stuffer messages, uint32_t fragment_size) +{ + conn->max_outgoing_fragment_length = fragment_size; + + DEFER_CLEANUP(struct s2n_blob record_data = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&record_data, fragment_size)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint32_t remaining = 0; + while ((remaining = s2n_stuffer_data_available(&messages)) > 0) { + record_data.size = S2N_MIN(record_data.size, remaining); + RESULT_GUARD_POSIX(s2n_stuffer_read(&messages, &record_data)); + RESULT_GUARD(s2n_record_write(conn, TLS_HANDSHAKE, &record_data)); + RESULT_GUARD_POSIX(s2n_flush(conn, &blocked)); + }; + + return S2N_RESULT_OK; +} + +/* + * Verify that the receiver can receive a byte sent by the sender. + * In the process, we also verify that the receiver can receive all previous + * data sent by the sender, since TCP / TLS messages have a guaranteed order. + */ +static S2N_RESULT s2n_test_basic_recv(struct s2n_connection *sender, struct s2n_connection *receiver) +{ + uint8_t app_data[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(send_ret); + RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); + + /* Reset all counters */ + RESULT_GUARD(s2n_mem_test_wipe_callbacks()); + tickets_count = 0; + hello_request_count = 0; + + int recv_ret = s2n_recv(receiver, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(recv_ret); + RESULT_ENSURE_EQ(recv_ret, sizeof(app_data)); + + return S2N_RESULT_OK; +} + +/* Like s2n_test_basic_recv, + * but we make only one byte of data available at a time. + * This forces us to call s2n_recv repeatedly and verifies that s2n_recv + * can resume across s2n_recv calls while handling fragmented post-handshake messages. + */ +static S2N_RESULT s2n_test_blocking_recv(struct s2n_connection *sender, struct s2n_connection *receiver, + struct s2n_test_io_stuffer_pair *io_pair) +{ + uint8_t app_data[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(send_ret); + RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); + + /* Reset all counters */ + RESULT_GUARD(s2n_mem_test_wipe_callbacks()); + tickets_count = 0; + hello_request_count = 0; + + /* Modify the stuffer's write_cursor to make only one byte + * of the socket / input data available at a time. + */ + struct s2n_stuffer *in = &io_pair->client_in; + if (receiver->mode == S2N_SERVER) { + in = &io_pair->server_in; + } + uint32_t *write_cursor = &in->write_cursor; + uint32_t *read_cursor = &in->read_cursor; + RESULT_ENSURE_GT(write_cursor, read_cursor); + uint32_t saved_write_cursor = *write_cursor; + RESULT_ENSURE_GT(saved_write_cursor, 0); + *write_cursor = *read_cursor + 1; + + while (s2n_recv(receiver, app_data, sizeof(app_data), &blocked) < 0) { + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + (*write_cursor)++; + RESULT_ENSURE_LTE(*write_cursor, saved_write_cursor); + } + RESULT_ENSURE_EQ(*write_cursor, saved_write_cursor); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_init_sender_and_receiver(struct s2n_config *config, + struct s2n_connection *sender, struct s2n_connection *receiver, + struct s2n_test_io_stuffer_pair *io_pair) +{ + RESULT_GUARD_POSIX(s2n_connection_set_config(sender, config)); + RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(sender, S2N_TLS13)); + RESULT_GUARD(s2n_connection_set_secrets(sender)); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(sender, S2N_SELF_SERVICE_BLINDING)); + + RESULT_GUARD_POSIX(s2n_connection_set_config(receiver, config)); + RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(receiver, S2N_TLS13)); + RESULT_GUARD(s2n_connection_set_secrets(receiver)); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(receiver, S2N_SELF_SERVICE_BLINDING)); + + RESULT_GUARD(s2n_io_stuffer_pair_init(io_pair)); + if (sender->mode == S2N_SERVER) { + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(receiver, sender, io_pair)); + } else { + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(sender, receiver, io_pair)); + } + + /* Send and receive to initialize io buffers */ + EXPECT_OK(s2n_test_basic_recv(sender, receiver)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t unknown_message_type = UINT8_MAX; + const uint32_t test_large_message_size = 3001; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_ticket_count_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_hello_request_cb, NULL)); + + /* Some tests require sending and receiving tickets. + * Setup the config to handle tickets, but don't send any by default. + */ + uint8_t ticket_key_name[16] = "key name"; + uint8_t ticket_key[] = "key data"; + uint64_t current_time = 0; + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, sizeof(ticket_key_name), + ticket_key, sizeof(ticket_key), current_time / ONE_SEC_IN_NANOS)); + config->initial_tickets_to_send = 0; + + const uint32_t fragment_sizes[] = { + 1, + 2, + S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE, + TLS_HANDSHAKE_HEADER_LENGTH, + TLS_HANDSHAKE_HEADER_LENGTH + 1, + S2N_DEFAULT_FRAGMENT_LENGTH, + S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, + }; + const uint8_t modes[] = { S2N_CLIENT, S2N_SERVER }; + + /* Test: client and server receive small post-handshake messages (KeyUpdates) */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Write KeyUpdate message */ + struct s2n_stuffer message = { 0 }; + DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&message_blob, S2N_KEY_UPDATE_MESSAGE_SIZE)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + EXPECT_SUCCESS(s2n_key_update_write(&message_blob)); + EXPECT_SUCCESS(s2n_stuffer_init(&message, &message_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, S2N_KEY_UPDATE_MESSAGE_SIZE)); + + /* The TLS1.3 RFC says "Handshake messages MUST NOT span key changes". + * Because KeyUpdate messages trigger key changes, we cannot include multiple in one record. + * We must send individual KeyUpdate messages. + */ + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + /* Update the traffic keys for the next records */ + EXPECT_SUCCESS(s2n_update_application_traffic_keys(sender, sender->mode, SENDING)); + } + + /* + * We have no mechanism to count KeyUpdates, but we can assume they are processed + * if we successfully decrypt all records. If they were not processed, + * then we would try to use the wrong key to decrypt the next record. + */ + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_OK(s2n_test_basic_recv(sender, receiver)); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + + /* Test: client receives large post-handshake messages (NewSessionTickets) + * + * There is no server version of this test because there are no large post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + + /* Write NewSessionTicket message */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + } + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); + } + + /* Test: client receives large post-handshake messages of different sizes (NewSessionTickets) + * + * There is no server version of this test because there are no large post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + server->server_max_early_data_size = 10; + + size_t total_size = 0; + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < 3; i++) { + /* Write a basic NewSessionTicket */ + server->server_max_early_data_size_overridden = false; + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + size_t min_length = s2n_stuffer_data_available(&messages) - total_size; + total_size += min_length; + + /* Write a NewSesionTicket with early data enabled + * so that the early_data_indication extension is included + * and the message is therefore longer. + */ + server->server_max_early_data_size_overridden = true; + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + size_t max_length = s2n_stuffer_data_available(&messages) - total_size; + EXPECT_TRUE(max_length > min_length); + total_size += max_length; + } + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(tickets_count, server->tickets_to_send); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(tickets_count, server->tickets_to_send); + } + + /* Test: server rejects known, invalid post-handshake messages (NewSessionTickets) + * + * There is no client version of this test because the client accepts all supported + * post-handshake messages. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); + + /* Send NewSessionTicket message */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + client->tickets_to_send = 1; + EXPECT_OK(s2n_tls13_server_nst_write(client, &messages)); + EXPECT_OK(s2n_test_send_records(client, messages, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(tickets_count, 0); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + /* Test: server rejects fragmented post-handshake message (KeyUpdate) with an invalid size + * + * This response is unique to the server because we want to prevent a malicious + * client from forcing the server to allocate large amounts of memory. + * + * While we could extend the same validation to the client, the client accepts + * a variable-sized message (NewSessionTicket) so can't really be protected. + */ + { + /* This test is only interesting if the message is fragmented */ + uint32_t fragment_size = 2; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); + + /* Write large KeyUpdate messages */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(client, message, fragment_size)); + EXPECT_SUCCESS(s2n_update_application_traffic_keys(client, client->mode, SENDING)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + }; + + /* Test: client receives empty post-handshake messages (HelloRequests) + * + * There is no server version of this test because there are no empty post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + + /* HelloRequests are ignored if secure_renegotiation isn't set */ + client->secure_renegotiation = true; + + /* Write HelloRequest messages */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&messages, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&messages, 0)); + } + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + /* Test: client and server reject known, invalid messages (ClientHellos) */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Send fake ClientHello messages */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, TLS_HANDSHAKE_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + if (mode == S2N_SERVER) { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + } + + /* Test: client and server reject unknown messages */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Send unknown message */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, unknown_message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + if (mode == S2N_SERVER) { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *= type=test + *# - Handshake messages MUST NOT be interleaved with other record + *# types. That is, if a handshake message is split over two or more + *# records, there MUST NOT be any other records between them. + */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + /* This test is only interesting if the message is fragmented */ + uint32_t fragment_size = 2; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Write a partial message */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, S2N_KEY_UPDATE_LENGTH)); + /* Don't write the actual message body -- we want the message to be incomplete */ + + /* Verify we can't receive the records: s2n_test_send_records does not send + * the complete handshake message, so we receive the application data sent by + * s2n_test_basic_recv in the middle of the handshake message. + */ + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_prf_key_material_test.c b/tests/unit/s2n_prf_key_material_test.c index dc95a83d041..057e46b9f5f 100644 --- a/tests/unit/s2n_prf_key_material_test.c +++ b/tests/unit/s2n_prf_key_material_test.c @@ -1,236 +1,236 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_prf.h" -#include "utils/s2n_random.h" - -static S2N_RESULT s2n_test_validate_key_material(struct s2n_key_material *key_material, - struct s2n_blob *test_data_blob, uint8_t mac_size, uint8_t key_size, uint8_t iv_size) -{ - /* confirm that the data is copied to key_material */ - RESULT_ENSURE_EQ(test_data_blob->size, sizeof(key_material->key_block)); - RESULT_ENSURE_EQ(memcmp(test_data_blob->data, key_material->key_block, test_data_blob->size), 0); - - struct s2n_stuffer test_data_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&test_data_stuffer, test_data_blob)); - - /* test that its possible to access data from s2n_key_material */ - /* client MAC */ - uint8_t *test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_mac.data, mac_size), 0); - /* server MAC */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_mac.data, mac_size), 0); - - /* client KEY */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_key.data, key_size), 0); - /* server KEY */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_key.data, key_size), 0); - - /* client IV */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_iv.data, iv_size), 0); - /* server IV */ - test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); - RESULT_ENSURE_REF(test_ptr); - RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_iv.data, iv_size), 0); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* prepare test data */ - uint8_t test_data[S2N_MAX_KEY_BLOCK_LEN] = { 0 }; - struct s2n_blob test_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); - EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); - - /* fuzz s2n_key_material_init with different mac, key, iv sizes */ - { - for (uint8_t mac_size = 0; mac_size < SHA512_DIGEST_LENGTH; mac_size++) { - for (uint8_t key_size = 0; key_size < S2N_TLS_AES_256_GCM_KEY_LEN; key_size++) { - for (uint8_t iv_size = 0; iv_size < S2N_TLS_MAX_IV_LEN; iv_size++) { - if ((mac_size * 2 + key_size * 2 + iv_size * 2 > S2N_MAX_KEY_BLOCK_LEN)) { - continue; - } - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - /* test varying size of mac, key and iv */ - conn->actual_protocol_version = S2N_TLS10; - struct s2n_cipher temp_cipher = { - .type = S2N_COMPOSITE, - .key_material_size = key_size, - .io.comp = { - /* interpreted as iv size for composite ciphers.. which makes - * it easy for testing */ - .block_size = iv_size, - .mac_key_size = mac_size, - }, - }; - struct s2n_record_algorithm temp_record_alg = { - .cipher = &temp_cipher, - }; - struct s2n_cipher_suite temp_cipher_suite = { - .record_alg = &temp_record_alg, - }; - conn->secure->cipher_suite = &temp_cipher_suite; - - /* init s2n_key_material */ - struct s2n_key_material key_material = { 0 }; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - - /* assert that sizes match */ - EXPECT_EQUAL(key_material.client_mac.size, mac_size); - EXPECT_EQUAL(key_material.client_key.size, key_size); - EXPECT_EQUAL(key_material.client_iv.size, iv_size); - EXPECT_EQUAL(key_material.server_mac.size, mac_size); - EXPECT_EQUAL(key_material.server_key.size, key_size); - EXPECT_EQUAL(key_material.server_iv.size, iv_size); - - /* copy data into key_material and validate accessing key_material is sound */ - EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); - POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); - EXPECT_OK(s2n_test_validate_key_material(&key_material, &test_data_blob, mac_size, key_size, iv_size)); - } - } - } - } - - /* confirm that s2n_key_material can correctly handle all cipher suites */ - { - for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; - if (!cipher_suite->available) { - continue; - } - conn->secure->cipher_suite = cipher_suite; - - /* init s2n_key_material */ - struct s2n_key_material key_material = { 0 }; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - - /* copy data into key_material and validate accessing key_material is sound */ - EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); - POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); - /* test that its possible to access mac, key and iv correctly from s2n_key_material */ - EXPECT_OK(s2n_test_validate_key_material( - &key_material, - &test_data_blob, - key_material.client_mac.size, - key_material.client_key.size, - key_material.client_iv.size)); - } - } - - /* AEAD cipher - * IV size should be the same regardless of protocol version - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - EXPECT_EQUAL(conn->secure->cipher_suite->record_alg->cipher->type, S2N_AEAD); - - struct s2n_key_material key_material = { 0 }; - - uint32_t mac = 0; - uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; - uint32_t iv = S2N_TLS13_FIXED_IV_LEN; - - /* initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS10; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - - /* re-initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS11; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - /* assert same IV size regardless of protocol version */ - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - } - - /* NON AEAD cipher - * IV size depends on protocol version - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha256; - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - /* assert that the cipher chosen is non AEAD */ - EXPECT_TRUE(cipher->type == S2N_COMPOSITE || cipher->type == S2N_CBC); - - struct s2n_key_material key_material = { 0 }; - - uint32_t mac = SHA256_DIGEST_LENGTH; - uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; - uint32_t iv = 16; - - /* initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS10; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - /* assert IV of non 0 if protocol version <= S2N_TLS10 */ - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - - /* re-initialize s2n_key_material */ - conn->actual_protocol_version = S2N_TLS11; - EXPECT_OK(s2n_key_material_init(&key_material, conn)); - /* assert IV of size == 0 if protocol version > S2N_TLS10 */ - iv = 0; - EXPECT_EQUAL(key_material.client_mac.size, mac); - EXPECT_EQUAL(key_material.client_key.size, key); - EXPECT_EQUAL(key_material.client_iv.size, iv); - EXPECT_EQUAL(key_material.server_mac.size, mac); - EXPECT_EQUAL(key_material.server_key.size, key); - EXPECT_EQUAL(key_material.server_iv.size, iv); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_prf.h" +#include "utils/s2n_random.h" + +static S2N_RESULT s2n_test_validate_key_material(struct s2n_key_material *key_material, + struct s2n_blob *test_data_blob, uint8_t mac_size, uint8_t key_size, uint8_t iv_size) +{ + /* confirm that the data is copied to key_material */ + RESULT_ENSURE_EQ(test_data_blob->size, sizeof(key_material->key_block)); + RESULT_ENSURE_EQ(memcmp(test_data_blob->data, key_material->key_block, test_data_blob->size), 0); + + struct s2n_stuffer test_data_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&test_data_stuffer, test_data_blob)); + + /* test that its possible to access data from s2n_key_material */ + /* client MAC */ + uint8_t *test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_mac.data, mac_size), 0); + /* server MAC */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_mac.data, mac_size), 0); + + /* client KEY */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_key.data, key_size), 0); + /* server KEY */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_key.data, key_size), 0); + + /* client IV */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_iv.data, iv_size), 0); + /* server IV */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_iv.data, iv_size), 0); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* prepare test data */ + uint8_t test_data[S2N_MAX_KEY_BLOCK_LEN] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* fuzz s2n_key_material_init with different mac, key, iv sizes */ + { + for (uint8_t mac_size = 0; mac_size < SHA512_DIGEST_LENGTH; mac_size++) { + for (uint8_t key_size = 0; key_size < S2N_TLS_AES_256_GCM_KEY_LEN; key_size++) { + for (uint8_t iv_size = 0; iv_size < S2N_TLS_MAX_IV_LEN; iv_size++) { + if ((mac_size * 2 + key_size * 2 + iv_size * 2 > S2N_MAX_KEY_BLOCK_LEN)) { + continue; + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + /* test varying size of mac, key and iv */ + conn->actual_protocol_version = S2N_TLS10; + struct s2n_cipher temp_cipher = { + .type = S2N_COMPOSITE, + .key_material_size = key_size, + .io.comp = { + /* interpreted as iv size for composite ciphers.. which makes + * it easy for testing */ + .block_size = iv_size, + .mac_key_size = mac_size, + }, + }; + struct s2n_record_algorithm temp_record_alg = { + .cipher = &temp_cipher, + }; + struct s2n_cipher_suite temp_cipher_suite = { + .record_alg = &temp_record_alg, + }; + conn->secure->cipher_suite = &temp_cipher_suite; + + /* init s2n_key_material */ + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + /* assert that sizes match */ + EXPECT_EQUAL(key_material.client_mac.size, mac_size); + EXPECT_EQUAL(key_material.client_key.size, key_size); + EXPECT_EQUAL(key_material.client_iv.size, iv_size); + EXPECT_EQUAL(key_material.server_mac.size, mac_size); + EXPECT_EQUAL(key_material.server_key.size, key_size); + EXPECT_EQUAL(key_material.server_iv.size, iv_size); + + /* copy data into key_material and validate accessing key_material is sound */ + EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); + POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); + EXPECT_OK(s2n_test_validate_key_material(&key_material, &test_data_blob, mac_size, key_size, iv_size)); + } + } + } + } + + /* confirm that s2n_key_material can correctly handle all cipher suites */ + { + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + if (!cipher_suite->available) { + continue; + } + conn->secure->cipher_suite = cipher_suite; + + /* init s2n_key_material */ + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + /* copy data into key_material and validate accessing key_material is sound */ + EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); + POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); + /* test that its possible to access mac, key and iv correctly from s2n_key_material */ + EXPECT_OK(s2n_test_validate_key_material( + &key_material, + &test_data_blob, + key_material.client_mac.size, + key_material.client_key.size, + key_material.client_iv.size)); + } + } + + /* AEAD cipher + * IV size should be the same regardless of protocol version + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_EQUAL(conn->secure->cipher_suite->record_alg->cipher->type, S2N_AEAD); + + struct s2n_key_material key_material = { 0 }; + + uint32_t mac = 0; + uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; + uint32_t iv = S2N_TLS13_FIXED_IV_LEN; + + /* initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + + /* re-initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert same IV size regardless of protocol version */ + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + } + + /* NON AEAD cipher + * IV size depends on protocol version + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha256; + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + /* assert that the cipher chosen is non AEAD */ + EXPECT_TRUE(cipher->type == S2N_COMPOSITE || cipher->type == S2N_CBC); + + struct s2n_key_material key_material = { 0 }; + + uint32_t mac = SHA256_DIGEST_LENGTH; + uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; + uint32_t iv = 16; + + /* initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert IV of non 0 if protocol version <= S2N_TLS10 */ + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + + /* re-initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert IV of size == 0 if protocol version > S2N_TLS10 */ + iv = 0; + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_quic_support_io_test.c b/tests/unit/s2n_quic_support_io_test.c index 8ea9afa803f..a55eff90584 100644 --- a/tests/unit/s2n_quic_support_io_test.c +++ b/tests/unit/s2n_quic_support_io_test.c @@ -1,609 +1,609 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_mem.h" - -/* We need access to some io logic */ -#include "tls/s2n_handshake_io.c" - -#define TEST_TICKET_AGE_ADD 0x01, 0x02, 0x03, 0x04 -#define TEST_LIFETIME 0x00, 0x01, 0x01, 0x01 -#define TEST_TICKET 0x01, 0xFF, 0x23 - -static const uint8_t TEST_DATA[] = "test"; -static const size_t TEST_DATA_SIZE = sizeof(TEST_DATA); - -struct s2n_stuffer input_stuffer, output_stuffer; - -static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - uint8_t *count = (uint8_t *) ctx; - (*count)++; - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_setup_conn(struct s2n_connection *conn) -{ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&input_stuffer)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&output_stuffer)); - RESULT_GUARD_POSIX(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_setup_conn_for_client_hello(struct s2n_connection *conn) -{ - RESULT_GUARD(s2n_setup_conn(conn)); - conn->handshake.handshake_type = INITIAL; - conn->handshake.message_number = 0; - RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_setup_conn_for_server_hello(struct s2n_connection *conn) -{ - RESULT_GUARD(s2n_setup_conn(conn)); - - /* Use arbitrary cipher suite */ - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - /* Setup secrets */ - const struct s2n_ecc_preferences *ecc_preferences = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - if (conn->kex_params.server_ecc_evp_params.evp_pkey == NULL) { - RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); - } - if (conn->kex_params.client_ecc_evp_params.evp_pkey == NULL) { - RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - } - - /* Set handshake to write message */ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - conn->handshake.message_number = 1; - RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_write_test_message(struct s2n_blob *out, message_type_t message_type) -{ - RESULT_GUARD_POSIX(s2n_alloc(out, TEST_DATA_SIZE + TLS_HANDSHAKE_HEADER_LENGTH)); - - struct s2n_stuffer stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, out)); - - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&stuffer, message_type)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - - return S2N_RESULT_OK; -} - -static int s2n_test_write_handler(struct s2n_connection *conn) -{ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, TEST_DATA, TEST_DATA_SIZE)); - return S2N_SUCCESS; -} - -static int s2n_test_read_handler(struct s2n_connection *conn) -{ - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->handshake.io, TEST_DATA_SIZE), - TEST_DATA, TEST_DATA_SIZE); - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* Test: s2n_quic_write_handshake_message */ - { - /* Safety checks */ - EXPECT_ERROR(s2n_quic_write_handshake_message(NULL)); - - /* Writes handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - uint8_t message_data[] = "The client says hello"; - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, message_data, sizeof(message_data))); - - EXPECT_OK(s2n_quic_write_handshake_message(conn)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), sizeof(message_data)); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->out, sizeof(message_data)), - message_data, sizeof(message_data)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test: s2n_quic_read_handshake_message */ - { - /* Safety checks */ - { - struct s2n_connection conn = { 0 }; - uint8_t message_type = 0; - - EXPECT_ERROR(s2n_quic_read_handshake_message(NULL, &message_type)); - EXPECT_ERROR(s2n_quic_read_handshake_message(&conn, NULL)); - }; - - /* Reads basic handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - uint8_t expected_message_type = 7; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, expected_message_type)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - - uint8_t actual_message_type = 0; - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - - EXPECT_EQUAL(actual_message_type, expected_message_type); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TLS_HANDSHAKE_HEADER_LENGTH); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), - TEST_DATA, sizeof(TEST_DATA)); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Blocks on insufficient data for handshake message header */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - - uint8_t actual_message_type = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), - S2N_ERR_IO_BLOCKED); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Blocks on insufficient data for handshake message data */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE - 1)); - - uint8_t actual_message_type = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), - S2N_ERR_IO_BLOCKED); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Fails for an impossibly large handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH + 1)); - - uint8_t actual_message_type = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), - S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Succeeds for a handshake message larger than the input buffer */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - uint8_t actual_message_type = 0; - - /* Read a small message to initialize the input buffer */ - const size_t small_message_size = 10; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, small_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, small_message_size)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_message_size); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - EXPECT_OK(s2n_record_wipe(conn)); - const size_t max_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); - EXPECT_TRUE(max_buffer_size > small_message_size); - - /* Read a large message to force the input buffer to resize */ - const size_t large_message_size = max_buffer_size + 10; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, large_message_size)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, large_message_size)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), large_message_size); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - EXPECT_OK(s2n_record_wipe(conn)); - const size_t resized_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); - EXPECT_TRUE(resized_buffer_size >= large_message_size); - - /* Read another message to check that the resize doesn't prevent future reads */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), - TEST_DATA, sizeof(TEST_DATA)); - }; - - /* Succeeds for multiple messages */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - uint8_t actual_message_type = 0; - size_t expected_buffer_size = 0; - for (size_t i = 0; i < 100; i++) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), - TEST_DATA, sizeof(TEST_DATA)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); - EXPECT_OK(s2n_record_wipe(conn)); - - /* Ensure buffer size stays constant */ - const size_t buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); - if (i == 0) { - expected_buffer_size = buffer_size; - } - EXPECT_EQUAL(expected_buffer_size, buffer_size); - } - }; - }; - - /* Functional Tests */ - { - s2n_blocked_status blocked_status; - - /* Use handler stubs to avoid executing complicated handler implementations */ - for (size_t i = 0; i < s2n_array_len(tls13_state_machine); i++) { - tls13_state_machine[i].handler[S2N_SERVER] = s2n_test_read_handler; - tls13_state_machine[i].handler[S2N_CLIENT] = s2n_test_write_handler; - } - - /* Write test message */ - DEFER_CLEANUP(struct s2n_blob server_hello, s2n_free); - EXPECT_OK(s2n_write_test_message(&server_hello, TLS_SERVER_HELLO)); - - /* Setup IO buffers */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input_stuffer, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output_stuffer, 0)); - - /* Setup config */ - struct s2n_config *config = NULL; - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_enable_quic(config)); - - /* Functional: successfully reads full handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Functional: successfully reads fragmented handshake message */ - for (size_t i = 1; i < server_hello.size - 1; i++) { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Write initial fragment */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, server_hello.data, i)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - /* Write rest of message */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, - server_hello.data + i, server_hello.size - i)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Functional: successfully reads multiple handshake messages */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - DEFER_CLEANUP(struct s2n_blob encrypted_extensions, s2n_free); - EXPECT_OK(s2n_write_test_message(&encrypted_extensions, TLS_ENCRYPTED_EXTENSIONS)); - - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &encrypted_extensions)); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); - EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Function: fails to read record instead of handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Write the record: record type, protocol version, - * handshake message size, handshake message */ - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_HANDSHAKE)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, server_hello.size)); - EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Function: fails to read Change Cipher Spec record */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Write the record: record type, protocol version, - * record data size, standard "0x01" record data */ - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_CHANGE_CIPHER_SPEC)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); - POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, 1)); - POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 1)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - uint32_t client_hello_length = 0; - - /* Functional: successfully writes full handshake message */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - client_hello_length = s2n_stuffer_data_available(&output_stuffer); - - uint8_t actual_message_type = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output_stuffer, &actual_message_type)); - EXPECT_EQUAL(actual_message_type, TLS_CLIENT_HELLO); - - uint32_t actual_message_size = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint24(&output_stuffer, &actual_message_size)); - EXPECT_EQUAL(actual_message_size, TEST_DATA_SIZE); - - EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), TEST_DATA_SIZE); - EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&output_stuffer, TEST_DATA_SIZE), - TEST_DATA, TEST_DATA_SIZE); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Functional: successfully retries after blocked write */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Sabotage the output stuffer to block writing */ - struct s2n_stuffer bad_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &bad_stuffer, conn)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); - EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), 0); - - /* Fix the output stuffer */ - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); - EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), client_hello_length); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - EXPECT_SUCCESS(s2n_stuffer_free(&input_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_free(&output_stuffer)); - EXPECT_SUCCESS(s2n_config_free(config)); - }; - - /* Test: s2n_recv_quic_post_handshake_message */ - { - /* Safety checks */ - s2n_blocked_status blocked = 0; - EXPECT_FAILURE(s2n_recv_quic_post_handshake_message(NULL, &blocked)); - - /* Parsable session ticket message */ - uint8_t ticket_message_bytes[] = { - TLS_SERVER_NEW_SESSION_TICKET, - 0x00, 0x00, 0x12, /* message size */ - TEST_LIFETIME, /* ticket lifetime */ - TEST_TICKET_AGE_ADD, /* ticket age add */ - 0x02, /* nonce len */ - 0x00, 0x00, /* nonce */ - 0x00, 0x03, /* ticket len */ - TEST_TICKET, /* ticket */ - 0x00, 0x00, /* extensions len */ - }; - - /* Test: fails to read post-handshake message that is not a ST */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - /* Create a post-handshake message that isn't supported by quic */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, TLS_KEY_UPDATE)); - EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_UNSUPPORTED_WITH_QUIC); - }; - - /* Test: successfully reads and processes post-handshake message */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - uint8_t session_ticket_cb_count = 0; - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - /* Construct and process multiple ST handshake messages */ - for (size_t i = 1; i < 10; i++) { - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, sizeof(ticket_message_bytes))); - EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); - - /* Callback was triggered */ - EXPECT_EQUAL(session_ticket_cb_count, i); - } - }; - - /* Test: successfully reads and processes fragmented post-handshake message */ - for (size_t i = 1; i < sizeof(ticket_message_bytes); i++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - uint8_t session_ticket_cb_count = 0; - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); - - /* Mock receiving a fragmented handshake message */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, i)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Callback was not triggered */ - EXPECT_EQUAL(session_ticket_cb_count, 0); - - /* "Write" the rest of the message */ - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes + i, sizeof(ticket_message_bytes) - i)); - - EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); - - /* Callback was triggered */ - EXPECT_EQUAL(session_ticket_cb_count, 1); - }; - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_mem.h" + +/* We need access to some io logic */ +#include "tls/s2n_handshake_io.c" + +#define TEST_TICKET_AGE_ADD 0x01, 0x02, 0x03, 0x04 +#define TEST_LIFETIME 0x00, 0x01, 0x01, 0x01 +#define TEST_TICKET 0x01, 0xFF, 0x23 + +static const uint8_t TEST_DATA[] = "test"; +static const size_t TEST_DATA_SIZE = sizeof(TEST_DATA); + +struct s2n_stuffer input_stuffer, output_stuffer; + +static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + uint8_t *count = (uint8_t *) ctx; + (*count)++; + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_setup_conn(struct s2n_connection *conn) +{ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&input_stuffer)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&output_stuffer)); + RESULT_GUARD_POSIX(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_conn_for_client_hello(struct s2n_connection *conn) +{ + RESULT_GUARD(s2n_setup_conn(conn)); + conn->handshake.handshake_type = INITIAL; + conn->handshake.message_number = 0; + RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_conn_for_server_hello(struct s2n_connection *conn) +{ + RESULT_GUARD(s2n_setup_conn(conn)); + + /* Use arbitrary cipher suite */ + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Setup secrets */ + const struct s2n_ecc_preferences *ecc_preferences = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + if (conn->kex_params.server_ecc_evp_params.evp_pkey == NULL) { + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + } + if (conn->kex_params.client_ecc_evp_params.evp_pkey == NULL) { + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + } + + /* Set handshake to write message */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = 1; + RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_write_test_message(struct s2n_blob *out, message_type_t message_type) +{ + RESULT_GUARD_POSIX(s2n_alloc(out, TEST_DATA_SIZE + TLS_HANDSHAKE_HEADER_LENGTH)); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, out)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&stuffer, message_type)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + return S2N_RESULT_OK; +} + +static int s2n_test_write_handler(struct s2n_connection *conn) +{ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, TEST_DATA, TEST_DATA_SIZE)); + return S2N_SUCCESS; +} + +static int s2n_test_read_handler(struct s2n_connection *conn) +{ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->handshake.io, TEST_DATA_SIZE), + TEST_DATA, TEST_DATA_SIZE); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test: s2n_quic_write_handshake_message */ + { + /* Safety checks */ + EXPECT_ERROR(s2n_quic_write_handshake_message(NULL)); + + /* Writes handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + uint8_t message_data[] = "The client says hello"; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, message_data, sizeof(message_data))); + + EXPECT_OK(s2n_quic_write_handshake_message(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), sizeof(message_data)); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->out, sizeof(message_data)), + message_data, sizeof(message_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: s2n_quic_read_handshake_message */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + uint8_t message_type = 0; + + EXPECT_ERROR(s2n_quic_read_handshake_message(NULL, &message_type)); + EXPECT_ERROR(s2n_quic_read_handshake_message(&conn, NULL)); + }; + + /* Reads basic handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t expected_message_type = 7; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, expected_message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + uint8_t actual_message_type = 0; + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + + EXPECT_EQUAL(actual_message_type, expected_message_type); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TLS_HANDSHAKE_HEADER_LENGTH); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Blocks on insufficient data for handshake message header */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_IO_BLOCKED); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Blocks on insufficient data for handshake message data */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE - 1)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_IO_BLOCKED); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fails for an impossibly large handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH + 1)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Succeeds for a handshake message larger than the input buffer */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t actual_message_type = 0; + + /* Read a small message to initialize the input buffer */ + const size_t small_message_size = 10; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, small_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, small_message_size)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_message_size); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + const size_t max_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_TRUE(max_buffer_size > small_message_size); + + /* Read a large message to force the input buffer to resize */ + const size_t large_message_size = max_buffer_size + 10; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, large_message_size)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), large_message_size); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + const size_t resized_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_TRUE(resized_buffer_size >= large_message_size); + + /* Read another message to check that the resize doesn't prevent future reads */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + }; + + /* Succeeds for multiple messages */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t actual_message_type = 0; + size_t expected_buffer_size = 0; + for (size_t i = 0; i < 100; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + + /* Ensure buffer size stays constant */ + const size_t buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + if (i == 0) { + expected_buffer_size = buffer_size; + } + EXPECT_EQUAL(expected_buffer_size, buffer_size); + } + }; + }; + + /* Functional Tests */ + { + s2n_blocked_status blocked_status; + + /* Use handler stubs to avoid executing complicated handler implementations */ + for (size_t i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[S2N_SERVER] = s2n_test_read_handler; + tls13_state_machine[i].handler[S2N_CLIENT] = s2n_test_write_handler; + } + + /* Write test message */ + DEFER_CLEANUP(struct s2n_blob server_hello, s2n_free); + EXPECT_OK(s2n_write_test_message(&server_hello, TLS_SERVER_HELLO)); + + /* Setup IO buffers */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output_stuffer, 0)); + + /* Setup config */ + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + /* Functional: successfully reads full handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional: successfully reads fragmented handshake message */ + for (size_t i = 1; i < server_hello.size - 1; i++) { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write initial fragment */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, server_hello.data, i)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + /* Write rest of message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, + server_hello.data + i, server_hello.size - i)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Functional: successfully reads multiple handshake messages */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_blob encrypted_extensions, s2n_free); + EXPECT_OK(s2n_write_test_message(&encrypted_extensions, TLS_ENCRYPTED_EXTENSIONS)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &encrypted_extensions)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Function: fails to read record instead of handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write the record: record type, protocol version, + * handshake message size, handshake message */ + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_HANDSHAKE)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, server_hello.size)); + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Function: fails to read Change Cipher Spec record */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write the record: record type, protocol version, + * record data size, standard "0x01" record data */ + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_CHANGE_CIPHER_SPEC)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, 1)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + uint32_t client_hello_length = 0; + + /* Functional: successfully writes full handshake message */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + client_hello_length = s2n_stuffer_data_available(&output_stuffer); + + uint8_t actual_message_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output_stuffer, &actual_message_type)); + EXPECT_EQUAL(actual_message_type, TLS_CLIENT_HELLO); + + uint32_t actual_message_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&output_stuffer, &actual_message_size)); + EXPECT_EQUAL(actual_message_size, TEST_DATA_SIZE); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&output_stuffer, TEST_DATA_SIZE), + TEST_DATA, TEST_DATA_SIZE); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional: successfully retries after blocked write */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Sabotage the output stuffer to block writing */ + struct s2n_stuffer bad_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &bad_stuffer, conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), 0); + + /* Fix the output stuffer */ + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), client_hello_length); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&input_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_free(&output_stuffer)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test: s2n_recv_quic_post_handshake_message */ + { + /* Safety checks */ + s2n_blocked_status blocked = 0; + EXPECT_FAILURE(s2n_recv_quic_post_handshake_message(NULL, &blocked)); + + /* Parsable session ticket message */ + uint8_t ticket_message_bytes[] = { + TLS_SERVER_NEW_SESSION_TICKET, + 0x00, 0x00, 0x12, /* message size */ + TEST_LIFETIME, /* ticket lifetime */ + TEST_TICKET_AGE_ADD, /* ticket age add */ + 0x02, /* nonce len */ + 0x00, 0x00, /* nonce */ + 0x00, 0x03, /* ticket len */ + TEST_TICKET, /* ticket */ + 0x00, 0x00, /* extensions len */ + }; + + /* Test: fails to read post-handshake message that is not a ST */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Create a post-handshake message that isn't supported by quic */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_UNSUPPORTED_WITH_QUIC); + }; + + /* Test: successfully reads and processes post-handshake message */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + uint8_t session_ticket_cb_count = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Construct and process multiple ST handshake messages */ + for (size_t i = 1; i < 10; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, sizeof(ticket_message_bytes))); + EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); + + /* Callback was triggered */ + EXPECT_EQUAL(session_ticket_cb_count, i); + } + }; + + /* Test: successfully reads and processes fragmented post-handshake message */ + for (size_t i = 1; i < sizeof(ticket_message_bytes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + uint8_t session_ticket_cb_count = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Mock receiving a fragmented handshake message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes, i)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Callback was not triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 0); + + /* "Write" the rest of the message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message_bytes + i, sizeof(ticket_message_bytes) - i)); + + EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); + + /* Callback was triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 1); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_record_size_test.c b/tests/unit/s2n_record_size_test.c index a05e46b86ec..5e363a0b081 100644 --- a/tests/unit/s2n_record_size_test.c +++ b/tests/unit/s2n_record_size_test.c @@ -1,523 +1,523 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_prf.h" -#include "tls/s2n_record.h" -#include "utils/s2n_random.h" - -#define ONE_BLOCK 1024 -#define ONE_HUNDRED_K 100000 -#define RECORD_SIZE_HIGH_BYTE_ORDER 3 -#define RECORD_SIZE_LOW_BYTE_ORDER 4 -#define BYTE_SHIFT 8 -#define RECORD_SIZE(data) ((data[RECORD_SIZE_HIGH_BYTE_ORDER] << BYTE_SHIFT) | data[RECORD_SIZE_LOW_BYTE_ORDER]) - -#define EXPECT_LESS_THAN_EQUAL(p1, p2) EXPECT_TRUE((p1) <= (p2)) - -static int destroy_server_keys(struct s2n_connection *server_conn) -{ - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); - - return S2N_SUCCESS; -} - -static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) -{ - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); - POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); - - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - uint8_t mac_key[] = "sample mac key"; - - uint8_t random_data[S2N_LARGE_RECORD_LENGTH + 1]; - struct s2n_blob r = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); - EXPECT_OK(s2n_get_public_random_data(&r)); - - uint8_t aes128_key[] = "123456789012345"; - struct s2n_blob aes128 = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); - - /* Test record sizes with s2n_record_write */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - /* Client and server are in sync */ - conn->server = conn->secure; - conn->client = conn->secure; - - /* test the AES128 cipher with a SHA1 hash */ - conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes128_sha; - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes128)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes128)); - EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); - EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); - conn->actual_protocol_version = S2N_TLS11; - - /* Test that different modes allows for different fragment/payload sizes. - * Record overheads (IV, HMAC, padding) do not count towards these size */ - const int small_payload = S2N_SMALL_FRAGMENT_LENGTH; - const int large_payload = S2N_LARGE_FRAGMENT_LENGTH; - const int medium_payload = S2N_DEFAULT_FRAGMENT_LENGTH; - struct s2n_blob fragment = r; - - /* Check the default: medium records */ - fragment.size = medium_payload; - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); - EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); - - /* Check explicitly small records */ - fragment.size = small_payload; - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); - EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); - - /* Check explicitly large records */ - fragment.size = large_payload; - EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); - EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); - - /* Clean up */ - conn->secure->cipher_suite->record_alg = &s2n_record_alg_null; /* restore mutated null cipher suite */ - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); - EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); - }; - - /* Test s2n_record_max_write_payload_size() have proper checks in place */ - { - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - /* we deal with the default null cipher suite for now, as it makes reasoning - * about easier s2n_record_max_write_payload_size(), as it incur 0 overheads */ - uint16_t size = 0; - server_conn->max_outgoing_fragment_length = ONE_BLOCK; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, ONE_BLOCK); - - /* Trigger an overlarge payload by setting a maximum uint16_t value to max fragment length */ - server_conn->max_outgoing_fragment_length = UINT16_MAX; - /* Check that we are bound by S2N_TLS_MAXIMUM_FRAGMENT_LENGTH */ - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* trigger a payload that is under the limits */ - server_conn->max_outgoing_fragment_length = 0; - EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_payload_size(server_conn, &size), S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - - /* Test boundary cases */ - - /* This is the theoretical maximum mfl allowed */ - server_conn->max_outgoing_fragment_length = S2N_TLS_MAXIMUM_FRAGMENT_LENGTH; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* MFL over limit is not allowed, but size is reduced to S2N_TLS_MAXIMUM_FRAGMENT_LENGTH*/ - server_conn->max_outgoing_fragment_length++; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* Test against different cipher suites */ - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->server->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - server_conn->max_outgoing_fragment_length = ONE_BLOCK; - EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, ONE_BLOCK); /* Verify size matches exactly specified max fragment length */ - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test s2n_record_max_write_payload_size with custom send buffer size */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Min buffer size */ - { - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); - }; - - /* Small buffer size */ - { - const uint32_t frag_len = 1000; - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, frag_len); - }; - - /* Buffer exactly fits one record */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - const uint32_t frag_len = conn->max_outgoing_fragment_length; - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, frag_len); - }; - - /* Buffer larger than one record */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - const uint32_t frag_len = conn->max_outgoing_fragment_length + 10; - const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint16_t size = 0; - EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); - EXPECT_EQUAL(size, conn->max_outgoing_fragment_length); - }; - }; - - /* Test s2n_record_min_write_payload_size() */ - { - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - uint16_t size = 0; - const int RECORD_SIZE_LESS_OVERHEADS = 1415; - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(RECORD_SIZE_LESS_OVERHEADS, size); - - const int MIN_SIZE = RECORD_SIZE_LESS_OVERHEADS + S2N_TLS_RECORD_HEADER_LENGTH; - - /* CBC */ - { - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - server_conn->actual_protocol_version = S2N_TLS11; - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_3des_sha; - uint8_t des3_key[] = "12345678901234567890123"; - struct s2n_blob des3 = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&des3, des3_key, sizeof(des3_key))); - server_conn->server = server_conn->secure; - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->server_key)); - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->client_key)); - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->secure->server_key, &des3)); - EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->secure->client_key, &des3)); - EXPECT_SUCCESS(s2n_hmac_init(&server_conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - r.size = size; - const int after_overheads = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % 8; /* rounded down to cbc block size (8) */ - const uint16_t PADDING_LENGTH_BYTE = 1; - const uint16_t RECORD_IV_SIZE = 8; - const uint16_t HMAC_DIGEST = 20; - EXPECT_EQUAL(size, after_overheads - HMAC_DIGEST - RECORD_IV_SIZE - PADDING_LENGTH_BYTE); - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - }; - - /* AEAD */ - { - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; - EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - r.size = size; - const uint16_t IV = 8; - const uint16_t TAG = 16; - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG); - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - }; - - /* TLS1.3 AEAD */ - { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_aes128_gcm; - EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - r.size = size; - const uint16_t IV = 0; - const uint16_t TAG = 16; - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG - S2N_TLS_CONTENT_TYPE_LENGTH); - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - }; - - /* chacha20 */ - if (s2n_chacha20_poly1305.is_available()) { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_chacha20_poly1305; - uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; - struct s2n_blob chacha20_poly1305_key = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); - - EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN); - r.size = size; - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - } - - /* TLS1.3 chacha20 */ - if (s2n_chacha20_poly1305.is_available()) { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_chacha20_poly1305; - uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; - struct s2n_blob chacha20_poly1305_key = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); - - EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN - S2N_TLS_CONTENT_TYPE_LENGTH); - r.size = size; - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - } - - /* composite */ - if (s2n_aes128_sha.is_available() && s2n_aes128_sha256.is_available()) { - EXPECT_SUCCESS(destroy_server_keys(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha_composite; - server_conn->actual_protocol_version = S2N_TLS11; - uint8_t mac_key_sha[20] = "server key shaserve"; - EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, &aes128)); - EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, &aes128)); - EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); - EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); - - EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); - const uint16_t COMPOSITE_BLOCK_SIZE = 16; - const uint16_t COMPOSITE_DIGEST_LENGTH = 20; - const uint16_t COMPOSITE_PADDING_LENGTH = 1; - const uint16_t size_aligned_to_block = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % COMPOSITE_BLOCK_SIZE - COMPOSITE_DIGEST_LENGTH - COMPOSITE_PADDING_LENGTH; - const uint16_t explicit_iv_len = 16; - const uint16_t size_after_overheads = size_aligned_to_block - explicit_iv_len; - EXPECT_EQUAL(size, size_after_overheads); - r.size = size; - - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); - const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); - EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); - EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); - } - - r.size = sizeof(random_data); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test large fragment/record sending for TLS 1.3 */ - { - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - server_conn->actual_protocol_version = S2N_TLS13; - server_conn->server->cipher_suite = cipher_suite; - - struct s2n_session_key *session_key = &server_conn->server->server_key; - uint8_t *implicit_iv = server_conn->server->server_implicit_iv; - - /* init record algorithm */ - EXPECT_OK(cipher_suite->record_alg->cipher->init(session_key)); - S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); - EXPECT_OK(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); - EXPECT_OK(cipher_suite->record_alg->cipher->set_decryption_key(session_key, &key)); - - S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); - - /* copy iv bytes from input data */ - for (size_t i = 0; i < iv.size; i++) { - implicit_iv[i] = iv.data[i]; - } - - /* Configure to use s2n maximum fragment / record settings */ - EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); - - /* Testing with a small blob */ - s2n_stack_blob(small_blob, ONE_BLOCK, ONE_BLOCK); - struct iovec small_io_vec = { 0 }; - small_io_vec.iov_base = small_blob.data; - small_io_vec.iov_len = small_blob.size; - - int bytes_taken = 0; - - const uint16_t TLS13_RECORD_OVERHEAD = 22; - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &small_io_vec, 1, 0, small_blob.size)); - EXPECT_EQUAL(bytes_taken, ONE_BLOCK); /* we wrote the full blob size */ - EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), ONE_BLOCK + TLS13_RECORD_OVERHEAD); /* bytes on the wire */ - - /* Check we get a friendly error if we use s2n_record_write again */ - EXPECT_ERROR_WITH_ERRNO(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - /* Testing a big 100k blob to be written */ - s2n_stack_blob(big_blob, ONE_HUNDRED_K, ONE_HUNDRED_K); - struct iovec big_io_vec = { 0 }; - big_io_vec.iov_base = big_blob.data; - big_io_vec.iov_len = big_blob.size; - - /* Test that s2n_record_writev() doesn't error on writing large payloads. - * Also asserts the bytes written on the wire. - */ - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); - - /* We verify that s2n_record_writev() is able to send the maximum fragment length as specified by TLS RFCs */ - const uint16_t TLS_MAX_FRAG_LEN = 16384; - EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); /* plaintext bytes taken */ - EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), TLS_MAX_FRAG_LEN + TLS13_RECORD_OVERHEAD); /* bytes sent on the wire */ - - /* These are invariant regardless of s2n implementation */ - EXPECT_TRUE(bytes_taken <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); /* Plaintext max size - 2^14 = 16384 */ - EXPECT_TRUE(bytes_taken <= (S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 255)); /* Max record size for TLS 1.3 - 2^14 + 255 = 16639 */ - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS_MAXIMUM_RECORD_LENGTH); - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS13_MAXIMUM_RECORD_LENGTH); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - /* Now escape the sandbox and attempt to get record_write to use a larger plaintext bytes */ - /* However, the max fragment length should still be bounded based on the protocol specification */ - const uint16_t MAX_FORCED_OUTGOING_FRAGMENT_LENGTH = 16400; - - server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; /* Trigger fragment length bounding */ - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); - EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - /* Force a generous 100k resize on the outgoing record stuffer */ - EXPECT_SUCCESS(s2n_stuffer_resize(&server_conn->out, ONE_HUNDRED_K)); - server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; - EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); - EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* s2n_record_max_write_size */ - { - uint16_t result = 0; - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(NULL, 1, &result), S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(conn, 1, NULL), S2N_ERR_NULL); - - conn->actual_protocol_version = 0; - conn->handshake.handshake_type = INITIAL; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); - - conn->handshake.handshake_type = NEGOTIATED; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS12_MAXIMUM_RECORD_LENGTH); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); - EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH); - - uint16_t diff = 10; - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH - diff, &result)); - EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH - diff); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +#define ONE_BLOCK 1024 +#define ONE_HUNDRED_K 100000 +#define RECORD_SIZE_HIGH_BYTE_ORDER 3 +#define RECORD_SIZE_LOW_BYTE_ORDER 4 +#define BYTE_SHIFT 8 +#define RECORD_SIZE(data) ((data[RECORD_SIZE_HIGH_BYTE_ORDER] << BYTE_SHIFT) | data[RECORD_SIZE_LOW_BYTE_ORDER]) + +#define EXPECT_LESS_THAN_EQUAL(p1, p2) EXPECT_TRUE((p1) <= (p2)) + +static int destroy_server_keys(struct s2n_connection *server_conn) +{ + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); + + return S2N_SUCCESS; +} + +static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) +{ + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); + POSIX_GUARD_RESULT(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t mac_key[] = "sample mac key"; + + uint8_t random_data[S2N_LARGE_RECORD_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + EXPECT_OK(s2n_get_public_random_data(&r)); + + uint8_t aes128_key[] = "123456789012345"; + struct s2n_blob aes128 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); + + /* Test record sizes with s2n_record_write */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Client and server are in sync */ + conn->server = conn->secure; + conn->client = conn->secure; + + /* test the AES128 cipher with a SHA1 hash */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes128_sha; + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes128)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes128)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + /* Test that different modes allows for different fragment/payload sizes. + * Record overheads (IV, HMAC, padding) do not count towards these size */ + const int small_payload = S2N_SMALL_FRAGMENT_LENGTH; + const int large_payload = S2N_LARGE_FRAGMENT_LENGTH; + const int medium_payload = S2N_DEFAULT_FRAGMENT_LENGTH; + struct s2n_blob fragment = r; + + /* Check the default: medium records */ + fragment.size = medium_payload; + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Check explicitly small records */ + fragment.size = small_payload; + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Check explicitly large records */ + fragment.size = large_payload; + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Clean up */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_null; /* restore mutated null cipher suite */ + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_OK(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + }; + + /* Test s2n_record_max_write_payload_size() have proper checks in place */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* we deal with the default null cipher suite for now, as it makes reasoning + * about easier s2n_record_max_write_payload_size(), as it incur 0 overheads */ + uint16_t size = 0; + server_conn->max_outgoing_fragment_length = ONE_BLOCK; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, ONE_BLOCK); + + /* Trigger an overlarge payload by setting a maximum uint16_t value to max fragment length */ + server_conn->max_outgoing_fragment_length = UINT16_MAX; + /* Check that we are bound by S2N_TLS_MAXIMUM_FRAGMENT_LENGTH */ + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* trigger a payload that is under the limits */ + server_conn->max_outgoing_fragment_length = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_payload_size(server_conn, &size), S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + + /* Test boundary cases */ + + /* This is the theoretical maximum mfl allowed */ + server_conn->max_outgoing_fragment_length = S2N_TLS_MAXIMUM_FRAGMENT_LENGTH; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* MFL over limit is not allowed, but size is reduced to S2N_TLS_MAXIMUM_FRAGMENT_LENGTH*/ + server_conn->max_outgoing_fragment_length++; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* Test against different cipher suites */ + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->max_outgoing_fragment_length = ONE_BLOCK; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, ONE_BLOCK); /* Verify size matches exactly specified max fragment length */ + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_record_max_write_payload_size with custom send buffer size */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Min buffer size */ + { + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); + }; + + /* Small buffer size */ + { + const uint32_t frag_len = 1000; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, frag_len); + }; + + /* Buffer exactly fits one record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + const uint32_t frag_len = conn->max_outgoing_fragment_length; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, frag_len); + }; + + /* Buffer larger than one record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + const uint32_t frag_len = conn->max_outgoing_fragment_length + 10; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, conn->max_outgoing_fragment_length); + }; + }; + + /* Test s2n_record_min_write_payload_size() */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + uint16_t size = 0; + const int RECORD_SIZE_LESS_OVERHEADS = 1415; + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(RECORD_SIZE_LESS_OVERHEADS, size); + + const int MIN_SIZE = RECORD_SIZE_LESS_OVERHEADS + S2N_TLS_RECORD_HEADER_LENGTH; + + /* CBC */ + { + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + server_conn->actual_protocol_version = S2N_TLS11; + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_3des_sha; + uint8_t des3_key[] = "12345678901234567890123"; + struct s2n_blob des3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&des3, des3_key, sizeof(des3_key))); + server_conn->server = server_conn->secure; + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->server_key)); + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->client_key)); + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->secure->server_key, &des3)); + EXPECT_OK(server_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->secure->client_key, &des3)); + EXPECT_SUCCESS(s2n_hmac_init(&server_conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const int after_overheads = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % 8; /* rounded down to cbc block size (8) */ + const uint16_t PADDING_LENGTH_BYTE = 1; + const uint16_t RECORD_IV_SIZE = 8; + const uint16_t HMAC_DIGEST = 20; + EXPECT_EQUAL(size, after_overheads - HMAC_DIGEST - RECORD_IV_SIZE - PADDING_LENGTH_BYTE); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* AEAD */ + { + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const uint16_t IV = 8; + const uint16_t TAG = 16; + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* TLS1.3 AEAD */ + { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const uint16_t IV = 0; + const uint16_t TAG = 16; + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG - S2N_TLS_CONTENT_TYPE_LENGTH); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* chacha20 */ + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_chacha20_poly1305; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + + EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + /* TLS1.3 chacha20 */ + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_chacha20_poly1305; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + + EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN - S2N_TLS_CONTENT_TYPE_LENGTH); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + /* composite */ + if (s2n_aes128_sha.is_available() && s2n_aes128_sha256.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha_composite; + server_conn->actual_protocol_version = S2N_TLS11; + uint8_t mac_key_sha[20] = "server key shaserve"; + EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, &aes128)); + EXPECT_OK(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, &aes128)); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + const uint16_t COMPOSITE_BLOCK_SIZE = 16; + const uint16_t COMPOSITE_DIGEST_LENGTH = 20; + const uint16_t COMPOSITE_PADDING_LENGTH = 1; + const uint16_t size_aligned_to_block = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % COMPOSITE_BLOCK_SIZE - COMPOSITE_DIGEST_LENGTH - COMPOSITE_PADDING_LENGTH; + const uint16_t explicit_iv_len = 16; + const uint16_t size_after_overheads = size_aligned_to_block - explicit_iv_len; + EXPECT_EQUAL(size, size_after_overheads); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + r.size = sizeof(random_data); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test large fragment/record sending for TLS 1.3 */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server->cipher_suite = cipher_suite; + + struct s2n_session_key *session_key = &server_conn->server->server_key; + uint8_t *implicit_iv = server_conn->server->server_implicit_iv; + + /* init record algorithm */ + EXPECT_OK(cipher_suite->record_alg->cipher->init(session_key)); + S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); + EXPECT_OK(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); + EXPECT_OK(cipher_suite->record_alg->cipher->set_decryption_key(session_key, &key)); + + S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); + + /* copy iv bytes from input data */ + for (size_t i = 0; i < iv.size; i++) { + implicit_iv[i] = iv.data[i]; + } + + /* Configure to use s2n maximum fragment / record settings */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + /* Testing with a small blob */ + s2n_stack_blob(small_blob, ONE_BLOCK, ONE_BLOCK); + struct iovec small_io_vec = { 0 }; + small_io_vec.iov_base = small_blob.data; + small_io_vec.iov_len = small_blob.size; + + int bytes_taken = 0; + + const uint16_t TLS13_RECORD_OVERHEAD = 22; + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &small_io_vec, 1, 0, small_blob.size)); + EXPECT_EQUAL(bytes_taken, ONE_BLOCK); /* we wrote the full blob size */ + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), ONE_BLOCK + TLS13_RECORD_OVERHEAD); /* bytes on the wire */ + + /* Check we get a friendly error if we use s2n_record_write again */ + EXPECT_ERROR_WITH_ERRNO(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Testing a big 100k blob to be written */ + s2n_stack_blob(big_blob, ONE_HUNDRED_K, ONE_HUNDRED_K); + struct iovec big_io_vec = { 0 }; + big_io_vec.iov_base = big_blob.data; + big_io_vec.iov_len = big_blob.size; + + /* Test that s2n_record_writev() doesn't error on writing large payloads. + * Also asserts the bytes written on the wire. + */ + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + + /* We verify that s2n_record_writev() is able to send the maximum fragment length as specified by TLS RFCs */ + const uint16_t TLS_MAX_FRAG_LEN = 16384; + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); /* plaintext bytes taken */ + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), TLS_MAX_FRAG_LEN + TLS13_RECORD_OVERHEAD); /* bytes sent on the wire */ + + /* These are invariant regardless of s2n implementation */ + EXPECT_TRUE(bytes_taken <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); /* Plaintext max size - 2^14 = 16384 */ + EXPECT_TRUE(bytes_taken <= (S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 255)); /* Max record size for TLS 1.3 - 2^14 + 255 = 16639 */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS_MAXIMUM_RECORD_LENGTH); + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS13_MAXIMUM_RECORD_LENGTH); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Now escape the sandbox and attempt to get record_write to use a larger plaintext bytes */ + /* However, the max fragment length should still be bounded based on the protocol specification */ + const uint16_t MAX_FORCED_OUTGOING_FRAGMENT_LENGTH = 16400; + + server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; /* Trigger fragment length bounding */ + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Force a generous 100k resize on the outgoing record stuffer */ + EXPECT_SUCCESS(s2n_stuffer_resize(&server_conn->out, ONE_HUNDRED_K)); + server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* s2n_record_max_write_size */ + { + uint16_t result = 0; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(NULL, 1, &result), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(conn, 1, NULL), S2N_ERR_NULL); + + conn->actual_protocol_version = 0; + conn->handshake.handshake_type = INITIAL; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); + + conn->handshake.handshake_type = NEGOTIATED; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS12_MAXIMUM_RECORD_LENGTH); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH); + + uint16_t diff = 10; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH - diff, &result)); + EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH - diff); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_recv_test.c b/tests/unit/s2n_recv_test.c index 3e4725713a1..3585355e760 100644 --- a/tests/unit/s2n_recv_test.c +++ b/tests/unit/s2n_recv_test.c @@ -1,671 +1,671 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/unstable/renegotiate.h" -#include "s2n_test.h" -#include "testlib/s2n_ktls_test_utils.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_random.h" - -bool s2n_custom_recv_fn_called = false; - -int s2n_expect_concurrent_error_recv_fn(void *io_context, uint8_t *buf, uint32_t len) -{ - struct s2n_connection *conn = (struct s2n_connection *) io_context; - s2n_custom_recv_fn_called = true; - - s2n_blocked_status blocked = 0; - ssize_t result = s2n_recv(conn, buf, len, &blocked); - EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); - return result; -} - -static ssize_t s2n_test_ktls_recvmsg_cb(void *io_context, struct msghdr *msg) -{ - POSIX_ENSURE_REF(io_context); - return *(ssize_t *) io_context; -} - -static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, - s2n_renegotiate_response *response) -{ - POSIX_ENSURE_REF(context); - size_t *count = (size_t *) context; - (*count)++; - *response = S2N_RENEGOTIATE_IGNORE; - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* s2n_peek */ - { - /* We do full handshakes and send with a real connection here instead of - * just calling s2n_connection_set_secrets because s2n_peek depends on details - * of how data is encrypted, and we don't want to make any incorrect assumptions. - */ - - /* Safety check */ - EXPECT_EQUAL(s2n_peek(NULL), 0); - - const uint8_t test_data[100] = "hello world"; - const size_t test_data_size = sizeof(test_data); - - /* s2n_peek reports available plaintext bytes */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Write some data */ - EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - /* Initially, no data reported as available */ - EXPECT_EQUAL(s2n_peek(server_conn), 0); - - /* Read some, but not all, of the data written */ - uint8_t output[sizeof(test_data)] = { 0 }; - const size_t expected_peek_size = 10; - const size_t recv_size = test_data_size - expected_peek_size; - EXPECT_EQUAL(s2n_recv(server_conn, output, recv_size, &blocked), recv_size); - - /* After a partial read, some data reported as available */ - EXPECT_EQUAL(s2n_peek(server_conn), expected_peek_size); - - /* Read the rest of the data */ - EXPECT_EQUAL(s2n_recv(server_conn, output, expected_peek_size, &blocked), expected_peek_size); - - /* After the complete read, no data reported as available */ - EXPECT_EQUAL(s2n_peek(server_conn), 0); - }; - - /* s2n_peek doesn't report bytes belonging to partially read, still encrypted records */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Use stuffers for IO so that we can trigger a block on a read */ - DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Write some data */ - EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - /* Drop some of the data */ - EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); - - /* Try to read the data, but block */ - uint8_t output[sizeof(test_data)] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(test_data), &blocked), - S2N_ERR_IO_BLOCKED); - - /* conn->in contains data, but s2n_peek reports no data available */ - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); - EXPECT_EQUAL(s2n_peek(server_conn), 0); - }; - - /* s2n_peek doesn't report bytes belonging to post-handshake messages */ - if (s2n_is_tls13_fully_supported()) { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Use stuffers for IO so that we can trigger a block on a read */ - DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Send a KeyUpdate message */ - s2n_atomic_flag_set(&client_conn->key_update_pending); - EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); - EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending)); - - /* Drop some of the data */ - EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); - - /* Try to read the KeyUpdate message, but block */ - uint8_t output[1] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(output), &blocked), - S2N_ERR_IO_BLOCKED); - - /* conn->in contains data, but s2n_peek reports no data available */ - EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); - EXPECT_EQUAL(s2n_peek(server_conn), 0); - }; - }; - - /* s2n_recv cannot be called concurrently */ - { - /* Setup connection */ - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - /* Setup bad recv callback */ - EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_expect_concurrent_error_recv_fn)); - EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, (void *) conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - - uint8_t test_data[100] = { 0 }; - s2n_blocked_status blocked = 0; - s2n_custom_recv_fn_called = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, test_data, sizeof(test_data), &blocked), - S2N_ERR_IO); - EXPECT_TRUE(s2n_custom_recv_fn_called); - - /* Cleanup */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* s2n_config_set_recv_multi_record */ - { - const uint8_t test_data_size = 100; - DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); - - const size_t recv_size = test_data_size * 2; - DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&output, recv_size)); - - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Write some data, in three records */ - for (size_t i = 0; i < 3; i++) { - EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); - } - - /* Disable multi-record recv, set legacy behavior */ - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, false)); - - EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), test_data_size); - - /* Now enable multi record recv */ - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, true)); - - /* So we should be able to read the remaining two records in a single call */ - EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), recv_size); - } - } - - /* recv blocked status - * - * This test preserves the `blocked` parameter contract with various states of the connection - */ - { - const uint8_t test_data_size = 100; - const size_t record_count = 3; - DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); - - const size_t total_data_size = test_data_size * record_count; - DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&output, total_data_size)); - - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - for (size_t multi_record = 0; multi_record <= 1; multi_record++) { - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, multi_record)); - size_t max_recv_size = test_data_size; - - /* In multi-record, we can read all of the records in one go */ - if (multi_record) { - max_recv_size *= record_count; - } - - for (size_t read_size = 1; read_size <= total_data_size; read_size++) { - /* Write some data across multiple records */ - for (size_t send_count = 0; send_count < record_count; send_count++) { - EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); - } - - /* Call `s2n_recv` multiple times with an empty buffer to make sure that's handled correctly */ - for (size_t empty_count = 0; empty_count < 10; empty_count++) { - EXPECT_EQUAL(s2n_recv(server_conn, output.data, 0, &blocked), 0); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - } - - size_t recv_bytes = 0; - while (recv_bytes < total_data_size) { - size_t expected_recv_size = S2N_MIN(S2N_MIN(read_size, total_data_size - recv_bytes), max_recv_size); - - /* Perform the actual recv call */ - ssize_t actual_recv_size = s2n_recv(server_conn, output.data, read_size, &blocked); - - if (multi_record) { - /* In multi-record mode we should always read the size we expect */ - EXPECT_EQUAL(actual_recv_size, expected_recv_size); - } else { - /* In single-record mode, we could potentially get a smaller read than a full record due to - * random record boundaries so we can only assert it's within the range we expect. */ - EXPECT_NOT_EQUAL(actual_recv_size, 0); - EXPECT_TRUE(actual_recv_size <= expected_recv_size); - } - - /* Keep track of the total amount of bytes read */ - recv_bytes += actual_recv_size; - - /* Due to the history of this API, some applications depend on the blocked status to know if - * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. - * - * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing - * without conflating being blocked on reading from the OS socket vs blocked on the application's - * buffer size. - */ - if (s2n_peek(server_conn) == 0) { - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - } else { - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - } - } - - /* The final read should return blocked since we don't have any more data from the socket */ - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output.data, read_size, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - } - } - - EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client_conn, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Call `s2n_recv` multiple times at the end of the stream after receiving a shutdown */ - for (size_t eos_count = 0; eos_count < 10; eos_count++) { - EXPECT_EQUAL(s2n_recv(server_conn, output.data, output.size, &blocked), 0); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - } - - EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - }; - - /* Test with ktls */ - { - uint8_t test_data[100] = { 0 }; - struct s2n_blob test_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); - EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); - - const struct iovec test_iovec = { - .iov_base = test_data, - .iov_len = sizeof(test_data), - }; - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - /* Test: receive all requested application data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &test_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(test_data)); - - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, written); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, read); - }; - - /* Test: receive partial application data */ - { - const size_t partial_size = sizeof(test_data) / 2; - struct iovec partial_iovec = test_iovec; - partial_iovec.iov_len = partial_size; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &partial_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, partial_size); - - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, written); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, read); - }; - - /* Test: drain buffered application data */ - { - const size_t partial_size = sizeof(test_data) / 2; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &test_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(test_data)); - - /* The first read doesn't read all the available data */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, partial_size, &blocked); - EXPECT_EQUAL(read, partial_size); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); - EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); - - /* The second read drains the remaining data */ - const size_t remaining = sizeof(test_data) - partial_size; - read = s2n_recv(conn, output + read, remaining, &blocked); - EXPECT_EQUAL(read, remaining); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); - EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); - }; - - /* Test: receive blocks */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - }; - - /* Test: receive indicates end-of-data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - ssize_t ret_val = 0; - EXPECT_OK(s2n_ktls_set_recvmsg_cb(conn, s2n_test_ktls_recvmsg_cb, &ret_val)); - - uint8_t output[10] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Error fatal but not blinded */ - EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); - EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); - }; - - /* Test: receive alert */ - { - /* Use a specific alert -- if we just use random data, we might - * stumble into a close_notify or user_canceled. - */ - uint8_t alert_data[] = { - S2N_TLS_ALERT_LEVEL_FATAL, - S2N_TLS_ALERT_DECRYPT_ERROR, - }; - const struct iovec alert_iovec = { - .iov_base = alert_data, - .iov_len = sizeof(alert_data), - }; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &alert_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(alert_data)); - - uint8_t output[10] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_ALERT); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - - /* Error fatal but not blinded */ - EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); - EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); - }; - - /* Test: receive handshake message */ - { - DEFER_CLEANUP(struct s2n_config *reneg_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(reneg_config); - - size_t reneg_request_count = 0; - EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(reneg_config, - s2n_test_reneg_req_cb, &reneg_request_count)); - - uint8_t hello_request[TLS_HANDSHAKE_HEADER_LENGTH] = { TLS_HELLO_REQUEST }; - const struct iovec hello_request_iovec = { - .iov_base = hello_request, - .iov_len = sizeof(hello_request), - }; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, reneg_config)); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - conn->secure_renegotiation = true; - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - size_t written = 0; - - /* Send the handshake message */ - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_HANDSHAKE, - &hello_request_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(hello_request)); - - /* Also send some application data */ - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &test_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, sizeof(test_data)); - - /* Verify that we received the application data */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, read); - - /* Verify that we received and processed the handshake message */ - EXPECT_EQUAL(reneg_request_count, 1); - }; - - /* Test: Multirecord mode */ - { - DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(multi_config); - EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true)); - - /* Test: receive all requested application data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - /* Write a lot of very small records */ - struct iovec offset_iovec = { 0 }; - for (size_t offset = 0; offset < sizeof(test_data); offset++) { - offset_iovec.iov_base = test_data + offset; - offset_iovec.iov_len = 1; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &offset_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, 1); - } - - /* Receive all the data from the many small records */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); - }; - - /* Test: receive partial application data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); - s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); - - DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, - s2n_ktls_io_stuffer_pair_free); - EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); - struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; - - /* Write a lot of very small records, but don't write the full - * expected test data size. */ - const size_t partial_size = sizeof(test_data) / 2; - struct iovec offset_iovec = { 0 }; - for (size_t offset = 0; offset < partial_size; offset++) { - offset_iovec.iov_base = test_data + offset; - offset_iovec.iov_len = 1; - - size_t written = 0; - EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, - &offset_iovec, 1, &blocked, &written)); - EXPECT_EQUAL(written, 1); - } - - /* Receive the partial data */ - uint8_t output[sizeof(test_data)] = { 0 }; - int read = s2n_recv(conn, output, sizeof(output), &blocked); - EXPECT_EQUAL(read, partial_size); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); - }; - }; - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/unstable/renegotiate.h" +#include "s2n_test.h" +#include "testlib/s2n_ktls_test_utils.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +bool s2n_custom_recv_fn_called = false; + +int s2n_expect_concurrent_error_recv_fn(void *io_context, uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_recv_fn_called = true; + + s2n_blocked_status blocked = 0; + ssize_t result = s2n_recv(conn, buf, len, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +static ssize_t s2n_test_ktls_recvmsg_cb(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + return *(ssize_t *) io_context; +} + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, + s2n_renegotiate_response *response) +{ + POSIX_ENSURE_REF(context); + size_t *count = (size_t *) context; + (*count)++; + *response = S2N_RENEGOTIATE_IGNORE; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* s2n_peek */ + { + /* We do full handshakes and send with a real connection here instead of + * just calling s2n_connection_set_secrets because s2n_peek depends on details + * of how data is encrypted, and we don't want to make any incorrect assumptions. + */ + + /* Safety check */ + EXPECT_EQUAL(s2n_peek(NULL), 0); + + const uint8_t test_data[100] = "hello world"; + const size_t test_data_size = sizeof(test_data); + + /* s2n_peek reports available plaintext bytes */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data */ + EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Initially, no data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), 0); + + /* Read some, but not all, of the data written */ + uint8_t output[sizeof(test_data)] = { 0 }; + const size_t expected_peek_size = 10; + const size_t recv_size = test_data_size - expected_peek_size; + EXPECT_EQUAL(s2n_recv(server_conn, output, recv_size, &blocked), recv_size); + + /* After a partial read, some data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), expected_peek_size); + + /* Read the rest of the data */ + EXPECT_EQUAL(s2n_recv(server_conn, output, expected_peek_size, &blocked), expected_peek_size); + + /* After the complete read, no data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + + /* s2n_peek doesn't report bytes belonging to partially read, still encrypted records */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Use stuffers for IO so that we can trigger a block on a read */ + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data */ + EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Drop some of the data */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); + + /* Try to read the data, but block */ + uint8_t output[sizeof(test_data)] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(test_data), &blocked), + S2N_ERR_IO_BLOCKED); + + /* conn->in contains data, but s2n_peek reports no data available */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + + /* s2n_peek doesn't report bytes belonging to post-handshake messages */ + if (s2n_is_tls13_fully_supported()) { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Use stuffers for IO so that we can trigger a block on a read */ + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Send a KeyUpdate message */ + s2n_atomic_flag_set(&client_conn->key_update_pending); + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending)); + + /* Drop some of the data */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); + + /* Try to read the KeyUpdate message, but block */ + uint8_t output[1] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(output), &blocked), + S2N_ERR_IO_BLOCKED); + + /* conn->in contains data, but s2n_peek reports no data available */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + }; + + /* s2n_recv cannot be called concurrently */ + { + /* Setup connection */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Setup bad recv callback */ + EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_expect_concurrent_error_recv_fn)); + EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, (void *) conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + uint8_t test_data[100] = { 0 }; + s2n_blocked_status blocked = 0; + s2n_custom_recv_fn_called = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_recv_fn_called); + + /* Cleanup */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_config_set_recv_multi_record */ + { + const uint8_t test_data_size = 100; + DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); + + const size_t recv_size = test_data_size * 2; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&output, recv_size)); + + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data, in three records */ + for (size_t i = 0; i < 3; i++) { + EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); + } + + /* Disable multi-record recv, set legacy behavior */ + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, false)); + + EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), test_data_size); + + /* Now enable multi record recv */ + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, true)); + + /* So we should be able to read the remaining two records in a single call */ + EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), recv_size); + } + } + + /* recv blocked status + * + * This test preserves the `blocked` parameter contract with various states of the connection + */ + { + const uint8_t test_data_size = 100; + const size_t record_count = 3; + DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); + + const size_t total_data_size = test_data_size * record_count; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&output, total_data_size)); + + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + for (size_t multi_record = 0; multi_record <= 1; multi_record++) { + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, multi_record)); + size_t max_recv_size = test_data_size; + + /* In multi-record, we can read all of the records in one go */ + if (multi_record) { + max_recv_size *= record_count; + } + + for (size_t read_size = 1; read_size <= total_data_size; read_size++) { + /* Write some data across multiple records */ + for (size_t send_count = 0; send_count < record_count; send_count++) { + EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); + } + + /* Call `s2n_recv` multiple times with an empty buffer to make sure that's handled correctly */ + for (size_t empty_count = 0; empty_count < 10; empty_count++) { + EXPECT_EQUAL(s2n_recv(server_conn, output.data, 0, &blocked), 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + + size_t recv_bytes = 0; + while (recv_bytes < total_data_size) { + size_t expected_recv_size = S2N_MIN(S2N_MIN(read_size, total_data_size - recv_bytes), max_recv_size); + + /* Perform the actual recv call */ + ssize_t actual_recv_size = s2n_recv(server_conn, output.data, read_size, &blocked); + + if (multi_record) { + /* In multi-record mode we should always read the size we expect */ + EXPECT_EQUAL(actual_recv_size, expected_recv_size); + } else { + /* In single-record mode, we could potentially get a smaller read than a full record due to + * random record boundaries so we can only assert it's within the range we expect. */ + EXPECT_NOT_EQUAL(actual_recv_size, 0); + EXPECT_TRUE(actual_recv_size <= expected_recv_size); + } + + /* Keep track of the total amount of bytes read */ + recv_bytes += actual_recv_size; + + /* Due to the history of this API, some applications depend on the blocked status to know if + * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. + * + * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing + * without conflating being blocked on reading from the OS socket vs blocked on the application's + * buffer size. + */ + if (s2n_peek(server_conn) == 0) { + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } else { + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + } + + /* The final read should return blocked since we don't have any more data from the socket */ + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output.data, read_size, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Call `s2n_recv` multiple times at the end of the stream after receiving a shutdown */ + for (size_t eos_count = 0; eos_count < 10; eos_count++) { + EXPECT_EQUAL(s2n_recv(server_conn, output.data, output.size, &blocked), 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + }; + + /* Test with ktls */ + { + uint8_t test_data[100] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: receive all requested application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + }; + + /* Test: receive partial application data */ + { + const size_t partial_size = sizeof(test_data) / 2; + struct iovec partial_iovec = test_iovec; + partial_iovec.iov_len = partial_size; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &partial_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, partial_size); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + }; + + /* Test: drain buffered application data */ + { + const size_t partial_size = sizeof(test_data) / 2; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + /* The first read doesn't read all the available data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, partial_size, &blocked); + EXPECT_EQUAL(read, partial_size); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); + EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); + + /* The second read drains the remaining data */ + const size_t remaining = sizeof(test_data) - partial_size; + read = s2n_recv(conn, output + read, remaining, &blocked); + EXPECT_EQUAL(read, remaining); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); + EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); + }; + + /* Test: receive blocks */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + }; + + /* Test: receive indicates end-of-data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + ssize_t ret_val = 0; + EXPECT_OK(s2n_ktls_set_recvmsg_cb(conn, s2n_test_ktls_recvmsg_cb, &ret_val)); + + uint8_t output[10] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + }; + + /* Test: receive alert */ + { + /* Use a specific alert -- if we just use random data, we might + * stumble into a close_notify or user_canceled. + */ + uint8_t alert_data[] = { + S2N_TLS_ALERT_LEVEL_FATAL, + S2N_TLS_ALERT_DECRYPT_ERROR, + }; + const struct iovec alert_iovec = { + .iov_base = alert_data, + .iov_len = sizeof(alert_data), + }; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &alert_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(alert_data)); + + uint8_t output[10] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_ALERT); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + }; + + /* Test: receive handshake message */ + { + DEFER_CLEANUP(struct s2n_config *reneg_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(reneg_config); + + size_t reneg_request_count = 0; + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(reneg_config, + s2n_test_reneg_req_cb, &reneg_request_count)); + + uint8_t hello_request[TLS_HANDSHAKE_HEADER_LENGTH] = { TLS_HELLO_REQUEST }; + const struct iovec hello_request_iovec = { + .iov_base = hello_request, + .iov_len = sizeof(hello_request), + }; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, reneg_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + + /* Send the handshake message */ + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_HANDSHAKE, + &hello_request_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(hello_request)); + + /* Also send some application data */ + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + /* Verify that we received the application data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + + /* Verify that we received and processed the handshake message */ + EXPECT_EQUAL(reneg_request_count, 1); + }; + + /* Test: Multirecord mode */ + { + DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(multi_config); + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true)); + + /* Test: receive all requested application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write a lot of very small records */ + struct iovec offset_iovec = { 0 }; + for (size_t offset = 0; offset < sizeof(test_data); offset++) { + offset_iovec.iov_base = test_data + offset; + offset_iovec.iov_len = 1; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, 1); + } + + /* Receive all the data from the many small records */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); + }; + + /* Test: receive partial application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write a lot of very small records, but don't write the full + * expected test data size. */ + const size_t partial_size = sizeof(test_data) / 2; + struct iovec offset_iovec = { 0 }; + for (size_t offset = 0; offset < partial_size; offset++) { + offset_iovec.iov_base = test_data + offset; + offset_iovec.iov_len = 1; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, 1); + } + + /* Receive the partial data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, partial_size); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); + }; + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_release_non_empty_buffers_test.c b/tests/unit/s2n_release_non_empty_buffers_test.c index 9843c5c5d81..d13d0d4b381 100644 --- a/tests/unit/s2n_release_non_empty_buffers_test.c +++ b/tests/unit/s2n_release_non_empty_buffers_test.c @@ -1,208 +1,208 @@ -#include "utils/s2n_prelude.h" -/* - * 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 -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_random.h" - -#define MAX_BUF_SIZE 10000 - -static const uint8_t buf_to_send[1023] = { 27 }; - -int mock_client(struct s2n_test_io_pair *io_pair) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - - conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - s2n_connection_set_config(conn, client_config); - - /* Unlike the server, the client just passes ownership of I/O to s2n */ - s2n_connection_set_io_pair(conn, io_pair); - - result = s2n_negotiate(conn, &blocked); - if (result < 0) { - _exit(1); - } - - if (s2n_send(conn, buf_to_send, sizeof(buf_to_send), &blocked) != sizeof(buf_to_send)) { - _exit(2); - } - - s2n_shutdown(conn, &blocked); - s2n_connection_free(conn); - s2n_config_free(client_config); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_CLIENT)); - s2n_cleanup(); - - exit(0); -} - -/** - * This test ensures that we don't allow releasing connection buffers if they contain part - * of the unprocessed record, avoiding connection corruption. - */ -int main(int argc, char **argv) -{ - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - uint8_t buf[sizeof(buf_to_send)]; - uint32_t n = 0; - ssize_t ret = 0; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Create a pipe */ - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Run the client */ - mock_client(&io_pair); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Make pipes non-blocking */ - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); - - /* Negotiate the handshake. */ - do { - ret = s2n_negotiate(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - - /* check to see if we need to copy more over from the pipes to the buffers - * to continue the handshake */ - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (blocked); - - /* Receive only 100 bytes of the record and try to call s2n_recv */ - while (n < 100) { - ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, 100 - n, &n); - - if (errno == EAGAIN || errno == EWOULDBLOCK) { - continue; - } else { - POSIX_GUARD(ret); - } - } - - /* s2n_recv should fail as we received only part of the record */ - EXPECT_FAILURE(s2n_recv(conn, buf, sizeof(buf), &blocked)); - EXPECT_TRUE(blocked == S2N_BLOCKED_ON_READ); - - /* Now try to release the buffers and expect failure as buffers are not empty */ - EXPECT_FAILURE(s2n_connection_release_buffers(conn)); - - /* Read the rest of the buffer and expect s2n_recv to succeed */ - do { - ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - - if (errno == EAGAIN || errno == EWOULDBLOCK) { - continue; - } else { - POSIX_GUARD(ret); - } - - ret = s2n_recv(conn, buf, sizeof(buf), &blocked); - } while (ret < 0 && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED - && blocked == S2N_BLOCKED_ON_READ); - - /* Expect that we read the data client sent us */ - EXPECT_TRUE(ret == sizeof(buf_to_send)); - EXPECT_TRUE(memcmp(buf, buf_to_send, ret) == 0); - - /* Since full record was processed, we should be able to release buffers */ - EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); - - /* Shutdown after negotiating */ - uint8_t server_shutdown = 0; - do { - ret = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - if (ret == 0) { - server_shutdown = 1; - } - - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (!server_shutdown); - - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - - /* Clean up */ - free(cert_chain_pem); - free(private_key_pem); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - s2n_cleanup(); - - END_TEST(); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_random.h" + +#define MAX_BUF_SIZE 10000 + +static const uint8_t buf_to_send[1023] = { 27 }; + +int mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + + conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + s2n_connection_set_config(conn, client_config); + + /* Unlike the server, the client just passes ownership of I/O to s2n */ + s2n_connection_set_io_pair(conn, io_pair); + + result = s2n_negotiate(conn, &blocked); + if (result < 0) { + _exit(1); + } + + if (s2n_send(conn, buf_to_send, sizeof(buf_to_send), &blocked) != sizeof(buf_to_send)) { + _exit(2); + } + + s2n_shutdown(conn, &blocked); + s2n_connection_free(conn); + s2n_config_free(client_config); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_CLIENT)); + s2n_cleanup(); + + exit(0); +} + +/** + * This test ensures that we don't allow releasing connection buffers if they contain part + * of the unprocessed record, avoiding connection corruption. + */ +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + uint8_t buf[sizeof(buf_to_send)]; + uint32_t n = 0; + ssize_t ret = 0; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Create a pipe */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + mock_client(&io_pair); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Make pipes non-blocking */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); + + /* Negotiate the handshake. */ + do { + ret = s2n_negotiate(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + + /* check to see if we need to copy more over from the pipes to the buffers + * to continue the handshake */ + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (blocked); + + /* Receive only 100 bytes of the record and try to call s2n_recv */ + while (n < 100) { + ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, 100 - n, &n); + + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + POSIX_GUARD(ret); + } + } + + /* s2n_recv should fail as we received only part of the record */ + EXPECT_FAILURE(s2n_recv(conn, buf, sizeof(buf), &blocked)); + EXPECT_TRUE(blocked == S2N_BLOCKED_ON_READ); + + /* Now try to release the buffers and expect failure as buffers are not empty */ + EXPECT_FAILURE(s2n_connection_release_buffers(conn)); + + /* Read the rest of the buffer and expect s2n_recv to succeed */ + do { + ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + POSIX_GUARD(ret); + } + + ret = s2n_recv(conn, buf, sizeof(buf), &blocked); + } while (ret < 0 && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED + && blocked == S2N_BLOCKED_ON_READ); + + /* Expect that we read the data client sent us */ + EXPECT_TRUE(ret == sizeof(buf_to_send)); + EXPECT_TRUE(memcmp(buf, buf_to_send, ret) == 0); + + /* Since full record was processed, we should be able to release buffers */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + + /* Shutdown after negotiating */ + uint8_t server_shutdown = 0; + do { + ret = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + if (ret == 0) { + server_shutdown = 1; + } + + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (!server_shutdown); + + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Clean up */ + free(cert_chain_pem); + free(private_key_pem); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + s2n_cleanup(); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_renegotiate_test.c b/tests/unit/s2n_renegotiate_test.c index 5d5afa64e08..1fa07e9168e 100644 --- a/tests/unit/s2n_renegotiate_test.c +++ b/tests/unit/s2n_renegotiate_test.c @@ -1,621 +1,621 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_renegotiate.h" - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" - -struct s2n_reneg_test_case { - uint8_t protocol_version; - struct s2n_cipher_suite *cipher_suite; - uint8_t max_frag_code; -}; - -const struct s2n_reneg_test_case dhe_test_cases[] = { - { - .protocol_version = S2N_SSLv3, - .cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha, - .max_frag_code = 0, - }, - { - .protocol_version = S2N_TLS10, - .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_512, - }, - { - .protocol_version = S2N_TLS11, - .cipher_suite = &s2n_dhe_rsa_with_aes_256_cbc_sha, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_1024, - }, - { - .protocol_version = S2N_TLS12, - .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha256, - .max_frag_code = 0, - }, - { - .protocol_version = S2N_TLS12, - .cipher_suite = &s2n_dhe_rsa_with_aes_256_gcm_sha384, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_2048, - }, - { - .protocol_version = S2N_TLS12, - .cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256, - .max_frag_code = S2N_TLS_MAX_FRAG_LEN_4096, - }, -}; - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - char dh_params[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dh_params, sizeof(dh_params))); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - uint8_t app_data[] = "smaller hello world"; - uint8_t large_app_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = "hello world and a lot of zeroes"; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - /* Test s2n_renegotiate_wipe */ - { - /* Default IO unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* "io_pair" just uses file descriptors and the default io callbacks */ - 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_EQUAL(client_conn->send, s2n_socket_write); - EXPECT_TRUE(client_conn->managed_send_io); - EXPECT_EQUAL(client_conn->recv, s2n_socket_read); - EXPECT_TRUE(client_conn->managed_recv_io); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - }; - - /* Custom IO callbacks unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* "io_stuffers" use custom IO callbacks written for tests */ - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - EXPECT_NOT_EQUAL(client_conn->send, s2n_socket_write); - EXPECT_FALSE(client_conn->managed_send_io); - EXPECT_NOT_EQUAL(client_conn->recv, s2n_socket_read); - EXPECT_FALSE(client_conn->managed_recv_io); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - }; - - /* Fragment size unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(client_conn)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t original_out_size = s2n_stuffer_data_available(&out); - EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t wiped_out_size = s2n_stuffer_data_available(&out); - EXPECT_EQUAL(original_out_size, wiped_out_size); - }; - - /* Forced very small fragment size unaffected by wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - const size_t small_frag_len = S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE; - client_conn->max_outgoing_fragment_length = small_frag_len; - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t original_out_size = s2n_stuffer_data_available(&out); - EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, small_frag_len); - - EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - size_t wiped_out_size = s2n_stuffer_data_available(&out); - EXPECT_EQUAL(original_out_size, wiped_out_size); - }; - - /* Handshake succeeds after wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - }; - - /* Handshake with added client auth succeeds after wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_NONE)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(client_conn)); - EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(server_conn)); - }; - - /* Handshake with different fragment length succeeds after wipe */ - { - DEFER_CLEANUP(struct s2n_config *small_frag_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(small_frag_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(small_frag_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(small_frag_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(small_frag_config, "20240501")); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(small_frag_config)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(small_frag_config, S2N_TLS_MAX_FRAG_LEN_512)); - - DEFER_CLEANUP(struct s2n_config *larger_frag_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(larger_frag_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(larger_frag_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(larger_frag_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(larger_frag_config, "20240501")); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(larger_frag_config)); - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(larger_frag_config, S2N_TLS_MAX_FRAG_LEN_4096)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, small_frag_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, small_frag_config)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - }; - - /* renegotiation_info is non-empty after wipe */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the renegotiation_info was empty / missing */ - ssize_t renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, - S2N_EXTENSION_RENEGOTIATION_INFO); - EXPECT_EQUAL(renegotiation_info_len, 0); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - EXPECT_TRUE(client_conn->handshake.finished_len > 0); - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the renegotiation_info was not empty / missing */ - renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, - S2N_EXTENSION_RENEGOTIATION_INFO); - EXPECT_TRUE(renegotiation_info_len > sizeof(uint8_t)); - }; - - /* Wipe of insecure connection not allowed */ - { - 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, config)); - client_conn->actual_protocol_version = S2N_TLS12; - - EXPECT_FALSE(client_conn->secure_renegotiation); - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_NO_RENEGOTIATION); - client_conn->secure_renegotiation = true; - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe of TLS1.3 connection not allowed */ - { - 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, config)); - client_conn->secure_renegotiation = true; - - EXPECT_TRUE(client_conn->actual_protocol_version > S2N_TLS12); - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - client_conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe mid-write not allowed */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Initiate a partial send */ - uint16_t partial_send_len = client_conn->max_outgoing_fragment_length / 2; - DEFER_CLEANUP(struct s2n_stuffer small_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_alloc(&small_out, partial_send_len)); - EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&small_out, client_conn)); - EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); - - /* Finish the send */ - EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, client_conn)); - EXPECT_EQUAL(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), sizeof(large_app_data)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe mid-read not allowed */ - { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Initiate a partial recv */ - uint16_t partial_recv_len = sizeof(app_data) / 2; - uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, partial_recv_len, &blocked), partial_recv_len); - EXPECT_BYTEARRAY_EQUAL(app_data, recv_buffer, partial_recv_len); - EXPECT_TRUE(s2n_peek(client_conn) > 0); - - EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); - - /* Finish the recv */ - size_t remaining_recv_len = sizeof(app_data) - partial_recv_len; - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, remaining_recv_len, &blocked), remaining_recv_len); - EXPECT_BYTEARRAY_EQUAL(app_data + partial_recv_len, recv_buffer, remaining_recv_len); - EXPECT_EQUAL(s2n_peek(client_conn), 0); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - }; - - /* Wipe with next record buffered allowed, and data preserved */ - { - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* Write two records, but only receive one. - * Due to recv buffering, the second record will be read and buffered - * at the same time as the first record, but not processed yet. - */ - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_peek(client), 0); - EXPECT_TRUE(s2n_stuffer_data_available(&client->buffer_in)); - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client)); - - /* The second record is still available to read after the wipe */ - EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - }; - }; - - /* Test the basic renegotiation mechanism with a variety of connection parameters. - * A client should always be able to receive and negotiate after wiping a connection for renegotiation. - */ - { - /* Setup a security policy that only contains one cipher */ - struct s2n_cipher_preferences one_cipher_preference = { .count = 1, .suites = NULL }; - struct s2n_security_policy one_cipher_policy = security_policy_test_all; - one_cipher_policy.cipher_preferences = &one_cipher_preference; - - /* This config can only be used for servers, because currently only servers can have multiple certs */ - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dh_params)); - EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); - server_config->security_policy = &one_cipher_policy; - - /* Setting the max fragment length will require modifying the client config */ - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - - /* The oldest version s2n-tls supports is SSLv3. - * However, SSLv3 requires MD5 for its PRF. - */ - uint8_t oldest_tested_version = S2N_SSLv3; - if (!s2n_hash_is_available(S2N_HASH_MD5)) { - oldest_tested_version = S2N_TLS10; - } - - struct s2n_reneg_test_case test_cases[2000] = { 0 }; - size_t test_cases_count = 0; - - /* FFDHE is very, VERY slow. - * To avoid this test taking multiple minutes, - * we choose a limited number of dhe test cases. - */ - for (size_t i = 0; i < s2n_array_len(dhe_test_cases); i++) { - if (!dhe_test_cases[i].cipher_suite->available) { - continue; - } - if (dhe_test_cases[i].protocol_version < oldest_tested_version) { - continue; - } - test_cases[test_cases_count] = dhe_test_cases[i]; - test_cases_count++; - EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); - } - EXPECT_TRUE(test_cases_count > 0); - - const struct s2n_cipher_preferences *ciphers = security_policy_test_all.cipher_preferences; - for (uint8_t version = oldest_tested_version; version < S2N_TLS13; version++) { - for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { - struct s2n_cipher_suite *cipher = ciphers->suites[cipher_i]; - - if (!cipher->available) { - continue; - } - - if (version < cipher->minimum_required_tls_version) { - continue; - } - - if (cipher->key_exchange_alg == &s2n_dhe) { - /* See dhe_test_cases */ - continue; - } - - for (size_t max_frag_i = 0; max_frag_i < s2n_array_len(mfl_code_to_length); max_frag_i++) { - test_cases[test_cases_count] = (struct s2n_reneg_test_case){ - .protocol_version = version, - .cipher_suite = ciphers->suites[cipher_i], - .max_frag_code = max_frag_i, - }; - test_cases_count++; - EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); - } - } - } - - for (size_t i = 0; i < test_cases_count; i++) { - uint8_t recv_buffer[sizeof(app_data)] = { 0 }; - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - 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)); - - /* Setup test case */ - server_conn->server_protocol_version = test_cases[i].protocol_version; - EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, test_cases[i].max_frag_code)); - one_cipher_preference.suites = &test_cases[i].cipher_suite; - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify test case setup */ - EXPECT_EQUAL(client_conn->actual_protocol_version, test_cases[i].protocol_version); - EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, mfl_code_to_length[test_cases[i].max_frag_code]); - if (test_cases[i].protocol_version > S2N_SSLv3) { - EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite); - } else { - EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite->sslv3_cipher_suite); - } - - EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); - - /* Test that the client can still receive application data */ - EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); - EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); - EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); - - /* Test that a second handshake can occur. */ - EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_renegotiate.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +struct s2n_reneg_test_case { + uint8_t protocol_version; + struct s2n_cipher_suite *cipher_suite; + uint8_t max_frag_code; +}; + +const struct s2n_reneg_test_case dhe_test_cases[] = { + { + .protocol_version = S2N_SSLv3, + .cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha, + .max_frag_code = 0, + }, + { + .protocol_version = S2N_TLS10, + .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_512, + }, + { + .protocol_version = S2N_TLS11, + .cipher_suite = &s2n_dhe_rsa_with_aes_256_cbc_sha, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_1024, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha256, + .max_frag_code = 0, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_aes_256_gcm_sha384, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_2048, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_4096, + }, +}; + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + char dh_params[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dh_params, sizeof(dh_params))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + uint8_t app_data[] = "smaller hello world"; + uint8_t large_app_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = "hello world and a lot of zeroes"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test s2n_renegotiate_wipe */ + { + /* Default IO unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* "io_pair" just uses file descriptors and the default io callbacks */ + 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_EQUAL(client_conn->send, s2n_socket_write); + EXPECT_TRUE(client_conn->managed_send_io); + EXPECT_EQUAL(client_conn->recv, s2n_socket_read); + EXPECT_TRUE(client_conn->managed_recv_io); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + + /* Custom IO callbacks unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* "io_stuffers" use custom IO callbacks written for tests */ + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + EXPECT_NOT_EQUAL(client_conn->send, s2n_socket_write); + EXPECT_FALSE(client_conn->managed_send_io); + EXPECT_NOT_EQUAL(client_conn->recv, s2n_socket_read); + EXPECT_FALSE(client_conn->managed_recv_io); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + + /* Fragment size unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t original_out_size = s2n_stuffer_data_available(&out); + EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t wiped_out_size = s2n_stuffer_data_available(&out); + EXPECT_EQUAL(original_out_size, wiped_out_size); + }; + + /* Forced very small fragment size unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + const size_t small_frag_len = S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE; + client_conn->max_outgoing_fragment_length = small_frag_len; + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t original_out_size = s2n_stuffer_data_available(&out); + EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, small_frag_len); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t wiped_out_size = s2n_stuffer_data_available(&out); + EXPECT_EQUAL(original_out_size, wiped_out_size); + }; + + /* Handshake succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Handshake with added client auth succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(client_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(server_conn)); + }; + + /* Handshake with different fragment length succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_config *small_frag_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(small_frag_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(small_frag_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(small_frag_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(small_frag_config, "20240501")); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(small_frag_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(small_frag_config, S2N_TLS_MAX_FRAG_LEN_512)); + + DEFER_CLEANUP(struct s2n_config *larger_frag_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(larger_frag_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(larger_frag_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(larger_frag_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(larger_frag_config, "20240501")); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(larger_frag_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(larger_frag_config, S2N_TLS_MAX_FRAG_LEN_4096)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, small_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, small_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* renegotiation_info is non-empty after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the renegotiation_info was empty / missing */ + ssize_t renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, + S2N_EXTENSION_RENEGOTIATION_INFO); + EXPECT_EQUAL(renegotiation_info_len, 0); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_TRUE(client_conn->handshake.finished_len > 0); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the renegotiation_info was not empty / missing */ + renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, + S2N_EXTENSION_RENEGOTIATION_INFO); + EXPECT_TRUE(renegotiation_info_len > sizeof(uint8_t)); + }; + + /* Wipe of insecure connection not allowed */ + { + 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, config)); + client_conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FALSE(client_conn->secure_renegotiation); + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_NO_RENEGOTIATION); + client_conn->secure_renegotiation = true; + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe of TLS1.3 connection not allowed */ + { + 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, config)); + client_conn->secure_renegotiation = true; + + EXPECT_TRUE(client_conn->actual_protocol_version > S2N_TLS12); + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + client_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe mid-write not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Initiate a partial send */ + uint16_t partial_send_len = client_conn->max_outgoing_fragment_length / 2; + DEFER_CLEANUP(struct s2n_stuffer small_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&small_out, partial_send_len)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&small_out, client_conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); + + /* Finish the send */ + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, client_conn)); + EXPECT_EQUAL(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), sizeof(large_app_data)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe mid-read not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Initiate a partial recv */ + uint16_t partial_recv_len = sizeof(app_data) / 2; + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, partial_recv_len, &blocked), partial_recv_len); + EXPECT_BYTEARRAY_EQUAL(app_data, recv_buffer, partial_recv_len); + EXPECT_TRUE(s2n_peek(client_conn) > 0); + + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); + + /* Finish the recv */ + size_t remaining_recv_len = sizeof(app_data) - partial_recv_len; + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, remaining_recv_len, &blocked), remaining_recv_len); + EXPECT_BYTEARRAY_EQUAL(app_data + partial_recv_len, recv_buffer, remaining_recv_len); + EXPECT_EQUAL(s2n_peek(client_conn), 0); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe with next record buffered allowed, and data preserved */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Write two records, but only receive one. + * Due to recv buffering, the second record will be read and buffered + * at the same time as the first record, but not processed yet. + */ + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_peek(client), 0); + EXPECT_TRUE(s2n_stuffer_data_available(&client->buffer_in)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client)); + + /* The second record is still available to read after the wipe */ + EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + }; + + /* Test the basic renegotiation mechanism with a variety of connection parameters. + * A client should always be able to receive and negotiate after wiping a connection for renegotiation. + */ + { + /* Setup a security policy that only contains one cipher */ + struct s2n_cipher_preferences one_cipher_preference = { .count = 1, .suites = NULL }; + struct s2n_security_policy one_cipher_policy = security_policy_test_all; + one_cipher_policy.cipher_preferences = &one_cipher_preference; + + /* This config can only be used for servers, because currently only servers can have multiple certs */ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dh_params)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + server_config->security_policy = &one_cipher_policy; + + /* Setting the max fragment length will require modifying the client config */ + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + /* The oldest version s2n-tls supports is SSLv3. + * However, SSLv3 requires MD5 for its PRF. + */ + uint8_t oldest_tested_version = S2N_SSLv3; + if (!s2n_hash_is_available(S2N_HASH_MD5)) { + oldest_tested_version = S2N_TLS10; + } + + struct s2n_reneg_test_case test_cases[2000] = { 0 }; + size_t test_cases_count = 0; + + /* FFDHE is very, VERY slow. + * To avoid this test taking multiple minutes, + * we choose a limited number of dhe test cases. + */ + for (size_t i = 0; i < s2n_array_len(dhe_test_cases); i++) { + if (!dhe_test_cases[i].cipher_suite->available) { + continue; + } + if (dhe_test_cases[i].protocol_version < oldest_tested_version) { + continue; + } + test_cases[test_cases_count] = dhe_test_cases[i]; + test_cases_count++; + EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); + } + EXPECT_TRUE(test_cases_count > 0); + + const struct s2n_cipher_preferences *ciphers = security_policy_test_all.cipher_preferences; + for (uint8_t version = oldest_tested_version; version < S2N_TLS13; version++) { + for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { + struct s2n_cipher_suite *cipher = ciphers->suites[cipher_i]; + + if (!cipher->available) { + continue; + } + + if (version < cipher->minimum_required_tls_version) { + continue; + } + + if (cipher->key_exchange_alg == &s2n_dhe) { + /* See dhe_test_cases */ + continue; + } + + for (size_t max_frag_i = 0; max_frag_i < s2n_array_len(mfl_code_to_length); max_frag_i++) { + test_cases[test_cases_count] = (struct s2n_reneg_test_case){ + .protocol_version = version, + .cipher_suite = ciphers->suites[cipher_i], + .max_frag_code = max_frag_i, + }; + test_cases_count++; + EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); + } + } + } + + for (size_t i = 0; i < test_cases_count; i++) { + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + 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)); + + /* Setup test case */ + server_conn->server_protocol_version = test_cases[i].protocol_version; + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, test_cases[i].max_frag_code)); + one_cipher_preference.suites = &test_cases[i].cipher_suite; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify test case setup */ + EXPECT_EQUAL(client_conn->actual_protocol_version, test_cases[i].protocol_version); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, mfl_code_to_length[test_cases[i].max_frag_code]); + if (test_cases[i].protocol_version > S2N_SSLv3) { + EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite); + } else { + EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite->sslv3_cipher_suite); + } + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + /* Test that the client can still receive application data */ + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + + /* Test that a second handshake can occur. */ + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_rsa_pss_test.c b/tests/unit/s2n_rsa_pss_test.c index d8ec97cd4ce..0ac86b6c507 100644 --- a/tests/unit/s2n_rsa_pss_test.c +++ b/tests/unit/s2n_rsa_pss_test.c @@ -1,351 +1,351 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_rsa_pss.h" - -#include "crypto/s2n_certificate.h" -#include "crypto/s2n_dhe.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "utils/s2n_random.h" - -int s2n_flip_random_bit(struct s2n_blob *blob) -{ - /* Flip a random bit in the blob */ - uint64_t byte_flip_pos = 0; - POSIX_GUARD_RESULT(s2n_public_random(blob->size, &byte_flip_pos)); - uint64_t bit_flip_pos = 0; - POSIX_GUARD_RESULT(s2n_public_random(8, &bit_flip_pos)); - - uint8_t mask = 0x01 << (uint8_t) bit_flip_pos; - blob->data[byte_flip_pos] ^= mask; - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Don't use RSA-PSS certs if unsupported */ -#if !RSA_PSS_CERTS_SUPPORTED - EXPECT_FALSE(s2n_is_rsa_pss_certs_supported()); - END_TEST(); -#endif - EXPECT_TRUE(s2n_is_rsa_pss_certs_supported()); - - /* Check that s2n_is_rsa_pss_certs_supported() is a superset of s2n_is_rsa_pss_signing_supported() */ - EXPECT_TRUE(s2n_is_rsa_pss_signing_supported()); - - /* Positive Test: Ensure we can sign and verify a randomly generated signature. - * Pseudocode: assert(SUCCESS == verify(Key1_public, message, sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - - /* Load the Private Key */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - /* Load the Public Key */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - EXPECT_EQUAL(pkey_type, S2N_PKEY_TYPE_RSA_PSS); - - /* Sign and Verify a Random Value to ensure that Public and Private Key Matches */ - EXPECT_SUCCESS(s2n_pkey_match(&public_key, chain_and_key->private_key)); - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(cert_chain_pem); - free(private_key_pem); - - /* Verify repeated key frees. - * (Later calls should be a no-op) */ - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - }; - - /* Negative Test: Loading mismatching RSA PSS Public/Private Keys will fail. - * Pseudocode: assert(FAILURE == load_pem_pair(Key1_public, Key2_private)) - */ - { - struct s2n_config *server_config = NULL; - char *leaf_cert_chain_pem = NULL; - char *root_private_key_pem = NULL; - struct s2n_cert_chain_and_key *misconfigured_chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - - EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Incorrectly reading the CA's Private Key from disk, not the Leaf's Private Key */ - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(misconfigured_chain_and_key = s2n_cert_chain_and_key_new()); - - /* Attempting to Load RSA_PSS Certificate with wrong RSA_PSS Key should fail */ - EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(misconfigured_chain_and_key, leaf_cert_chain_pem, root_private_key_pem)); - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(misconfigured_chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(leaf_cert_chain_pem); - free(root_private_key_pem); - }; - - /* Negative Test: Ensure flipping a bit in the signature is rejected - * Pseudocode: assert(FAILURE == verify(Key1_public, message, bitflip(sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - /* Parse the leaf cert for the public key and certificate type */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); - EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); - - struct s2n_pkey *private_key = chain_and_key->private_key; - { - EXPECT_NOT_NULL(public_key.pkey); - EXPECT_NOT_NULL(private_key); - EXPECT_NOT_NULL(private_key->pkey); - - /* Generate a random blob to sign and verify */ - s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); - EXPECT_OK(s2n_get_private_random_data(&random_msg)); - - /* Sign/Verify API's only accept Hashes, so hash our Random Data */ - DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); - EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); - - DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); - EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); - - /* Sign and Verify the Hash of the Random Blob */ - s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); - EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); - - /* Flip a random bit in the signature */ - EXPECT_SUCCESS(s2n_flip_random_bit(&signature_data)); - EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); - }; - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(cert_chain_pem); - free(private_key_pem); - }; - - /* Negative Test: Ensure Verification with wrong key fails - * Pseudocode: assert(FAILURE == verify(Key2_public, message, sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *root_cert_chain_pem = NULL; - char *root_private_key_pem = NULL; - char *leaf_cert_chain_pem = NULL; - char *leaf_private_key_pem = NULL; - struct s2n_cert_chain_and_key *root_chain_and_key = NULL; - struct s2n_cert_chain_and_key *leaf_chain_and_key = NULL; - struct s2n_pkey root_public_key = { 0 }; - struct s2n_pkey leaf_public_key = { 0 }; - s2n_pkey_type root_pkey_type = S2N_PKEY_TYPE_UNKNOWN; - s2n_pkey_type leaf_pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(root_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(leaf_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, root_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, leaf_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(root_chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_NOT_NULL(leaf_chain_and_key = s2n_cert_chain_and_key_new()); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(root_chain_and_key, root_cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(root_chain_and_key, root_private_key_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(leaf_chain_and_key, leaf_cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(leaf_chain_and_key, leaf_private_key_pem)); - - /* Parse the cert for the public key and certificate type */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&root_public_key, &root_pkey_type, &root_chain_and_key->cert_chain->head->raw)); - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&leaf_public_key, &leaf_pkey_type, &leaf_chain_and_key->cert_chain->head->raw)); - EXPECT_NOT_EQUAL(root_pkey_type, S2N_PKEY_TYPE_UNKNOWN); - EXPECT_NOT_EQUAL(leaf_pkey_type, S2N_PKEY_TYPE_UNKNOWN); - - EXPECT_SUCCESS(s2n_cert_set_cert_type(root_chain_and_key->cert_chain->head, root_pkey_type)); - EXPECT_SUCCESS(s2n_cert_set_cert_type(leaf_chain_and_key->cert_chain->head, leaf_pkey_type)); - - struct s2n_pkey *root_private_key = root_chain_and_key->private_key; - struct s2n_pkey *leaf_private_key = leaf_chain_and_key->private_key; - { - EXPECT_NOT_NULL(root_public_key.pkey); - EXPECT_NOT_NULL(leaf_public_key.pkey); - - EXPECT_NOT_NULL(root_private_key); - EXPECT_NOT_NULL(root_private_key->pkey); - EXPECT_NOT_NULL(leaf_private_key); - EXPECT_NOT_NULL(leaf_private_key->pkey); - - /* Generate a random blob to sign and verify */ - s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); - EXPECT_OK(s2n_get_private_random_data(&random_msg)); - - /* Sign/Verify API's only accept Hashes, so hash our Random Data */ - DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); - EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); - - DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); - EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); - - /* Sign and Verify the Hash of the Random Blob */ - s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); - - /* Sign with Root's Key, but verify with Leaf's Key. This should fail. */ - EXPECT_SUCCESS(s2n_pkey_sign(root_private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); - EXPECT_FAILURE(s2n_pkey_verify(&leaf_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); - }; - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(root_chain_and_key)); - EXPECT_SUCCESS(s2n_pkey_free(&root_public_key)); - free(root_cert_chain_pem); - free(root_private_key_pem); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(leaf_chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&leaf_public_key)); - free(leaf_cert_chain_pem); - free(leaf_private_key_pem); - }; - - /* Negative Test: Ensure flipping a bit in message given to verification fails - * Pseudocode: assert(FAILURE == verify(Key1_public, bitflip(message), sign(Key1_private, message))) - */ - { - struct s2n_config *server_config = NULL; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_pkey public_key = { 0 }; - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); - - /* Parse the leaf cert for the public key and certificate type */ - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); - EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); - EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); - - struct s2n_pkey *private_key = chain_and_key->private_key; - { - EXPECT_NOT_NULL(public_key.pkey); - EXPECT_NOT_NULL(private_key); - EXPECT_NOT_NULL(private_key->pkey); - - /* Generate a random blob to sign and verify */ - s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); - EXPECT_OK(s2n_get_private_random_data(&random_msg)); - - /* Sign/Verify API's only accept Hashes, so hash our Random Data */ - DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); - EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); - - /* Flip a random bit in the message before verification */ - EXPECT_SUCCESS(s2n_flip_random_bit(&random_msg)); - - DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); - EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); - - /* Sign and Verify the Hash of the Random Blob */ - s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); - EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); - EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); - }; - - /* Release Resources */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_pkey_free(&public_key)); - free(cert_chain_pem); - free(private_key_pem); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_rsa_pss.h" + +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_dhe.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_random.h" + +int s2n_flip_random_bit(struct s2n_blob *blob) +{ + /* Flip a random bit in the blob */ + uint64_t byte_flip_pos = 0; + POSIX_GUARD_RESULT(s2n_public_random(blob->size, &byte_flip_pos)); + uint64_t bit_flip_pos = 0; + POSIX_GUARD_RESULT(s2n_public_random(8, &bit_flip_pos)); + + uint8_t mask = 0x01 << (uint8_t) bit_flip_pos; + blob->data[byte_flip_pos] ^= mask; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Don't use RSA-PSS certs if unsupported */ +#if !RSA_PSS_CERTS_SUPPORTED + EXPECT_FALSE(s2n_is_rsa_pss_certs_supported()); + END_TEST(); +#endif + EXPECT_TRUE(s2n_is_rsa_pss_certs_supported()); + + /* Check that s2n_is_rsa_pss_certs_supported() is a superset of s2n_is_rsa_pss_signing_supported() */ + EXPECT_TRUE(s2n_is_rsa_pss_signing_supported()); + + /* Positive Test: Ensure we can sign and verify a randomly generated signature. + * Pseudocode: assert(SUCCESS == verify(Key1_public, message, sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + /* Load the Private Key */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + /* Load the Public Key */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_EQUAL(pkey_type, S2N_PKEY_TYPE_RSA_PSS); + + /* Sign and Verify a Random Value to ensure that Public and Private Key Matches */ + EXPECT_SUCCESS(s2n_pkey_match(&public_key, chain_and_key->private_key)); + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + + /* Verify repeated key frees. + * (Later calls should be a no-op) */ + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + }; + + /* Negative Test: Loading mismatching RSA PSS Public/Private Keys will fail. + * Pseudocode: assert(FAILURE == load_pem_pair(Key1_public, Key2_private)) + */ + { + struct s2n_config *server_config = NULL; + char *leaf_cert_chain_pem = NULL; + char *root_private_key_pem = NULL; + struct s2n_cert_chain_and_key *misconfigured_chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + + EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Incorrectly reading the CA's Private Key from disk, not the Leaf's Private Key */ + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(misconfigured_chain_and_key = s2n_cert_chain_and_key_new()); + + /* Attempting to Load RSA_PSS Certificate with wrong RSA_PSS Key should fail */ + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(misconfigured_chain_and_key, leaf_cert_chain_pem, root_private_key_pem)); + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(misconfigured_chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(leaf_cert_chain_pem); + free(root_private_key_pem); + }; + + /* Negative Test: Ensure flipping a bit in the signature is rejected + * Pseudocode: assert(FAILURE == verify(Key1_public, message, bitflip(sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + struct s2n_pkey *private_key = chain_and_key->private_key; + { + EXPECT_NOT_NULL(public_key.pkey); + EXPECT_NOT_NULL(private_key); + EXPECT_NOT_NULL(private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + + /* Flip a random bit in the signature */ + EXPECT_SUCCESS(s2n_flip_random_bit(&signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + }; + + /* Negative Test: Ensure Verification with wrong key fails + * Pseudocode: assert(FAILURE == verify(Key2_public, message, sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *root_cert_chain_pem = NULL; + char *root_private_key_pem = NULL; + char *leaf_cert_chain_pem = NULL; + char *leaf_private_key_pem = NULL; + struct s2n_cert_chain_and_key *root_chain_and_key = NULL; + struct s2n_cert_chain_and_key *leaf_chain_and_key = NULL; + struct s2n_pkey root_public_key = { 0 }; + struct s2n_pkey leaf_public_key = { 0 }; + s2n_pkey_type root_pkey_type = S2N_PKEY_TYPE_UNKNOWN; + s2n_pkey_type leaf_pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(root_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, root_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, leaf_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(leaf_chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(root_chain_and_key, root_cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(root_chain_and_key, root_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(leaf_chain_and_key, leaf_cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(leaf_chain_and_key, leaf_private_key_pem)); + + /* Parse the cert for the public key and certificate type */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&root_public_key, &root_pkey_type, &root_chain_and_key->cert_chain->head->raw)); + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&leaf_public_key, &leaf_pkey_type, &leaf_chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(root_pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_NOT_EQUAL(leaf_pkey_type, S2N_PKEY_TYPE_UNKNOWN); + + EXPECT_SUCCESS(s2n_cert_set_cert_type(root_chain_and_key->cert_chain->head, root_pkey_type)); + EXPECT_SUCCESS(s2n_cert_set_cert_type(leaf_chain_and_key->cert_chain->head, leaf_pkey_type)); + + struct s2n_pkey *root_private_key = root_chain_and_key->private_key; + struct s2n_pkey *leaf_private_key = leaf_chain_and_key->private_key; + { + EXPECT_NOT_NULL(root_public_key.pkey); + EXPECT_NOT_NULL(leaf_public_key.pkey); + + EXPECT_NOT_NULL(root_private_key); + EXPECT_NOT_NULL(root_private_key->pkey); + EXPECT_NOT_NULL(leaf_private_key); + EXPECT_NOT_NULL(leaf_private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + + /* Sign with Root's Key, but verify with Leaf's Key. This should fail. */ + EXPECT_SUCCESS(s2n_pkey_sign(root_private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&leaf_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(root_chain_and_key)); + EXPECT_SUCCESS(s2n_pkey_free(&root_public_key)); + free(root_cert_chain_pem); + free(root_private_key_pem); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(leaf_chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&leaf_public_key)); + free(leaf_cert_chain_pem); + free(leaf_private_key_pem); + }; + + /* Negative Test: Ensure flipping a bit in message given to verification fails + * Pseudocode: assert(FAILURE == verify(Key1_public, bitflip(message), sign(Key1_private, message))) + */ + { + struct s2n_config *server_config = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + struct s2n_pkey *private_key = chain_and_key->private_key; + { + EXPECT_NOT_NULL(public_key.pkey); + EXPECT_NOT_NULL(private_key); + EXPECT_NOT_NULL(private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + /* Flip a random bit in the message before verification */ + EXPECT_SUCCESS(s2n_flip_random_bit(&random_msg)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_seccomp_handshake_test.c b/tests/unit/s2n_seccomp_handshake_test.c index f1afa81e0d4..cceb6983cb5 100644 --- a/tests/unit/s2n_seccomp_handshake_test.c +++ b/tests/unit/s2n_seccomp_handshake_test.c @@ -1,86 +1,86 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "s2n_test.h" -#include "testlib/s2n_testlib.h" - -int main(int argc, char **argv) -{ - /* We need to execute s2n_init before the seccomp filter is applied. - * Some one-time initialization involves opening files, like "dev/urandom". - * If built with aws-lc, s2n-tls also needs to call CRYPTO_pre_sandbox_init() - * before seccomp starts sandboxing. - * - * An application using s2n-tls with seccomp would need to do the same. - */ - BEGIN_TEST(); - - /* One of the primary purposes of seccomp is to block opening new files. - * So before we enable seccomp, we need to open any files that the test would - * need. In this case, we need to load certificate pems from files. - * - * An application using s2n-tls with seccomp would need to do the same. - */ - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, - private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* No unexpected syscalls allowed beyond this point */ - EXPECT_OK(s2n_seccomp_init()); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, cert_chain_pem)); - - const char *security_policies[] = { "test_all_tls12", "default_tls13" }; - - for (size_t i = 0; i < s2n_array_len(security_policies); i++) { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policies[i])); - EXPECT_SUCCESS(s2n_set_server_name(client, "127.0.0.1")); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, security_policies[i])); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - const uint8_t data[] = "hello world"; - uint8_t buffer[100] = { 0 }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_EQUAL(s2n_send(client, data, sizeof(data), &blocked), sizeof(data)); - EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(data)); - EXPECT_BYTEARRAY_EQUAL(buffer, data, sizeof(data)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + /* We need to execute s2n_init before the seccomp filter is applied. + * Some one-time initialization involves opening files, like "dev/urandom". + * If built with aws-lc, s2n-tls also needs to call CRYPTO_pre_sandbox_init() + * before seccomp starts sandboxing. + * + * An application using s2n-tls with seccomp would need to do the same. + */ + BEGIN_TEST(); + + /* One of the primary purposes of seccomp is to block opening new files. + * So before we enable seccomp, we need to open any files that the test would + * need. In this case, we need to load certificate pems from files. + * + * An application using s2n-tls with seccomp would need to do the same. + */ + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, + private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* No unexpected syscalls allowed beyond this point */ + EXPECT_OK(s2n_seccomp_init()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, cert_chain_pem)); + + const char *security_policies[] = { "test_all_tls12", "default_tls13" }; + + for (size_t i = 0; i < s2n_array_len(security_policies); i++) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policies[i])); + EXPECT_SUCCESS(s2n_set_server_name(client, "127.0.0.1")); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, security_policies[i])); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + const uint8_t data[] = "hello world"; + uint8_t buffer[100] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(client, data, sizeof(data), &blocked), sizeof(data)); + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(data)); + EXPECT_BYTEARRAY_EQUAL(buffer, data, sizeof(data)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_alerts_test.c b/tests/unit/s2n_self_talk_alerts_test.c index fc68a4808ed..c49da1c47ff 100644 --- a/tests/unit/s2n_self_talk_alerts_test.c +++ b/tests/unit/s2n_self_talk_alerts_test.c @@ -1,336 +1,336 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -#define TLS_ALERT 21 -#define TLS_ALERT_VERSION 0x03, 0x03 -#define TLS_ALERT_LENGTH 0x00, 0x02 - -#define TLS_ALERT_LEVEL_WARNING 1 -#define TLS_ALERT_LEVEL_FATAL 2 - -#define TLS_ALERT_CLOSE_NOTIFY 0 -#define TLS_ALERT_UNRECOGNIZED_NAME 122 - -struct alert_ctx { - int write_fd; - int invoked; - int count; - - uint8_t level; - uint8_t code; -}; - -int mock_client(struct s2n_test_io_pair *io_pair, s2n_alert_behavior alert_behavior, int expect_failure) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int result = 0; - int rc = 0; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - s2n_config_disable_x509_verification(config); - s2n_config_set_alert_behavior(config, alert_behavior); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - rc = s2n_negotiate(conn, &blocked); - if (expect_failure) { - if (!rc) { - result = 1; - } - } else { - char buffer[0xffff]; - if (rc < 0) { - result = 1; - } - - for (size_t i = 1; i < 0xffff; i += 100) { - memset(buffer, 33, sizeof(char) * i); - s2n_send(conn, buffer, i, &blocked); - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - } while (shutdown_rc != 0); - } - - s2n_connection_free(conn); - s2n_config_free(config); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - s2n_cleanup(); - - exit(result); -} - -int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) -{ - static int called = 0; - - /* When first called return 0 seconds */ - *nanoseconds = 0; - - /* When next called return 31 seconds */ - if (called) { - *nanoseconds += (uint64_t) 31 * 1000000000; - } - - called = 1; - - return 0; -} - -int client_hello_send_alerts(struct s2n_connection *conn, void *ctx) -{ - struct alert_ctx *alert = ctx; - uint8_t alert_msg[] = { TLS_ALERT, TLS_ALERT_VERSION, TLS_ALERT_LENGTH, alert->level, alert->code }; - - for (int i = 0; i < alert->count; i++) { - if (write(alert->write_fd, alert_msg, sizeof(alert_msg)) != sizeof(alert_msg)) { - exit(100); - } - - alert->invoked++; - } - - return 0; -} - -S2N_RESULT cleanup(char **cert_chain_pem, char **private_key_pem, - struct s2n_cert_chain_and_key **chain_and_key) -{ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(*chain_and_key)); - free(*cert_chain_pem); - free(*private_key_pem); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - char buffer[0xffff]; - struct s2n_connection *conn = NULL; - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - BEGIN_TEST(); - - /* Ignore SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - - /* Test that we ignore Warning Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ - { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 0); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - /* Set up the callback to send an alert after receiving ClientHello */ - struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 2, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); - - /* This is the parent */ - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure that callback was invoked */ - EXPECT_EQUAL(warning_alert.invoked, 2); - - for (size_t i = 1; i < 0xffff; i += 100) { - char *ptr = buffer; - int size = i; - - do { - int bytes_read = 0; - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); - - size -= bytes_read; - ptr += bytes_read; - } while (size); - - for (int j = 0; j < i; j++) { - EXPECT_EQUAL(buffer[j], 33); - } - } - - EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - }; - - /* Test that we don't ignore Fatal Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ - { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 1); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - /* Set up the callback to send an alert after receiving ClientHello */ - struct alert_ctx fatal_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_FATAL, .code = TLS_ALERT_UNRECOGNIZED_NAME }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &fatal_alert)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure that callback was invoked */ - EXPECT_EQUAL(fatal_alert.invoked, 1); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - }; - - /* Test that we don't ignore Warning Alerts in S2N_ALERT_FAIL_ON_WARNINGS mode in TLS1.2 */ - { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - mock_client(&io_pair, S2N_ALERT_FAIL_ON_WARNINGS, 1); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - - /* Set up the callback to send an alert after receiving ClientHello */ - struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure that callback was invoked */ - EXPECT_EQUAL(warning_alert.invoked, 1); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - }; - - /* Shutdown */ - EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); - - END_TEST(); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define TLS_ALERT 21 +#define TLS_ALERT_VERSION 0x03, 0x03 +#define TLS_ALERT_LENGTH 0x00, 0x02 + +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNRECOGNIZED_NAME 122 + +struct alert_ctx { + int write_fd; + int invoked; + int count; + + uint8_t level; + uint8_t code; +}; + +int mock_client(struct s2n_test_io_pair *io_pair, s2n_alert_behavior alert_behavior, int expect_failure) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int result = 0; + int rc = 0; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + s2n_config_disable_x509_verification(config); + s2n_config_set_alert_behavior(config, alert_behavior); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + rc = s2n_negotiate(conn, &blocked); + if (expect_failure) { + if (!rc) { + result = 1; + } + } else { + char buffer[0xffff]; + if (rc < 0) { + result = 1; + } + + for (size_t i = 1; i < 0xffff; i += 100) { + memset(buffer, 33, sizeof(char) * i); + s2n_send(conn, buffer, i, &blocked); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + } while (shutdown_rc != 0); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + s2n_cleanup(); + + exit(result); +} + +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + static int called = 0; + + /* When first called return 0 seconds */ + *nanoseconds = 0; + + /* When next called return 31 seconds */ + if (called) { + *nanoseconds += (uint64_t) 31 * 1000000000; + } + + called = 1; + + return 0; +} + +int client_hello_send_alerts(struct s2n_connection *conn, void *ctx) +{ + struct alert_ctx *alert = ctx; + uint8_t alert_msg[] = { TLS_ALERT, TLS_ALERT_VERSION, TLS_ALERT_LENGTH, alert->level, alert->code }; + + for (int i = 0; i < alert->count; i++) { + if (write(alert->write_fd, alert_msg, sizeof(alert_msg)) != sizeof(alert_msg)) { + exit(100); + } + + alert->invoked++; + } + + return 0; +} + +S2N_RESULT cleanup(char **cert_chain_pem, char **private_key_pem, + struct s2n_cert_chain_and_key **chain_and_key) +{ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(*chain_and_key)); + free(*cert_chain_pem); + free(*private_key_pem); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + char buffer[0xffff]; + struct s2n_connection *conn = NULL; + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + BEGIN_TEST(); + + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + /* Test that we ignore Warning Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 0); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 2, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); + + /* This is the parent */ + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(warning_alert.invoked, 2); + + for (size_t i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Test that we don't ignore Fatal Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 1); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx fatal_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_FATAL, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &fatal_alert)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(fatal_alert.invoked, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Test that we don't ignore Warning Alerts in S2N_ALERT_FAIL_ON_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_FAIL_ON_WARNINGS, 1); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(warning_alert.invoked, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Shutdown */ + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_self_talk_broken_pipe_test.c b/tests/unit/s2n_self_talk_broken_pipe_test.c index 8d47563c303..2527fffe8a9 100644 --- a/tests/unit/s2n_self_talk_broken_pipe_test.c +++ b/tests/unit/s2n_self_talk_broken_pipe_test.c @@ -1,180 +1,180 @@ -#include "utils/s2n_prelude.h" -/* - * 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 - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -#define SUPPORTED_CERTIFICATE_FORMATS (2) - -static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; -static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - s2n_config_disable_x509_verification(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - int result = s2n_negotiate(conn, &blocked); - if (result < 0) { - exit(1); - } - - result = s2n_connection_free_handshake(conn); - if (result < 0) { - exit(1); - } - -#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) - /* On FreeBSD shutdown from one end of the socket pair does not give EPIPE. Must use close. */ - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); -#else - /* Close client read fd to mock half closed pipe at server side */ - s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_RD); -#endif - /* Give server a chance to send data on broken pipe */ - sleep(2); - - s2n_shutdown(conn, &blocked); - - result = s2n_connection_free(conn); - if (result < 0) { - exit(1); - } - - result = s2n_config_free(config); - if (result < 0) { - exit(1); - } - - /* Give the server a chance to avoid a sigpipe */ - sleep(1); - - s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_WR); - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int status = 0; - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - - BEGIN_TEST(); - - for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { - struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; - - /* Create a pipe */ - DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); - } - - if (is_dh_key_exchange) { - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - } - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Give client a chance to close pipe at the receiving end */ - sleep(1); - char buffer[1]; - /* Fist flush on half closed pipe should get EPIPE */ - ssize_t w = s2n_send(conn, buffer, 1, &blocked); - EXPECT_EQUAL(w, -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); - EXPECT_EQUAL(errno, EPIPE); - - /* Second flush on half closed pipe should not get EPIPE as we write is skipped */ - w = s2n_shutdown(conn, &blocked); - EXPECT_EQUAL(w, -1); - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); - EXPECT_EQUAL(errno, 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); - } - EXPECT_SUCCESS(s2n_config_free(config)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - if (getenv("S2N_VALGRIND") == NULL) { - EXPECT_EQUAL(status, 0); - } - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + int result = s2n_negotiate(conn, &blocked); + if (result < 0) { + exit(1); + } + + result = s2n_connection_free_handshake(conn); + if (result < 0) { + exit(1); + } + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) + /* On FreeBSD shutdown from one end of the socket pair does not give EPIPE. Must use close. */ + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); +#else + /* Close client read fd to mock half closed pipe at server side */ + s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_RD); +#endif + /* Give server a chance to send data on broken pipe */ + sleep(2); + + s2n_shutdown(conn, &blocked); + + result = s2n_connection_free(conn); + if (result < 0) { + exit(1); + } + + result = s2n_config_free(config); + if (result < 0) { + exit(1); + } + + /* Give the server a chance to avoid a sigpipe */ + sleep(1); + + s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_WR); + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status = 0; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + + BEGIN_TEST(); + + for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Give client a chance to close pipe at the receiving end */ + sleep(1); + char buffer[1]; + /* Fist flush on half closed pipe should get EPIPE */ + ssize_t w = s2n_send(conn, buffer, 1, &blocked); + EXPECT_EQUAL(w, -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + EXPECT_EQUAL(errno, EPIPE); + + /* Second flush on half closed pipe should not get EPIPE as we write is skipped */ + w = s2n_shutdown(conn, &blocked); + EXPECT_EQUAL(w, -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + EXPECT_EQUAL(errno, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + if (getenv("S2N_VALGRIND") == NULL) { + EXPECT_EQUAL(status, 0); + } + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_certificates_test.c b/tests/unit/s2n_self_talk_certificates_test.c index 369361e8a20..02bfd24fccb 100644 --- a/tests/unit/s2n_self_talk_certificates_test.c +++ b/tests/unit/s2n_self_talk_certificates_test.c @@ -1,140 +1,140 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_rsa_pss.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" - -static uint8_t s2n_noop_verify_host_fn(const char *name, size_t len, void *data) -{ - return 1; -} - -static S2N_RESULT s2n_test_load_certificate(struct s2n_cert_chain_and_key **chain_out, - char *chain_pem_out, const char *chain_pem_path, const char *key_pem_path) -{ - RESULT_ENSURE_REF(chain_out); - - char key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - RESULT_GUARD_POSIX(s2n_read_test_pem(chain_pem_path, chain_pem_out, S2N_MAX_TEST_PEM_SIZE)); - RESULT_GUARD_POSIX(s2n_read_test_pem(key_pem_path, key_pem, S2N_MAX_TEST_PEM_SIZE)); - - *chain_out = s2n_cert_chain_and_key_new(); - RESULT_ENSURE_REF(*chain_out); - RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_out, chain_pem_out, key_pem)); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_rsa_pss_certs_supported() || !s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - const uint8_t test_versions[] = { S2N_TLS13, S2N_TLS12, S2N_TLS11, S2N_TLS10 }; - struct { - const char *cert; - const char *key; - uint8_t min_version; - } test_certs[] = { - { - .cert = S2N_RSA_2048_PKCS1_SHA256_CERT_CHAIN, - .key = S2N_RSA_2048_PKCS1_SHA256_CERT_KEY, - }, - { - .cert = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, - .key = S2N_ECDSA_P384_PKCS1_KEY, - }, - { - .cert = S2N_ECDSA_P512_CERT_CHAIN, - .key = S2N_ECDSA_P512_KEY, - }, - { - .cert = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, - .key = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, - .min_version = S2N_TLS12, - }, - }; - - for (size_t cert_i = 0; cert_i < s2n_array_len(test_certs); cert_i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, - s2n_cert_chain_and_key_ptr_free); - char pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_OK(s2n_test_load_certificate(&chain, pem, - test_certs[cert_i].cert, test_certs[cert_i].key)); - - for (size_t version_i = 0; version_i < s2n_array_len(test_versions); version_i++) { - uint8_t version = test_versions[version_i]; - bool expect_success = (version >= test_certs[cert_i].min_version); - - /* 20240501 only supports up to TLS1.2 */ - const char *security_policy = "20240501"; - if (version >= S2N_TLS13) { - security_policy = "default_tls13"; - } else if (version < S2N_TLS12) { - /* The default policies don't support legacy versions */ - security_policy = "test_all"; - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, pem)); - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, s2n_noop_verify_host_fn, NULL)); - EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(config, 0)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, security_policy)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, config)); - server->server_protocol_version = version; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, config)); - client->client_protocol_version = version; - - 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_connections_set_io_pair(client, server, &io_pair)); - - bool handshake_success = - (s2n_negotiate_test_server_and_client(server, client) == S2N_SUCCESS); - if (handshake_success) { - EXPECT_EQUAL(server->actual_protocol_version, version); - EXPECT_EQUAL(client->actual_protocol_version, version); - } - - const char *error_message = "Handshake failed"; - if (!expect_success) { - error_message = "Handshake unexpectedly succeeded"; - } - if (handshake_success != expect_success) { - fprintf(stderr, "%s version=%i cert=%s\n", - error_message, version, test_certs[cert_i].cert); - FAIL_MSG(error_message); - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static uint8_t s2n_noop_verify_host_fn(const char *name, size_t len, void *data) +{ + return 1; +} + +static S2N_RESULT s2n_test_load_certificate(struct s2n_cert_chain_and_key **chain_out, + char *chain_pem_out, const char *chain_pem_path, const char *key_pem_path) +{ + RESULT_ENSURE_REF(chain_out); + + char key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + RESULT_GUARD_POSIX(s2n_read_test_pem(chain_pem_path, chain_pem_out, S2N_MAX_TEST_PEM_SIZE)); + RESULT_GUARD_POSIX(s2n_read_test_pem(key_pem_path, key_pem, S2N_MAX_TEST_PEM_SIZE)); + + *chain_out = s2n_cert_chain_and_key_new(); + RESULT_ENSURE_REF(*chain_out); + RESULT_GUARD_POSIX(s2n_cert_chain_and_key_load_pem(*chain_out, chain_pem_out, key_pem)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_rsa_pss_certs_supported() || !s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint8_t test_versions[] = { S2N_TLS13, S2N_TLS12, S2N_TLS11, S2N_TLS10 }; + struct { + const char *cert; + const char *key; + uint8_t min_version; + } test_certs[] = { + { + .cert = S2N_RSA_2048_PKCS1_SHA256_CERT_CHAIN, + .key = S2N_RSA_2048_PKCS1_SHA256_CERT_KEY, + }, + { + .cert = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, + .key = S2N_ECDSA_P384_PKCS1_KEY, + }, + { + .cert = S2N_ECDSA_P512_CERT_CHAIN, + .key = S2N_ECDSA_P512_KEY, + }, + { + .cert = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .min_version = S2N_TLS12, + }, + }; + + for (size_t cert_i = 0; cert_i < s2n_array_len(test_certs); cert_i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + char pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_OK(s2n_test_load_certificate(&chain, pem, + test_certs[cert_i].cert, test_certs[cert_i].key)); + + for (size_t version_i = 0; version_i < s2n_array_len(test_versions); version_i++) { + uint8_t version = test_versions[version_i]; + bool expect_success = (version >= test_certs[cert_i].min_version); + + /* 20240501 only supports up to TLS1.2 */ + const char *security_policy = "20240501"; + if (version >= S2N_TLS13) { + security_policy = "default_tls13"; + } else if (version < S2N_TLS12) { + /* The default policies don't support legacy versions */ + security_policy = "test_all"; + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, pem)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, s2n_noop_verify_host_fn, NULL)); + EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(config, 0)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, security_policy)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + server->server_protocol_version = version; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + client->client_protocol_version = version; + + 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_connections_set_io_pair(client, server, &io_pair)); + + bool handshake_success = + (s2n_negotiate_test_server_and_client(server, client) == S2N_SUCCESS); + if (handshake_success) { + EXPECT_EQUAL(server->actual_protocol_version, version); + EXPECT_EQUAL(client->actual_protocol_version, version); + } + + const char *error_message = "Handshake failed"; + if (!expect_success) { + error_message = "Handshake unexpectedly succeeded"; + } + if (handshake_success != expect_success) { + fprintf(stderr, "%s version=%i cert=%s\n", + error_message, version, test_certs[cert_i].cert); + FAIL_MSG(error_message); + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_custom_io_test.c b/tests/unit/s2n_self_talk_custom_io_test.c index 67841cacb99..fb0fa6e74c7 100644 --- a/tests/unit/s2n_self_talk_custom_io_test.c +++ b/tests/unit/s2n_self_talk_custom_io_test.c @@ -1,221 +1,221 @@ -#include "utils/s2n_prelude.h" -/* - * 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 -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_random.h" - -#define MAX_BUF_SIZE 10000 - -int mock_client(struct s2n_test_io_pair *io_pair) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - - conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - s2n_connection_set_config(conn, client_config); - - /* Unlike the server, the client just passes ownership of I/O to s2n */ - s2n_connection_set_io_pair(conn, io_pair); - - result = s2n_negotiate(conn, &blocked); - if (result < 0) { - exit(1); - } - - s2n_shutdown(conn, &blocked); - s2n_connection_free(conn); - s2n_config_free(client_config); - s2n_cleanup(); - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - /** - * This test creates a server, client, and a pair of pipes. The client uses the - * pipes directly for I/O in s2n. The server copies data from the pipes into - * stuffers and manages s2n I/O with a set of I/O callbacks that read and write - * from the stuffers. - */ - { - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - - /* For convenience, this test will intentionally try to write to closed pipes during shutdown. Ignore the signal to - * avoid exiting the process on SIGPIPE. - */ - signal(SIGPIPE, SIG_IGN); - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Run the client */ - mock_client(&io_pair); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_FAILURE(s2n_connection_use_corked_io(conn)); - - /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); - - /* Make our pipes non-blocking */ - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); - - /* Negotiate the handshake. */ - do { - int ret = 0; - - ret = s2n_negotiate(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - - /* check to see if we need to copy more over from the pipes to the buffers - * to continue the handshake - */ - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (blocked); - - /* Shutdown after negotiating */ - uint8_t server_shutdown = 0; - do { - int ret = 0; - - ret = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); - if (ret == 0) { - server_shutdown = 1; - } - - s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); - s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); - } while (!server_shutdown); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - }; - - /* Clients and servers can utilize both custom IO and default IO for their sending and receiving */ - { - /* Setup connections */ - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - /* Setup config */ - struct s2n_config *config_with_certs = NULL; - EXPECT_NOT_NULL(config_with_certs = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_certs, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_certs)); - struct s2n_cert_chain_and_key *chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, - S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_certs, chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_certs)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_certs)); - - /* Create nonblocking pipes */ - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - /* Server writes to fd and client reads from fd */ - EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, io_pair.server)); - EXPECT_SUCCESS(s2n_connection_set_read_fd(client_conn, io_pair.client)); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, S2N_DEFAULT_RECORD_LENGTH)); - - /* Client writes to stuffer and server reads from stuffer */ - EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&stuffer, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&stuffer, server_conn)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Clean-up */ - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(config_with_certs)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_random.h" + +#define MAX_BUF_SIZE 10000 + +int mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + + conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + s2n_connection_set_config(conn, client_config); + + /* Unlike the server, the client just passes ownership of I/O to s2n */ + s2n_connection_set_io_pair(conn, io_pair); + + result = s2n_negotiate(conn, &blocked); + if (result < 0) { + exit(1); + } + + s2n_shutdown(conn, &blocked); + s2n_connection_free(conn); + s2n_config_free(client_config); + s2n_cleanup(); + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + /** + * This test creates a server, client, and a pair of pipes. The client uses the + * pipes directly for I/O in s2n. The server copies data from the pipes into + * stuffers and manages s2n I/O with a set of I/O callbacks that read and write + * from the stuffers. + */ + { + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + + /* For convenience, this test will intentionally try to write to closed pipes during shutdown. Ignore the signal to + * avoid exiting the process on SIGPIPE. + */ + signal(SIGPIPE, SIG_IGN); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + mock_client(&io_pair); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FAILURE(s2n_connection_use_corked_io(conn)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); + + /* Make our pipes non-blocking */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + + /* Negotiate the handshake. */ + do { + int ret = 0; + + ret = s2n_negotiate(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + + /* check to see if we need to copy more over from the pipes to the buffers + * to continue the handshake + */ + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (blocked); + + /* Shutdown after negotiating */ + uint8_t server_shutdown = 0; + do { + int ret = 0; + + ret = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + if (ret == 0) { + server_shutdown = 1; + } + + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (!server_shutdown); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + }; + + /* Clients and servers can utilize both custom IO and default IO for their sending and receiving */ + { + /* Setup connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_config *config_with_certs = NULL; + EXPECT_NOT_NULL(config_with_certs = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_certs, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_certs)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_certs, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_certs)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_certs)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Server writes to fd and client reads from fd */ + EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, io_pair.server)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(client_conn, io_pair.client)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, S2N_DEFAULT_RECORD_LENGTH)); + + /* Client writes to stuffer and server reads from stuffer */ + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&stuffer, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config_with_certs)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_min_protocol_version_test.c b/tests/unit/s2n_self_talk_min_protocol_version_test.c index bab61a42af9..cb14131013c 100644 --- a/tests/unit/s2n_self_talk_min_protocol_version_test.c +++ b/tests/unit/s2n_self_talk_min_protocol_version_test.c @@ -1,130 +1,130 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -int mock_client(struct s2n_test_io_pair *io_pair, uint8_t version) -{ - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - - client_conn = s2n_connection_new(S2N_CLIENT); - s2n_connection_set_config(client_conn, client_config); - s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING); - - /* Force TLSv1 on a client so that server will fail handshake */ - client_conn->client_protocol_version = S2N_TLS10; - if (version >= S2N_TLS13) { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); - } else { - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); - } - - s2n_connection_set_io_pair(client_conn, io_pair); - - result = s2n_negotiate(client_conn, &blocked); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - s2n_connection_free(client_conn); - s2n_config_free(client_config); - - s2n_cleanup(); - - /* Expect failure of handshake */ - exit(result == 0 ? 1 : 0); -} - -int main(int argc, char **argv) -{ - s2n_blocked_status blocked; - int status = 0; - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - - BEGIN_TEST(); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* TLS1.2 and TLS1.3 have different version negotiation mechanisms. - * We should test both. - */ - for (uint8_t version = S2N_TLS12; version <= S2N_TLS13; version++) { - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Send the client hello with TLSv1 and validate that we failed handshake */ - mock_client(&io_pair, version); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), - s2n_config_ptr_free); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - /* Pick cipher preference with TLSv1.2 as a minimum version */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "CloudFront-TLS-1-2-2019")); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - /* Check that blinding was not invoked */ - EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); - - /* Close the pipes */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +int mock_client(struct s2n_test_io_pair *io_pair, uint8_t version) +{ + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + + client_conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_config(client_conn, client_config); + s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING); + + /* Force TLSv1 on a client so that server will fail handshake */ + client_conn->client_protocol_version = S2N_TLS10; + if (version >= S2N_TLS13) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + } else { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + } + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + s2n_cleanup(); + + /* Expect failure of handshake */ + exit(result == 0 ? 1 : 0); +} + +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status = 0; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* TLS1.2 and TLS1.3 have different version negotiation mechanisms. + * We should test both. + */ + for (uint8_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Send the client hello with TLSv1 and validate that we failed handshake */ + mock_client(&io_pair, version); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + /* Pick cipher preference with TLSv1.2 as a minimum version */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "CloudFront-TLS-1-2-2019")); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /* Check that blinding was not invoked */ + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + + /* Close the pipes */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_nonblocking_test.c b/tests/unit/s2n_self_talk_nonblocking_test.c index d0882a11b12..e9255f53804 100644 --- a/tests/unit/s2n_self_talk_nonblocking_test.c +++ b/tests/unit/s2n_self_talk_nonblocking_test.c @@ -1,393 +1,393 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -static const float minimum_send_percent = 5.0; - -#define MIN_PERCENT_COMPLETE(remaining, total) ((((total - remaining) / (total * 1.0)) * 100.0) > minimum_send_percent) - -int mock_client(struct s2n_test_io_pair *io_pair, uint8_t *expected_data, uint32_t size) -{ - uint8_t *buffer = malloc(size); - uint8_t *ptr = buffer; - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - /* If something goes wrong, and the server never finishes sending, - * we'll want to have the child process die eventually, or certain - * CI/CD pipelines might never complete */ - int should_block = 1; - - /* Give the server a chance to listen */ - sleep(1); - - client_conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - s2n_connection_set_config(client_conn, client_config); - POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); - - s2n_connection_set_io_pair(client_conn, io_pair); - - result = s2n_negotiate(client_conn, &blocked); - if (result < 0) { - return 1; - } - - /* Receive 10MB of data */ - uint32_t remaining = size; - while (remaining) { - int r = s2n_recv(client_conn, ptr, remaining, &blocked); - if (r < 0) { - return 1; - } - remaining -= r; - ptr += r; - if (should_block && MIN_PERCENT_COMPLETE(remaining, size)) { - raise(SIGSTOP); - should_block = 0; - } - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(client_conn, &blocked); - } while (shutdown_rc != 0); - - for (size_t i = 0; i < size; i++) { - if (buffer[i] != expected_data[i]) { - return 1; - } - } - - free(buffer); - s2n_connection_free(client_conn); - s2n_config_free(client_config); - - s2n_cleanup(); - - return 0; -} - -int mock_client_iov(struct s2n_test_io_pair *io_pair, struct iovec *iov, uint32_t iov_size) -{ - struct s2n_connection *client_conn = NULL; - struct s2n_config *client_config = NULL; - s2n_blocked_status blocked; - int result = 0; - int total_size = 0, i = 0; - int should_block = 1; - - for (i = 0; i < iov_size; i++) { - total_size += iov[i].iov_len; - } - uint8_t *buffer = malloc(total_size + iov[0].iov_len); - int buffer_offs = 0; - - /* Give the server a chance to listen */ - sleep(1); - - client_conn = s2n_connection_new(S2N_CLIENT); - client_config = s2n_config_new(); - s2n_config_disable_x509_verification(client_config); - s2n_connection_set_config(client_conn, client_config); - POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); - - s2n_connection_set_io_pair(client_conn, io_pair); - - result = s2n_negotiate(client_conn, &blocked); - if (result < 0) { - return 1; - } - - uint32_t remaining = total_size; - while (remaining) { - int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); - if (r < 0) { - return 1; - } - remaining -= r; - buffer_offs += r; - if (should_block && MIN_PERCENT_COMPLETE(remaining, total_size)) { - raise(SIGSTOP); - should_block = 0; - } - } - - remaining = iov[0].iov_len; - while (remaining) { - int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); - if (r < 0) { - return 1; - } - remaining -= r; - buffer_offs += r; - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(client_conn, &blocked); - } while (shutdown_rc != 0); - - for (i = 0, buffer_offs = 0; i < iov_size; i++) { - if (memcmp(iov[i].iov_base, &buffer[buffer_offs], iov[i].iov_len)) { - return 1; - } - buffer_offs += iov[i].iov_len; - } - - if (memcmp(iov[0].iov_base, &buffer[buffer_offs], iov[0].iov_len)) { - return 1; - } - - free(buffer); - s2n_connection_free(client_conn); - s2n_config_free(client_config); - - return 0; -} - -S2N_RESULT cleanup_io_data(struct iovec **iov, int iov_size, struct s2n_blob *blob) -{ - if (*iov) { - for (int i = 0; i < iov_size; i++) { - free((*iov)[i].iov_base); - } - free(*iov); - } else { - s2n_free(blob); - } - - return S2N_RESULT_OK; -} - -int test_send(int use_tls13, int use_iov, int prefer_throughput) -{ - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; - - /* Get some random data to send/receive */ - uint32_t data_size = 0; - struct s2n_blob blob = { 0 }; - - /* These numbers are chosen so that some of the payload is bigger - * than max TLS1.3 record size (2**14 + 1), which is needed to validate - * that we handle record sizing correctly. - * (see https://github.com/awslabs/s2n/pull/1780). - * - * Note that for each iov in the list, the payload size is doubled - * to ensure the implementation handles various lengths. - * - * With the current values, it will include - * * 8192 bytes - * * 16384 bytes - * * 32768 bytes - * * 65536 bytes - * * 131072 bytes - * * 262144 bytes - * * 524288 bytes */ - int iov_payload_size = 8192, iov_size = 7; - - struct iovec *iov = NULL; - if (!use_iov) { - data_size = 10000000; - EXPECT_SUCCESS(s2n_alloc(&blob, data_size)); - EXPECT_OK(s2n_get_public_random_data(&blob)); - } else { - iov = malloc(sizeof(*iov) * iov_size); - data_size = 0; - for (int i = 0; i < iov_size; i++, iov_payload_size *= 2) { - struct s2n_blob blob_local = { 0 }; - iov[i].iov_base = blob_local.data = malloc(iov_payload_size); - iov[i].iov_len = blob_local.size = iov_payload_size; - EXPECT_NOT_NULL(blob_local.data); - EXPECT_OK(s2n_get_public_random_data(&blob_local)); - data_size += iov_payload_size; - } - } - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Run the client */ - const int client_rc = !use_iov ? mock_client(&io_pair, blob.data, data_size) : mock_client_iov(&io_pair, iov, iov_size); - - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); - exit(client_rc); - } - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), s2n_cert_chain_and_key_ptr_free); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - - if (use_tls13) { - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all")); - } else { - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all_tls12")); - } - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - if (prefer_throughput) { - EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); - } else { - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); - } - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - EXPECT_SUCCESS(s2n_connection_use_corked_io(conn)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - /* Make sure we negotiated the expected version */ - if (use_tls13) { - EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); - } else { - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - } - - /* Make our pipes non-blocking */ - s2n_fd_set_non_blocking(io_pair.server); - - /* Try to all 10MB of data, should be enough to fill PIPEBUF, so - we'll get blocked at some point */ - uint32_t remaining = data_size; - uint8_t *ptr = blob.data; - uint32_t iov_offs = 0; - - while (remaining) { - int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : - s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); - /* We will send up to minimum_send_percent, after which the client will automatically block itself. - * This allows us to cover the case where s2n_send gets EAGAIN on the very first call - * which can happen on certain platforms. By making sure we've successfully sent something - * we can ensure write -> block -> client drain -> write ordering.*/ - if (r < 0 && !MIN_PERCENT_COMPLETE(remaining, data_size)) { - continue; - } - - if (r < 0 && blocked == S2N_BLOCKED_ON_WRITE) { - /* We reached a blocked state and made no forward progress last call */ - break; - } - - EXPECT_TRUE(r > 0); - remaining -= r; - if (!use_iov) { - ptr += r; - } else { - iov_offs += r; - } - } - - /* Remaining should be between data_size and 0 */ - EXPECT_TRUE(remaining < data_size); - EXPECT_TRUE(remaining > 0); - - /* Wait for the child process to read some bytes and block itself*/ - sleep(1); - /* Wake the child process by sending it SIGCONT */ - EXPECT_SUCCESS(kill(pid, SIGCONT)); - - /* Make our sockets blocking again */ - s2n_fd_set_blocking(io_pair.server); - - /* Actually send the remaining data */ - while (remaining) { - int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : - s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); - EXPECT_TRUE(r > 0); - remaining -= r; - if (!use_iov) { - ptr += r; - } else { - iov_offs += r; - } - } - - if (use_iov) { - int r = s2n_sendv(conn, iov, 1, &blocked); - EXPECT_TRUE(r > 0); - } - - EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); - - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - - /* Clean up */ - EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - return 0; -} - -int main(int argc, char **argv) -{ - /* Ignore SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - - BEGIN_TEST(); - - for (int use_tls13 = 0; use_tls13 < 2; use_tls13++) { - for (int use_iovec = 0; use_iovec < 2; use_iovec++) { - for (int use_throughput = 0; use_throughput < 2; use_throughput++) { - test_send(use_tls13, use_iovec, use_throughput); - } - } - } - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static const float minimum_send_percent = 5.0; + +#define MIN_PERCENT_COMPLETE(remaining, total) ((((total - remaining) / (total * 1.0)) * 100.0) > minimum_send_percent) + +int mock_client(struct s2n_test_io_pair *io_pair, uint8_t *expected_data, uint32_t size) +{ + uint8_t *buffer = malloc(size); + uint8_t *ptr = buffer; + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + /* If something goes wrong, and the server never finishes sending, + * we'll want to have the child process die eventually, or certain + * CI/CD pipelines might never complete */ + int should_block = 1; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + return 1; + } + + /* Receive 10MB of data */ + uint32_t remaining = size; + while (remaining) { + int r = s2n_recv(client_conn, ptr, remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + ptr += r; + if (should_block && MIN_PERCENT_COMPLETE(remaining, size)) { + raise(SIGSTOP); + should_block = 0; + } + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + + for (size_t i = 0; i < size; i++) { + if (buffer[i] != expected_data[i]) { + return 1; + } + } + + free(buffer); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + s2n_cleanup(); + + return 0; +} + +int mock_client_iov(struct s2n_test_io_pair *io_pair, struct iovec *iov, uint32_t iov_size) +{ + struct s2n_connection *client_conn = NULL; + struct s2n_config *client_config = NULL; + s2n_blocked_status blocked; + int result = 0; + int total_size = 0, i = 0; + int should_block = 1; + + for (i = 0; i < iov_size; i++) { + total_size += iov[i].iov_len; + } + uint8_t *buffer = malloc(total_size + iov[0].iov_len); + int buffer_offs = 0; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + return 1; + } + + uint32_t remaining = total_size; + while (remaining) { + int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + buffer_offs += r; + if (should_block && MIN_PERCENT_COMPLETE(remaining, total_size)) { + raise(SIGSTOP); + should_block = 0; + } + } + + remaining = iov[0].iov_len; + while (remaining) { + int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + buffer_offs += r; + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + + for (i = 0, buffer_offs = 0; i < iov_size; i++) { + if (memcmp(iov[i].iov_base, &buffer[buffer_offs], iov[i].iov_len)) { + return 1; + } + buffer_offs += iov[i].iov_len; + } + + if (memcmp(iov[0].iov_base, &buffer[buffer_offs], iov[0].iov_len)) { + return 1; + } + + free(buffer); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + return 0; +} + +S2N_RESULT cleanup_io_data(struct iovec **iov, int iov_size, struct s2n_blob *blob) +{ + if (*iov) { + for (int i = 0; i < iov_size; i++) { + free((*iov)[i].iov_base); + } + free(*iov); + } else { + s2n_free(blob); + } + + return S2N_RESULT_OK; +} + +int test_send(int use_tls13, int use_iov, int prefer_throughput) +{ + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + + /* Get some random data to send/receive */ + uint32_t data_size = 0; + struct s2n_blob blob = { 0 }; + + /* These numbers are chosen so that some of the payload is bigger + * than max TLS1.3 record size (2**14 + 1), which is needed to validate + * that we handle record sizing correctly. + * (see https://github.com/awslabs/s2n/pull/1780). + * + * Note that for each iov in the list, the payload size is doubled + * to ensure the implementation handles various lengths. + * + * With the current values, it will include + * * 8192 bytes + * * 16384 bytes + * * 32768 bytes + * * 65536 bytes + * * 131072 bytes + * * 262144 bytes + * * 524288 bytes */ + int iov_payload_size = 8192, iov_size = 7; + + struct iovec *iov = NULL; + if (!use_iov) { + data_size = 10000000; + EXPECT_SUCCESS(s2n_alloc(&blob, data_size)); + EXPECT_OK(s2n_get_public_random_data(&blob)); + } else { + iov = malloc(sizeof(*iov) * iov_size); + data_size = 0; + for (int i = 0; i < iov_size; i++, iov_payload_size *= 2) { + struct s2n_blob blob_local = { 0 }; + iov[i].iov_base = blob_local.data = malloc(iov_payload_size); + iov[i].iov_len = blob_local.size = iov_payload_size; + EXPECT_NOT_NULL(blob_local.data); + EXPECT_OK(s2n_get_public_random_data(&blob_local)); + data_size += iov_payload_size; + } + } + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + const int client_rc = !use_iov ? mock_client(&io_pair, blob.data, data_size) : mock_client_iov(&io_pair, iov, iov_size); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); + exit(client_rc); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), s2n_cert_chain_and_key_ptr_free); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + if (use_tls13) { + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + if (prefer_throughput) { + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + } else { + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + } + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_use_corked_io(conn)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure we negotiated the expected version */ + if (use_tls13) { + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + } else { + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + } + + /* Make our pipes non-blocking */ + s2n_fd_set_non_blocking(io_pair.server); + + /* Try to all 10MB of data, should be enough to fill PIPEBUF, so + we'll get blocked at some point */ + uint32_t remaining = data_size; + uint8_t *ptr = blob.data; + uint32_t iov_offs = 0; + + while (remaining) { + int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : + s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); + /* We will send up to minimum_send_percent, after which the client will automatically block itself. + * This allows us to cover the case where s2n_send gets EAGAIN on the very first call + * which can happen on certain platforms. By making sure we've successfully sent something + * we can ensure write -> block -> client drain -> write ordering.*/ + if (r < 0 && !MIN_PERCENT_COMPLETE(remaining, data_size)) { + continue; + } + + if (r < 0 && blocked == S2N_BLOCKED_ON_WRITE) { + /* We reached a blocked state and made no forward progress last call */ + break; + } + + EXPECT_TRUE(r > 0); + remaining -= r; + if (!use_iov) { + ptr += r; + } else { + iov_offs += r; + } + } + + /* Remaining should be between data_size and 0 */ + EXPECT_TRUE(remaining < data_size); + EXPECT_TRUE(remaining > 0); + + /* Wait for the child process to read some bytes and block itself*/ + sleep(1); + /* Wake the child process by sending it SIGCONT */ + EXPECT_SUCCESS(kill(pid, SIGCONT)); + + /* Make our sockets blocking again */ + s2n_fd_set_blocking(io_pair.server); + + /* Actually send the remaining data */ + while (remaining) { + int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : + s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); + EXPECT_TRUE(r > 0); + remaining -= r; + if (!use_iov) { + ptr += r; + } else { + iov_offs += r; + } + } + + if (use_iov) { + int r = s2n_sendv(conn, iov, 1, &blocked); + EXPECT_TRUE(r > 0); + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Clean up */ + EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + return 0; +} + +int main(int argc, char **argv) +{ + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + BEGIN_TEST(); + + for (int use_tls13 = 0; use_tls13 < 2; use_tls13++) { + for (int use_iovec = 0; use_iovec < 2; use_iovec++) { + for (int use_throughput = 0; use_throughput < 2; use_throughput++) { + test_send(use_tls13, use_iovec, use_throughput); + } + } + } + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_session_id_test.c b/tests/unit/s2n_self_talk_session_id_test.c index fd46110dad1..9cb4fb1e2e4 100644 --- a/tests/unit/s2n_self_talk_session_id_test.c +++ b/tests/unit/s2n_self_talk_session_id_test.c @@ -1,673 +1,673 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_bitmap.h" - -#define MAX_KEY_LEN 32 -#define MAX_VAL_LEN 255 - -static const char SESSION_ID[] = "0123456789abcdef0123456789abcdef"; -static const char MSG[] = "Test"; - -struct session_cache_entry { - uint8_t key[MAX_KEY_LEN]; - uint8_t key_len; - uint8_t value[MAX_VAL_LEN]; - uint8_t value_len; - uint8_t lock; -}; - -struct session_cache_entry session_cache[256]; - -int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) -{ - struct session_cache_entry *cache = ctx; - - if (key_size == 0 || key_size > MAX_KEY_LEN) { - return -1; - } - if (value_size == 0 || value_size > MAX_VAL_LEN) { - return -1; - } - - uint8_t idx = ((const uint8_t *) key)[0]; - - EXPECT_MEMCPY_SUCCESS(cache[idx].key, key, key_size); - EXPECT_MEMCPY_SUCCESS(cache[idx].value, value, value_size); - - cache[idx].key_len = key_size; - cache[idx].value_len = value_size; - - return 0; -} - -int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) -{ - struct session_cache_entry *cache = ctx; - - if (key_size == 0 || key_size > MAX_KEY_LEN) { - return -1; - } - - uint8_t idx = ((const uint8_t *) key)[0]; - - if (cache[idx].lock) { - /* here we mock a remote connection/event blocking the handshake - * state machine, until lock is free - */ - cache[idx].lock = 0; - return S2N_CALLBACK_BLOCKED; - } - - if (cache[idx].key_len != key_size) { - return -1; - } - - if (memcmp(cache[idx].key, key, key_size)) { - return -1; - } - - if (*value_size < cache[idx].value_len) { - return -1; - } - - *value_size = cache[idx].value_len; - EXPECT_MEMCPY_SUCCESS(value, cache[idx].value, cache[idx].value_len); - - return 0; -} - -int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) -{ - struct session_cache_entry *cache = ctx; - - if (key_size == 0 || key_size > MAX_KEY_LEN) { - return -1; - } - - uint8_t idx = ((const uint8_t *) key)[0]; - - if (cache[idx].key_len == 0) { - return 0; - } - - if (cache[idx].key_len != key_size) { - return -1; - } - - if (memcmp(cache[idx].key, key, key_size)) { - return -1; - } - - cache[idx].key_len = 0; - cache[idx].value_len = 0; - - return 0; -} - -/* init session cache lock field, which is used to mock a remote - * connection/event block - */ -static void initialize_cache() -{ - for (int i = 0; i < 256; i++) { - session_cache[i].lock = 1; - } -} - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - size_t serialized_session_state_length = 0; - uint8_t serialized_session_state[256] = { 0 }; - - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int result = 0; - - /* Give the server a chance to listen */ - sleep(1); - - /* Initial handshake */ - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - s2n_config_set_cipher_preferences(config, "20240501"); - s2n_config_disable_x509_verification(config); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - /* Set the session id to ensure we're able to fallback to full handshake if session is not in server cache */ - EXPECT_MEMCPY_SUCCESS(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN); - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - - if (s2n_negotiate(conn, &blocked) != 0) { - result = 1; - } - - /* Make sure we did a full handshake */ - if (!IS_FULL_HANDSHAKE(conn)) { - result = 2; - } - - /* Save session state from the connection */ - memset(serialized_session_state, 0, sizeof(serialized_session_state)); - serialized_session_state_length = s2n_connection_get_session_length(conn); - if (serialized_session_state_length > sizeof(serialized_session_state)) { - result = 3; - } - - /* Send very low session buffer size and see that you can get an error */ - if (s2n_connection_get_session(conn, serialized_session_state, 1) == 0) { - result = 4; - } - - if (s2n_errno != S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG) { - result = 5; - } - - if ((size_t) s2n_connection_get_session(conn, serialized_session_state, serialized_session_state_length) != serialized_session_state_length) { - result = 6; - } - - /* server would choose a session ID for client */ - if (memcmp(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN) == 0) { - result = 7; - } - - if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { - result = 8; - } - - int shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - - /* Give the server a chance to avoid sigpipe */ - sleep(1); - - /* Session resumption */ - conn = s2n_connection_new(S2N_CLIENT); - s2n_connection_set_io_pair(conn, io_pair); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - - /* Set session state on client connection */ - if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { - result = 9; - } - - if (s2n_negotiate(conn, &blocked) != 0) { - result = 10; - } - - /* Make sure we did a abbreviated handshake */ - if (!IS_RESUMPTION_HANDSHAKE(conn)) { - result = 11; - } - - if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { - result = 12; - } - - shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - - /* Give the server a chance to avoid sigpipe */ - sleep(1); - - /* Session resumption with bad session state */ - conn = s2n_connection_new(S2N_CLIENT); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - s2n_connection_set_io_pair(conn, io_pair); - - /* Change the format of the session state and check we cannot deserialize it */ - serialized_session_state[0] = 3; - if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) == 0) { - result = 13; - } - - if (s2n_errno != S2N_ERR_INVALID_SERIALIZED_SESSION_STATE) { - result = 14; - } - - serialized_session_state[0] = 0; - /* Change the protocol version (36th byte) in session state */ - if (serialized_session_state_length < 36) { - result = 15; - } - - serialized_session_state[35] = 30; - if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { - result = 16; - } - - if (s2n_negotiate(conn, &blocked) == 0) { - result = 17; - } - - if (s2n_errno != S2N_ERR_BAD_MESSAGE) { - result = 18; - } - - s2n_connection_free(conn); - s2n_config_free(config); - - /* Give the server a chance to avoid sigpipe */ - sleep(1); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - exit(result); -} - -int main(int argc, char **argv) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int status = 0; - pid_t pid = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - char buffer[256]; - int bytes_read = 0; - int shutdown_rc = -1; - uint64_t now = 0; - uint8_t session_id_from_server[MAX_KEY_LEN]; - uint8_t session_id_from_client[MAX_KEY_LEN]; - - /* aes keys. Used for session ticket/session data encryption. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ - uint8_t ticket_key_name[16] = "2018.07.26.15\0"; - uint8_t ticket_key[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, - 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, - 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, - 0xcb, 0x04 }; - - BEGIN_TEST(); - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - /* Initial handshake */ - { - /* Initialize the cache so the client and server start off on the same page */ - initialize_cache(); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache)); - EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache)); - EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache)); - - /* Although we disable session ticket, as long as session cache - * callbacks are binded, session ticket key storage would be initialized - */ - POSIX_GUARD(s2n_config_set_session_cache_onoff(config, 1)); - POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), ticket_key, sizeof(ticket_key), now / ONE_SEC_IN_NANOS)); - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - int r = s2n_negotiate(conn, &blocked); - /* first time it always blocks the handshake, as we mock a remote - * connection/event from the lock - */ - EXPECT_EQUAL(r, -1); - EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - /* Make sure the get_session_id and get_session_id_length APIs are - * working as expected */ - EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); - EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_server, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); - - /* Make sure we did a full TLS1.2 handshake */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(conn)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - /* Ensure the message was delivered */ - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); - EXPECT_EQUAL(bytes_read, sizeof(MSG)); - EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); - - /* Shutdown handshake */ - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - /* Clean up */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Session resumption */ - { - initialize_cache(); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - int r = s2n_negotiate(conn, &blocked); - /* first time it always blocks the handshake, as we mock a remote - * connection/event from the lock - */ - EXPECT_EQUAL(r, -1); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - - /* Make sure the get_session_id and get_session_id_length APIs are - * working as expected */ - EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); - EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_client, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); - EXPECT_EQUAL(0, memcmp(session_id_from_client, session_id_from_server, MAX_KEY_LEN)); - - /* Make sure we did a abbreviated handshake */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(conn)); - - /* Ensure the message was delivered */ - memset(buffer, 0, sizeof(buffer)); - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); - EXPECT_EQUAL(bytes_read, sizeof(MSG)); - EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); - - /* Shutdown handshake */ - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - /* Clean up */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Session resumption with bad session state on client side */ - { - initialize_cache(); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - int r = s2n_negotiate(conn, &blocked); - EXPECT_EQUAL(r, -1); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); - /* Verify we failed to negotiate */ - EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); - - /* Clean up */ - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Close the pipes */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Session caching with a server that does not support EMS */ - { - initialize_cache(); - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - s2n_config_disable_x509_verification(config); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate until server has read the Client Hello message but hasn't written the server hello message */ - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); - - /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false - * and removing the EMS extension from our received extensions. */ - server_conn->ems_negotiated = false; - s2n_extension_type_id ems_ext_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); - S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); - - /* Connection is successful and EMS is not negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_FALSE(server_conn->ems_negotiated); - EXPECT_FALSE(client_conn->ems_negotiated); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); - uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Wipe connections and set up new handshake */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Server will block the first time cache is accessed */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); - - /* Resumed connection is successful and EMS is not negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 0); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_FALSE(server_conn->ems_negotiated); - EXPECT_FALSE(client_conn->ems_negotiated); - EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *= type=test - *# If the original session used the "extended_master_secret" - *# extension but the new ClientHello does not contain it, the server - *# MUST abort the abbreviated handshake. - **/ - { - initialize_cache(); - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - s2n_config_disable_x509_verification(config); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Connection is successful and EMS is negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(server_conn->ems_negotiated); - EXPECT_TRUE(client_conn->ems_negotiated); - - size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); - uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Wipe connections and set up new handshake */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the client to not send the EMS extension */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - client_conn->ems_negotiated = false; - - /* Server will block the first time cache is accessed */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); - - /* Server did not receive the EMS extension from client */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_MISSING_EXTENSION); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *= type=test - *# If the original session did not use the "extended_master_secret" - *# extension but the new ClientHello contains the extension, then the - *# server MUST NOT perform the abbreviated handshake. Instead, it - *# SHOULD continue with a full handshake (as described in - *# Section 5.2) to negotiate a new session. - **/ - { - initialize_cache(); - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - s2n_config_disable_x509_verification(config); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Negotiate until server has read the Client Hello message but hasn't written the Server Hello message */ - EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); - EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); - - /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false - * and removing the EMS extension from our received extensions. */ - server_conn->ems_negotiated = false; - s2n_extension_type_id ems_ext_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); - S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); - - /* Connection is successful and EMS is not negotiated */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_FALSE(server_conn->ems_negotiated); - EXPECT_FALSE(client_conn->ems_negotiated); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); - uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - - /* Wipe connections and set up new handshake */ - EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force the client to send the EMS extension even though the original session did not negotiate EMS */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); - client_conn->ems_negotiated = true; - - /* Server will block the first time cache is accessed */ - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); - - /* Fallback to full handshake */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 1); - EXPECT_TRUE(server_conn->ems_negotiated); - EXPECT_TRUE(client_conn->ems_negotiated); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Clean up */ - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - free(cert_chain_pem); - free(private_key_pem); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_bitmap.h" + +#define MAX_KEY_LEN 32 +#define MAX_VAL_LEN 255 + +static const char SESSION_ID[] = "0123456789abcdef0123456789abcdef"; +static const char MSG[] = "Test"; + +struct session_cache_entry { + uint8_t key[MAX_KEY_LEN]; + uint8_t key_len; + uint8_t value[MAX_VAL_LEN]; + uint8_t value_len; + uint8_t lock; +}; + +struct session_cache_entry session_cache[256]; + +int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + if (value_size == 0 || value_size > MAX_VAL_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + EXPECT_MEMCPY_SUCCESS(cache[idx].key, key, key_size); + EXPECT_MEMCPY_SUCCESS(cache[idx].value, value, value_size); + + cache[idx].key_len = key_size; + cache[idx].value_len = value_size; + + return 0; +} + +int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].lock) { + /* here we mock a remote connection/event blocking the handshake + * state machine, until lock is free + */ + cache[idx].lock = 0; + return S2N_CALLBACK_BLOCKED; + } + + if (cache[idx].key_len != key_size) { + return -1; + } + + if (memcmp(cache[idx].key, key, key_size)) { + return -1; + } + + if (*value_size < cache[idx].value_len) { + return -1; + } + + *value_size = cache[idx].value_len; + EXPECT_MEMCPY_SUCCESS(value, cache[idx].value, cache[idx].value_len); + + return 0; +} + +int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].key_len == 0) { + return 0; + } + + if (cache[idx].key_len != key_size) { + return -1; + } + + if (memcmp(cache[idx].key, key, key_size)) { + return -1; + } + + cache[idx].key_len = 0; + cache[idx].value_len = 0; + + return 0; +} + +/* init session cache lock field, which is used to mock a remote + * connection/event block + */ +static void initialize_cache() +{ + for (int i = 0; i < 256; i++) { + session_cache[i].lock = 1; + } +} + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + size_t serialized_session_state_length = 0; + uint8_t serialized_session_state[256] = { 0 }; + + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int result = 0; + + /* Give the server a chance to listen */ + sleep(1); + + /* Initial handshake */ + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_set_cipher_preferences(config, "20240501"); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + /* Set the session id to ensure we're able to fallback to full handshake if session is not in server cache */ + EXPECT_MEMCPY_SUCCESS(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + + if (s2n_negotiate(conn, &blocked) != 0) { + result = 1; + } + + /* Make sure we did a full handshake */ + if (!IS_FULL_HANDSHAKE(conn)) { + result = 2; + } + + /* Save session state from the connection */ + memset(serialized_session_state, 0, sizeof(serialized_session_state)); + serialized_session_state_length = s2n_connection_get_session_length(conn); + if (serialized_session_state_length > sizeof(serialized_session_state)) { + result = 3; + } + + /* Send very low session buffer size and see that you can get an error */ + if (s2n_connection_get_session(conn, serialized_session_state, 1) == 0) { + result = 4; + } + + if (s2n_errno != S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG) { + result = 5; + } + + if ((size_t) s2n_connection_get_session(conn, serialized_session_state, serialized_session_state_length) != serialized_session_state_length) { + result = 6; + } + + /* server would choose a session ID for client */ + if (memcmp(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN) == 0) { + result = 7; + } + + if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { + result = 8; + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + /* Session resumption */ + conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_io_pair(conn, io_pair); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + + /* Set session state on client connection */ + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { + result = 9; + } + + if (s2n_negotiate(conn, &blocked) != 0) { + result = 10; + } + + /* Make sure we did a abbreviated handshake */ + if (!IS_RESUMPTION_HANDSHAKE(conn)) { + result = 11; + } + + if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { + result = 12; + } + + shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + /* Session resumption with bad session state */ + conn = s2n_connection_new(S2N_CLIENT); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + s2n_connection_set_io_pair(conn, io_pair); + + /* Change the format of the session state and check we cannot deserialize it */ + serialized_session_state[0] = 3; + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) == 0) { + result = 13; + } + + if (s2n_errno != S2N_ERR_INVALID_SERIALIZED_SESSION_STATE) { + result = 14; + } + + serialized_session_state[0] = 0; + /* Change the protocol version (36th byte) in session state */ + if (serialized_session_state_length < 36) { + result = 15; + } + + serialized_session_state[35] = 30; + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { + result = 16; + } + + if (s2n_negotiate(conn, &blocked) == 0) { + result = 17; + } + + if (s2n_errno != S2N_ERR_BAD_MESSAGE) { + result = 18; + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + exit(result); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status = 0; + pid_t pid = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + char buffer[256]; + int bytes_read = 0; + int shutdown_rc = -1; + uint64_t now = 0; + uint8_t session_id_from_server[MAX_KEY_LEN]; + uint8_t session_id_from_client[MAX_KEY_LEN]; + + /* aes keys. Used for session ticket/session data encryption. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name[16] = "2018.07.26.15\0"; + uint8_t ticket_key[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }; + + BEGIN_TEST(); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + /* Initial handshake */ + { + /* Initialize the cache so the client and server start off on the same page */ + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache)); + EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache)); + EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache)); + + /* Although we disable session ticket, as long as session cache + * callbacks are binded, session ticket key storage would be initialized + */ + POSIX_GUARD(s2n_config_set_session_cache_onoff(config, 1)); + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), ticket_key, sizeof(ticket_key), now / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + int r = s2n_negotiate(conn, &blocked); + /* first time it always blocks the handshake, as we mock a remote + * connection/event from the lock + */ + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure the get_session_id and get_session_id_length APIs are + * working as expected */ + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); + EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_server, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); + + /* Make sure we did a full TLS1.2 handshake */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure the message was delivered */ + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); + EXPECT_EQUAL(bytes_read, sizeof(MSG)); + EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); + + /* Shutdown handshake */ + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session resumption */ + { + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + int r = s2n_negotiate(conn, &blocked); + /* first time it always blocks the handshake, as we mock a remote + * connection/event from the lock + */ + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure the get_session_id and get_session_id_length APIs are + * working as expected */ + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); + EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_client, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); + EXPECT_EQUAL(0, memcmp(session_id_from_client, session_id_from_server, MAX_KEY_LEN)); + + /* Make sure we did a abbreviated handshake */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(conn)); + + /* Ensure the message was delivered */ + memset(buffer, 0, sizeof(buffer)); + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); + EXPECT_EQUAL(bytes_read, sizeof(MSG)); + EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); + + /* Shutdown handshake */ + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session resumption with bad session state on client side */ + { + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + int r = s2n_negotiate(conn, &blocked); + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + /* Verify we failed to negotiate */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Close the pipes */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Session caching with a server that does not support EMS */ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message but hasn't written the server hello message */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Resumed connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 0); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session used the "extended_master_secret" + *# extension but the new ClientHello does not contain it, the server + *# MUST abort the abbreviated handshake. + **/ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Connection is successful and EMS is negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the client to not send the EMS extension */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + client_conn->ems_negotiated = false; + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Server did not receive the EMS extension from client */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + **/ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message but hasn't written the Server Hello message */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the client to send the EMS extension even though the original session did not negotiate EMS */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + client_conn->ems_negotiated = true; + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Fallback to full handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 1); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Clean up */ + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_tls12_test.c b/tests/unit/s2n_self_talk_tls12_test.c index 90df3c3e6dc..8f8a7fd9516 100644 --- a/tests/unit/s2n_self_talk_tls12_test.c +++ b/tests/unit/s2n_self_talk_tls12_test.c @@ -1,207 +1,207 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" - -#define SUPPORTED_CERTIFICATE_FORMATS (2) - -static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; -static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; - -void mock_client(struct s2n_test_io_pair *io_pair) -{ - char buffer[0xffff]; - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - - /* Give the server a chance to listen */ - sleep(1); - - conn = s2n_connection_new(S2N_CLIENT); - config = s2n_config_new(); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - s2n_config_disable_x509_verification(config); - s2n_connection_set_config(conn, config); - - s2n_connection_set_io_pair(conn, io_pair); - - s2n_negotiate(conn, &blocked); - - s2n_connection_free_handshake(conn); - - uint16_t timeout = 1; - s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); - int i = 0; - for (i = 1; i < 0xffff - 100; i += 100) { - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - s2n_send(conn, buffer, i, &blocked); - } - - for (int j = 0; j < i; j++) { - buffer[j] = 33; - } - - /* release the buffers here to validate we can continue IO after */ - s2n_connection_release_buffers(conn); - - /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ - struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; - int r = 0; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - /* Active application bytes consumed is reset to 0 in before writing data. */ - /* Its value should equal to bytes written after writing */ - ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); - if (bytes_written != conn->active_application_bytes_consumed) { - exit(0); - } - - int shutdown_rc = -1; - while (shutdown_rc != 0) { - shutdown_rc = s2n_shutdown(conn, &blocked); - } - - s2n_connection_free(conn); - s2n_config_free(config); - - /* Give the server a chance to a void a sigpipe */ - sleep(1); - - s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - - exit(0); -} - -int main(int argc, char **argv) -{ - struct s2n_connection *conn = NULL; - struct s2n_config *config = NULL; - s2n_blocked_status blocked; - int status = 0; - char *cert_chain_pem = NULL; - char *private_key_pem = NULL; - char *dhparams_pem = NULL; - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { - struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; - - /* Create a pipe */ - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); - - /* Create a child process */ - pid_t pid = fork(); - if (pid == 0) { - /* This is the client process, close the server end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - /* Write the fragmented hello message */ - mock_client(&io_pair); - } - - EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); - - /* This is the server process, close the client end of the pipe */ - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); - } - - if (is_dh_key_exchange) { - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); - } - - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Set up the connection to read from the fd */ - EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); - - /* Negotiate the handshake. */ - EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - - char buffer[0xffff]; - for (int i = 1; i < 0xffff; i += 100) { - char *ptr = buffer; - int size = i; - - do { - int bytes_read = 0; - EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); - - size -= bytes_read; - ptr += bytes_read; - } while (size); - - for (int j = 0; j < i; j++) { - EXPECT_EQUAL(buffer[j], 33); - } - - /* release the buffers here to validate we can continue IO after */ - EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); - } - - int shutdown_rc = -1; - do { - shutdown_rc = s2n_shutdown(conn, &blocked); - EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); - } while (shutdown_rc != 0); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); - } - EXPECT_SUCCESS(s2n_config_free(config)); - - /* Clean up */ - EXPECT_EQUAL(waitpid(-1, &status, 0), pid); - EXPECT_EQUAL(status, 0); - EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); - - free(cert_chain_pem); - free(private_key_pem); - free(dhparams_pem); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + char buffer[0xffff]; + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + s2n_negotiate(conn, &blocked); + + s2n_connection_free_handshake(conn); + + uint16_t timeout = 1; + s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); + int i = 0; + for (i = 1; i < 0xffff - 100; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + s2n_send(conn, buffer, i, &blocked); + } + + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + /* release the buffers here to validate we can continue IO after */ + s2n_connection_release_buffers(conn); + + /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ + struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; + int r = 0; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + /* Active application bytes consumed is reset to 0 in before writing data. */ + /* Its value should equal to bytes written after writing */ + ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); + if (bytes_written != conn->active_application_bytes_consumed) { + exit(0); + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status = 0; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid_t pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + char buffer[0xffff]; + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + + /* release the buffers here to validate we can continue IO after */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_send_key_update_test.c b/tests/unit/s2n_send_key_update_test.c index f7f217f7731..d632b82712e 100644 --- a/tests/unit/s2n_send_key_update_test.c +++ b/tests/unit/s2n_send_key_update_test.c @@ -1,198 +1,198 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" - -int s2n_key_update_write(struct s2n_blob *out); - -static int s2n_test_init_encryption(struct s2n_connection *conn) -{ - struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - conn->server->cipher_suite = cipher_suite; - conn->client->cipher_suite = cipher_suite; - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - - /* Just some data that's the right length */ - S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); - S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); - S2N_BLOB_FROM_HEX(application_secret, - "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" - "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); - - struct s2n_session_key *server_session_key = &conn->server->server_key; - struct s2n_session_key *client_session_key = &conn->server->server_key; - uint8_t *server_implicit_iv = conn->server->server_implicit_iv; - uint8_t *client_implicit_iv = conn->client->client_implicit_iv; - - /* Initialize record algorithm */ - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(server_session_key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(client_session_key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(server_session_key, &key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(client_session_key, &key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(server_session_key, &key)); - POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(client_session_key, &key)); - - /* Initialized secrets */ - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, application_secret.data, application_secret.size); - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); - - /* Copy iv bytes from input data */ - POSIX_CHECKED_MEMCPY(server_implicit_iv, iv.data, iv.size); - POSIX_CHECKED_MEMCPY(client_implicit_iv, iv.data, iv.size); - - return S2N_SUCCESS; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* The maximum record number converted to base 256 */ - uint8_t max_record_limit[S2N_TLS_SEQUENCE_NUM_LEN] = { 0, 0, 0, 0, 1, 106, 9, 229 }; - - /* s2n_send sends key update if necessary */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - server_conn->actual_protocol_version = S2N_TLS13; - client_conn->actual_protocol_version = S2N_TLS13; - - uint8_t zero_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; - - EXPECT_SUCCESS(s2n_test_init_encryption(server_conn)); - EXPECT_SUCCESS(s2n_test_init_encryption(client_conn)); - - DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); - - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); - - /* Mimic key update send conditions */ - for (size_t i = 0; i < S2N_TLS_SEQUENCE_NUM_LEN; i++) { - server_conn->secure->server_sequence_number[i] = max_record_limit[i]; - } - - /* Next message to send will trigger key update message*/ - s2n_blocked_status blocked; - char message[] = "sent message"; - EXPECT_SUCCESS(s2n_send(server_conn, message, sizeof(message), &blocked)); - - /* Verify key update happened */ - EXPECT_BYTEARRAY_NOT_EQUAL(server_conn->secrets.version.tls13.server_app_secret, client_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); - EXPECT_BYTEARRAY_EQUAL(server_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); - - /* Receive keyupdate message */ - uint8_t data[100]; - EXPECT_SUCCESS(s2n_recv(client_conn, data, sizeof(message), &blocked)); - EXPECT_BYTEARRAY_EQUAL(data, message, sizeof(message)); - EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.server_app_secret, server_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); - EXPECT_BYTEARRAY_EQUAL(client_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* TLS 1.2 Server that receives TLS 1.3 KeyUpdate from Client should close connection */ - { - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - char *cert_chain = NULL; - char *private_key = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *server_config = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - struct s2n_config *client_config = NULL; - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - client_conn->actual_protocol_version = S2N_TLS12; - client_conn->server_protocol_version = S2N_TLS12; - client_conn->client_protocol_version = S2N_TLS12; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - server_conn->actual_protocol_version = S2N_TLS12; - server_conn->server_protocol_version = S2N_TLS12; - server_conn->client_protocol_version = S2N_TLS12; - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Force Client to send a TLS 1.3 KeyUpdate Message over TLS 1.2 connection */ - uint8_t key_update_data[S2N_KEY_UPDATE_MESSAGE_SIZE] = { 0 }; - struct s2n_blob key_update_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&key_update_blob, key_update_data, sizeof(key_update_data))); - EXPECT_SUCCESS(s2n_key_update_write(&key_update_blob)); - EXPECT_OK(s2n_record_write(client_conn, TLS_HANDSHAKE, &key_update_blob)); - EXPECT_SUCCESS(s2n_flush(client_conn, &blocked)); - - /* Attempt to recv on Server conn, see KeyUpdate Message, and confirm connection is closed. */ - uint8_t server_message[128]; - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, server_message, sizeof(server_message), &blocked), S2N_ERR_BAD_MESSAGE); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - free(cert_chain); - free(private_key); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +int s2n_key_update_write(struct s2n_blob *out); + +static int s2n_test_init_encryption(struct s2n_connection *conn) +{ + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->server->cipher_suite = cipher_suite; + conn->client->cipher_suite = cipher_suite; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Just some data that's the right length */ + S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); + S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); + S2N_BLOB_FROM_HEX(application_secret, + "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" + "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); + + struct s2n_session_key *server_session_key = &conn->server->server_key; + struct s2n_session_key *client_session_key = &conn->server->server_key; + uint8_t *server_implicit_iv = conn->server->server_implicit_iv; + uint8_t *client_implicit_iv = conn->client->client_implicit_iv; + + /* Initialize record algorithm */ + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(server_session_key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->init(client_session_key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(server_session_key, &key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_encryption_key(client_session_key, &key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(server_session_key, &key)); + POSIX_GUARD_RESULT(cipher_suite->record_alg->cipher->set_decryption_key(client_session_key, &key)); + + /* Initialized secrets */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, application_secret.data, application_secret.size); + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Copy iv bytes from input data */ + POSIX_CHECKED_MEMCPY(server_implicit_iv, iv.data, iv.size); + POSIX_CHECKED_MEMCPY(client_implicit_iv, iv.data, iv.size); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* The maximum record number converted to base 256 */ + uint8_t max_record_limit[S2N_TLS_SEQUENCE_NUM_LEN] = { 0, 0, 0, 0, 1, 106, 9, 229 }; + + /* s2n_send sends key update if necessary */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + + uint8_t zero_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + + EXPECT_SUCCESS(s2n_test_init_encryption(server_conn)); + EXPECT_SUCCESS(s2n_test_init_encryption(client_conn)); + + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + /* Mimic key update send conditions */ + for (size_t i = 0; i < S2N_TLS_SEQUENCE_NUM_LEN; i++) { + server_conn->secure->server_sequence_number[i] = max_record_limit[i]; + } + + /* Next message to send will trigger key update message*/ + s2n_blocked_status blocked; + char message[] = "sent message"; + EXPECT_SUCCESS(s2n_send(server_conn, message, sizeof(message), &blocked)); + + /* Verify key update happened */ + EXPECT_BYTEARRAY_NOT_EQUAL(server_conn->secrets.version.tls13.server_app_secret, client_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(server_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + + /* Receive keyupdate message */ + uint8_t data[100]; + EXPECT_SUCCESS(s2n_recv(client_conn, data, sizeof(message), &blocked)); + EXPECT_BYTEARRAY_EQUAL(data, message, sizeof(message)); + EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.server_app_secret, server_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* TLS 1.2 Server that receives TLS 1.3 KeyUpdate from Client should close connection */ + { + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char *cert_chain = NULL; + char *private_key = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *server_config = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_config *client_config = NULL; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Force Client to send a TLS 1.3 KeyUpdate Message over TLS 1.2 connection */ + uint8_t key_update_data[S2N_KEY_UPDATE_MESSAGE_SIZE] = { 0 }; + struct s2n_blob key_update_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&key_update_blob, key_update_data, sizeof(key_update_data))); + EXPECT_SUCCESS(s2n_key_update_write(&key_update_blob)); + EXPECT_OK(s2n_record_write(client_conn, TLS_HANDSHAKE, &key_update_blob)); + EXPECT_SUCCESS(s2n_flush(client_conn, &blocked)); + + /* Attempt to recv on Server conn, see KeyUpdate Message, and confirm connection is closed. */ + uint8_t server_message[128]; + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, server_message, sizeof(server_message), &blocked), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + free(cert_chain); + free(private_key); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_send_multirecord_test.c b/tests/unit/s2n_send_multirecord_test.c index f97ee5a063d..7a3f3d289fd 100644 --- a/tests/unit/s2n_send_multirecord_test.c +++ b/tests/unit/s2n_send_multirecord_test.c @@ -1,583 +1,583 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_random.h" - -/* clang-format off */ -#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } -#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } -#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } -#define EXPECTED_SEND_RESULT(bytes) { .result = bytes, .assert_result = true } -#define OK_SEND_RESULT { .result = INT_MAX } -/* clang-format on */ - -int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); -bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size); - -struct s2n_send_result { - int result; - int error; - bool assert_result; -}; - -struct s2n_send_context { - size_t calls; - size_t bytes_sent; - const struct s2n_send_result *results; - const size_t results_len; -}; - -static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) -{ - struct s2n_send_context *context = (struct s2n_send_context *) io_context; - POSIX_ENSURE_REF(context); - - POSIX_ENSURE_LT(context->calls, context->results_len); - const struct s2n_send_result *result = &context->results[context->calls]; - - int retval = S2N_MIN((int) len, result->result); - if (result->assert_result) { - POSIX_ENSURE_EQ(retval, len); - } - - context->calls++; - if (retval > 0) { - context->bytes_sent += retval; - } - - errno = result->error; - return retval; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - uint8_t test_data[] = "hello world"; - - uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; - struct s2n_blob large_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); - EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); - - /* Small record sizes will require a LOT of calls to s2n_send. - * Use this context when they should all succeed. - */ - struct s2n_send_result results_all_ok[50] = { 0 }; - for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { - results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; - } - const struct s2n_send_context context_all_ok = { - .results = results_all_ok, - .results_len = s2n_array_len(results_all_ok) - }; - - /* Setup a large output buffer that can contain all of large_test_data */ - const size_t buffer_size = 20000; - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); - - /* Setup an output buffer that is slightly too small for large_test_data */ - const uint32_t smaller_buffer_size = 17300; - DEFER_CLEANUP(struct s2n_config *smaller_buffer_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(smaller_buffer_config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(smaller_buffer_config, smaller_buffer_size)); - - /* Test s2n_should_flush */ - { - /* Flush if multirecord send not enabled */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - /* Multirecord send not enabled */ - EXPECT_FALSE(conn->multirecord_send); - EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); - - /* Multirecord send enabled */ - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_TRUE(conn->multirecord_send); - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - }; - - /* Flush if all data sent */ - { - ssize_t send_size = 100; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* No data sent */ - EXPECT_FALSE(s2n_should_flush(conn, send_size)); - - /* Some data sent */ - conn->current_user_data_consumed = send_size / 2; - EXPECT_FALSE(s2n_should_flush(conn, send_size)); - - /* All data sent */ - conn->current_user_data_consumed = send_size; - EXPECT_TRUE(s2n_should_flush(conn, send_size)); - }; - - /* Flush if buffer can't hold max size record */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - /* Uninitialized buffer */ - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - - /* Empty buffer */ - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - - /* Buffer not empty, but sufficient space remains */ - size_t max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, buffer_size - max_record_size)); - EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); - - /* Insufficient space in buffer */ - EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, 1)); - EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); - }; - }; - - /* Total data fits in a single record. - * Equivalent to not using multirecord. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_TRUE(context.bytes_sent > sizeof(test_data)); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Send buffer was configured too small for even a single record. - * Send smaller records. - * - * The minimum buffer size we allow generates a fragment size of 5, to prevent - * fragmenting KeyUpdate messages, which are always 5 bytes. At this minimum size, - * application data is also fragmented into 5 byte chunks, which is pretty silly, - * but is an edge case. - */ - { - DEFER_CLEANUP(struct s2n_config *min_buffer_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(min_buffer_config); - EXPECT_SUCCESS(s2n_config_set_send_buffer_size(min_buffer_config, S2N_MIN_SEND_BUFFER_SIZE)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, min_buffer_config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - ssize_t send_size = sizeof(test_data); - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, send_size, &blocked), send_size); - - /* Since each record only contains two bytes of payload, - * we need to send a number of records equal to our total send ceil(size / 2). - */ - uint8_t remainder = (send_size % S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) ? 1 : 0; - EXPECT_EQUAL(context.calls, (send_size / S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) + remainder); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_TRUE(context.bytes_sent > send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, S2N_MIN_SEND_BUFFER_SIZE); - }; - - /* Total data fits in multiple records. - * Without multirecord, this would result in multiple calls to send. - */ - uint16_t large_test_data_send_size = 0; - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_TRUE(context.bytes_sent > sizeof(large_test_data)); - large_test_data_send_size = context.bytes_sent; - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Total data with multiple records too large for the send buffer. - * Call send multiple times. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - - EXPECT_EQUAL(context.calls, 2); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - /* Even though it took more send calls, - * we still sent the same number of records with the same overhead. - */ - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); - }; - - /* Block while buffering multiple records. - * Send blocks until all buffered data is sent. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - const uint32_t partial_send = 10; - const uint32_t at_least_one_record = large_test_data_send_size - partial_send - 1; - const struct s2n_send_result results[] = { - /* First send writes less than one record before blocking */ - PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, - /* Second send writes at least one record before blocking */ - PARTIAL_SEND_RESULT(at_least_one_record), BLOCK_SEND_RESULT, - /* Third send completes */ - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send); - - /* Unlike when we buffer a single record at a time, s2n_send does not report each fragment / record flushed. - * Instead, it won't report any data as sent until all buffered data is flushed. - */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, at_least_one_record + partial_send); - - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Block while buffering multiple records across multiple send calls. - * Each send blocks until all buffered data is flushed. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); - - const uint32_t partial_send = 10; - const uint32_t at_least_one_flush = smaller_buffer_size - partial_send; - const struct s2n_send_result results[] = { - /* First send writes less than one record before blocking */ - PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, - /* Second send flushes the output buffer before blocking */ - PARTIAL_SEND_RESULT(at_least_one_flush), BLOCK_SEND_RESULT, - /* Third send completes */ - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send); - - /* Write the buffer, which contains two records. */ - ssize_t expected_sent = S2N_DEFAULT_FRAGMENT_LENGTH * 2; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_sent); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - - size_t offset = expected_sent; - expected_sent = sizeof(large_test_data) - offset; - EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), expected_sent); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); - }; - - /* Send a post-handshake message when records are buffered. - * - * We only test the KeyUpdate post-handshake message. That can trigger - * part way through a call to s2n_send if the encryption limit is reached. - * - * We can't reliably test NewSessionTicket post-handshake messages. - * Those could only trigger if an application called s2n_connection_add_new_tickets_to_send - * part way through a call to s2n_send, which requires calling from another thread - * at just the right (wrong?) time. - */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - /* Find size of a KeyUpdate record */ - size_t key_update_size = 0; - { - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_atomic_flag_set(&conn->key_update_pending); - EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - key_update_size = context.bytes_sent; - }; - EXPECT_TRUE(key_update_size > 0); - - const struct s2n_send_result results[] = { - /* We expect the buffer to be flushed before the post handshake message */ - OK_SEND_RESULT, - /* We expect the buffer to be flushed again after the post handshake message */ - EXPECTED_SEND_RESULT(key_update_size), - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - /* Find record limit */ - uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; - EXPECT_TRUE(limit > 0); - - /* Initialize sequence number */ - struct s2n_blob seq_num_blob = { 0 }; - struct s2n_stuffer seq_num_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); - - /* Set the sequence number so that a KeyUpdate triggers after one more record. */ - uint64_t initial_seq_num = limit - 1; - EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); - EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - - /* Send */ - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - - /* Verify KeyUpdate happened: the sequence number was reset */ - uint64_t final_seq_num = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); - EXPECT_TRUE(final_seq_num < initial_seq_num); - - /* Verify expected send behavior */ - size_t expected_calls = 1 /* first record */ + 1 /* KeyUpdate */ + 1 /* remaining records */; - EXPECT_EQUAL(context.calls, expected_calls); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - /* Test: Alert records are not buffered with ApplicationData records - * - * We flush before sending an alert, even if there is sufficient - * space for the alert record in the send buffer. - * - * If this behavior changed, then s2n_should_flush would need to consider - * the size of a possible alert. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - const uint32_t send_size = 10; - const uint32_t max_app_data_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(send_size); - const uint32_t max_alert_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_ALERT_LENGTH); - const uint32_t min_send_buffer_size = max_app_data_record_size + max_alert_record_size; - EXPECT_TRUE(min_send_buffer_size <= buffer_size); - - /* Queue the alert */ - EXPECT_OK(s2n_queue_reader_no_renegotiation_alert(conn)); - - /* Send the Application Data and Alert */ - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, send_size, &blocked), send_size); - - /* We expect two separate send calls: one for the application data record - * and one for the alert record. - */ - EXPECT_EQUAL(context.calls, 2); - - /* We expect that the output buffer never contained all data sent, - * since that data was split between two records. - */ - EXPECT_TRUE(conn->out.high_water_mark < context.bytes_sent); - }; - - /* Send a post-handshake message when records are buffered, and IO blocks */ - { - s2n_blocked_status blocked = 0; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - /* Find size of a KeyUpdate record */ - size_t key_update_size = 0; - { - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_atomic_flag_set(&conn->key_update_pending); - EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - key_update_size = context.bytes_sent; - } - EXPECT_TRUE(key_update_size > 0); - - /* Block the first two calls to send, only allowing the third to succeed. */ - const struct s2n_send_result results[] = { - /* Initial ApplicationData records */ - BLOCK_SEND_RESULT, - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - /* KeyUpdate record */ - BLOCK_SEND_RESULT, - BLOCK_SEND_RESULT, - EXPECTED_SEND_RESULT(key_update_size), - /* Remaining ApplicationData records */ - BLOCK_SEND_RESULT, - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - /* Find record limit */ - uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; - EXPECT_TRUE(limit > 0); - - /* Initialize sequence number */ - struct s2n_blob seq_num_blob = { 0 }; - struct s2n_stuffer seq_num_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); - - /* Set the sequence number so that a KeyUpdate triggers after one more record. */ - uint64_t initial_seq_num = limit - 1; - EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); - EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); - EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); - - /* Send until all data written */ - size_t total = 0; - while (total < sizeof(large_test_data)) { - ssize_t sent = s2n_send(conn, large_test_data + total, sizeof(large_test_data) - total, &blocked); - if (sent >= S2N_SUCCESS) { - total += sent; - } else { - EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - } - } - - /* Verify KeyUpdate happened: the sequence number was reset */ - uint64_t final_seq_num = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); - EXPECT_TRUE(final_seq_num < initial_seq_num); - - /* Verify expected send behavior */ - size_t expected_calls = s2n_array_len(results); - EXPECT_EQUAL(context.calls, expected_calls); - EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, buffer_size); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* clang-format off */ +#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } +#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } +#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } +#define EXPECTED_SEND_RESULT(bytes) { .result = bytes, .assert_result = true } +#define OK_SEND_RESULT { .result = INT_MAX } +/* clang-format on */ + +int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); +bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size); + +struct s2n_send_result { + int result; + int error; + bool assert_result; +}; + +struct s2n_send_context { + size_t calls; + size_t bytes_sent; + const struct s2n_send_result *results; + const size_t results_len; +}; + +static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_send_context *context = (struct s2n_send_context *) io_context; + POSIX_ENSURE_REF(context); + + POSIX_ENSURE_LT(context->calls, context->results_len); + const struct s2n_send_result *result = &context->results[context->calls]; + + int retval = S2N_MIN((int) len, result->result); + if (result->assert_result) { + POSIX_ENSURE_EQ(retval, len); + } + + context->calls++; + if (retval > 0) { + context->bytes_sent += retval; + } + + errno = result->error; + return retval; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_data[] = "hello world"; + + uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; + struct s2n_blob large_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); + EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); + + /* Small record sizes will require a LOT of calls to s2n_send. + * Use this context when they should all succeed. + */ + struct s2n_send_result results_all_ok[50] = { 0 }; + for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { + results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; + } + const struct s2n_send_context context_all_ok = { + .results = results_all_ok, + .results_len = s2n_array_len(results_all_ok) + }; + + /* Setup a large output buffer that can contain all of large_test_data */ + const size_t buffer_size = 20000; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + /* Setup an output buffer that is slightly too small for large_test_data */ + const uint32_t smaller_buffer_size = 17300; + DEFER_CLEANUP(struct s2n_config *smaller_buffer_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(smaller_buffer_config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(smaller_buffer_config, smaller_buffer_size)); + + /* Test s2n_should_flush */ + { + /* Flush if multirecord send not enabled */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + /* Multirecord send not enabled */ + EXPECT_FALSE(conn->multirecord_send); + EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); + + /* Multirecord send enabled */ + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_TRUE(conn->multirecord_send); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + }; + + /* Flush if all data sent */ + { + ssize_t send_size = 100; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* No data sent */ + EXPECT_FALSE(s2n_should_flush(conn, send_size)); + + /* Some data sent */ + conn->current_user_data_consumed = send_size / 2; + EXPECT_FALSE(s2n_should_flush(conn, send_size)); + + /* All data sent */ + conn->current_user_data_consumed = send_size; + EXPECT_TRUE(s2n_should_flush(conn, send_size)); + }; + + /* Flush if buffer can't hold max size record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Uninitialized buffer */ + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Empty buffer */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Buffer not empty, but sufficient space remains */ + size_t max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, buffer_size - max_record_size)); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Insufficient space in buffer */ + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, 1)); + EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); + }; + }; + + /* Total data fits in a single record. + * Equivalent to not using multirecord. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > sizeof(test_data)); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Send buffer was configured too small for even a single record. + * Send smaller records. + * + * The minimum buffer size we allow generates a fragment size of 5, to prevent + * fragmenting KeyUpdate messages, which are always 5 bytes. At this minimum size, + * application data is also fragmented into 5 byte chunks, which is pretty silly, + * but is an edge case. + */ + { + DEFER_CLEANUP(struct s2n_config *min_buffer_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(min_buffer_config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(min_buffer_config, S2N_MIN_SEND_BUFFER_SIZE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, min_buffer_config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + ssize_t send_size = sizeof(test_data); + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, send_size, &blocked), send_size); + + /* Since each record only contains two bytes of payload, + * we need to send a number of records equal to our total send ceil(size / 2). + */ + uint8_t remainder = (send_size % S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) ? 1 : 0; + EXPECT_EQUAL(context.calls, (send_size / S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) + remainder); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, S2N_MIN_SEND_BUFFER_SIZE); + }; + + /* Total data fits in multiple records. + * Without multirecord, this would result in multiple calls to send. + */ + uint16_t large_test_data_send_size = 0; + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > sizeof(large_test_data)); + large_test_data_send_size = context.bytes_sent; + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Total data with multiple records too large for the send buffer. + * Call send multiple times. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + EXPECT_EQUAL(context.calls, 2); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + /* Even though it took more send calls, + * we still sent the same number of records with the same overhead. + */ + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); + }; + + /* Block while buffering multiple records. + * Send blocks until all buffered data is sent. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const uint32_t partial_send = 10; + const uint32_t at_least_one_record = large_test_data_send_size - partial_send - 1; + const struct s2n_send_result results[] = { + /* First send writes less than one record before blocking */ + PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, + /* Second send writes at least one record before blocking */ + PARTIAL_SEND_RESULT(at_least_one_record), BLOCK_SEND_RESULT, + /* Third send completes */ + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Unlike when we buffer a single record at a time, s2n_send does not report each fragment / record flushed. + * Instead, it won't report any data as sent until all buffered data is flushed. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, at_least_one_record + partial_send); + + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Block while buffering multiple records across multiple send calls. + * Each send blocks until all buffered data is flushed. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); + + const uint32_t partial_send = 10; + const uint32_t at_least_one_flush = smaller_buffer_size - partial_send; + const struct s2n_send_result results[] = { + /* First send writes less than one record before blocking */ + PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, + /* Second send flushes the output buffer before blocking */ + PARTIAL_SEND_RESULT(at_least_one_flush), BLOCK_SEND_RESULT, + /* Third send completes */ + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Write the buffer, which contains two records. */ + ssize_t expected_sent = S2N_DEFAULT_FRAGMENT_LENGTH * 2; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_sent); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + size_t offset = expected_sent; + expected_sent = sizeof(large_test_data) - offset; + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), expected_sent); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); + }; + + /* Send a post-handshake message when records are buffered. + * + * We only test the KeyUpdate post-handshake message. That can trigger + * part way through a call to s2n_send if the encryption limit is reached. + * + * We can't reliably test NewSessionTicket post-handshake messages. + * Those could only trigger if an application called s2n_connection_add_new_tickets_to_send + * part way through a call to s2n_send, which requires calling from another thread + * at just the right (wrong?) time. + */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + /* Find size of a KeyUpdate record */ + size_t key_update_size = 0; + { + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + key_update_size = context.bytes_sent; + }; + EXPECT_TRUE(key_update_size > 0); + + const struct s2n_send_result results[] = { + /* We expect the buffer to be flushed before the post handshake message */ + OK_SEND_RESULT, + /* We expect the buffer to be flushed again after the post handshake message */ + EXPECTED_SEND_RESULT(key_update_size), + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + /* Find record limit */ + uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; + EXPECT_TRUE(limit > 0); + + /* Initialize sequence number */ + struct s2n_blob seq_num_blob = { 0 }; + struct s2n_stuffer seq_num_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); + + /* Set the sequence number so that a KeyUpdate triggers after one more record. */ + uint64_t initial_seq_num = limit - 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Send */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + /* Verify KeyUpdate happened: the sequence number was reset */ + uint64_t final_seq_num = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); + EXPECT_TRUE(final_seq_num < initial_seq_num); + + /* Verify expected send behavior */ + size_t expected_calls = 1 /* first record */ + 1 /* KeyUpdate */ + 1 /* remaining records */; + EXPECT_EQUAL(context.calls, expected_calls); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Test: Alert records are not buffered with ApplicationData records + * + * We flush before sending an alert, even if there is sufficient + * space for the alert record in the send buffer. + * + * If this behavior changed, then s2n_should_flush would need to consider + * the size of a possible alert. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + const uint32_t send_size = 10; + const uint32_t max_app_data_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(send_size); + const uint32_t max_alert_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_ALERT_LENGTH); + const uint32_t min_send_buffer_size = max_app_data_record_size + max_alert_record_size; + EXPECT_TRUE(min_send_buffer_size <= buffer_size); + + /* Queue the alert */ + EXPECT_OK(s2n_queue_reader_no_renegotiation_alert(conn)); + + /* Send the Application Data and Alert */ + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, send_size, &blocked), send_size); + + /* We expect two separate send calls: one for the application data record + * and one for the alert record. + */ + EXPECT_EQUAL(context.calls, 2); + + /* We expect that the output buffer never contained all data sent, + * since that data was split between two records. + */ + EXPECT_TRUE(conn->out.high_water_mark < context.bytes_sent); + }; + + /* Send a post-handshake message when records are buffered, and IO blocks */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + /* Find size of a KeyUpdate record */ + size_t key_update_size = 0; + { + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + key_update_size = context.bytes_sent; + } + EXPECT_TRUE(key_update_size > 0); + + /* Block the first two calls to send, only allowing the third to succeed. */ + const struct s2n_send_result results[] = { + /* Initial ApplicationData records */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + /* KeyUpdate record */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + EXPECTED_SEND_RESULT(key_update_size), + /* Remaining ApplicationData records */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + /* Find record limit */ + uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; + EXPECT_TRUE(limit > 0); + + /* Initialize sequence number */ + struct s2n_blob seq_num_blob = { 0 }; + struct s2n_stuffer seq_num_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); + + /* Set the sequence number so that a KeyUpdate triggers after one more record. */ + uint64_t initial_seq_num = limit - 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Send until all data written */ + size_t total = 0; + while (total < sizeof(large_test_data)) { + ssize_t sent = s2n_send(conn, large_test_data + total, sizeof(large_test_data) - total, &blocked); + if (sent >= S2N_SUCCESS) { + total += sent; + } else { + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + } + } + + /* Verify KeyUpdate happened: the sequence number was reset */ + uint64_t final_seq_num = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); + EXPECT_TRUE(final_seq_num < initial_seq_num); + + /* Verify expected send behavior */ + size_t expected_calls = s2n_array_len(results); + EXPECT_EQUAL(context.calls, expected_calls); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_send_test.c b/tests/unit/s2n_send_test.c index 5387f06e069..97f2e324dba 100644 --- a/tests/unit/s2n_send_test.c +++ b/tests/unit/s2n_send_test.c @@ -1,809 +1,809 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_random.h" - -/* clang-format off */ -#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } -#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } -#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } -#define OK_SEND_RESULT { .result = INT_MAX } -/* clang-format on */ - -enum s2n_test_mfl { - S2N_MFL_DEFAULT = 0, - S2N_MFL_LARGE, - S2N_MFL_SMALL, - S2N_MFL_MINIMUM, - S2N_MFL_COUNT, -}; - -static S2N_RESULT s2n_set_test_max_fragment_len(struct s2n_connection *conn, enum s2n_test_mfl mfl) -{ - switch (mfl) { - case S2N_MFL_DEFAULT: - break; - case S2N_MFL_LARGE: - EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); - break; - case S2N_MFL_SMALL: - EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); - break; - case S2N_MFL_MINIMUM: - conn->max_outgoing_fragment_length = mfl_code_to_length[1]; - break; - case S2N_MFL_COUNT: - RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); - break; - } - return S2N_RESULT_OK; -} - -struct s2n_send_result { - int result; - int error; -}; - -struct s2n_send_context { - size_t calls; - size_t bytes_sent; - const struct s2n_send_result *results; - const size_t results_len; -}; - -bool s2n_custom_send_fn_called = false; -int s2n_expect_concurrent_error_send_fn(void *io_context, const uint8_t *buf, uint32_t len) -{ - struct s2n_connection *conn = (struct s2n_connection *) io_context; - s2n_custom_send_fn_called = true; - - s2n_blocked_status blocked = 0; - ssize_t result = s2n_send(conn, buf, len, &blocked); - EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); - return result; -} - -static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) -{ - struct s2n_send_context *context = (struct s2n_send_context *) io_context; - POSIX_ENSURE_REF(context); - - POSIX_ENSURE_LT(context->calls, context->results_len); - const struct s2n_send_result *result = &context->results[context->calls]; - - int retval = S2N_MIN((int) len, result->result); - - context->calls++; - if (retval > 0) { - context->bytes_sent += retval; - } - - errno = result->error; - return retval; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - uint8_t test_data[] = "hello world"; - - uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; - struct s2n_blob large_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); - EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); - - /* Small record sizes will require a LOT of calls to s2n_send. - * Use this context when they should all succeed. - */ - struct s2n_send_result results_all_ok[50] = { 0 }; - for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { - results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; - } - const struct s2n_send_context context_all_ok = { - .results = results_all_ok, - .results_len = s2n_array_len(results_all_ok) - }; - - /* Calculating the record size for given data can be tricky. - * Instead, let's set the values based on the results of tests. - */ - ssize_t test_data_bytes_sent = 0; - - /* s2n_send */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - - /* Set the expected record size for future tests */ - test_data_bytes_sent = context.bytes_sent; - EXPECT_TRUE(test_data_bytes_sent > sizeof(test_data)); - }; - - /* Calculating the max record size for a given max fragment length can be tricky. - * Instead, let's set the values based on the results of tests. - */ - ssize_t max_frag_bytes_sent[S2N_MFL_COUNT] = { 0 }; - - /* Track the size of the output buffer. - * It should be constant across all tests with the same max fragment length. - */ - uint32_t out_size[S2N_MFL_COUNT] = { 0 }; - - /* Send exactly the maximum fragment size */ - for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); - uint32_t fragment_len = conn->max_outgoing_fragment_length; - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - /* Send exactly the fragment length */ - EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len, &blocked), fragment_len); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, 1); - - /* Set the expected record size for future tests */ - max_frag_bytes_sent[mfl] = context.bytes_sent; - EXPECT_TRUE(max_frag_bytes_sent[mfl] > 0); - - /* Set the expected output buffer size for future tests */ - out_size[mfl] = conn->out.blob.size; - EXPECT_TRUE(out_size[mfl] > 0); - - /* Sanity check: Send one byte more than the fragment length. - * If this is actually the maximum fragment length, one extra byte will - * lead to an extra record / extra call to send. - */ - context.calls = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len + 1, &blocked), fragment_len + 1); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, 2); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); - } - - /* s2n_send cannot be called concurrently */ - { - /* Setup connections */ - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - /* Setup bad send callback */ - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_expect_concurrent_error_send_fn)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) conn)); - EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); - - s2n_blocked_status blocked = 0; - s2n_custom_send_fn_called = false; - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), - S2N_ERR_IO); - EXPECT_TRUE(s2n_custom_send_fn_called); - EXPECT_EQUAL(0, conn->wire_bytes_out); - }; - - /* s2n_send tracks conn->wire_bytes_out on send */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_EQUAL(0, conn->wire_bytes_out); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - - EXPECT_EQUAL(context.calls, 1); - EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); - }; - - /* s2n_send tracks conn->wire_bytes_out on partial send */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_EQUAL(0, conn->wire_bytes_out); - - const uint32_t partial_send = 10; - const struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(partial_send), - CLOSED_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO); - - EXPECT_EQUAL(context.calls, 2); - EXPECT_EQUAL(context.bytes_sent, partial_send); - EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); - EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); - }; - - /* s2n_send sends all data, despite partial writes */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - const struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(1), - PARTIAL_SEND_RESULT(5), - PARTIAL_SEND_RESULT(2), - OK_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, s2n_array_len(results)); - EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send would block and must be retried */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - const uint32_t partial_send = 10; - const struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - /* First attempt blocks */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send); - - /* Second attempt blocks */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, partial_send * 2); - - /* Third attempt completes */ - EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* Calculating the record size for given data can be tricky. - * Instead, let's set the values based on the results of tests. - */ - ssize_t large_test_data_bytes_sent = 0; - - /* s2n_send sends multiple records worth of data */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_result results[] = { OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, s2n_array_len(results)); - - large_test_data_bytes_sent = context.bytes_sent; - EXPECT_TRUE(large_test_data_bytes_sent > sizeof(large_test_data)); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send sends all records and data, despite partial writes */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_result results[] = { - PARTIAL_SEND_RESULT(10), OK_SEND_RESULT, - OK_SEND_RESULT, - PARTIAL_SEND_RESULT(5), PARTIAL_SEND_RESULT(1), OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.calls, s2n_array_len(results)); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send would block while sending multiple records */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - const uint32_t partial_send = 10; - struct s2n_send_result results[] = { - OK_SEND_RESULT, - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - PARTIAL_SEND_RESULT(partial_send), - BLOCK_SEND_RESULT, - OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; - - /* First attempt blocks after writing one record */ - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), - S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, record_size + partial_send); - - /* Don't re-send the data already sent. */ - const uint32_t offset = S2N_DEFAULT_FRAGMENT_LENGTH; - - /* Second attempt blocks without writing another record */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, record_size + partial_send + partial_send); - - /* Third attempt completes */ - EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), - sizeof(large_test_data) - S2N_DEFAULT_FRAGMENT_LENGTH); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* s2n_send would block after sending multiple records. - * ALL flushed records must be reported to the caller. - */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_result results[] = { - OK_SEND_RESULT, - OK_SEND_RESULT, - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; - - /* First attempt blocks after writing two records */ - ssize_t expected_send = S2N_DEFAULT_FRAGMENT_LENGTH * 2; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_send); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, record_size * 2); - - /* Don't re-send the data already sent. */ - const uint32_t offset = expected_send; - - /* Second attempt completes */ - EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), - sizeof(large_test_data) - offset); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* Sending multiple records supports different maximum fragment lengths */ - for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); - - struct s2n_send_context context = context_all_ok; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - - /* We expect enough calls to send to split the payload into records */ - size_t expected_calls = ceil(sizeof(large_test_data) / (double) conn->max_outgoing_fragment_length); - EXPECT_EQUAL(context.calls, expected_calls); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); - } - - /* Test dynamic record threshold record fragmentation */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - /* Retrieve the fragment size to expect */ - uint16_t single_mtu_mfl = 0; - EXPECT_OK(s2n_record_min_write_payload_size(conn, &single_mtu_mfl)); - - /* Set the dynamic record threshold large enough for two small records */ - const uint32_t resize_threshold = single_mtu_mfl * 2; - EXPECT_SUCCESS(s2n_connection_set_dynamic_record_threshold(conn, resize_threshold, UINT16_MAX)); - - struct s2n_send_result results[] = { - /* Block before sending the first record so that we can examine - * the connection state after buffering the first record. - */ - BLOCK_SEND_RESULT, OK_SEND_RESULT, - /* Send the second record */ - BLOCK_SEND_RESULT, OK_SEND_RESULT, - /* Send the third record */ - OK_SEND_RESULT, - BLOCK_SEND_RESULT - }; - struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - - s2n_blocked_status blocked = 0; - const size_t send_size = single_mtu_mfl * 2; - - /* The first call to s2n_send blocks before sending the first record. */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, send_size, &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - /* No records have been sent yet. */ - EXPECT_EQUAL(context.bytes_sent, 0); - /* The first record is buffered, - * so its bytes still count towards the resize_threshold. - * We have NOT passed the threshold. - */ - EXPECT_EQUAL(conn->active_application_bytes_consumed, single_mtu_mfl); - EXPECT_TRUE(conn->active_application_bytes_consumed < resize_threshold); - /* Output buffer should be able to handle the default size, not the single MTU size. - * Otherwise, the output buffer would need to resize later. - */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - - /* The second call to s2n_send flushes the buffered first record, - * but blocks before sending the second record. - */ - ssize_t result = s2n_send(conn, large_test_data, send_size, &blocked); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - /* First small, single-MTU record was sent. */ - EXPECT_EQUAL(result, single_mtu_mfl); - EXPECT_TRUE(context.bytes_sent < ETH_MTU); - /* The second record is buffered, - * so its bytes count towards the resize_threshold. - * We have therefore hit the threshold. - */ - EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); - /* Output buffer should be able to handle the default size, not the single MTU size. - * Otherwise, the output buffer would need to resize later. - */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - - /* The third call to s2n_send flushes the second record. */ - result = s2n_send(conn, large_test_data, send_size - single_mtu_mfl, &blocked); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - /* Second small, single-MTU record was sent. */ - EXPECT_EQUAL(result, single_mtu_mfl); - /* There should be no change regarding the resize_threshold, - * since we did not construct any new records. - */ - EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); - /* Output buffer should be able to handle the default size, not the single MTU size. - * Otherwise, the output buffer would need to resize later. - */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - - /* The fourth call to s2n_send sends the third record. */ - result = s2n_send(conn, large_test_data, conn->max_outgoing_fragment_length * 2, &blocked); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - /* We have passed the resize_threshold, so records are no longer small. - * Instead they use the standard connection fragment length. - */ - EXPECT_TRUE(result > single_mtu_mfl); - EXPECT_EQUAL(result, conn->max_outgoing_fragment_length); - - /* Verify output buffer */ - EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); - }; - - /* Test: s2n_sendv_with_offset_total_size */ - { - const struct iovec test_multiple_bufs[] = { - { .iov_len = 0 }, - { .iov_len = 1 }, - { .iov_len = 2 }, - { .iov_len = 0 }, - { .iov_len = 14 }, - { .iov_len = 0 }, - { .iov_len = 3 }, - { .iov_len = 0 }, - }; - const ssize_t test_multiple_bufs_total_size = 20; - - /* Safety */ - { - const struct iovec test_buf = { 0 }; - ssize_t out = 0; - - /* Check null safety */ - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(&test_buf, 1, 0, NULL), - S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(NULL, 1, 0, &out), - S2N_ERR_NULL); - - /* Check negative safety */ - EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, -1, 0, &out)); - EXPECT_EQUAL(out, 0); - EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, -1, 0, &out)); - EXPECT_EQUAL(out, 0); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(NULL, 0, -1, &out), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(&test_buf, 1, -1, &out), - S2N_ERR_INVALID_ARGUMENT); - } - - /* No iovecs */ - { - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, 0, 0, &out)); - EXPECT_EQUAL(out, 0); - } - - /* Array of zero-length iovecs */ - { - const struct iovec test_bufs[10] = { 0 }; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size( - test_bufs, s2n_array_len(test_bufs), 0, &out)); - EXPECT_EQUAL(out, 0); - } - - /* Single iovec */ - { - const ssize_t expected_size = 10; - const struct iovec test_buf = { .iov_len = expected_size }; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, 0, &out)); - EXPECT_EQUAL(out, expected_size); - } - - /* Single iovec with offset */ - { - const struct iovec test_buf = { .iov_len = 10 }; - const ssize_t offset = 5; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, offset, &out)); - EXPECT_EQUAL(out, test_buf.iov_len - offset); - } - - /* Multiple iovecs */ - { - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size( - test_multiple_bufs, s2n_array_len(test_multiple_bufs), 0, &out)); - EXPECT_EQUAL(out, test_multiple_bufs_total_size); - } - - /* Multiple iovecs with offset */ - { - const ssize_t offset = 10; - ssize_t out = 0; - EXPECT_OK(s2n_sendv_with_offset_total_size( - test_multiple_bufs, s2n_array_len(test_multiple_bufs), offset, &out)); - EXPECT_EQUAL(out, test_multiple_bufs_total_size - offset); - } - - /* Offset with no data */ - { - const struct iovec test_bufs[10] = { 0 }; - ssize_t out = 0; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(NULL, 0, 1, &out), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs, 0, 1, &out), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs, s2n_array_len(test_bufs), 1, &out), - S2N_ERR_INVALID_ARGUMENT); - } - - /* Offset larger than available data */ - { - const struct iovec test_buf = { .iov_len = 10 }; - ssize_t out = 0; - - ssize_t test_buf_offset = test_buf.iov_len + 1; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(&test_buf, 1, test_buf_offset, &out), - S2N_ERR_INVALID_ARGUMENT); - - ssize_t test_multiple_bufs_offset = test_multiple_bufs_total_size + 1; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_multiple_bufs, - s2n_array_len(test_multiple_bufs), test_multiple_bufs_offset, &out), - S2N_ERR_INVALID_ARGUMENT); - } - - /* Too much data to count - * - * This isn't really practically possible since an application would need - * to allocate more than SIZE_MAX memory for the iovec buffers, but we - * should ensure that the inputs don't cause unexpected behavior. - */ - { - ssize_t out = 0; - - const struct iovec test_bufs_ssize[] = { - { .iov_len = SSIZE_MAX }, - { .iov_len = 1 }, - }; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs_ssize, s2n_array_len(test_bufs_ssize), 0, &out), - S2N_ERR_INVALID_ARGUMENT); - - const struct iovec test_bufs_size[] = { - { .iov_len = SIZE_MAX }, - { .iov_len = 1 }, - }; - EXPECT_ERROR_WITH_ERRNO( - s2n_sendv_with_offset_total_size(test_bufs_size, s2n_array_len(test_bufs_size), 0, &out), - S2N_ERR_INVALID_ARGUMENT); - } - }; - - /* Test: s2n_flush is necessary and not achievable with s2n_send */ - { - bool use_send[] = { true, false }; - - /* To reproduce the problematic scenario, we need to block on - * sending a record during a multi-record write. - */ - struct s2n_send_result results[] = { - BLOCK_SEND_RESULT, - OK_SEND_RESULT, - OK_SEND_RESULT, - OK_SEND_RESULT, - }; - - for (size_t i = 0; i < s2n_array_len(use_send); i++) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_secrets(conn)); - - struct s2n_send_context context = { - .results = results, - .results_len = s2n_array_len(results) - }; - EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); - EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); - - s2n_blocked_status blocked = 0; - - /* First attempt blocks */ - EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), - S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); - EXPECT_EQUAL(context.bytes_sent, 0); - - /* For our control case, we attempt to use a zero-length send as flush */ - if (use_send[i]) { - EXPECT_FAILURE_WITH_ERRNO( - s2n_send(conn, large_test_data, 0, &blocked), - S2N_ERR_SEND_SIZE); - continue; - } - - /* Unlike the zero-length send, s2n_flush succeeds */ - EXPECT_SUCCESS(s2n_flush(conn, &blocked)); - EXPECT_EQUAL(context.bytes_sent, max_frag_bytes_sent[S2N_MFL_DEFAULT]); - - /* We can also successfully finish sending */ - EXPECT_EQUAL( - s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), - sizeof(large_test_data)); - EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* clang-format off */ +#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } +#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } +#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } +#define OK_SEND_RESULT { .result = INT_MAX } +/* clang-format on */ + +enum s2n_test_mfl { + S2N_MFL_DEFAULT = 0, + S2N_MFL_LARGE, + S2N_MFL_SMALL, + S2N_MFL_MINIMUM, + S2N_MFL_COUNT, +}; + +static S2N_RESULT s2n_set_test_max_fragment_len(struct s2n_connection *conn, enum s2n_test_mfl mfl) +{ + switch (mfl) { + case S2N_MFL_DEFAULT: + break; + case S2N_MFL_LARGE: + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + break; + case S2N_MFL_SMALL: + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + break; + case S2N_MFL_MINIMUM: + conn->max_outgoing_fragment_length = mfl_code_to_length[1]; + break; + case S2N_MFL_COUNT: + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); + break; + } + return S2N_RESULT_OK; +} + +struct s2n_send_result { + int result; + int error; +}; + +struct s2n_send_context { + size_t calls; + size_t bytes_sent; + const struct s2n_send_result *results; + const size_t results_len; +}; + +bool s2n_custom_send_fn_called = false; +int s2n_expect_concurrent_error_send_fn(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_send_fn_called = true; + + s2n_blocked_status blocked = 0; + ssize_t result = s2n_send(conn, buf, len, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_send_context *context = (struct s2n_send_context *) io_context; + POSIX_ENSURE_REF(context); + + POSIX_ENSURE_LT(context->calls, context->results_len); + const struct s2n_send_result *result = &context->results[context->calls]; + + int retval = S2N_MIN((int) len, result->result); + + context->calls++; + if (retval > 0) { + context->bytes_sent += retval; + } + + errno = result->error; + return retval; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_data[] = "hello world"; + + uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; + struct s2n_blob large_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); + EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); + + /* Small record sizes will require a LOT of calls to s2n_send. + * Use this context when they should all succeed. + */ + struct s2n_send_result results_all_ok[50] = { 0 }; + for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { + results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; + } + const struct s2n_send_context context_all_ok = { + .results = results_all_ok, + .results_len = s2n_array_len(results_all_ok) + }; + + /* Calculating the record size for given data can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t test_data_bytes_sent = 0; + + /* s2n_send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + + /* Set the expected record size for future tests */ + test_data_bytes_sent = context.bytes_sent; + EXPECT_TRUE(test_data_bytes_sent > sizeof(test_data)); + }; + + /* Calculating the max record size for a given max fragment length can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t max_frag_bytes_sent[S2N_MFL_COUNT] = { 0 }; + + /* Track the size of the output buffer. + * It should be constant across all tests with the same max fragment length. + */ + uint32_t out_size[S2N_MFL_COUNT] = { 0 }; + + /* Send exactly the maximum fragment size */ + for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); + uint32_t fragment_len = conn->max_outgoing_fragment_length; + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* Send exactly the fragment length */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len, &blocked), fragment_len); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, 1); + + /* Set the expected record size for future tests */ + max_frag_bytes_sent[mfl] = context.bytes_sent; + EXPECT_TRUE(max_frag_bytes_sent[mfl] > 0); + + /* Set the expected output buffer size for future tests */ + out_size[mfl] = conn->out.blob.size; + EXPECT_TRUE(out_size[mfl] > 0); + + /* Sanity check: Send one byte more than the fragment length. + * If this is actually the maximum fragment length, one extra byte will + * lead to an extra record / extra call to send. + */ + context.calls = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len + 1, &blocked), fragment_len + 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, 2); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); + } + + /* s2n_send cannot be called concurrently */ + { + /* Setup connections */ + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Setup bad send callback */ + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_expect_concurrent_error_send_fn)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + s2n_blocked_status blocked = 0; + s2n_custom_send_fn_called = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_send_fn_called); + EXPECT_EQUAL(0, conn->wire_bytes_out); + }; + + /* s2n_send tracks conn->wire_bytes_out on send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_EQUAL(0, conn->wire_bytes_out); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* s2n_send tracks conn->wire_bytes_out on partial send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_EQUAL(0, conn->wire_bytes_out); + + const uint32_t partial_send = 10; + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(partial_send), + CLOSED_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO); + + EXPECT_EQUAL(context.calls, 2); + EXPECT_EQUAL(context.bytes_sent, partial_send); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* s2n_send sends all data, despite partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(1), + PARTIAL_SEND_RESULT(5), + PARTIAL_SEND_RESULT(2), + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block and must be retried */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const uint32_t partial_send = 10; + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* First attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Second attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send * 2); + + /* Third attempt completes */ + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Calculating the record size for given data can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t large_test_data_bytes_sent = 0; + + /* s2n_send sends multiple records worth of data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + + large_test_data_bytes_sent = context.bytes_sent; + EXPECT_TRUE(large_test_data_bytes_sent > sizeof(large_test_data)); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send sends all records and data, despite partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(10), OK_SEND_RESULT, + OK_SEND_RESULT, + PARTIAL_SEND_RESULT(5), PARTIAL_SEND_RESULT(1), OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block while sending multiple records */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const uint32_t partial_send = 10; + struct s2n_send_result results[] = { + OK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; + + /* First attempt blocks after writing one record */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size + partial_send); + + /* Don't re-send the data already sent. */ + const uint32_t offset = S2N_DEFAULT_FRAGMENT_LENGTH; + + /* Second attempt blocks without writing another record */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size + partial_send + partial_send); + + /* Third attempt completes */ + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + sizeof(large_test_data) - S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block after sending multiple records. + * ALL flushed records must be reported to the caller. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { + OK_SEND_RESULT, + OK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; + + /* First attempt blocks after writing two records */ + ssize_t expected_send = S2N_DEFAULT_FRAGMENT_LENGTH * 2; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_send); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size * 2); + + /* Don't re-send the data already sent. */ + const uint32_t offset = expected_send; + + /* Second attempt completes */ + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + sizeof(large_test_data) - offset); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Sending multiple records supports different maximum fragment lengths */ + for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* We expect enough calls to send to split the payload into records */ + size_t expected_calls = ceil(sizeof(large_test_data) / (double) conn->max_outgoing_fragment_length); + EXPECT_EQUAL(context.calls, expected_calls); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); + } + + /* Test dynamic record threshold record fragmentation */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Retrieve the fragment size to expect */ + uint16_t single_mtu_mfl = 0; + EXPECT_OK(s2n_record_min_write_payload_size(conn, &single_mtu_mfl)); + + /* Set the dynamic record threshold large enough for two small records */ + const uint32_t resize_threshold = single_mtu_mfl * 2; + EXPECT_SUCCESS(s2n_connection_set_dynamic_record_threshold(conn, resize_threshold, UINT16_MAX)); + + struct s2n_send_result results[] = { + /* Block before sending the first record so that we can examine + * the connection state after buffering the first record. + */ + BLOCK_SEND_RESULT, OK_SEND_RESULT, + /* Send the second record */ + BLOCK_SEND_RESULT, OK_SEND_RESULT, + /* Send the third record */ + OK_SEND_RESULT, + BLOCK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + s2n_blocked_status blocked = 0; + const size_t send_size = single_mtu_mfl * 2; + + /* The first call to s2n_send blocks before sending the first record. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, send_size, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* No records have been sent yet. */ + EXPECT_EQUAL(context.bytes_sent, 0); + /* The first record is buffered, + * so its bytes still count towards the resize_threshold. + * We have NOT passed the threshold. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, single_mtu_mfl); + EXPECT_TRUE(conn->active_application_bytes_consumed < resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The second call to s2n_send flushes the buffered first record, + * but blocks before sending the second record. + */ + ssize_t result = s2n_send(conn, large_test_data, send_size, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* First small, single-MTU record was sent. */ + EXPECT_EQUAL(result, single_mtu_mfl); + EXPECT_TRUE(context.bytes_sent < ETH_MTU); + /* The second record is buffered, + * so its bytes count towards the resize_threshold. + * We have therefore hit the threshold. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The third call to s2n_send flushes the second record. */ + result = s2n_send(conn, large_test_data, send_size - single_mtu_mfl, &blocked); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + /* Second small, single-MTU record was sent. */ + EXPECT_EQUAL(result, single_mtu_mfl); + /* There should be no change regarding the resize_threshold, + * since we did not construct any new records. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The fourth call to s2n_send sends the third record. */ + result = s2n_send(conn, large_test_data, conn->max_outgoing_fragment_length * 2, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* We have passed the resize_threshold, so records are no longer small. + * Instead they use the standard connection fragment length. + */ + EXPECT_TRUE(result > single_mtu_mfl); + EXPECT_EQUAL(result, conn->max_outgoing_fragment_length); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Test: s2n_sendv_with_offset_total_size */ + { + const struct iovec test_multiple_bufs[] = { + { .iov_len = 0 }, + { .iov_len = 1 }, + { .iov_len = 2 }, + { .iov_len = 0 }, + { .iov_len = 14 }, + { .iov_len = 0 }, + { .iov_len = 3 }, + { .iov_len = 0 }, + }; + const ssize_t test_multiple_bufs_total_size = 20; + + /* Safety */ + { + const struct iovec test_buf = { 0 }; + ssize_t out = 0; + + /* Check null safety */ + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(&test_buf, 1, 0, NULL), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(NULL, 1, 0, &out), + S2N_ERR_NULL); + + /* Check negative safety */ + EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, -1, 0, &out)); + EXPECT_EQUAL(out, 0); + EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, -1, 0, &out)); + EXPECT_EQUAL(out, 0); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(NULL, 0, -1, &out), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(&test_buf, 1, -1, &out), + S2N_ERR_INVALID_ARGUMENT); + } + + /* No iovecs */ + { + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size(NULL, 0, 0, &out)); + EXPECT_EQUAL(out, 0); + } + + /* Array of zero-length iovecs */ + { + const struct iovec test_bufs[10] = { 0 }; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size( + test_bufs, s2n_array_len(test_bufs), 0, &out)); + EXPECT_EQUAL(out, 0); + } + + /* Single iovec */ + { + const ssize_t expected_size = 10; + const struct iovec test_buf = { .iov_len = expected_size }; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, 0, &out)); + EXPECT_EQUAL(out, expected_size); + } + + /* Single iovec with offset */ + { + const struct iovec test_buf = { .iov_len = 10 }; + const ssize_t offset = 5; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size(&test_buf, 1, offset, &out)); + EXPECT_EQUAL(out, test_buf.iov_len - offset); + } + + /* Multiple iovecs */ + { + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size( + test_multiple_bufs, s2n_array_len(test_multiple_bufs), 0, &out)); + EXPECT_EQUAL(out, test_multiple_bufs_total_size); + } + + /* Multiple iovecs with offset */ + { + const ssize_t offset = 10; + ssize_t out = 0; + EXPECT_OK(s2n_sendv_with_offset_total_size( + test_multiple_bufs, s2n_array_len(test_multiple_bufs), offset, &out)); + EXPECT_EQUAL(out, test_multiple_bufs_total_size - offset); + } + + /* Offset with no data */ + { + const struct iovec test_bufs[10] = { 0 }; + ssize_t out = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(NULL, 0, 1, &out), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs, 0, 1, &out), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs, s2n_array_len(test_bufs), 1, &out), + S2N_ERR_INVALID_ARGUMENT); + } + + /* Offset larger than available data */ + { + const struct iovec test_buf = { .iov_len = 10 }; + ssize_t out = 0; + + ssize_t test_buf_offset = test_buf.iov_len + 1; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(&test_buf, 1, test_buf_offset, &out), + S2N_ERR_INVALID_ARGUMENT); + + ssize_t test_multiple_bufs_offset = test_multiple_bufs_total_size + 1; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_multiple_bufs, + s2n_array_len(test_multiple_bufs), test_multiple_bufs_offset, &out), + S2N_ERR_INVALID_ARGUMENT); + } + + /* Too much data to count + * + * This isn't really practically possible since an application would need + * to allocate more than SIZE_MAX memory for the iovec buffers, but we + * should ensure that the inputs don't cause unexpected behavior. + */ + { + ssize_t out = 0; + + const struct iovec test_bufs_ssize[] = { + { .iov_len = SSIZE_MAX }, + { .iov_len = 1 }, + }; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs_ssize, s2n_array_len(test_bufs_ssize), 0, &out), + S2N_ERR_INVALID_ARGUMENT); + + const struct iovec test_bufs_size[] = { + { .iov_len = SIZE_MAX }, + { .iov_len = 1 }, + }; + EXPECT_ERROR_WITH_ERRNO( + s2n_sendv_with_offset_total_size(test_bufs_size, s2n_array_len(test_bufs_size), 0, &out), + S2N_ERR_INVALID_ARGUMENT); + } + }; + + /* Test: s2n_flush is necessary and not achievable with s2n_send */ + { + bool use_send[] = { true, false }; + + /* To reproduce the problematic scenario, we need to block on + * sending a record during a multi-record write. + */ + struct s2n_send_result results[] = { + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + OK_SEND_RESULT, + OK_SEND_RESULT, + }; + + for (size_t i = 0; i < s2n_array_len(use_send); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_context context = { + .results = results, + .results_len = s2n_array_len(results) + }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* First attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, 0); + + /* For our control case, we attempt to use a zero-length send as flush */ + if (use_send[i]) { + EXPECT_FAILURE_WITH_ERRNO( + s2n_send(conn, large_test_data, 0, &blocked), + S2N_ERR_SEND_SIZE); + continue; + } + + /* Unlike the zero-length send, s2n_flush succeeds */ + EXPECT_SUCCESS(s2n_flush(conn, &blocked)); + EXPECT_EQUAL(context.bytes_sent, max_frag_bytes_sent[S2N_MFL_DEFAULT]); + + /* We can also successfully finish sending */ + EXPECT_EQUAL( + s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + sizeof(large_test_data)); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_server_hello_retry_test.c b/tests/unit/s2n_server_hello_retry_test.c index fc2aa2ec32f..9ca4b53645e 100644 --- a/tests/unit/s2n_server_hello_retry_test.c +++ b/tests/unit/s2n_server_hello_retry_test.c @@ -1,627 +1,627 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_key_share.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/extensions/s2n_server_supported_versions.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_result.h" - -#define HELLO_RETRY_MSG_NO 1 - -const uint8_t SESSION_ID_SIZE = 1; -const uint8_t COMPRESSION_METHOD_SIZE = 1; - -struct client_hello_context { - int invocations; - s2n_client_hello_cb_mode mode; - bool mark_done; -}; - -int s2n_negotiate_poll_hello_retry(struct s2n_connection *server_conn, - struct s2n_connection *client_conn, - struct client_hello_context *client_hello_ctx) -{ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_IO_BLOCKED); - - /* complete the callback on the next call */ - client_hello_ctx->mark_done = true; - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* - * hello retry will invoke the s2n_negotiate twice but the callback should - * be called once regardless of polling - */ - EXPECT_EQUAL(client_hello_ctx->invocations, 1); - - return S2N_SUCCESS; -} - -static int client_hello_detect_duplicate_calls(struct s2n_connection *conn, void *ctx) -{ - if (ctx == NULL) { - return -1; - } - - struct client_hello_context *client_hello_ctx = ctx; - - /* Incremet counter */ - client_hello_ctx->invocations++; - EXPECT_EQUAL(client_hello_ctx->invocations, 1); - if (client_hello_ctx->mode == S2N_CLIENT_HELLO_CB_NONBLOCKING) { - EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); - } - return 0; -} - -int s2n_client_hello_poll_cb(struct s2n_connection *conn, void *ctx) -{ - struct client_hello_context *client_hello_ctx = NULL; - if (ctx == NULL) { - return -1; - } - client_hello_ctx = ctx; - /* Increment counter to ensure that callback was invoked */ - client_hello_ctx->invocations++; - - if (client_hello_ctx->mark_done) { - EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); - return S2N_SUCCESS; - } - - return S2N_SUCCESS; -} - -S2N_RESULT hello_retry_client_hello_cb_test() -{ - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - EXPECT_NOT_NULL(tls13_chain_and_key); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_NOT_NULL(client_conn); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - struct s2n_test_io_pair io_pair = { 0 }; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* setup the client hello callback */ - struct client_hello_context client_hello_ctx = { .invocations = 0, - .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING, - .mark_done = false }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, - s2n_client_hello_poll_cb, &client_hello_ctx)); - EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, - S2N_CLIENT_HELLO_CB_NONBLOCKING)); - - /* negotiate and make assertions */ - EXPECT_SUCCESS(s2n_negotiate_poll_hello_retry(server_conn, client_conn, &client_hello_ctx)); - - /* check hello retry state */ - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - - /* cleanup */ - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Send Hello Retry Request messages */ - { - struct s2n_config *server_config = NULL; - struct s2n_connection *server_conn = NULL; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - struct s2n_stuffer *server_stuffer = &server_conn->handshake.io; - - uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN - + S2N_TLS_RANDOM_DATA_LEN - + SESSION_ID_SIZE - + server_conn->session_id_len - + S2N_TLS_CIPHER_SUITE_LEN - + COMPRESSION_METHOD_SIZE; - - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); - server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); - - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); - - /* The client will need a key share extension to properly parse the hello */ - /* Total extension size + size of each extension */ - total += 2 + s2n_extensions_server_supported_versions_size(server_conn) - + s2n_extensions_server_key_share_send_size(server_conn); - - EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_EQUAL(s2n_stuffer_data_available(server_stuffer), total); - - EXPECT_NOT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.evp_pkey); - EXPECT_TRUE(memcmp(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN) == 0); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Verify the requires_retry flag causes a retry to be sent */ - { - struct s2n_config *conf = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(conf = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); - - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); - - EXPECT_TRUE(s2n_is_hello_retry_message(conn)); - EXPECT_SUCCESS(s2n_server_hello_retry_send(conn)); - - EXPECT_SUCCESS(s2n_config_free(conf)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Retry requests with incorrect random data are not accepted */ - { - struct s2n_config *conf = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(conf = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); - - struct s2n_stuffer *io = &conn->handshake.io; - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); - - /* protocol version */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); - - /* random data */ - uint8_t bad_retry_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, bad_retry_random, S2N_TLS_RANDOM_DATA_LEN)); - - /* session id */ - uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); - - /* cipher suites */ - EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, 0x1301)); - - /* no compression */ - EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); - - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_FALSE(s2n_is_hello_retry_message(conn)); - - EXPECT_SUCCESS(s2n_config_free(conf)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Verify the client key share extension properly handles HelloRetryRequests */ - { - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - struct s2n_stuffer *extension_stuffer = &server_conn->handshake.io; - - EXPECT_SUCCESS(s2n_connection_allow_response_extension(client_conn, s2n_server_key_share_extension.iana_value)); - EXPECT_SUCCESS(s2n_connection_allow_response_extension(server_conn, s2n_server_key_share_extension.iana_value)); - - POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); - server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); - EXPECT_SUCCESS(s2n_extensions_server_key_share_send(server_conn, extension_stuffer)); - - S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, TLS_EXTENSION_KEY_SHARE, uint16); - /* 4 = S2N_SIZE_OF_EXTENSION_TYPE + S2N_SIZE_OF_EXTENSION_DATA_SIZE */ - S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, s2n_extensions_server_key_share_send_size(server_conn) - 4, uint16); - - client_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); - - /* Setup the client to receive a HelloRetryRequest */ - POSIX_CHECKED_MEMCPY(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); - - /* Setup the handshake type and message number to simulate a condition where a HelloRetry should be sent */ - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); - EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); - - /* Parse the key share */ - EXPECT_SUCCESS(s2n_extensions_server_key_share_recv(client_conn, extension_stuffer)); - EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); - - /* Server negotiated curve value will be non-null, if the extension succeeded */ - EXPECT_NOT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* Verify that the hash transcript recreation function correctly takes the existing ClientHello1 - * hash, and generates a synthetic message. */ - { - struct s2n_config *conf = NULL; - struct s2n_connection *conn = NULL; - - EXPECT_NOT_NULL(conf = s2n_config_new()); - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); - - conn->server_protocol_version = S2N_TLS13; - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - /* This blob is taken from the functional test RFC. That RFC does not actually provide hash transcript - * values, so the expected hashes are taken from what our hash functions generated and the hash - * generated from the transcript recreation. - * https://tools.ietf.org/html/rfc8448#section-5 */ - S2N_BLOB_FROM_HEX(client_hello1, - "010000c00303cb34ecb1e78163" - "ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283" - "024dece7000006130113031302010000910000000b" - "0009000006736572766572ff01000100000a001400" - "12001d001700180019010001010102010301040023" - "0000003300260024001d002099381de560e4bd43d2" - "3d8e435a7dbafeb3c06e51c13cae4d5413691e529a" - "af2c002b0003020304000d0020001e040305030603" - "020308040805080604010501060102010402050206" - "020202002d00020101001c00024001"); - - S2N_BLOB_FROM_HEX(client_hello1_expected_hash, - "4db255f30da09a407c841720be831a06a5aa9b3662a5f44267d37706b73c2b8c"); - - S2N_BLOB_FROM_HEX(synthetic_message_with_ch1_expected_hash, - "ff1135ed878322e29699da3e451d2f08bf11fc693038769978e75bb63304a225"); - - EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(conn, &client_hello1)); - - s2n_tls13_connection_keys(keys, conn); - uint8_t hash_digest_length = keys.size; - struct s2n_blob compare_blob = { 0 }; - - DEFER_CLEANUP(struct s2n_hash_state client_hello1_hash = { 0 }, s2n_hash_free); - EXPECT_SUCCESS(s2n_hash_new(&client_hello1_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &client_hello1_hash)); - - uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); - - EXPECT_SUCCESS(s2n_blob_init(&compare_blob, client_hello1_digest_out, hash_digest_length)); - S2N_BLOB_EXPECT_EQUAL(client_hello1_expected_hash, compare_blob); - - EXPECT_SUCCESS(s2n_server_hello_retry_recreate_transcript(conn)); - - DEFER_CLEANUP(struct s2n_hash_state recreated_hash = { 0 }, s2n_hash_free); - uint8_t recreated_transcript_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_SUCCESS(s2n_hash_new(&recreated_hash)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &recreated_hash)); - EXPECT_SUCCESS(s2n_hash_digest(&recreated_hash, recreated_transcript_digest_out, hash_digest_length)); - - EXPECT_SUCCESS(s2n_blob_init(&compare_blob, recreated_transcript_digest_out, hash_digest_length)); - S2N_BLOB_EXPECT_EQUAL(synthetic_message_with_ch1_expected_hash, compare_blob); - - EXPECT_SUCCESS(s2n_config_free(conf)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Send and receive Hello Retry Request messages */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - struct client_hello_context client_hello_ctx = { .invocations = 0, .mode = S2N_CLIENT_HELLO_CB_BLOCKING }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, client_hello_detect_duplicate_calls, &client_hello_ctx)); - - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); - - /* Force HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* Send the first CH message */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Receive the CH and send an HRR, which will execute the HRR code paths */ - EXPECT_EQUAL(client_hello_ctx.invocations, 0); - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(client_hello_ctx.invocations, 1); - - EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); - EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); - EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); - - EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; - EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); - - /* Send the second CH message */ - EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); - EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, - s2n_stuffer_data_available(&client_conn->handshake.io))); - - /* Verify that receiving the second CH message does not execute the callback */ - EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); - EXPECT_EQUAL(client_hello_ctx.invocations, 1); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - }; - - /* Send and receive Hello Retry Request messages, test for non blocking client hello callback */ - { - struct s2n_config *server_config = NULL; - struct s2n_config *client_config = NULL; - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - - struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); - - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, - S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Force HRR path */ - client_conn->security_policy_override = &security_policy_test_tls13_retry; - - /* setup the client hello callback */ - struct client_hello_context client_hello_ctx = { .invocations = 0, - .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING }; - EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, - client_hello_detect_duplicate_calls, &client_hello_ctx)); - EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(server_config, - S2N_CLIENT_HELLO_CB_NONBLOCKING)); - - /* ensure that handshake succeeds via HRR path using non_blocking CH */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - EXPECT_TRUE(server_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - EXPECT_EQUAL(client_hello_ctx.invocations, 1); - - EXPECT_NOT_NULL(s2n_connection_get_client_hello(server_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - }; - - /* Hello Retry Request + (poll and no-poll) client hello callback */ - { - EXPECT_OK(hello_retry_client_hello_cb_test()); - }; - - /* Test s2n_set_hello_retry_required correctly sets the handshake type to HELLO_RETRY_REQUEST, - * when conn->actual_protocol_version is set to TLS1.3 version */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); - - EXPECT_SUCCESS(s2n_set_hello_retry_required(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test s2n_set_hello_retry_required raises a S2N_ERR_INVALID_HELLO_RETRY error - * when conn->actual_protocol_version is less than TLS1.3 */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - conn->actual_protocol_version = S2N_TLS12; - - EXPECT_FAILURE_WITH_ERRNO(s2n_set_hello_retry_required(conn), S2N_ERR_INVALID_HELLO_RETRY); - EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 - *= type=test - *# Clients MUST abort the handshake with an - *# "illegal_parameter" alert if the HelloRetryRequest would not result - *# in any change in the ClientHello. - */ - { - const struct s2n_security_policy *security_policy = NULL; - EXPECT_SUCCESS(s2n_find_security_policy_from_version("20201021", &security_policy)); - EXPECT_NOT_NULL(security_policy); - const struct s2n_ecc_named_curve *test_curve = security_policy->ecc_preferences->ecc_curves[0]; - - /** - * Retry for key share is valid - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 - *= type=test - *# and (2) the selected_group field does not - *# correspond to a group which was provided in the "key_share" extension - *# in the original ClientHello. - **/ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); - conn->actual_protocol_version = S2N_TLS13; - conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - - conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; - - /* Server requested key share is NOT present: allow retry */ - EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); - - /* Server requested key share is present: do NOT allow retry */ - conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; - conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); - EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), - S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Retry for multiple reasons is valid */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); - conn->actual_protocol_version = S2N_TLS13; - conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - - conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; - - /* All retry conditions met: allow retry */ - conn->early_data_state = S2N_EARLY_DATA_REQUESTED; - EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); - - /* No retry conditions met: do NOT allow retry */ - conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; - conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; - conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); - EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); - EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), - S2N_ERR_INVALID_HELLO_RETRY); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_result.h" + +#define HELLO_RETRY_MSG_NO 1 + +const uint8_t SESSION_ID_SIZE = 1; +const uint8_t COMPRESSION_METHOD_SIZE = 1; + +struct client_hello_context { + int invocations; + s2n_client_hello_cb_mode mode; + bool mark_done; +}; + +int s2n_negotiate_poll_hello_retry(struct s2n_connection *server_conn, + struct s2n_connection *client_conn, + struct client_hello_context *client_hello_ctx) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + + /* complete the callback on the next call */ + client_hello_ctx->mark_done = true; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* + * hello retry will invoke the s2n_negotiate twice but the callback should + * be called once regardless of polling + */ + EXPECT_EQUAL(client_hello_ctx->invocations, 1); + + return S2N_SUCCESS; +} + +static int client_hello_detect_duplicate_calls(struct s2n_connection *conn, void *ctx) +{ + if (ctx == NULL) { + return -1; + } + + struct client_hello_context *client_hello_ctx = ctx; + + /* Incremet counter */ + client_hello_ctx->invocations++; + EXPECT_EQUAL(client_hello_ctx->invocations, 1); + if (client_hello_ctx->mode == S2N_CLIENT_HELLO_CB_NONBLOCKING) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + } + return 0; +} + +int s2n_client_hello_poll_cb(struct s2n_connection *conn, void *ctx) +{ + struct client_hello_context *client_hello_ctx = NULL; + if (ctx == NULL) { + return -1; + } + client_hello_ctx = ctx; + /* Increment counter to ensure that callback was invoked */ + client_hello_ctx->invocations++; + + if (client_hello_ctx->mark_done) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + return S2N_SUCCESS; + } + + return S2N_SUCCESS; +} + +S2N_RESULT hello_retry_client_hello_cb_test() +{ + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_NOT_NULL(tls13_chain_and_key); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* setup the client hello callback */ + struct client_hello_context client_hello_ctx = { .invocations = 0, + .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING, + .mark_done = false }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, + s2n_client_hello_poll_cb, &client_hello_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, + S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* negotiate and make assertions */ + EXPECT_SUCCESS(s2n_negotiate_poll_hello_retry(server_conn, client_conn, &client_hello_ctx)); + + /* check hello retry state */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* cleanup */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Send Hello Retry Request messages */ + { + struct s2n_config *server_config = NULL; + struct s2n_connection *server_conn = NULL; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + struct s2n_stuffer *server_stuffer = &server_conn->handshake.io; + + uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN + + S2N_TLS_RANDOM_DATA_LEN + + SESSION_ID_SIZE + + server_conn->session_id_len + + S2N_TLS_CIPHER_SUITE_LEN + + COMPRESSION_METHOD_SIZE; + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + + /* The client will need a key share extension to properly parse the hello */ + /* Total extension size + size of each extension */ + total += 2 + s2n_extensions_server_supported_versions_size(server_conn) + + s2n_extensions_server_key_share_send_size(server_conn); + + EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(server_stuffer), total); + + EXPECT_NOT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.evp_pkey); + EXPECT_TRUE(memcmp(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN) == 0); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Verify the requires_retry flag causes a retry to be sent */ + { + struct s2n_config *conf = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + + EXPECT_TRUE(s2n_is_hello_retry_message(conn)); + EXPECT_SUCCESS(s2n_server_hello_retry_send(conn)); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Retry requests with incorrect random data are not accepted */ + { + struct s2n_config *conf = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + struct s2n_stuffer *io = &conn->handshake.io; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + /* random data */ + uint8_t bad_retry_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, bad_retry_random, S2N_TLS_RANDOM_DATA_LEN)); + + /* session id */ + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, 0x1301)); + + /* no compression */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_FALSE(s2n_is_hello_retry_message(conn)); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Verify the client key share extension properly handles HelloRetryRequests */ + { + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer *extension_stuffer = &server_conn->handshake.io; + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(client_conn, s2n_server_key_share_extension.iana_value)); + EXPECT_SUCCESS(s2n_connection_allow_response_extension(server_conn, s2n_server_key_share_extension.iana_value)); + + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_extensions_server_key_share_send(server_conn, extension_stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, TLS_EXTENSION_KEY_SHARE, uint16); + /* 4 = S2N_SIZE_OF_EXTENSION_TYPE + S2N_SIZE_OF_EXTENSION_DATA_SIZE */ + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, s2n_extensions_server_key_share_send_size(server_conn) - 4, uint16); + + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + /* Setup the client to receive a HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + + /* Setup the handshake type and message number to simulate a condition where a HelloRetry should be sent */ + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + + /* Parse the key share */ + EXPECT_SUCCESS(s2n_extensions_server_key_share_recv(client_conn, extension_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); + + /* Server negotiated curve value will be non-null, if the extension succeeded */ + EXPECT_NOT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Verify that the hash transcript recreation function correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. */ + { + struct s2n_config *conf = NULL; + struct s2n_connection *conn = NULL; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + conn->server_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + /* This blob is taken from the functional test RFC. That RFC does not actually provide hash transcript + * values, so the expected hashes are taken from what our hash functions generated and the hash + * generated from the transcript recreation. + * https://tools.ietf.org/html/rfc8448#section-5 */ + S2N_BLOB_FROM_HEX(client_hello1, + "010000c00303cb34ecb1e78163" + "ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283" + "024dece7000006130113031302010000910000000b" + "0009000006736572766572ff01000100000a001400" + "12001d001700180019010001010102010301040023" + "0000003300260024001d002099381de560e4bd43d2" + "3d8e435a7dbafeb3c06e51c13cae4d5413691e529a" + "af2c002b0003020304000d0020001e040305030603" + "020308040805080604010501060102010402050206" + "020202002d00020101001c00024001"); + + S2N_BLOB_FROM_HEX(client_hello1_expected_hash, + "4db255f30da09a407c841720be831a06a5aa9b3662a5f44267d37706b73c2b8c"); + + S2N_BLOB_FROM_HEX(synthetic_message_with_ch1_expected_hash, + "ff1135ed878322e29699da3e451d2f08bf11fc693038769978e75bb63304a225"); + + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(conn, &client_hello1)); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + struct s2n_blob compare_blob = { 0 }; + + DEFER_CLEANUP(struct s2n_hash_state client_hello1_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&client_hello1_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &client_hello1_hash)); + + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, client_hello1_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(client_hello1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_server_hello_retry_recreate_transcript(conn)); + + DEFER_CLEANUP(struct s2n_hash_state recreated_hash = { 0 }, s2n_hash_free); + uint8_t recreated_transcript_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_hash_new(&recreated_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &recreated_hash)); + EXPECT_SUCCESS(s2n_hash_digest(&recreated_hash, recreated_transcript_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, recreated_transcript_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(synthetic_message_with_ch1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Send and receive Hello Retry Request messages */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + struct client_hello_context client_hello_ctx = { .invocations = 0, .mode = S2N_CLIENT_HELLO_CB_BLOCKING }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, client_hello_detect_duplicate_calls, &client_hello_ctx)); + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send the first CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Receive the CH and send an HRR, which will execute the HRR code paths */ + EXPECT_EQUAL(client_hello_ctx.invocations, 0); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* Send the second CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Verify that receiving the second CH message does not execute the callback */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /* Send and receive Hello Retry Request messages, test for non blocking client hello callback */ + { + struct s2n_config *server_config = NULL; + struct s2n_config *client_config = NULL; + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* setup the client hello callback */ + struct client_hello_context client_hello_ctx = { .invocations = 0, + .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, + client_hello_detect_duplicate_calls, &client_hello_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(server_config, + S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* ensure that handshake succeeds via HRR path using non_blocking CH */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_NOT_NULL(s2n_connection_get_client_hello(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Hello Retry Request + (poll and no-poll) client hello callback */ + { + EXPECT_OK(hello_retry_client_hello_cb_test()); + }; + + /* Test s2n_set_hello_retry_required correctly sets the handshake type to HELLO_RETRY_REQUEST, + * when conn->actual_protocol_version is set to TLS1.3 version */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + + EXPECT_SUCCESS(s2n_set_hello_retry_required(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_set_hello_retry_required raises a S2N_ERR_INVALID_HELLO_RETRY error + * when conn->actual_protocol_version is less than TLS1.3 */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_set_hello_retry_required(conn), S2N_ERR_INVALID_HELLO_RETRY); + EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 + *= type=test + *# Clients MUST abort the handshake with an + *# "illegal_parameter" alert if the HelloRetryRequest would not result + *# in any change in the ClientHello. + */ + { + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20201021", &security_policy)); + EXPECT_NOT_NULL(security_policy); + const struct s2n_ecc_named_curve *test_curve = security_policy->ecc_preferences->ecc_curves[0]; + + /** + * Retry for key share is valid + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 + *= type=test + *# and (2) the selected_group field does not + *# correspond to a group which was provided in the "key_share" extension + *# in the original ClientHello. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + + /* Server requested key share is NOT present: allow retry */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + /* Server requested key share is present: do NOT allow retry */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), + S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Retry for multiple reasons is valid */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + + /* All retry conditions met: allow retry */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + /* No retry conditions met: do NOT allow retry */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), + S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_session_ticket_test.c b/tests/unit/s2n_session_ticket_test.c index 5eac57836e5..8efbf762f12 100644 --- a/tests/unit/s2n_session_ticket_test.c +++ b/tests/unit/s2n_session_ticket_test.c @@ -1,1795 +1,1795 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_NANOS (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) -#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS S2N_SESSION_STATE_CONFIGURABLE_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 - -/** - * This function is used to "skip" time in unit tests. It will mock the system - * time to be current_time (ns) + data (ns). The "data" parameter is a uint64_t - * passed in as a void*. - */ -int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) -{ - struct timespec current_time; - - clock_gettime(S2N_CLOCK_SYS, ¤t_time); - - /** - * current_time fields are represented as time_t, and time_t has a platform - * dependent size. On 32 bit platforms, attempting to convert the current - * system time to nanoseconds will overflow, causing odd failures in unit - * tests. We upcast current_time fields to uint64_t before multiplying to - * avoid this. - */ - *nanoseconds = 0; - *nanoseconds += (uint64_t) current_time.tv_sec * ONE_SEC_IN_NANOS; - *nanoseconds += (uint64_t) current_time.tv_nsec; - *nanoseconds += *(uint64_t *) data; - - return 0; -} - -static int mock_time(void *data, uint64_t *nanoseconds) -{ - if (data) { - *nanoseconds = *((uint64_t *) data); - } else { - *nanoseconds = 1000000000; - } - return S2N_SUCCESS; -} - -uint8_t cb_session_data[S2N_TLS12_SESSION_SIZE * 2] = { 0 }; -size_t cb_session_data_len = 0; -uint32_t cb_session_lifetime = 0; -static int s2n_test_session_ticket_callback(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) -{ - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(ticket); - - /* Store the callback data for comparison at the end of the connection. */ - EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); - EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, sizeof(cb_session_data), cb_session_data)); - EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); - - return S2N_SUCCESS; -} - -/* make a struct with a ticket name and key adjacent in memory */ -struct small_name_ticket { - uint8_t name[1]; - uint8_t key[32]; -}; - -int main(int argc, char **argv) -{ - char *cert_chain = NULL; - char *private_key = NULL; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - struct s2n_config *client_config = NULL; - struct s2n_config *server_config = NULL; - uint64_t now = 0; - struct s2n_ticket_key *ticket_key = NULL; - uint32_t ticket_keys_len = 0; - - size_t serialized_session_state_length = 0; - uint8_t s2n_state_with_session_id = S2N_STATE_WITH_SESSION_ID; - uint8_t serialized_session_state[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES + S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; - - /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ - uint8_t ticket_key_name1[1] = "A"; - uint8_t ticket_key_name2[4] = "BBBB"; - uint8_t ticket_key_name3[16] = "CCCCCCCCCCCCCCCC"; - uint8_t ticket_key1[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, - 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, - 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, - 0xb3, 0xe5 }; - uint8_t ticket_key2[32] = { 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, - 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, - 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, - 0xc2, 0x44 }; - uint8_t ticket_key3[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, - 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, - 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, - 0xcb, 0x04 }; - - /* Testcases: - * 1) Client sends empty ST extension. Server issues NST. - * 2) Client sends empty ST extension. Server does a full handshake, but is unable to issue NST due to absence of an encrypt-decrypt key. - * 3) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. - * 4) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST even though the key is in decrypt-only state. - * 5) Client sends non-empty ST extension. Server does an abbreviated handshake, but does not issue a NST even though the key is in - * decrypt-only state due to absence of encrypt-decrypt key. - * 6) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key is not found. - * 7) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key has expired. - * 8) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. - * 9) Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. - * 10) Client sets corrupted ST extension. - * 11) User tries adding a duplicate key to the server. - * 12) Testing expired keys are removed from the server config while adding new keys. - * 13) Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. - * 14) Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. - * 15) Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and s2n_config_set_ticket_decrypt_key_lifetime calls. - * 16) Add keys out of order and pre-emptively add a key. - * 17) Handshake with client auth and session ticket enabled. - * 18) Session resumption APIs and session_ticket_cb return the same values when receiving a new ticket in TLS1.2 - * 19) Session resumption APIs and session_ticket_cb return sane values when receiving a new ticket in TLS1.3 - * 20) Client has TLS1.3 ticket but negotiates TLS1.2, so does full handshake - */ - - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - DEFER_CLEANUP(struct s2n_stuffer tls13_serialized_session_state = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_serialized_session_state, 0)); - - struct s2n_test_io_pair io_pair; - EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - - struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); - - /* Test ticket name handling */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - - /* Enable session tickets */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - - /* The name should be greater than 0 and less than or equal to 16 */ - uint8_t too_large_name[17] = { 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 0, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 17, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Add a ticket with a single-byte name */ - struct small_name_ticket small = { .name = { 0xAA }, .key = { 0xBB, 0xBB, 0xBB, 0xBB } }; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, small.name, s2n_array_len(small.name), small.key, s2n_array_len(small.key), 0)); - - /* Ensure a ticket with the same name is not added */ - struct small_name_ticket small2 = { .name = { 0xAA }, .key = { 0xCC, 0xCC, 0xCC, 0xCC } }; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, small2.name, s2n_array_len(small2.name), small2.key, s2n_array_len(small2.key), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Ensure a ticket with a zero-padded name is not added */ - uint8_t padded_name[16] = { 0xAA, 0 }; - EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, padded_name, s2n_array_len(padded_name), ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - }; - - /* Client sends empty ST extension. Server issues NST. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* A newly created connection should not be considered resumed */ - EXPECT_FALSE(s2n_connection_is_session_resumed(server_conn)); - EXPECT_FALSE(s2n_connection_is_session_resumed(client_conn)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends empty ST extension. Server does a full handshake, but is unable - * to issue NST due to absence of an encrypt-decrypt key. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and did not issue NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did an abbreviated handshake and not issue NST */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); - EXPECT_TRUE(s2n_connection_is_session_resumed(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that client_ticket is same as before because server didn't issue a NST */ - uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; - EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - EXPECT_TRUE(s2n_connection_is_session_resumed(client_conn)); - s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - /* Verify that the server lifetime hint is 0 because server didn't issue a NST */ - EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing a NST - * even though the key is in decrypt-only state. - */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add a mock delay such that key 1 moves to decrypt-only state */ - mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; - - uint32_t key_intro_time = mock_current_time / ONE_SEC_IN_NANOS; - 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), key_intro_time)); - - /* Verify there is an encrypt key available */ - EXPECT_OK(s2n_config_is_encrypt_key_available(server_config)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did an abbreviated handshake without issuing NST */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that client_ticket is the same as before because server didn't issue a NST */ - uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; - EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); - EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - /* Verify the lifetime hint from the server */ - EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does an abbreviated handshake, - * but does not issue a NST even though the key is in decrypt-only state due to - * the absence of encrypt-decrypt key. - */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add a mock delay such that key 1 moves to decrypt-only state */ - mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did an abbreviated handshake and did not issue a NST */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that client_ticket is same as before because server did not issue a NST */ - uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; - EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); - - s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); - EXPECT_FALSE(memcmp(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does a full handshake and issues - * a NST because the key used to encrypt the session ticket is not found. - */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - 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)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_TICKET_KEY_NAME_LOCATION, - ticket_key_name1, s2n_array_len(ticket_key_name1)); - - /* Verify the lifetime hint from the server */ - uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); - EXPECT_EQUAL(session_ticket_lifetime, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does a full handshake and issues a NST - * because the key has expired. - */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - 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)); - - /* Add a mock delay such that the key used to encrypt ST expires */ - mock_current_time += server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* Verify that the server has only the unexpired key */ - EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); - EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name2, s2n_array_len(ticket_key_name2)); - EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); - EXPECT_EQUAL(ticket_keys_len, 1); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Tamper session state to make session ticket size smaller than what we expect */ - /* Verify that client_ticket is same as before because server did not issue a NST */ - uint8_t tampered_session_state[sizeof(serialized_session_state) - 1]; - /* Copy session format */ - tampered_session_state[0] = serialized_session_state[0]; - /* Copy and reduce by 1 the session ticket length */ - tampered_session_state[1] = serialized_session_state[1]; - tampered_session_state[2] = serialized_session_state[2] - 1; - /* Skip 1 byte of the session ticket and copy the rest */ - EXPECT_MEMCPY_SUCCESS(tampered_session_state + 3, serialized_session_state + 4, sizeof(tampered_session_state) - 4); - - /* Set client tampered ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tampered_session_state, serialized_session_state_length - 1)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - 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)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - /* Set client ST and session state */ - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Not enabling resumption using ST */ - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and did not issue NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Client sets corrupted ST extension. */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - memset(serialized_session_state, 0, serialized_session_state_length); - - /* Set client ST and session state */ - EXPECT_FAILURE(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* User tries adding a duplicate key to the server */ - { - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - - 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)); - - /* Try adding the same key, but with a different name */ - EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key1, s2n_array_len(ticket_key1), 0)); - - /* Try adding a different key, but with the same name */ - EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key2, s2n_array_len(ticket_key2), 0)); - EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Try adding a key with invalid key length */ - EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, 0, 0)); - EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_LENGTH); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Testing expired keys are removed from the server config while adding new keys. */ - { - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - - /* Add 2 ST keys */ - 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_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add a mock delay such that the first two keys expire */ - uint64_t mock_delay = server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - - /* Add a third ST key */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); - - /* Try adding the expired keys */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - 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)); - - /* Verify that the config has three unexpired keys */ - EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); - /* ticket_key3 should have "rotated" to the first index as other keys expired */ - EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name3, s2n_array_len(ticket_key_name3)); - EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); - EXPECT_EQUAL(ticket_keys_len, 3); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Attempting to add more than S2N_MAX_TICKET_KEYS causes failures. */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - uint8_t id = 0; - uint8_t ticket_key_buf[32] = { 0 }; - - for (uint8_t i = 0; i < S2N_MAX_TICKET_KEYS; i++) { - id = i; - ticket_key_buf[0] = i; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, - &id, sizeof(id), ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); - } - - id = S2N_MAX_TICKET_KEYS; - ticket_key_buf[0] = S2N_MAX_TICKET_KEYS; - EXPECT_FAILURE(s2n_config_add_ticket_crypto_key(config, &id, sizeof(id), - ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); - }; - - /* Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - 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)); - - /* Add a mock delay such that the first key is close to it's encryption peak */ - uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; - mock_current_time += delay_in_nanos; - - /* Add two more ST keys */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_TICKET_KEY_NAME_LOCATION, - ticket_key_name1, s2n_array_len(ticket_key_name1)); - - /* Verify the lifetime hint from the server */ - uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); - uint32_t first_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - delay_in_nanos / ONE_SEC_IN_NANOS; - EXPECT_EQUAL(session_ticket_lifetime, first_key_remaining_lifetime); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST */ - { - const size_t allowed_failures = 1; - size_t failures = 0; - bool expected_key_chosen = false; - - /* This test sets up three different ticket encryption keys at various times in their encryption lifetime. The test - * is meant to check that the weighted random selection algorithm correctly selects the key that is at its - * encryption peak. However the test will sometimes pick a key that is not at its encryption peak because the - * selection function uses a weighted random selection algorithm. Here we retry the test once if the key chosen - * is not the expected key. - * - * The wrong key will be chosen 0.02% of the time. This value is drawn from the weight of the expected key, - * which does not change per test run. Therefore, the probability that the test chooses the wrong key - * more than allowed_failures times is 0.0002 ^ 2 = 0.00000004, which is extremely unlikely to occur. If - * the logic changes to chose the wrong key at a higher rate, say 50% of the time, this test would fail at a - * 0.5 ^ 2 = 0.25 or 25% of the time. This rate is high enough for us to notice and investigate. - */ - while (expected_key_chosen == false) { - EXPECT_TRUE(failures <= allowed_failures); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - uint64_t mock_current_time = 0; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); - - /* Add one ST key */ - 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)); - - /* Add second key when the first key is very close to it's encryption peak */ - uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; - mock_current_time += delay_in_nanos; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), - ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add third key when the second key is very close to it's encryption peak and - * the first key is about to transition from encrypt-decrypt state to decrypt-only state - */ - mock_current_time += delay_in_nanos; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), - ticket_key3, s2n_array_len(ticket_key3), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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); - 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; - } else { - failures += 1; - } - - /* Verify the lifetime hint from the server */ - uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); - uint32_t second_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - (delay_in_nanos / ONE_SEC_IN_NANOS); - EXPECT_EQUAL(session_ticket_lifetime, second_key_remaining_lifetime); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - } - }; - - /* Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and - * s2n_config_set_ticket_decrypt_key_lifetime calls */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - /* Set encrypt-decrypt key expire time to 24 hours */ - EXPECT_SUCCESS(s2n_config_set_ticket_encrypt_decrypt_key_lifetime(server_config, 86400)); - - /* Set decrypt-only key expire time to 5 hours */ - EXPECT_SUCCESS(s2n_config_set_ticket_decrypt_key_lifetime(server_config, 18000)); - - /* Add one ST key */ - 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)); - - /* Add second key when the first key is very close to it's encryption peak */ - uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); - - /* Add third key when the second key is very close to it's encryption peak and - * the first key is about to transition from encrypt-decrypt state to decrypt-only state - */ - mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos - ONE_SEC_IN_NANOS; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Add keys out of order and pre-emptively add a key */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - /* Add a key. After 1 hour it will be considered an encrypt-decrypt key. */ - POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); - 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), (now / ONE_SEC_IN_NANOS) + 3600)); - - /* Add a key. After 1 hour it will reach it's peak */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), now / ONE_SEC_IN_NANOS)); - - /* Add a key pre-emptively. It can be used only after 10 hours */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), now / ONE_SEC_IN_NANOS + 36000)); - - /* Add a mock delay such that negotiation happens after 1 hour */ - uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and issued NST */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - /* 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_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)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* Handshake with client auth and session ticket enabled */ - { - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - /* Client has session ticket and mutual auth enabled */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - - /* Server has session ticket and mutual auth enabled */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that the server did a full handshake and did not issue NST since client - * auth is enabled in server mode */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - /* s2n_resume_decrypt_session fails to decrypt when presented with a valid ticket_key, valid iv and invalid encrypted blob */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - - /* Add Session Ticket key on the server config */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - 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 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))); - - uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; - POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); - - server_conn->session_ticket_status = S2N_DECRYPT_TICKET; - EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_DECRYPT); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* s2n_resume_decrypt_session fails with a key not found error when presented with an invalid ticket_key, valid iv and invalid encrypted blob */ - { - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - - /* Add Session Ticket key on the server config */ - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - 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 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))); - - uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; - POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); - - server_conn->session_ticket_status = S2N_DECRYPT_TICKET; - EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - }; - - /* Test s2n_connection_is_session_resumed */ - { - /* TLS1.2 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - - conn->handshake.handshake_type = INITIAL; - EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); - - conn->handshake.handshake_type = NEGOTIATED | WITH_SESSION_TICKET; - EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); - - /* Ignores PSK mode */ - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS13; - - conn->handshake.handshake_type = INITIAL; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); - - conn->handshake.handshake_type = NEGOTIATED; - conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); - - conn->handshake.handshake_type = NEGOTIATED; - conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; - EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Session resumption APIs and session_ticket_cb return the same values - * when receiving a new ticket in TLS1.2 - */ - { - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); - EXPECT_SUCCESS(s2n_config_set_wall_clock(client_config, mock_time, NULL)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - - /* Client will use callback when server nst is received */ - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_callback, NULL)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - /* Create nonblocking pipes */ - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - - /* Add one ST key */ - POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); - 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), now / ONE_SEC_IN_NANOS)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Expect values from the session_ticket_cb are equivalent to values from the APIs */ - EXPECT_EQUAL(cb_session_data_len, s2n_connection_get_session_length(client_conn)); - uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, session_data, cb_session_data_len)); - EXPECT_BYTEARRAY_EQUAL(cb_session_data, session_data, cb_session_data_len); - - EXPECT_EQUAL(cb_session_lifetime, s2n_connection_get_session_ticket_lifetime_hint(client_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - }; - - EXPECT_SUCCESS(s2n_reset_tls13_in_test()); - - /* Session resumption APIs and session_ticket_cb return the same values - * when receiving a new ticket in TLS1.3 - */ - if (s2n_is_tls13_fully_supported()) { - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - - /* Freeze time */ - POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); - EXPECT_OK(s2n_config_mock_wall_clock(config, &now)); - - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), - ticket_key1, s2n_array_len(ticket_key1), 0)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - /* Send one NewSessionTicket */ - cb_session_data_len = 0; - EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_callback, NULL)); - EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 1)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that TLS1.3 was negotiated */ - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); - - /* Old TLS1.2 customer code will likely attempt to read the ticket here -- ensure we indicate no ticket yet */ - EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); - - /* Receive and save the issued session ticket for the next test */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - uint8_t out = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, &out, 1, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_NOT_EQUAL(cb_session_data_len, 0); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&tls13_serialized_session_state, cb_session_data, cb_session_data_len)); - - /* Verify correct session ticket lifetime "hint" */ - EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), cb_session_lifetime); - - /* Verify the session ticket APIs produce the same results as the callback */ - DEFER_CLEANUP(struct s2n_blob legacy_api_ticket = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_realloc(&legacy_api_ticket, cb_session_data_len)); - EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), cb_session_data_len); - EXPECT_SUCCESS(s2n_connection_get_session(client_conn, legacy_api_ticket.data, legacy_api_ticket.size)); - EXPECT_BYTEARRAY_EQUAL(cb_session_data, legacy_api_ticket.data, legacy_api_ticket.size); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - } - - /* Client has TLS1.3 ticket but negotiates TLS1.2 */ - if (s2n_is_tls13_fully_supported()) { - s2n_extension_type_id client_session_ticket_ext_id = 0, psk_ext_id = 0; - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); - EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &client_session_ticket_ext_id)); - - struct s2n_config *config = s2n_config_new(); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), - ticket_key1, s2n_array_len(ticket_key1), 0)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); - EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_serialized_session_state.blob.data, - s2n_stuffer_data_available(&tls13_serialized_session_state))); - - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "20240501")); - - EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - /* Verify that TLS1.2 was negotiated */ - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); - - /* Verify that the client did NOT try to use TLS1.2 tickets */ - EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); - EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); - - /* Verify that the client tried to use TLS1.3 tickets, but the server ignored them */ - EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, psk_ext_id)); - EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, psk_ext_id)); - - /* Verify that a full handshake occurred instead */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); - - EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_config_free(config)); - } - - /* Test: TLS1.3 resumption is successful when key used to encrypt ticket is in decrypt-only state */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(client_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); - - DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(server_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_configuration)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, - chain_and_key)); - - /* Add the key that encrypted the session ticket so that the server will be able to decrypt - * the ticket successfully. - */ - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - /* Add a mock delay such that key 1 moves to decrypt-only state */ - uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, - &mock_delay)); - - /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ - POSIX_GUARD(server_configuration->wall_clock(server_configuration->sys_clock_ctx, &now)); - uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name2, - s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), key_intro_time)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_session(client, tls13_serialized_session_state.blob.data, - s2n_stuffer_data_available(&tls13_serialized_session_state))); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); - - /* Create nonblocking pipes */ - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* Verify that TLS1.3 was negotiated */ - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); - - /* Expect a resumption handshake because the session ticket is valid. - * If a full handshake is performed instead, then the session ticket is incorrectly - * being evaluated as invalid. This was previously known to happen with a decrypt-only - * key because we'd incorrectly try to set a TLS1.2-only handshake type flag, - * triggering an error while decrypting the session ticket. - */ - EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server)); - } - - /* Test TLS 1.2 Server sends a zero-length ticket in the NewSessionTicket handshake - * if the ticket key was expired after SERVER_HELLO - */ - { - DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(client_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "20240501")); - - DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(server_configuration); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "20240501")); - - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - /* Stop the handshake after the peers have established that a ticket - * will be sent in this handshake. - */ - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, - CLIENT_FINISHED)); - - /* Expire current session ticket key so that server no longer holds a valid key */ - uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; - EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, - &mock_delay)); - - /* Attempt to send a NewSessionTicket. This should send a zero-length NST message */ - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - /* Verify that TLS1.2 was negotiated */ - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - /* Verify that the server issued zero-length session ticket */ - EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server)); - - /* Client does not have a session ticket since it received zero-length NST message */ - EXPECT_EQUAL(client->client_ticket.size, 0); - EXPECT_EQUAL(client->ticket_lifetime_hint, 0); - } - - /* Test: Server disables tls12 tickets */ - { - DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(forward_secret_config); - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(forward_secret_config, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(forward_secret_config, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_client_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_client_config, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_client_config)); - /* Security policy that does not support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); - - DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_client_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_client_config, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls13_client_config)); - /* Security policy that does support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "default_tls13")); - - /* Server does not send ticket when forward secrecy is enforced and TLS1.2 is negotiated */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - uint16_t tickets_sent = 0; - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 0); - } - - /* Server does send tickets when forward secrecy is enforced and TLS1.3 is negotiated */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls13_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); - - uint16_t tickets_sent = 0; - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 1); - } - - /* Server does not accept valid TLS1.2 ticket when forward secrecy is enforced */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - /* Disable forward secrecy for the first handshake */ - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, false)); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - uint16_t tickets_sent = 0; - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 1); - - uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_connection_get_session(client, session_data, sizeof(session_data))); - - /* Enable forward secrecy for the second handshake */ - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); - - EXPECT_SUCCESS(s2n_connection_wipe(client)); - EXPECT_SUCCESS(s2n_connection_wipe(server)); - - EXPECT_SUCCESS(s2n_connection_set_session(client, session_data, sizeof(session_data))); - - EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - /* Session ticket not accepted */ - EXPECT_TRUE(IS_FULL_HANDSHAKE(client)); - EXPECT_TRUE(IS_FULL_HANDSHAKE(server)); - - /* No ticket issued */ - EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); - EXPECT_EQUAL(tickets_sent, 0); - } - } - - /* Test: Client disables tls12 tickets */ - { - DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(forward_secret_config); - EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(forward_secret_config)); - - DEFER_CLEANUP(struct s2n_config *tls12_server_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls12_server_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, 1)); - /* Security policy that does not support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls12_server_config, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - DEFER_CLEANUP(struct s2n_config *tls13_server_config = s2n_config_new(), - s2n_config_ptr_free); - EXPECT_NOT_NULL(tls13_server_config); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_server_config, 1)); - /* Security policy that does support TLS1.3 */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_server_config, "default_tls13")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_server_config, - chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls13_server_config, ticket_key_name1, - s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); - - /* No ticket is received when forward secrecy is enforced and TLS1.2 is negotiated */ - { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_server_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); - - EXPECT_EQUAL(client->client_ticket.size, 0); - } - - /* A ticket is received when forward secrecy is enforced and TLS1.3 is negotiated */ - if (s2n_is_tls13_fully_supported()) { - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - - EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); - EXPECT_SUCCESS(s2n_connection_set_config(server, tls13_server_config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, - s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - - EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); - - /* Do a recv call to pick up TLS1.3 ticket */ - uint8_t data = 1; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client, &data, 1, &blocked), S2N_ERR_IO_BLOCKED); - - EXPECT_NOT_EQUAL(client->client_ticket.size, 0); - } - } - - EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); - free(cert_chain); - free(private_key); - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_NANOS (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) +#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS S2N_SESSION_STATE_CONFIGURABLE_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 + +/** + * This function is used to "skip" time in unit tests. It will mock the system + * time to be current_time (ns) + data (ns). The "data" parameter is a uint64_t + * passed in as a void*. + */ +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + struct timespec current_time; + + clock_gettime(S2N_CLOCK_SYS, ¤t_time); + + /** + * current_time fields are represented as time_t, and time_t has a platform + * dependent size. On 32 bit platforms, attempting to convert the current + * system time to nanoseconds will overflow, causing odd failures in unit + * tests. We upcast current_time fields to uint64_t before multiplying to + * avoid this. + */ + *nanoseconds = 0; + *nanoseconds += (uint64_t) current_time.tv_sec * ONE_SEC_IN_NANOS; + *nanoseconds += (uint64_t) current_time.tv_nsec; + *nanoseconds += *(uint64_t *) data; + + return 0; +} + +static int mock_time(void *data, uint64_t *nanoseconds) +{ + if (data) { + *nanoseconds = *((uint64_t *) data); + } else { + *nanoseconds = 1000000000; + } + return S2N_SUCCESS; +} + +uint8_t cb_session_data[S2N_TLS12_SESSION_SIZE * 2] = { 0 }; +size_t cb_session_data_len = 0; +uint32_t cb_session_lifetime = 0; +static int s2n_test_session_ticket_callback(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ticket); + + /* Store the callback data for comparison at the end of the connection. */ + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); + EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, sizeof(cb_session_data), cb_session_data)); + EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); + + return S2N_SUCCESS; +} + +/* make a struct with a ticket name and key adjacent in memory */ +struct small_name_ticket { + uint8_t name[1]; + uint8_t key[32]; +}; + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + struct s2n_config *client_config = NULL; + struct s2n_config *server_config = NULL; + uint64_t now = 0; + struct s2n_ticket_key *ticket_key = NULL; + uint32_t ticket_keys_len = 0; + + size_t serialized_session_state_length = 0; + uint8_t s2n_state_with_session_id = S2N_STATE_WITH_SESSION_ID; + uint8_t serialized_session_state[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES + S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + + /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name1[1] = "A"; + uint8_t ticket_key_name2[4] = "BBBB"; + uint8_t ticket_key_name3[16] = "CCCCCCCCCCCCCCCC"; + uint8_t ticket_key1[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, + 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, + 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, + 0xb3, 0xe5 }; + uint8_t ticket_key2[32] = { 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, + 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, + 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, + 0xc2, 0x44 }; + uint8_t ticket_key3[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }; + + /* Testcases: + * 1) Client sends empty ST extension. Server issues NST. + * 2) Client sends empty ST extension. Server does a full handshake, but is unable to issue NST due to absence of an encrypt-decrypt key. + * 3) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. + * 4) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST even though the key is in decrypt-only state. + * 5) Client sends non-empty ST extension. Server does an abbreviated handshake, but does not issue a NST even though the key is in + * decrypt-only state due to absence of encrypt-decrypt key. + * 6) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key is not found. + * 7) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key has expired. + * 8) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. + * 9) Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. + * 10) Client sets corrupted ST extension. + * 11) User tries adding a duplicate key to the server. + * 12) Testing expired keys are removed from the server config while adding new keys. + * 13) Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. + * 14) Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. + * 15) Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and s2n_config_set_ticket_decrypt_key_lifetime calls. + * 16) Add keys out of order and pre-emptively add a key. + * 17) Handshake with client auth and session ticket enabled. + * 18) Session resumption APIs and session_ticket_cb return the same values when receiving a new ticket in TLS1.2 + * 19) Session resumption APIs and session_ticket_cb return sane values when receiving a new ticket in TLS1.3 + * 20) Client has TLS1.3 ticket but negotiates TLS1.2, so does full handshake + */ + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_stuffer tls13_serialized_session_state = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_serialized_session_state, 0)); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + + struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Test ticket name handling */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Enable session tickets */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + + /* The name should be greater than 0 and less than or equal to 16 */ + uint8_t too_large_name[17] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 0, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 17, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Add a ticket with a single-byte name */ + struct small_name_ticket small = { .name = { 0xAA }, .key = { 0xBB, 0xBB, 0xBB, 0xBB } }; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, small.name, s2n_array_len(small.name), small.key, s2n_array_len(small.key), 0)); + + /* Ensure a ticket with the same name is not added */ + struct small_name_ticket small2 = { .name = { 0xAA }, .key = { 0xCC, 0xCC, 0xCC, 0xCC } }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, small2.name, s2n_array_len(small2.name), small2.key, s2n_array_len(small2.key), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Ensure a ticket with a zero-padded name is not added */ + uint8_t padded_name[16] = { 0xAA, 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, padded_name, s2n_array_len(padded_name), ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + }; + + /* Client sends empty ST extension. Server issues NST. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* A newly created connection should not be considered resumed */ + EXPECT_FALSE(s2n_connection_is_session_resumed(server_conn)); + EXPECT_FALSE(s2n_connection_is_session_resumed(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends empty ST extension. Server does a full handshake, but is unable + * to issue NST due to absence of an encrypt-decrypt key. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and not issue NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_TRUE(s2n_connection_is_session_resumed(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is same as before because server didn't issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + EXPECT_TRUE(s2n_connection_is_session_resumed(client_conn)); + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + /* Verify that the server lifetime hint is 0 because server didn't issue a NST */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing a NST + * even though the key is in decrypt-only state. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; + + uint32_t key_intro_time = mock_current_time / ONE_SEC_IN_NANOS; + 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), key_intro_time)); + + /* Verify there is an encrypt key available */ + EXPECT_OK(s2n_config_is_encrypt_key_available(server_config)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake without issuing NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is the same as before because server didn't issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake, + * but does not issue a NST even though the key is in decrypt-only state due to + * the absence of encrypt-decrypt key. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + mock_current_time += server_config->encrypt_decrypt_key_lifetime_in_nanos; + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and did not issue a NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is same as before because server did not issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_FALSE(memcmp(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues + * a NST because the key used to encrypt the session ticket is not found. + */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + 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)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); + EXPECT_EQUAL(session_ticket_lifetime, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues a NST + * because the key has expired. + */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + 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)); + + /* Add a mock delay such that the key used to encrypt ST expires */ + mock_current_time += server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the server has only the unexpired key */ + EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 1); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Tamper session state to make session ticket size smaller than what we expect */ + /* Verify that client_ticket is same as before because server did not issue a NST */ + uint8_t tampered_session_state[sizeof(serialized_session_state) - 1]; + /* Copy session format */ + tampered_session_state[0] = serialized_session_state[0]; + /* Copy and reduce by 1 the session ticket length */ + tampered_session_state[1] = serialized_session_state[1]; + tampered_session_state[2] = serialized_session_state[2] - 1; + /* Skip 1 byte of the session ticket and copy the rest */ + EXPECT_MEMCPY_SUCCESS(tampered_session_state + 3, serialized_session_state + 4, sizeof(tampered_session_state) - 4); + + /* Set client tampered ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tampered_session_state, serialized_session_state_length - 1)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + 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)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Not enabling resumption using ST */ + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sets corrupted ST extension. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + memset(serialized_session_state, 0, serialized_session_state_length); + + /* Set client ST and session state */ + EXPECT_FAILURE(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* User tries adding a duplicate key to the server */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + + 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)); + + /* Try adding the same key, but with a different name */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Try adding a different key, but with the same name */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key2, s2n_array_len(ticket_key2), 0)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Try adding a key with invalid key length */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, 0, 0)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_LENGTH); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Testing expired keys are removed from the server config while adding new keys. */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + + /* Add 2 ST keys */ + 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_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that the first two keys expire */ + uint64_t mock_delay = server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + /* Add a third ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + /* Try adding the expired keys */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + 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)); + + /* Verify that the config has three unexpired keys */ + EXPECT_OK(s2n_array_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + /* ticket_key3 should have "rotated" to the first index as other keys expired */ + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name3, s2n_array_len(ticket_key_name3)); + EXPECT_OK(s2n_array_num_elements(server_config->ticket_keys, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 3); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Attempting to add more than S2N_MAX_TICKET_KEYS causes failures. */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + uint8_t id = 0; + uint8_t ticket_key_buf[32] = { 0 }; + + for (uint8_t i = 0; i < S2N_MAX_TICKET_KEYS; i++) { + id = i; + ticket_key_buf[0] = i; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, + &id, sizeof(id), ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); + } + + id = S2N_MAX_TICKET_KEYS; + ticket_key_buf[0] = S2N_MAX_TICKET_KEYS; + EXPECT_FAILURE(s2n_config_add_ticket_crypto_key(config, &id, sizeof(id), + ticket_key_buf, s2n_array_len(ticket_key_buf), 0)); + }; + + /* Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + 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)); + + /* Add a mock delay such that the first key is close to it's encryption peak */ + uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; + mock_current_time += delay_in_nanos; + + /* Add two more ST keys */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_TICKET_KEY_NAME_LOCATION, + ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); + uint32_t first_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - delay_in_nanos / ONE_SEC_IN_NANOS; + EXPECT_EQUAL(session_ticket_lifetime, first_key_remaining_lifetime); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST */ + { + const size_t allowed_failures = 1; + size_t failures = 0; + bool expected_key_chosen = false; + + /* This test sets up three different ticket encryption keys at various times in their encryption lifetime. The test + * is meant to check that the weighted random selection algorithm correctly selects the key that is at its + * encryption peak. However the test will sometimes pick a key that is not at its encryption peak because the + * selection function uses a weighted random selection algorithm. Here we retry the test once if the key chosen + * is not the expected key. + * + * The wrong key will be chosen 0.02% of the time. This value is drawn from the weight of the expected key, + * which does not change per test run. Therefore, the probability that the test chooses the wrong key + * more than allowed_failures times is 0.0002 ^ 2 = 0.00000004, which is extremely unlikely to occur. If + * the logic changes to chose the wrong key at a higher rate, say 50% of the time, this test would fail at a + * 0.5 ^ 2 = 0.25 or 25% of the time. This rate is high enough for us to notice and investigate. + */ + while (expected_key_chosen == false) { + EXPECT_TRUE(failures <= allowed_failures); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + uint64_t mock_current_time = 0; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_time, &mock_current_time)); + + /* Add one ST key */ + 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)); + + /* Add second key when the first key is very close to it's encryption peak */ + uint64_t delay_in_nanos = server_config->encrypt_decrypt_key_lifetime_in_nanos / 2; + mock_current_time += delay_in_nanos; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add third key when the second key is very close to it's encryption peak and + * the first key is about to transition from encrypt-decrypt state to decrypt-only state + */ + mock_current_time += delay_in_nanos; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), + ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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); + 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; + } else { + failures += 1; + } + + /* Verify the lifetime hint from the server */ + uint32_t session_ticket_lifetime = s2n_connection_get_session_ticket_lifetime_hint(client_conn); + uint32_t second_key_remaining_lifetime = S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS - (delay_in_nanos / ONE_SEC_IN_NANOS); + EXPECT_EQUAL(session_ticket_lifetime, second_key_remaining_lifetime); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + }; + + /* Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and + * s2n_config_set_ticket_decrypt_key_lifetime calls */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Set encrypt-decrypt key expire time to 24 hours */ + EXPECT_SUCCESS(s2n_config_set_ticket_encrypt_decrypt_key_lifetime(server_config, 86400)); + + /* Set decrypt-only key expire time to 5 hours */ + EXPECT_SUCCESS(s2n_config_set_ticket_decrypt_key_lifetime(server_config, 18000)); + + /* Add one ST key */ + 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)); + + /* Add second key when the first key is very close to it's encryption peak */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add third key when the second key is very close to it's encryption peak and + * the first key is about to transition from encrypt-decrypt state to decrypt-only state + */ + mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Add keys out of order and pre-emptively add a key */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add a key. After 1 hour it will be considered an encrypt-decrypt key. */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + 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), (now / ONE_SEC_IN_NANOS) + 3600)); + + /* Add a key. After 1 hour it will reach it's peak */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), now / ONE_SEC_IN_NANOS)); + + /* Add a key pre-emptively. It can be used only after 10 hours */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), now / ONE_SEC_IN_NANOS + 36000)); + + /* Add a mock delay such that negotiation happens after 1 hour */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* 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_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)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Handshake with client auth and session ticket enabled */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + /* Client has session ticket and mutual auth enabled */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Server has session ticket and mutual auth enabled */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST since client + * auth is enabled in server mode */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* s2n_resume_decrypt_session fails to decrypt when presented with a valid ticket_key, valid iv and invalid encrypted blob */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + + /* Add Session Ticket key on the server config */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + 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 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))); + + uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); + + server_conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_DECRYPT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* s2n_resume_decrypt_session fails with a key not found error when presented with an invalid ticket_key, valid iv and invalid encrypted blob */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + /* Add Session Ticket key on the server config */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + 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 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))); + + uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); + + server_conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_ERROR_WITH_ERRNO(s2n_resume_decrypt_session(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Test s2n_connection_is_session_resumed */ + { + /* TLS1.2 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + conn->handshake.handshake_type = INITIAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED | WITH_SESSION_TICKET; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + /* Ignores PSK mode */ + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + conn->handshake.handshake_type = INITIAL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED; + conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Session resumption APIs and session_ticket_cb return the same values + * when receiving a new ticket in TLS1.2 + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(client_config)); + EXPECT_SUCCESS(s2n_config_set_wall_clock(client_config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + /* Client will use callback when server nst is received */ + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_callback, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_OK(s2n_config_set_tls12_security_policy(server_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + 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), now / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Expect values from the session_ticket_cb are equivalent to values from the APIs */ + EXPECT_EQUAL(cb_session_data_len, s2n_connection_get_session_length(client_conn)); + uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, session_data, cb_session_data_len)); + EXPECT_BYTEARRAY_EQUAL(cb_session_data, session_data, cb_session_data_len); + + EXPECT_EQUAL(cb_session_lifetime, s2n_connection_get_session_ticket_lifetime_hint(client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Session resumption APIs and session_ticket_cb return the same values + * when receiving a new ticket in TLS1.3 + */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + /* Freeze time */ + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); + EXPECT_OK(s2n_config_mock_wall_clock(config, &now)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + /* Send one NewSessionTicket */ + cb_session_data_len = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_callback, NULL)); + EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 1)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that TLS1.3 was negotiated */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Old TLS1.2 customer code will likely attempt to read the ticket here -- ensure we indicate no ticket yet */ + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); + + /* Receive and save the issued session ticket for the next test */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t out = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, &out, 1, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_NOT_EQUAL(cb_session_data_len, 0); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&tls13_serialized_session_state, cb_session_data, cb_session_data_len)); + + /* Verify correct session ticket lifetime "hint" */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), cb_session_lifetime); + + /* Verify the session ticket APIs produce the same results as the callback */ + DEFER_CLEANUP(struct s2n_blob legacy_api_ticket = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_realloc(&legacy_api_ticket, cb_session_data_len)); + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), cb_session_data_len); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, legacy_api_ticket.data, legacy_api_ticket.size)); + EXPECT_BYTEARRAY_EQUAL(cb_session_data, legacy_api_ticket.data, legacy_api_ticket.size); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Client has TLS1.3 ticket but negotiates TLS1.2 */ + if (s2n_is_tls13_fully_supported()) { + s2n_extension_type_id client_session_ticket_ext_id = 0, psk_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &client_session_ticket_ext_id)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_serialized_session_state.blob.data, + s2n_stuffer_data_available(&tls13_serialized_session_state))); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "20240501")); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that TLS1.2 was negotiated */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* Verify that the client did NOT try to use TLS1.2 tickets */ + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); + + /* Verify that the client tried to use TLS1.3 tickets, but the server ignored them */ + EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, psk_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, psk_ext_id)); + + /* Verify that a full handshake occurred instead */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Test: TLS1.3 resumption is successful when key used to encrypt ticket is in decrypt-only state */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(client_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); + + DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(server_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_configuration)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, + chain_and_key)); + + /* Add the key that encrypted the session ticket so that the server will be able to decrypt + * the ticket successfully. + */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, + &mock_delay)); + + /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_configuration->wall_clock(server_configuration->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name2, + s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), key_intro_time)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_session(client, tls13_serialized_session_state.blob.data, + s2n_stuffer_data_available(&tls13_serialized_session_state))); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Verify that TLS1.3 was negotiated */ + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + /* Expect a resumption handshake because the session ticket is valid. + * If a full handshake is performed instead, then the session ticket is incorrectly + * being evaluated as invalid. This was previously known to happen with a decrypt-only + * key because we'd incorrectly try to set a TLS1.2-only handshake type flag, + * triggering an error while decrypting the session ticket. + */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server)); + } + + /* Test TLS 1.2 Server sends a zero-length ticket in the NewSessionTicket handshake + * if the ticket key was expired after SERVER_HELLO + */ + { + DEFER_CLEANUP(struct s2n_config *client_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(client_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_configuration, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_configuration)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_configuration, "20240501")); + + DEFER_CLEANUP(struct s2n_config *server_configuration = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(server_configuration); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_configuration, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_configuration, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_configuration, "20240501")); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_configuration, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_configuration)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_configuration)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + /* Stop the handshake after the peers have established that a ticket + * will be sent in this handshake. + */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, + CLIENT_FINISHED)); + + /* Expire current session ticket key so that server no longer holds a valid key */ + uint64_t mock_delay = server_configuration->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_configuration, mock_nanoseconds_since_epoch, + &mock_delay)); + + /* Attempt to send a NewSessionTicket. This should send a zero-length NST message */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Verify that TLS1.2 was negotiated */ + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + /* Verify that the server issued zero-length session ticket */ + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server)); + + /* Client does not have a session ticket since it received zero-length NST message */ + EXPECT_EQUAL(client->client_ticket.size, 0); + EXPECT_EQUAL(client->ticket_lifetime_hint, 0); + } + + /* Test: Server disables tls12 tickets */ + { + DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(forward_secret_config); + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(forward_secret_config, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(forward_secret_config, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_client_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_client_config)); + /* Security policy that does not support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501")); + + DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_client_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls13_client_config)); + /* Security policy that does support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "default_tls13")); + + /* Server does not send ticket when forward secrecy is enforced and TLS1.2 is negotiated */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 0); + } + + /* Server does send tickets when forward secrecy is enforced and TLS1.3 is negotiated */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 1); + } + + /* Server does not accept valid TLS1.2 ticket when forward secrecy is enforced */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + /* Disable forward secrecy for the first handshake */ + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, false)); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 1); + + uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client, session_data, sizeof(session_data))); + + /* Enable forward secrecy for the second handshake */ + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); + + EXPECT_SUCCESS(s2n_connection_wipe(client)); + EXPECT_SUCCESS(s2n_connection_wipe(server)); + + EXPECT_SUCCESS(s2n_connection_set_session(client, session_data, sizeof(session_data))); + + EXPECT_SUCCESS(s2n_connection_set_config(client, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, forward_secret_config)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + /* Session ticket not accepted */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(client)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server)); + + /* No ticket issued */ + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 0); + } + } + + /* Test: Client disables tls12 tickets */ + { + DEFER_CLEANUP(struct s2n_config *forward_secret_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(forward_secret_config); + EXPECT_SUCCESS(s2n_config_require_ticket_forward_secrecy(forward_secret_config, true)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(forward_secret_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(forward_secret_config, 1)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(forward_secret_config)); + + DEFER_CLEANUP(struct s2n_config *tls12_server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls12_server_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, 1)); + /* Security policy that does not support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls12_server_config, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + DEFER_CLEANUP(struct s2n_config *tls13_server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(tls13_server_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_server_config, 1)); + /* Security policy that does support TLS1.3 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_server_config, + chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(tls13_server_config, ticket_key_name1, + s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* No ticket is received when forward secrecy is enforced and TLS1.2 is negotiated */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_server_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + EXPECT_EQUAL(client->client_ticket.size, 0); + } + + /* A ticket is received when forward secrecy is enforced and TLS1.3 is negotiated */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, forward_secret_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, tls13_server_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&test_io)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &test_io)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + EXPECT_EQUAL(client->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + /* Do a recv call to pick up TLS1.3 ticket */ + uint8_t data = 1; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client, &data, 1, &blocked), S2N_ERR_IO_BLOCKED); + + EXPECT_NOT_EQUAL(client->client_ticket.size, 0); + } + } + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + free(cert_chain); + free(private_key); + END_TEST(); +} diff --git a/tests/unit/s2n_sslv3_test.c b/tests/unit/s2n_sslv3_test.c index ce95730306e..304248475e6 100644 --- a/tests/unit/s2n_sslv3_test.c +++ b/tests/unit/s2n_sslv3_test.c @@ -1,132 +1,132 @@ -#include "utils/s2n_prelude.h" -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_random.h" - -#define S2N_TEST_DATA_SIZE 100 - -S2N_RESULT s2n_test_send_receive_data(struct s2n_connection *sender, struct s2n_connection *receiver) -{ - uint8_t test_data[S2N_TEST_DATA_SIZE] = { 0 }; - struct s2n_blob test_data_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); - EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); - - /* Send data */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - ssize_t bytes_written = 0; - while (bytes_written < S2N_TEST_DATA_SIZE) { - ssize_t w = s2n_send(sender, test_data + bytes_written, S2N_TEST_DATA_SIZE - bytes_written, &blocked); - EXPECT_TRUE(w >= 0); - bytes_written += w; - } - - /* Receive data */ - uint8_t buffer[S2N_TEST_DATA_SIZE] = { 0 }; - ssize_t bytes_received = 0; - while (bytes_received < S2N_TEST_DATA_SIZE) { - ssize_t r = s2n_recv(receiver, buffer + bytes_received, S2N_TEST_DATA_SIZE - bytes_received, &blocked); - EXPECT_TRUE(r > 0); - bytes_received += r; - } - - EXPECT_BYTEARRAY_EQUAL(test_data, buffer, S2N_TEST_DATA_SIZE); - - return S2N_RESULT_OK; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, - S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - - char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); - - /* Self-talk test */ - { - size_t supported_record_alg_count = 0; - - for (size_t i = 0; i < security_policy_test_all.cipher_preferences->count; i++) { - struct s2n_cipher_suite *cipher_suite = security_policy_test_all.cipher_preferences->suites[i]; - - /* Skip non-sslv3 cipher suites. */ - if (!cipher_suite->sslv3_record_alg) { - continue; - } - - /* Skip unsupported record algorithms. */ - if (!cipher_suite->sslv3_record_alg->cipher->is_available()) { - continue; - } - supported_record_alg_count += 1; - - struct s2n_cipher_preferences test_cipher_preferences = { - .count = 1, - .suites = &cipher_suite, - }; - struct s2n_security_policy test_policy = security_policy_test_all; - test_policy.cipher_preferences = &test_cipher_preferences; - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); - client_config->security_policy = &test_policy; - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); - EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); - server_config->security_policy = &test_policy; - - DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client); - EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); - client->client_protocol_version = S2N_SSLv3; - - DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server); - EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); - - 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_connections_set_io_pair(client, server, &io_pair)); - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); - EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), S2N_SSLv3); - EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), S2N_SSLv3); - - EXPECT_OK(s2n_test_send_receive_data(client, server)); - EXPECT_OK(s2n_test_send_receive_data(server, client)); - } - - /* Ensure that a supported record algorithm was found, and SSLv3 was tested at least once. */ - EXPECT_TRUE(supported_record_alg_count > 0); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_DATA_SIZE 100 + +S2N_RESULT s2n_test_send_receive_data(struct s2n_connection *sender, struct s2n_connection *receiver) +{ + uint8_t test_data[S2N_TEST_DATA_SIZE] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* Send data */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t bytes_written = 0; + while (bytes_written < S2N_TEST_DATA_SIZE) { + ssize_t w = s2n_send(sender, test_data + bytes_written, S2N_TEST_DATA_SIZE - bytes_written, &blocked); + EXPECT_TRUE(w >= 0); + bytes_written += w; + } + + /* Receive data */ + uint8_t buffer[S2N_TEST_DATA_SIZE] = { 0 }; + ssize_t bytes_received = 0; + while (bytes_received < S2N_TEST_DATA_SIZE) { + ssize_t r = s2n_recv(receiver, buffer + bytes_received, S2N_TEST_DATA_SIZE - bytes_received, &blocked); + EXPECT_TRUE(r > 0); + bytes_received += r; + } + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, S2N_TEST_DATA_SIZE); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Self-talk test */ + { + size_t supported_record_alg_count = 0; + + for (size_t i = 0; i < security_policy_test_all.cipher_preferences->count; i++) { + struct s2n_cipher_suite *cipher_suite = security_policy_test_all.cipher_preferences->suites[i]; + + /* Skip non-sslv3 cipher suites. */ + if (!cipher_suite->sslv3_record_alg) { + continue; + } + + /* Skip unsupported record algorithms. */ + if (!cipher_suite->sslv3_record_alg->cipher->is_available()) { + continue; + } + supported_record_alg_count += 1; + + struct s2n_cipher_preferences test_cipher_preferences = { + .count = 1, + .suites = &cipher_suite, + }; + struct s2n_security_policy test_policy = security_policy_test_all; + test_policy.cipher_preferences = &test_cipher_preferences; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + client_config->security_policy = &test_policy; + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + server_config->security_policy = &test_policy; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + client->client_protocol_version = S2N_SSLv3; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + 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_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), S2N_SSLv3); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), S2N_SSLv3); + + EXPECT_OK(s2n_test_send_receive_data(client, server)); + EXPECT_OK(s2n_test_send_receive_data(server, client)); + } + + /* Ensure that a supported record algorithm was found, and SSLv3 was tested at least once. */ + EXPECT_TRUE(supported_record_alg_count > 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls12_handshake_test.c b/tests/unit/s2n_tls12_handshake_test.c index f4381bd2374..9c84e194aaa 100644 --- a/tests/unit/s2n_tls12_handshake_test.c +++ b/tests/unit/s2n_tls12_handshake_test.c @@ -1,559 +1,559 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "utils/s2n_safety.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" - -static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; - -static int expected_handler_called; -static int unexpected_handler_called; - -static int s2n_test_handler(struct s2n_connection *conn) -{ - unexpected_handler_called = 1; - return 0; -} - -static int s2n_test_expected_handler(struct s2n_connection *conn) -{ - expected_handler_called = 1; - return 0; -} - -static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) -{ - for (int i = 0; i < s2n_array_len(state_machine); i++) { - state_machine[i].handler[0] = s2n_test_handler; - state_machine[i].handler[1] = s2n_test_handler; - } - - state_machine[expected].handler[direction] = s2n_test_expected_handler; - - expected_handler_called = 0; - unexpected_handler_called = 0; - - return 0; -} - -static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) -{ - POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); - - /* TLS1.2 protocol version */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - - if (record_type == TLS_HANDSHAKE) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); - - POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); - - /* Handshake message data size */ - POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); - return 0; - } - - if (record_type == TLS_CHANGE_CIPHER_SPEC) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); - - /* change spec is always just 0x01 */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); - return 0; - } - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Construct an array of all valid tls1.2 handshake_types */ - uint16_t valid_tls12_handshakes[S2N_HANDSHAKES_COUNT]; - int valid_tls12_handshakes_size = 0; - for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { - if (memcmp(handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { - valid_tls12_handshakes[valid_tls12_handshakes_size] = i; - valid_tls12_handshakes_size++; - } - } - EXPECT_TRUE(valid_tls12_handshakes_size > 0); - EXPECT_TRUE(valid_tls12_handshakes_size < S2N_HANDSHAKES_COUNT); - - /* Test: When using TLS 1.2, use the existing state machine and handshakes */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &state_machine[0]); - EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &handshakes[0]); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 server waits for expected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: Client CCS messages always come before Client Finished messages */ - { - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - /* Initial handshake doesn't contain a CCS message */ - if (handshake == INITIAL) { - continue; - } - - bool ccs_encountered = false; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - ccs_encountered = true; - } - - if (handshakes[handshake][j] == CLIENT_FINISHED) { - EXPECT_TRUE(ccs_encountered); - } - } - /* Every valid handshake includes a CCS message */ - EXPECT_TRUE(ccs_encountered); - } - }; - - /* Test: TLS1.2 client waits for expected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 client handles expected server CCS messages - * but errors on unexpected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - conn->handshake.message_number = j; - conn->in_status = ENCRYPTED; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - EXPECT_TRUE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - EXPECT_FALSE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 server handles expected client CCS messages - * but errors on unexpected CCS messages */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - int handshake = valid_tls12_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); - conn->handshake.message_number = j; - conn->in_status = ENCRYPTED; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - EXPECT_TRUE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - EXPECT_FALSE(expected_handler_called); - EXPECT_FALSE(unexpected_handler_called); - } - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 client can receive a hello request message at any time. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { - uint16_t handshake = valid_tls12_handshakes[i]; - - for (size_t j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (handshakes[handshake][j] == APPLICATION_DATA) { - break; - } - - conn->handshake.message_number = j; - conn->in_status = ENCRYPTED; - conn->handshake.handshake_type = handshake; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_HELLO_REQUEST)); - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - EXPECT_EQUAL(conn->handshake.message_number, j); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_FALSE(unexpected_handler_called); - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.2 s2n_handshake_read_io should accept only the expected message */ - { - /* TLS1.2 should accept the expected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, 1); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.2 should error for an unexpected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.2 should error for an expected message from the wrong writer */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.2 should error for an expected message from the wrong record type */ - { - /* Unfortunately, all our non-handshake record types have a message type of 0, - * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) - * which can appear at any point in a TLS1.2 handshake. - * - * To test, temporarily modify the actions table. - * We MUST restore this after this test. - */ - uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS12; - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - uint8_t server_css_message_number = 2; - conn->handshake.handshake_type = NEGOTIATED; - conn->handshake.message_number = server_css_message_number; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; - }; - }; - - /* Test: TLS1.2 handshake type name maximum size is set correctly. - * The maximum size is the size of a name with all flags set. */ - { - size_t correct_size = 0; - for (size_t i = 0; i < s2n_array_len(tls12_handshake_type_names); i++) { - correct_size += strlen(tls12_handshake_type_names[i]); - } - if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { - fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); - FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.2 handshakes"); - } - }; - - /* Test: TLS 1.2 handshake types are all properly printed */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS12; - - conn->handshake.handshake_type = INITIAL; - EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); - - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); - - const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT|" - "TLS12_PERFECT_FORWARD_SECRECY|OCSP_STATUS|WITH_SESSION_TICKET|WITH_NPN"; - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN; - EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - - const char *handshake_type_name = NULL; - for (int i = 0; i < valid_tls12_handshakes_size; i++) { - conn->handshake.handshake_type = valid_tls12_handshakes[i]; - - handshake_type_name = s2n_connection_get_handshake_type_name(conn); - - /* The handshake type names must be unique */ - for (int j = 0; j < valid_tls12_handshakes_size; j++) { - conn->handshake.handshake_type = valid_tls12_handshakes[j]; - if (i == j) { - EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } else { - EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS 1.2 message types are all properly printed */ - { - uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN; - const char *expected[] = { "CLIENT_HELLO", - "SERVER_HELLO", "SERVER_CERT", "SERVER_CERT_STATUS", "SERVER_KEY", "SERVER_CERT_REQ", "SERVER_HELLO_DONE", - "CLIENT_CERT", "CLIENT_KEY", "CLIENT_CERT_VERIFY", "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_NPN", - "CLIENT_FINISHED", "SERVER_NEW_SESSION_TICKET", "SERVER_CHANGE_CIPHER_SPEC", "SERVER_FINISHED", - "APPLICATION_DATA" }; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - - conn->handshake.handshake_type = test_handshake_type; - - for (size_t i = 0; i < s2n_array_len(expected); i++) { - conn->handshake.message_number = i; - EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: A WITH_NPN form of every valid, negotiated handshake exists */ - { - for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { - uint32_t handshake_type_original = valid_tls12_handshakes[i]; - message_type_t *messages_original = handshakes[handshake_type_original]; - - /* Ignore INITIAL and WITH_NPN handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_NPN)) { - continue; - } - - /* Get the WITH_NPN form of the handshake */ - uint32_t handshake_type_npn = handshake_type_original | WITH_NPN; - message_type_t *messages_npn = handshakes[handshake_type_npn]; - - for (size_t j = 0, j_npn = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_npn < S2N_MAX_HANDSHAKE_LENGTH; j++, j_npn++) { - /* The original handshake cannot contain the Next Protocol message */ - EXPECT_NOT_EQUAL(messages_original[j], CLIENT_NPN); - - /* Skip the Next Protocol message in WITH_NPN handshake */ - if (messages_npn[j_npn] == CLIENT_NPN) { - j_npn++; - } - - /* Otherwise the handshakes must be equivalent */ - EXPECT_EQUAL(messages_original[j], messages_npn[j_npn]); - } - } - }; - - /* Test: ensure that there isn't a collision between TLS 1.2 and TLS 1.3 flags - * that share the same bit. - */ - { - /* sanity check: these flags are the same bit */ - EXPECT_EQUAL((int) TLS12_PERFECT_FORWARD_SECRECY, (int) HELLO_RETRY_REQUEST); - - uint32_t tls12_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; - uint32_t tls13_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(conn); - - /* Clear the cache entries so we start fresh */ - memset(handshake_type_str_tls12ish[tls12_type], 0, MAX_HANDSHAKE_TYPE_LEN); - memset(handshake_type_str_tls13[tls13_type], 0, MAX_HANDSHAKE_TYPE_LEN); - - /* First: populate cache via TLS 1.2 */ - conn->actual_protocol_version = S2N_TLS12; - conn->handshake.handshake_type = tls12_type; - const char *tls12_name = s2n_connection_get_handshake_type_name(conn); - EXPECT_NOT_NULL(tls12_name); - EXPECT_STRING_EQUAL(tls12_name, "NEGOTIATED|FULL_HANDSHAKE|TLS12_PERFECT_FORWARD_SECRECY"); - - /* Second: query cache via TLS 1.3 with same bitmap — should not collide */ - conn->actual_protocol_version = S2N_TLS13; - conn->handshake.handshake_type = tls13_type; - const char *tls13_name = s2n_connection_get_handshake_type_name(conn); - EXPECT_NOT_NULL(tls13_name); - EXPECT_STRING_EQUAL(tls13_name, "NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST"); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_safety.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" + +static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +static int expected_handler_called; +static int unexpected_handler_called; + +static int s2n_test_handler(struct s2n_connection *conn) +{ + unexpected_handler_called = 1; + return 0; +} + +static int s2n_test_expected_handler(struct s2n_connection *conn) +{ + expected_handler_called = 1; + return 0; +} + +static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) +{ + for (int i = 0; i < s2n_array_len(state_machine); i++) { + state_machine[i].handler[0] = s2n_test_handler; + state_machine[i].handler[1] = s2n_test_handler; + } + + state_machine[expected].handler[direction] = s2n_test_expected_handler; + + expected_handler_called = 0; + unexpected_handler_called = 0; + + return 0; +} + +static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); + + /* TLS1.2 protocol version */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + + if (record_type == TLS_HANDSHAKE) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); + + POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); + + /* Handshake message data size */ + POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); + return 0; + } + + if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); + + /* change spec is always just 0x01 */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); + return 0; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Construct an array of all valid tls1.2 handshake_types */ + uint16_t valid_tls12_handshakes[S2N_HANDSHAKES_COUNT]; + int valid_tls12_handshakes_size = 0; + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + if (memcmp(handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { + valid_tls12_handshakes[valid_tls12_handshakes_size] = i; + valid_tls12_handshakes_size++; + } + } + EXPECT_TRUE(valid_tls12_handshakes_size > 0); + EXPECT_TRUE(valid_tls12_handshakes_size < S2N_HANDSHAKES_COUNT); + + /* Test: When using TLS 1.2, use the existing state machine and handshakes */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &state_machine[0]); + EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &handshakes[0]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 server waits for expected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: Client CCS messages always come before Client Finished messages */ + { + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + /* Initial handshake doesn't contain a CCS message */ + if (handshake == INITIAL) { + continue; + } + + bool ccs_encountered = false; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + ccs_encountered = true; + } + + if (handshakes[handshake][j] == CLIENT_FINISHED) { + EXPECT_TRUE(ccs_encountered); + } + } + /* Every valid handshake includes a CCS message */ + EXPECT_TRUE(ccs_encountered); + } + }; + + /* Test: TLS1.2 client waits for expected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 client handles expected server CCS messages + * but errors on unexpected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_TRUE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 server handles expected client CCS messages + * but errors on unexpected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_TRUE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 client can receive a hello request message at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { + uint16_t handshake = valid_tls12_handshakes[i]; + + for (size_t j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == APPLICATION_DATA) { + break; + } + + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + conn->handshake.handshake_type = handshake; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_EQUAL(conn->handshake.message_number, j); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 s2n_handshake_read_io should accept only the expected message */ + { + /* TLS1.2 should accept the expected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, 1); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an unexpected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an expected message from the wrong writer */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an expected message from the wrong record type */ + { + /* Unfortunately, all our non-handshake record types have a message type of 0, + * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) + * which can appear at any point in a TLS1.2 handshake. + * + * To test, temporarily modify the actions table. + * We MUST restore this after this test. + */ + uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + uint8_t server_css_message_number = 2; + conn->handshake.handshake_type = NEGOTIATED; + conn->handshake.message_number = server_css_message_number; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; + }; + }; + + /* Test: TLS1.2 handshake type name maximum size is set correctly. + * The maximum size is the size of a name with all flags set. */ + { + size_t correct_size = 0; + for (size_t i = 0; i < s2n_array_len(tls12_handshake_type_names); i++) { + correct_size += strlen(tls12_handshake_type_names[i]); + } + if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { + fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); + FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.2 handshakes"); + } + }; + + /* Test: TLS 1.2 handshake types are all properly printed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + conn->handshake.handshake_type = INITIAL; + EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + + const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT|" + "TLS12_PERFECT_FORWARD_SECRECY|OCSP_STATUS|WITH_SESSION_TICKET|WITH_NPN"; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN; + EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + + const char *handshake_type_name = NULL; + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + conn->handshake.handshake_type = valid_tls12_handshakes[i]; + + handshake_type_name = s2n_connection_get_handshake_type_name(conn); + + /* The handshake type names must be unique */ + for (int j = 0; j < valid_tls12_handshakes_size; j++) { + conn->handshake.handshake_type = valid_tls12_handshakes[j]; + if (i == j) { + EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } else { + EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.2 message types are all properly printed */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN; + const char *expected[] = { "CLIENT_HELLO", + "SERVER_HELLO", "SERVER_CERT", "SERVER_CERT_STATUS", "SERVER_KEY", "SERVER_CERT_REQ", "SERVER_HELLO_DONE", + "CLIENT_CERT", "CLIENT_KEY", "CLIENT_CERT_VERIFY", "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_NPN", + "CLIENT_FINISHED", "SERVER_NEW_SESSION_TICKET", "SERVER_CHANGE_CIPHER_SPEC", "SERVER_FINISHED", + "APPLICATION_DATA" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + + conn->handshake.handshake_type = test_handshake_type; + + for (size_t i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: A WITH_NPN form of every valid, negotiated handshake exists */ + { + for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { + uint32_t handshake_type_original = valid_tls12_handshakes[i]; + message_type_t *messages_original = handshakes[handshake_type_original]; + + /* Ignore INITIAL and WITH_NPN handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_NPN)) { + continue; + } + + /* Get the WITH_NPN form of the handshake */ + uint32_t handshake_type_npn = handshake_type_original | WITH_NPN; + message_type_t *messages_npn = handshakes[handshake_type_npn]; + + for (size_t j = 0, j_npn = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_npn < S2N_MAX_HANDSHAKE_LENGTH; j++, j_npn++) { + /* The original handshake cannot contain the Next Protocol message */ + EXPECT_NOT_EQUAL(messages_original[j], CLIENT_NPN); + + /* Skip the Next Protocol message in WITH_NPN handshake */ + if (messages_npn[j_npn] == CLIENT_NPN) { + j_npn++; + } + + /* Otherwise the handshakes must be equivalent */ + EXPECT_EQUAL(messages_original[j], messages_npn[j_npn]); + } + } + }; + + /* Test: ensure that there isn't a collision between TLS 1.2 and TLS 1.3 flags + * that share the same bit. + */ + { + /* sanity check: these flags are the same bit */ + EXPECT_EQUAL((int) TLS12_PERFECT_FORWARD_SECRECY, (int) HELLO_RETRY_REQUEST); + + uint32_t tls12_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; + uint32_t tls13_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + /* Clear the cache entries so we start fresh */ + memset(handshake_type_str_tls12ish[tls12_type], 0, MAX_HANDSHAKE_TYPE_LEN); + memset(handshake_type_str_tls13[tls13_type], 0, MAX_HANDSHAKE_TYPE_LEN); + + /* First: populate cache via TLS 1.2 */ + conn->actual_protocol_version = S2N_TLS12; + conn->handshake.handshake_type = tls12_type; + const char *tls12_name = s2n_connection_get_handshake_type_name(conn); + EXPECT_NOT_NULL(tls12_name); + EXPECT_STRING_EQUAL(tls12_name, "NEGOTIATED|FULL_HANDSHAKE|TLS12_PERFECT_FORWARD_SECRECY"); + + /* Second: query cache via TLS 1.3 with same bitmap — should not collide */ + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.handshake_type = tls13_type; + const char *tls13_name = s2n_connection_get_handshake_type_name(conn); + EXPECT_NOT_NULL(tls13_name); + EXPECT_STRING_EQUAL(tls13_name, "NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST"); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_cert_verify_test.c b/tests/unit/s2n_tls13_cert_verify_test.c index 2a71a8266ec..1014914a72d 100644 --- a/tests/unit/s2n_tls13_cert_verify_test.c +++ b/tests/unit/s2n_tls13_cert_verify_test.c @@ -1,361 +1,361 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_fips.h" -#include "crypto/s2n_rsa_pss.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_certificate_verify.c" - -uint8_t hello[] = "Hello, World!\n"; -uint8_t goodbye[] = "Goodbye, World!\n"; - -struct s2n_tls13_cert_verify_test { - const char *const cert_file; - const char *const key_file; - const struct s2n_signature_scheme *sig_scheme; - const struct s2n_signature_scheme *with_wrong_hash; -}; - -const struct s2n_tls13_cert_verify_test test_cases[] = { - { - .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, - .key_file = S2N_ECDSA_P256_PKCS1_KEY, - .sig_scheme = &s2n_ecdsa_sha256, - .with_wrong_hash = &s2n_ecdsa_sha384, - }, -#if RSA_PSS_CERTS_SUPPORTED - { - .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, - .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, - .sig_scheme = &s2n_rsa_pss_pss_sha256, - .with_wrong_hash = &s2n_rsa_pss_pss_sha384, - }, -#endif -}; - -S2N_RESULT s2n_cert_verify_connection_setup_and_send( - struct s2n_connection *sending_conn, struct s2n_connection *verifying_conn, - struct s2n_config *config, struct s2n_cert_chain_and_key *cert_chain, - struct s2n_signature_scheme *sig_scheme, struct s2n_blob *cert) -{ - sending_conn->handshake_params.our_chain_and_key = cert_chain; - sending_conn->handshake_params.server_cert_sig_scheme = sig_scheme; - sending_conn->handshake_params.client_cert_sig_scheme = sig_scheme; - sending_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - sending_conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_connection_set_config(sending_conn, config)); - - verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - verifying_conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); - - /* Extract public key from certificate and set it for verifying connection */ - s2n_pkey_type pkey_type = { 0 }; - if (verifying_conn->mode == S2N_CLIENT) { - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, cert)); - EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.server_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); - } else { - EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, cert)); - EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.client_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); - } - - /* Hash initialization */ - EXPECT_SUCCESS(s2n_hash_init(&sending_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&sending_conn->handshake.hashes->sha256, hello, sizeof(hello))); - EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); - EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, sizeof(hello))); - - /* Send cert verify */ - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, - s2n_stuffer_data_available(&sending_conn->handshake.io))); - - return S2N_RESULT_OK; -} - -int run_tests(const struct s2n_tls13_cert_verify_test *test_case, s2n_mode verifier_mode) -{ - const char *cert_file = test_case->cert_file; - const char *key_file = test_case->key_file; - struct s2n_signature_scheme sig_scheme = *test_case->sig_scheme; - - char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(cert_file, &cert_chain_pem[0], S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(key_file, &private_key_pem[0], S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_NOT_NULL(cert_chain); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); - - /* Initialize a certificate */ - DEFER_CLEANUP(struct s2n_stuffer certificate_in = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer certificate_out = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&certificate_in, (uint8_t *) cert_chain_pem, sizeof(cert_chain_pem))); - EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); - - uint32_t available_size = s2n_stuffer_data_available(&certificate_out); - struct s2n_blob cert = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&cert, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); - - /* Successfully send and receive certificate verify */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Receive and verify cert */ - EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); - - /* Repeat the above test successfully */ - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, - s2n_stuffer_data_available(&sending_conn->handshake.io))); - EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); - - /* Test fails if cipher suites hash is configured incorrectly */ - verifying_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); - EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, - s2n_stuffer_data_available(&sending_conn->handshake.io))); - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - }; - - /* Verifying connection errors with incorrect signed content */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Update receive hash with goodbye */ - EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, goodbye, sizeof(goodbye))); - - uint64_t verifying_bytes = 0; - uint64_t sending_bytes = 0; - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&verifying_conn->handshake.hashes->sha256, &verifying_bytes)); - EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&sending_conn->handshake.hashes->sha256, &sending_bytes)); - EXPECT_NOT_EQUAL(sending_bytes, verifying_bytes); - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - }; - - /* Verifying connection errors with even 1 bit incorrect */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Flip one bit in verifying_conn io buffer */ - EXPECT_TRUE(10 < s2n_stuffer_data_available(&verifying_conn->handshake.io)); - verifying_conn->handshake.io.blob.data[10] ^= 1; - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - }; - - /* Verifying connection errors with wrong hash algorithms */ - { - DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(sending_conn); - - DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(verifying_conn); - - EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); - - /* Use a hash algorithm different from sender by prepending corresponding iana value */ - struct s2n_stuffer rereader = verifying_conn->handshake.io; - EXPECT_SUCCESS(s2n_stuffer_rewrite(&rereader)); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&rereader, test_case->with_wrong_hash->iana_value)); - - /* We have special checks for ECDSA curves and therefore it errors earlier than signature verification */ - if (test_case->sig_scheme->sig_alg == S2N_SIGNATURE_ECDSA) { - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - } else { - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); - } - }; - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - /* Run all tests for server sending and client receiving/verifying cert_verify message */ - run_tests(&test_cases[i], S2N_CLIENT); - - /* Run all tests for client sending and server receiving/verifying cert_verify message */ - run_tests(&test_cases[i], S2N_SERVER); - } - - /* Self-talk: Ensure that the signature algorithm used to sign the CertificateVerify message - * is validated against the certificate type - */ - if (s2n_is_tls13_fully_supported()) { - struct s2n_tls13_cert_verify_test test_server_parameters[] = { - { - .cert_file = S2N_RSA_2048_PKCS1_CERT_CHAIN, - .key_file = S2N_RSA_2048_PKCS1_KEY, - .sig_scheme = &s2n_rsa_pss_rsae_sha256, - }, - { - .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, - .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, - .sig_scheme = &s2n_rsa_pss_pss_sha256, - }, - { - .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, - .key_file = S2N_ECDSA_P256_PKCS1_KEY, - .sig_scheme = &s2n_ecdsa_sha256, - } - }; - - const struct s2n_signature_scheme *test_client_sig_schemes[] = { - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_pss_sha256, - &s2n_ecdsa_sha256, - }; - - for (size_t param_idx = 0; param_idx < s2n_array_len(test_server_parameters); param_idx++) { - struct s2n_tls13_cert_verify_test server_parameters = test_server_parameters[param_idx]; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, server_parameters.cert_file, - server_parameters.key_file)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls13")); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - - /* The server only supports the single test certificate. Due to the fallback logic in - * the s2n-tls server, the signature algorithm corresponding with the test certificate - * will always be used to sign the CertificateVerify message, regardless of the - * client's advertised signature schemes. - */ - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); - - for (size_t sig_idx = 0; sig_idx < s2n_array_len(test_client_sig_schemes); sig_idx++) { - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - /* The client only supports the single test signature scheme, which allows for the - * server to sign the CertificateVerify message with a signature algorithm that - * isn't supported by the client. - */ - const struct s2n_signature_scheme *client_advertised_sig_scheme = test_client_sig_schemes[sig_idx]; - struct s2n_signature_preferences test_sig_preferences = { - .count = 1, - .signature_schemes = &client_advertised_sig_scheme, - }; - struct s2n_security_policy client_policy = security_policy_test_all_tls13; - client_policy.signature_preferences = &test_sig_preferences; - client_conn->security_policy_override = &client_policy; - - 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); - - /* Send the CertificateVerify message. */ - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, - SERVER_CERT_VERIFY)); - EXPECT_SUCCESS(s2n_stuffer_rewrite(&server_conn->handshake.io)); - EXPECT_SUCCESS(s2n_tls13_cert_verify_send(server_conn)); - - /* Check that the expected signature algorithm was used by the server. */ - EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme, server_parameters.sig_scheme); - - /* Overwrite the SignatureScheme field of the CertificateVerify message to lie to the - * client about which signature algorithm was used to sign the signature content. This - * will trick the client into always thinking its advertised signature algorithm was - * used. - */ - struct s2n_stuffer cert_verify_stuffer = server_conn->handshake.io; - EXPECT_SUCCESS(s2n_stuffer_rewrite(&cert_verify_stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&cert_verify_stuffer, - client_advertised_sig_scheme->iana_value)); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, - s2n_stuffer_data_available(&server_conn->handshake.io))); - - int ret = s2n_tls13_cert_verify_recv(client_conn); - - if (client_advertised_sig_scheme == server_parameters.sig_scheme) { - /* If the client's advertised signature scheme matches what the server actually - * used to sign the CertificateVerify message, validation should succeed. - */ - EXPECT_SUCCESS(ret); - } else { - /* Otherwise, the client should observe that the indicated signature algorithm - * from the server doesn't match the certificate type, and the connection - * should fail. - */ - EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - } - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_certificate_verify.c" + +uint8_t hello[] = "Hello, World!\n"; +uint8_t goodbye[] = "Goodbye, World!\n"; + +struct s2n_tls13_cert_verify_test { + const char *const cert_file; + const char *const key_file; + const struct s2n_signature_scheme *sig_scheme; + const struct s2n_signature_scheme *with_wrong_hash; +}; + +const struct s2n_tls13_cert_verify_test test_cases[] = { + { + .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, + .key_file = S2N_ECDSA_P256_PKCS1_KEY, + .sig_scheme = &s2n_ecdsa_sha256, + .with_wrong_hash = &s2n_ecdsa_sha384, + }, +#if RSA_PSS_CERTS_SUPPORTED + { + .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .sig_scheme = &s2n_rsa_pss_pss_sha256, + .with_wrong_hash = &s2n_rsa_pss_pss_sha384, + }, +#endif +}; + +S2N_RESULT s2n_cert_verify_connection_setup_and_send( + struct s2n_connection *sending_conn, struct s2n_connection *verifying_conn, + struct s2n_config *config, struct s2n_cert_chain_and_key *cert_chain, + struct s2n_signature_scheme *sig_scheme, struct s2n_blob *cert) +{ + sending_conn->handshake_params.our_chain_and_key = cert_chain; + sending_conn->handshake_params.server_cert_sig_scheme = sig_scheme; + sending_conn->handshake_params.client_cert_sig_scheme = sig_scheme; + sending_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + sending_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(sending_conn, config)); + + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + verifying_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); + + /* Extract public key from certificate and set it for verifying connection */ + s2n_pkey_type pkey_type = { 0 }; + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, cert)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.server_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); + } else { + EXPECT_OK(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, cert)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.client_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); + } + + /* Hash initialization */ + EXPECT_SUCCESS(s2n_hash_init(&sending_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sending_conn->handshake.hashes->sha256, hello, sizeof(hello))); + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, sizeof(hello))); + + /* Send cert verify */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, + s2n_stuffer_data_available(&sending_conn->handshake.io))); + + return S2N_RESULT_OK; +} + +int run_tests(const struct s2n_tls13_cert_verify_test *test_case, s2n_mode verifier_mode) +{ + const char *cert_file = test_case->cert_file; + const char *key_file = test_case->key_file; + struct s2n_signature_scheme sig_scheme = *test_case->sig_scheme; + + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(cert_file, &cert_chain_pem[0], S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(key_file, &private_key_pem[0], S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(cert_chain); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + + /* Initialize a certificate */ + DEFER_CLEANUP(struct s2n_stuffer certificate_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer certificate_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&certificate_in, (uint8_t *) cert_chain_pem, sizeof(cert_chain_pem))); + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + + uint32_t available_size = s2n_stuffer_data_available(&certificate_out); + struct s2n_blob cert = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&cert, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + + /* Successfully send and receive certificate verify */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Receive and verify cert */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Repeat the above test successfully */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, + s2n_stuffer_data_available(&sending_conn->handshake.io))); + EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Test fails if cipher suites hash is configured incorrectly */ + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, + s2n_stuffer_data_available(&sending_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Verifying connection errors with incorrect signed content */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Update receive hash with goodbye */ + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, goodbye, sizeof(goodbye))); + + uint64_t verifying_bytes = 0; + uint64_t sending_bytes = 0; + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&verifying_conn->handshake.hashes->sha256, &verifying_bytes)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&sending_conn->handshake.hashes->sha256, &sending_bytes)); + EXPECT_NOT_EQUAL(sending_bytes, verifying_bytes); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Verifying connection errors with even 1 bit incorrect */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Flip one bit in verifying_conn io buffer */ + EXPECT_TRUE(10 < s2n_stuffer_data_available(&verifying_conn->handshake.io)); + verifying_conn->handshake.io.blob.data[10] ^= 1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Verifying connection errors with wrong hash algorithms */ + { + DEFER_CLEANUP(struct s2n_connection *sending_conn = s2n_connection_new(S2N_PEER_MODE(verifier_mode)), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(sending_conn); + + DEFER_CLEANUP(struct s2n_connection *verifying_conn = s2n_connection_new(verifier_mode), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(verifying_conn); + + EXPECT_OK(s2n_cert_verify_connection_setup_and_send(sending_conn, verifying_conn, config, cert_chain, &sig_scheme, &cert)); + + /* Use a hash algorithm different from sender by prepending corresponding iana value */ + struct s2n_stuffer rereader = verifying_conn->handshake.io; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&rereader)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&rereader, test_case->with_wrong_hash->iana_value)); + + /* We have special checks for ECDSA curves and therefore it errors earlier than signature verification */ + if (test_case->sig_scheme->sig_alg == S2N_SIGNATURE_ECDSA) { + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + } + }; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + /* Run all tests for server sending and client receiving/verifying cert_verify message */ + run_tests(&test_cases[i], S2N_CLIENT); + + /* Run all tests for client sending and server receiving/verifying cert_verify message */ + run_tests(&test_cases[i], S2N_SERVER); + } + + /* Self-talk: Ensure that the signature algorithm used to sign the CertificateVerify message + * is validated against the certificate type + */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_tls13_cert_verify_test test_server_parameters[] = { + { + .cert_file = S2N_RSA_2048_PKCS1_CERT_CHAIN, + .key_file = S2N_RSA_2048_PKCS1_KEY, + .sig_scheme = &s2n_rsa_pss_rsae_sha256, + }, + { + .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, + .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, + .sig_scheme = &s2n_rsa_pss_pss_sha256, + }, + { + .cert_file = S2N_ECDSA_P256_PKCS1_CERT_CHAIN, + .key_file = S2N_ECDSA_P256_PKCS1_KEY, + .sig_scheme = &s2n_ecdsa_sha256, + } + }; + + const struct s2n_signature_scheme *test_client_sig_schemes[] = { + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_pss_sha256, + &s2n_ecdsa_sha256, + }; + + for (size_t param_idx = 0; param_idx < s2n_array_len(test_server_parameters); param_idx++) { + struct s2n_tls13_cert_verify_test server_parameters = test_server_parameters[param_idx]; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, server_parameters.cert_file, + server_parameters.key_file)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* The server only supports the single test certificate. Due to the fallback logic in + * the s2n-tls server, the signature algorithm corresponding with the test certificate + * will always be used to sign the CertificateVerify message, regardless of the + * client's advertised signature schemes. + */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + + for (size_t sig_idx = 0; sig_idx < s2n_array_len(test_client_sig_schemes); sig_idx++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* The client only supports the single test signature scheme, which allows for the + * server to sign the CertificateVerify message with a signature algorithm that + * isn't supported by the client. + */ + const struct s2n_signature_scheme *client_advertised_sig_scheme = test_client_sig_schemes[sig_idx]; + struct s2n_signature_preferences test_sig_preferences = { + .count = 1, + .signature_schemes = &client_advertised_sig_scheme, + }; + struct s2n_security_policy client_policy = security_policy_test_all_tls13; + client_policy.signature_preferences = &test_sig_preferences; + client_conn->security_policy_override = &client_policy; + + 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_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Send the CertificateVerify message. */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_CERT_VERIFY)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(server_conn)); + + /* Check that the expected signature algorithm was used by the server. */ + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme, server_parameters.sig_scheme); + + /* Overwrite the SignatureScheme field of the CertificateVerify message to lie to the + * client about which signature algorithm was used to sign the signature content. This + * will trick the client into always thinking its advertised signature algorithm was + * used. + */ + struct s2n_stuffer cert_verify_stuffer = server_conn->handshake.io; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cert_verify_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&cert_verify_stuffer, + client_advertised_sig_scheme->iana_value)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + int ret = s2n_tls13_cert_verify_recv(client_conn); + + if (client_advertised_sig_scheme == server_parameters.sig_scheme) { + /* If the client's advertised signature scheme matches what the server actually + * used to sign the CertificateVerify message, validation should succeed. + */ + EXPECT_SUCCESS(ret); + } else { + /* Otherwise, the client should observe that the indicated signature algorithm + * from the server doesn't match the certificate type, and the connection + * should fail. + */ + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + } + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_early_data_test.c b/tests/unit/s2n_tls13_handshake_early_data_test.c index 8eaab1c5875..8f93aa4b38f 100644 --- a/tests/unit/s2n_tls13_handshake_early_data_test.c +++ b/tests/unit/s2n_tls13_handshake_early_data_test.c @@ -1,357 +1,357 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_array.h" -#include "utils/s2n_mem.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_handshake_transcript.c" -#include "tls/s2n_tls13_handshake.c" - -#define S2N_SECRET_TYPE_COUNT 5 - -const uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; -message_type_t empty_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; - -int main() -{ - BEGIN_TEST(); - - /* Test early data encryption */ - { - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-3 - *= type=test - *# {server} generate resumption secret "tls13 resumption": - *# - *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf - *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c - *# - *# hash (2 octets): 00 00 - *# - *# info (22 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 75 6d 70 74 - *# 69 6f 6e 02 00 00 - *# - *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c - *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 - */ - S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ - a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-3 - *= type=test - *# {server} construct a NewSessionTicket handshake message: - *# - *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa - *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 - *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c - *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 - *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 - *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 - *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c - *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 - *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 - *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 - *# 04 00 00 04 00 - */ - /* Skip past the message type, message size, ticket lifetime, - * ticket age add, nonce, and ticket size: - * 04 00 00 c9 00 00 00 1e fa d6 aa - * c5 02 00 00 00 b2 - */ - S2N_BLOB_FROM_HEX(psk_identity, - "2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ - 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ - 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ - 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ - 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ - a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ - 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ - 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ - 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57"); - /* Skip past the total extensions size, early data extension type, - * and early data extension size: 00 08 00 2a 00 - * 04 - */ - const uint32_t max_early_data = 0x00000400; - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} send handshake record: - *# - *# payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff - *# 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 - *# 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 - *# 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 - *# 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 - *# 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 - *# 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 - *# 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 - *# 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 - *# 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 - *# 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 - *# ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 - *# 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 - *# 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 - *# 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 - *# 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 - *# ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d - *# e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa - *# cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d - *# ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d - */ - S2N_BLOB_FROM_HEX(client_hello_msg, - "01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff \ - 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 \ - 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 \ - 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 \ - 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 \ - 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 \ - 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 \ - 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 \ - 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 \ - 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 \ - 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 \ - ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 \ - 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 \ - 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 \ - 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 \ - 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 \ - ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d \ - e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa \ - cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d \ - ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d") - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# - *# complete record (517 octets): 16 03 01 02 00 01 00 01 fc 03 03 1b - *# c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 - *# d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 - *# 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 - *# 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 - *# 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d - *# 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 - *# 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e - *# 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 - *# 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 - *# 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - *# 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 - *# ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb - *# 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc - *# 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 - *# 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 - *# 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 - *# 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 - *# 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 - *# 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 - *# 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca - *# 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f - *# 9d - */ - S2N_BLOB_FROM_HEX(ch_record, "16 03 01 02 00 01 00 01 fc 03 03 1b \ - c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 \ - d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 \ - 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 \ - 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 \ - 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d \ - 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 \ - 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e \ - 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 \ - 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 \ - 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ - 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 \ - ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb \ - 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc \ - 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 \ - 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 \ - 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 \ - 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 \ - 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 \ - 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 \ - 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca \ - 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f \ - 9d"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} extract secret "early": - *# - *# salt: 0 (all zero octets) - *# - *# IKM (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 - *# 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 - *# - *# secret (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 - *# bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c - */ - S2N_BLOB_FROM_HEX(early_secret, - "9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 \ - bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} derive write traffic keys for early application data: - *# - *# PRK (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e - *# aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 - *# - *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 - *# - *# key expanded (16 octets): 92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 - *# 83 4f 54 - *# - *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 - *# - *# iv expanded (12 octets): 6d 47 5f 09 93 c8 e5 64 61 0d b2 b9 - */ - S2N_BLOB_FROM_HEX(iv, "6d 47 5f 09 93 c8 e5 64 61 0d b2 b9"); - - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# {client} send application_data record: - *# - *# payload (6 octets): 41 42 43 44 45 46 - */ - S2N_BLOB_FROM_HEX(payload, "41 42 43 44 45 46"); - /** - *= https://www.rfc-editor.org/rfc/rfc8448#section-4 - *= type=test - *# - *# complete record (28 octets): 17 03 03 00 17 ab 1d f4 20 e7 5c 45 - *# 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0 - */ - S2N_BLOB_FROM_HEX(complete_record, "17 03 03 00 17 ab 1d f4 20 e7 5c 45 \ - 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0"); - - /* Test client early data encryption against known client outputs */ - { - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->server_protocol_version = S2N_TLS13; - client_conn->early_data_state = S2N_EARLY_DATA_REQUESTED; - - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); - psk->hmac_alg = S2N_HMAC_SHA256; - EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); - client_conn->secure->cipher_suite = psk->early_data_config.cipher_suite; - - /* Rewrite early secret with known early secret. */ - EXPECT_SUCCESS(s2n_dup(&early_secret, &psk->early_secret)); - - /* Rewrite hashes with known ClientHello */ - EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(client_conn, &client_hello_msg)); - - EXPECT_OK(s2n_tls13_secrets_update(client_conn)); - EXPECT_OK(s2n_tls13_key_schedule_update(client_conn)); - - /* Check early secret secret set correctly */ - EXPECT_EQUAL(client_conn->secrets.extract_secret_type, S2N_EARLY_SECRET); - EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.extract_secret, early_secret.data, early_secret.size); - - /* Check IV calculated correctly */ - EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_implicit_iv, iv.data, iv.size); - - /* Check payload encrypted correctly */ - EXPECT_OK(s2n_record_write(client_conn, TLS_APPLICATION_DATA, &payload)); - EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->out), complete_record.size); - EXPECT_BYTEARRAY_EQUAL(client_conn->out.blob.data, complete_record.data, complete_record.size); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - -/* The known ClientHello uses the x25519 curve, - * which the S2N server won't accept if the EVP APIs are not supported */ -#if EVP_APIS_SUPPORTED - /* Test server early data encryption with known client inputs */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); - EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); - - DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); - psk->type = S2N_PSK_TYPE_RESUMPTION; - EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity.data, psk_identity.size)); - EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); - EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); - EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, psk)); - /* We need to explicitly set the psk_params type to skip our stateless session resumption recv - * code because the handshake traces we're using are meant for stateful session resumption. - * TODO: https://github.com/aws/s2n-tls/issues/2742 */ - server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; - - DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); - - EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); - - s2n_blocked_status blocked = 0; - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); - EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); - EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); - - EXPECT_SUCCESS(s2n_stuffer_write(&input, &complete_record)); - - DEFER_CLEANUP(struct s2n_blob actual_payload = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&actual_payload, payload.size)); - int r = s2n_recv(server_conn, actual_payload.data, actual_payload.size, &blocked); - EXPECT_EQUAL(r, payload.size); - EXPECT_BYTEARRAY_EQUAL(actual_payload.data, payload.data, payload.size); - EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; -#endif - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_handshake_transcript.c" +#include "tls/s2n_tls13_handshake.c" + +#define S2N_SECRET_TYPE_COUNT 5 + +const uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; +message_type_t empty_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +int main() +{ + BEGIN_TEST(); + + /* Test early data encryption */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-3 + *= type=test + *# {server} generate resumption secret "tls13 resumption": + *# + *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf + *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + *# + *# hash (2 octets): 00 00 + *# + *# info (22 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 75 6d 70 74 + *# 69 6f 6e 02 00 00 + *# + *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c + *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + */ + S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ + a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-3 + *= type=test + *# {server} construct a NewSessionTicket handshake message: + *# + *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa + *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 + *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c + *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 + *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 + *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 + *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c + *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 + *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 + *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 + *# 04 00 00 04 00 + */ + /* Skip past the message type, message size, ticket lifetime, + * ticket age add, nonce, and ticket size: + * 04 00 00 c9 00 00 00 1e fa d6 aa + * c5 02 00 00 00 b2 + */ + S2N_BLOB_FROM_HEX(psk_identity, + "2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ + 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ + 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ + 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ + 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ + a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ + 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ + 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ + 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57"); + /* Skip past the total extensions size, early data extension type, + * and early data extension size: 00 08 00 2a 00 + * 04 + */ + const uint32_t max_early_data = 0x00000400; + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send handshake record: + *# + *# payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff + *# 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 + *# 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 + *# 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 + *# 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 + *# 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 + *# 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 + *# 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 + *# 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 + *# 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 + *# 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 + *# ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 + *# 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 + *# 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 + *# 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 + *# 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 + *# ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d + *# e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa + *# cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d + *# ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d + */ + S2N_BLOB_FROM_HEX(client_hello_msg, + "01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff \ + 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 \ + 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 \ + 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 \ + 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 \ + 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 \ + 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 \ + 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 \ + 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 \ + 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 \ + 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 \ + ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 \ + 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 \ + 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 \ + 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 \ + 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 \ + ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d \ + e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa \ + cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d \ + ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d") + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (517 octets): 16 03 01 02 00 01 00 01 fc 03 03 1b + *# c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 + *# d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 + *# 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + *# 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + *# 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d + *# 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 + *# 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + *# 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + *# 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + *# 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 + *# ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb + *# 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc + *# 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 + *# 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 + *# 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 + *# 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 + *# 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 + *# 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 + *# 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca + *# 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f + *# 9d + */ + S2N_BLOB_FROM_HEX(ch_record, "16 03 01 02 00 01 00 01 fc 03 03 1b \ + c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 \ + d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 \ + 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 \ + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 \ + 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d \ + 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 \ + 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e \ + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 \ + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 \ + 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 \ + ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb \ + 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc \ + 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 \ + 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 \ + 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 \ + 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 \ + 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 \ + 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 \ + 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca \ + 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f \ + 9d"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} extract secret "early": + *# + *# salt: 0 (all zero octets) + *# + *# IKM (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 + *# 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + *# + *# secret (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 + *# bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c + */ + S2N_BLOB_FROM_HEX(early_secret, + "9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 \ + bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} derive write traffic keys for early application data: + *# + *# PRK (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e + *# aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 + *# 83 4f 54 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 6d 47 5f 09 93 c8 e5 64 61 0d b2 b9 + */ + S2N_BLOB_FROM_HEX(iv, "6d 47 5f 09 93 c8 e5 64 61 0d b2 b9"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send application_data record: + *# + *# payload (6 octets): 41 42 43 44 45 46 + */ + S2N_BLOB_FROM_HEX(payload, "41 42 43 44 45 46"); + /** + *= https://www.rfc-editor.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (28 octets): 17 03 03 00 17 ab 1d f4 20 e7 5c 45 + *# 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0 + */ + S2N_BLOB_FROM_HEX(complete_record, "17 03 03 00 17 ab 1d f4 20 e7 5c 45 \ + 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0"); + + /* Test client early data encryption against known client outputs */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); + client_conn->secure->cipher_suite = psk->early_data_config.cipher_suite; + + /* Rewrite early secret with known early secret. */ + EXPECT_SUCCESS(s2n_dup(&early_secret, &psk->early_secret)); + + /* Rewrite hashes with known ClientHello */ + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(client_conn, &client_hello_msg)); + + EXPECT_OK(s2n_tls13_secrets_update(client_conn)); + EXPECT_OK(s2n_tls13_key_schedule_update(client_conn)); + + /* Check early secret secret set correctly */ + EXPECT_EQUAL(client_conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.extract_secret, early_secret.data, early_secret.size); + + /* Check IV calculated correctly */ + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_implicit_iv, iv.data, iv.size); + + /* Check payload encrypted correctly */ + EXPECT_OK(s2n_record_write(client_conn, TLS_APPLICATION_DATA, &payload)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->out), complete_record.size); + EXPECT_BYTEARRAY_EQUAL(client_conn->out.blob.data, complete_record.data, complete_record.size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + +/* The known ClientHello uses the x25519 curve, + * which the S2N server won't accept if the EVP APIs are not supported */ +#if EVP_APIS_SUPPORTED + /* Test server early data encryption with known client inputs */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + psk->type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity.data, psk_identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, psk)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &complete_record)); + + DEFER_CLEANUP(struct s2n_blob actual_payload = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&actual_payload, payload.size)); + int r = s2n_recv(server_conn, actual_payload.data, actual_payload.size, &blocked); + EXPECT_EQUAL(r, payload.size); + EXPECT_BYTEARRAY_EQUAL(actual_payload.data, payload.data, payload.size); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; +#endif + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_state_machine_test.c b/tests/unit/s2n_tls13_handshake_state_machine_test.c index 6bfeb936ed3..d7b0d4d7b71 100644 --- a/tests/unit/s2n_tls13_handshake_state_machine_test.c +++ b/tests/unit/s2n_tls13_handshake_state_machine_test.c @@ -1,1008 +1,1008 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_tls13.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" - -static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; - -static int expected_handler_called; -static int unexpected_handler_called; - -static int s2n_test_handler(struct s2n_connection *conn) -{ - unexpected_handler_called = 1; - return 0; -} - -static int s2n_test_expected_handler(struct s2n_connection *conn) -{ - expected_handler_called = 1; - return 0; -} - -static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) -{ - for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { - tls13_state_machine[i].handler[0] = s2n_test_handler; - tls13_state_machine[i].handler[1] = s2n_test_handler; - } - - tls13_state_machine[expected].handler[direction] = s2n_test_expected_handler; - - expected_handler_called = 0; - unexpected_handler_called = 0; - - return 0; -} - -static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) -{ - POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); - - /* TLS1.2 protocol version */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); - - if (record_type == TLS_HANDSHAKE) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); - - POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); - - /* Handshake message data size */ - POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); - return 0; - } - - if (record_type == TLS_CHANGE_CIPHER_SPEC) { - /* Total message size */ - POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); - - /* change spec is always just 0x01 */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); - return 0; - } - - return 0; -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - /* Construct an array of all valid tls1.3 handshake_types */ - uint16_t valid_tls13_handshakes[S2N_HANDSHAKES_COUNT]; - int valid_tls13_handshakes_size = 0; - for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { - if (memcmp(tls13_handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { - valid_tls13_handshakes[valid_tls13_handshakes_size] = i; - valid_tls13_handshakes_size++; - } - } - EXPECT_TRUE(valid_tls13_handshakes_size > 0); - EXPECT_TRUE(valid_tls13_handshakes_size < S2N_HANDSHAKES_COUNT); - - /* Use handler stubs to avoid errors in handler implementation */ - for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { - tls13_state_machine[i].handler[0] = s2n_test_handler; - tls13_state_machine[i].handler[1] = s2n_test_handler; - } - - /* Test: A WITH_EARLY_DATA form of every non-full, non-retry handshake exists - * and matches the non-WITH_EARLY_DATA form EXCEPT for the END_OF_EARLY_DATA - * message and client CCS messages. - */ - { - uint32_t original_handshake_type = 0, early_data_handshake_type = 0; - message_type_t *original_messages = NULL, *early_data_messages = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - original_handshake_type = valid_tls13_handshakes[i]; - original_messages = tls13_handshakes[original_handshake_type]; - - /* WITH_EARLY_DATA form of the handshake */ - early_data_handshake_type = original_handshake_type | WITH_EARLY_DATA; - early_data_messages = tls13_handshakes[early_data_handshake_type]; - - /* No handshake exists for INITIAL, FULL_HANDSHAKE, or HELLO_RETRY_REQUEST handshakes */ - if (!(original_handshake_type & NEGOTIATED) || (original_handshake_type & FULL_HANDSHAKE) - || (original_handshake_type & HELLO_RETRY_REQUEST)) { - EXPECT_BYTEARRAY_EQUAL(early_data_messages, invalid_handshake, sizeof(invalid_handshake)); - continue; - } - - /* Ignore identical handshakes */ - if (original_handshake_type == early_data_handshake_type) { - continue; - } - - size_t end_of_early_data_messages = 0; - size_t j = 0, j_ed = 0; - while (j < S2N_MAX_HANDSHAKE_LENGTH && j_ed < S2N_MAX_HANDSHAKE_LENGTH) { - /* The original handshake must NOT contain END_OF_EARLY_DATA messages */ - EXPECT_NOT_EQUAL(original_messages[j], END_OF_EARLY_DATA); - - /* Count END_OF_EARLY_DATA messages in the WITH_EARLY_DATA handshake */ - if (early_data_messages[j_ed] == END_OF_EARLY_DATA) { - end_of_early_data_messages++; - j_ed++; - continue; - } - - /* Skip client CCS message in both handshakes - they won't appear at the same point */ - if (early_data_messages[j_ed] == CLIENT_CHANGE_CIPHER_SPEC) { - j_ed++; - continue; - } - if (original_messages[j] == CLIENT_CHANGE_CIPHER_SPEC) { - j++; - continue; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(original_messages[j], early_data_messages[j_ed]); - j++; - j_ed++; - } - if (original_handshake_type & NEGOTIATED) { - EXPECT_EQUAL(end_of_early_data_messages, 1); - } else { - EXPECT_EQUAL(end_of_early_data_messages, 0); - } - } - }; - - /* Test: A MIDDLEBOX_COMPAT form of every valid, negotiated handshake exists - * and matches the non-MIDDLEBOX_COMPAT form EXCEPT for CCS messages */ - { - uint32_t handshake_type_original = 0, handshake_type_mc = 0; - message_type_t *messages_original = NULL, *messages_mc = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - handshake_type_original = valid_tls13_handshakes[i]; - messages_original = tls13_handshakes[handshake_type_original]; - - /* Ignore INITIAL and MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & MIDDLEBOX_COMPAT)) { - continue; - } - - /* MIDDLEBOX_COMPAT form of the handshake */ - handshake_type_mc = handshake_type_original | MIDDLEBOX_COMPAT; - messages_mc = tls13_handshakes[handshake_type_mc]; - - for (size_t j = 0, j_mc = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_mc < S2N_MAX_HANDSHAKE_LENGTH; j++, j_mc++) { - /* The original handshake cannot contain CCS messages */ - EXPECT_NOT_EQUAL(messages_original[j], SERVER_CHANGE_CIPHER_SPEC); - EXPECT_NOT_EQUAL(messages_original[j], CLIENT_CHANGE_CIPHER_SPEC); - - /* Skip CCS messages in the MIDDLEBOX_COMPAT handshake */ - while (messages_mc[j_mc] == SERVER_CHANGE_CIPHER_SPEC - || messages_mc[j_mc] == CLIENT_CHANGE_CIPHER_SPEC) { - j_mc++; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(messages_original[j], messages_mc[j_mc]); - } - } - }; - - /* Test: A non-FULL_HANDSHAKE form of every valid, negotiated handshake exists */ - { - uint32_t handshake_type_original = 0, handshake_type_fh = 0; - message_type_t *messages_original = NULL, *messages_fh = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - handshake_type_original = valid_tls13_handshakes[i]; - messages_original = tls13_handshakes[handshake_type_original]; - - /* Ignore INITIAL and FULL_HANDSHAKE handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & FULL_HANDSHAKE)) { - continue; - } - - /* FULL_HANDSHAKE form of the handshake */ - handshake_type_fh = handshake_type_original | FULL_HANDSHAKE; - messages_fh = tls13_handshakes[handshake_type_fh]; - - /* No FULL handshake exists for INITIAL or WITH_EARLY_DATA handshakes */ - if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_EARLY_DATA)) { - EXPECT_BYTEARRAY_EQUAL(messages_fh, invalid_handshake, sizeof(invalid_handshake)); - continue; - } - - /* Ignore identical handshakes */ - if (handshake_type_original == handshake_type_fh) { - continue; - } - - for (size_t j = 0, j_fh = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_fh < S2N_MAX_HANDSHAKE_LENGTH; j++, j_fh++) { - /* The original handshake cannot contain authentication messages */ - EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT); - EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT_VERIFY); - - /* Skip authentication messages in the FULL_HANDSHAKE handshake */ - while (messages_fh[j_fh] == SERVER_CERT || messages_fh[j_fh] == SERVER_CERT_VERIFY) { - j_fh++; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(messages_original[j], messages_fh[j_fh]); - } - } - }; - - /* Test: A EARLY_CLIENT_CCS form of every middlebox compatible handshake exists. - * Any handshake could start with early data, even if that early data is later rejected. */ - { - uint32_t handshake_type_original = 0, handshake_type_test = 0; - message_type_t *messages_original = NULL, *messages_test = NULL; - - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - handshake_type_original = valid_tls13_handshakes[i]; - messages_original = tls13_handshakes[handshake_type_original]; - - /* Ignore non-MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type_original & MIDDLEBOX_COMPAT)) { - continue; - } - - /* EARLY_CLIENT_CCS form of the handshake */ - handshake_type_test = handshake_type_original | EARLY_CLIENT_CCS; - messages_test = tls13_handshakes[handshake_type_test]; - - /* Ignore identical handshakes */ - if (handshake_type_original == handshake_type_test) { - continue; - } - - for (size_t j = 0, j_test = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_test < S2N_MAX_HANDSHAKE_LENGTH; j++, j_test++) { - /* Skip Client CCS messages */ - while (messages_original[j] == CLIENT_CHANGE_CIPHER_SPEC) { - j++; - } - while (messages_test[j_test] == CLIENT_CHANGE_CIPHER_SPEC) { - j_test++; - } - - /* The handshakes must be otherwise equivalent */ - EXPECT_EQUAL(messages_original[j], messages_test[j_test]); - } - } - }; - - /* Test: When using TLS 1.3, use the new state machine and handshakes */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &tls13_state_machine[0]); - EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &tls13_handshakes[0]); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 server does not wait for client cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 server does not skip server cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 client does not wait for server cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 client does not skip client cipher change requests */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - conn->handshake.message_number = j - 1; - - EXPECT_SUCCESS(s2n_advance_message(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, j); - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); - - break; - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 client can receive a server cipher change spec at any time. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - conn->in_status = ENCRYPTED; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - conn->handshake.message_number = j; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - } else { - EXPECT_EQUAL(conn->handshake.message_number, j); - } - - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 server can receive a client cipher change request at any time. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); - - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - int handshake = valid_tls13_handshakes[i]; - - conn->handshake.handshake_type = handshake; - conn->in_status = ENCRYPTED; - - for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - conn->handshake.message_number = j; - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); - - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { - EXPECT_EQUAL(conn->handshake.message_number, j + 1); - } else { - EXPECT_EQUAL(conn->handshake.message_number, j); - } - - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); - } - } - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS1.3 s2n_handshake_read_io should accept only the expected message */ - { - /* TLS1.3 should accept the expected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_SUCCESS(s2n_handshake_read_io(conn)); - - EXPECT_EQUAL(conn->handshake.message_number, 1); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_TRUE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 should error for an unexpected message */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 should error for an expected message from the wrong writer */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - conn->handshake.handshake_type = 0; - conn->handshake.message_number = 0; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, 0); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* TLS1.3 should error for an expected message from the wrong record type */ - { - /* Unfortunately, all our non-handshake record types have a message type of 0, - * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) - * which can appear at any point in a TLS1.2 handshake. - * - * To test, temporarily modify the actions table. - * We MUST restore this after this test. - */ - uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; - - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - uint8_t server_css_message_number = 2; - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; - conn->handshake.message_number = server_css_message_number; - EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); - - EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); - EXPECT_FALSE(unexpected_handler_called); - EXPECT_FALSE(expected_handler_called); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; - }; - - /* Error if a client receives a client cert request in non-FULL_HANDSHAKE mode */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - POSIX_GUARD(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL)); - - struct s2n_stuffer input = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); - - EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERT_REQ)); - EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_HANDSHAKE_STATE); - - EXPECT_SUCCESS(s2n_stuffer_free(&input)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test: TLS 1.3 MIDDLEBOX_COMPAT handshakes all follow CCS middlebox compatibility rules. - * - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# Field measurements [Ben17a] [Ben17b] [Res17a] [Res17b] have found - *# that a significant number of middleboxes misbehave when a TLS - *# client/server pair negotiates TLS 1.3. Implementations can increase - *# the chance of making connections through those middleboxes by making - *# the TLS 1.3 handshake look more like a TLS 1.2 handshake: - */ - { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# If not offering early data, the client sends a dummy - *# change_cipher_spec record (see the third paragraph of Section 5) - *# immediately before its second flight. This may either be before - *# its second ClientHello or before its encrypted handshake flight. - **/ - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - bool change_cipher_spec_found = false; - uint32_t handshake_type = valid_tls13_handshakes[i]; - message_type_t *messages = tls13_handshakes[handshake_type]; - - /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type & NEGOTIATED) - || !(handshake_type & MIDDLEBOX_COMPAT) - || (handshake_type & EARLY_CLIENT_CCS)) { - continue; - } - - for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - /* Is it the second client flight? - * Have we switched from the server sending to the client sending? */ - if (tls13_state_machine[messages[j]].writer != 'C' - || tls13_state_machine[messages[j - 1]].writer != 'S') { - continue; - } - - EXPECT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); - EXPECT_EQUAL(tls13_state_machine[messages[j + 1]].writer, 'C'); - - /* CCS message found. We are done with this handshake. */ - change_cipher_spec_found = true; - break; - } - - EXPECT_TRUE(change_cipher_spec_found); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# If offering early data, the record is placed immediately after the - *# first ClientHello. - */ - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - uint32_t handshake_type = valid_tls13_handshakes[i]; - message_type_t *messages = tls13_handshakes[handshake_type]; - - /* Ignore handshakes where early data did not trigger the change in CCS behavior */ - if (!(handshake_type & EARLY_CLIENT_CCS)) { - continue; - } - - EXPECT_EQUAL(messages[0], CLIENT_HELLO); - EXPECT_EQUAL(messages[1], CLIENT_CHANGE_CIPHER_SPEC); - for (size_t j = 2; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - EXPECT_NOT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); - } - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 - *= type=test - *# The server sends a dummy change_cipher_spec record immediately - *# after its first handshake message. This may either be after a - *# ServerHello or a HelloRetryRequest. - **/ - for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { - bool change_cipher_spec_found = false; - uint32_t handshake_type = valid_tls13_handshakes[i]; - message_type_t *messages = tls13_handshakes[handshake_type]; - - /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ - if (!(handshake_type & NEGOTIATED) || !(handshake_type & MIDDLEBOX_COMPAT)) { - continue; - } - - for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - /* Is it the first server flight? - * Have we switched from the client sending to the server sending? */ - if (tls13_state_machine[messages[j]].writer != 'S' - || tls13_state_machine[messages[j - 1]].writer != 'C') { - continue; - } - - EXPECT_EQUAL(messages[j + 1], SERVER_CHANGE_CIPHER_SPEC); - EXPECT_TRUE(messages[j] == SERVER_HELLO || messages[j] == HELLO_RETRY_MSG); - - /* CCS message found. We are done with this handshake. */ - change_cipher_spec_found = true; - break; - } - - EXPECT_TRUE(change_cipher_spec_found); - } - }; - - /* Test: TLS1.3 s2n_conn_set_handshake_type sets only handshake flags allowed by TLS1.3 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - - /* Ensure WITH_SESSION_TICKETS is set */ - conn->config->use_tickets = 1; - conn->session_ticket_status = S2N_NEW_TICKET; - - /* Ensure CLIENT_AUTH is set */ - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_REQUIRED)); - - /* Ensure TLS12_PERFECT_FORWARD_SECRECY is set by choosing a cipher suite with is_ephemeral=1 on the kex */ - conn->secure->cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256; - - /* Ensure OCSP_STATUS is set by setting the connection status_type */ - conn->status_type = S2N_STATUS_REQUEST_OCSP; - - /* Verify that tls1.2 DOES set the flags allowed by tls1.2 */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & TLS12_PERFECT_FORWARD_SECRECY); - EXPECT_TRUE(conn->handshake.handshake_type & OCSP_STATUS); - EXPECT_TRUE(conn->handshake.handshake_type & WITH_SESSION_TICKET); - EXPECT_TRUE(conn->handshake.handshake_type & CLIENT_AUTH); - - /* Reset which state machine we're on */ - EXPECT_OK(s2n_handshake_type_reset(conn)); - conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; - - /* Verify that tls1.3 ONLY sets the flags allowed by tls1.3 */ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: s2n_conn_set_handshake_type only allows HELLO_RETRY_REQUEST with TLS1.3 */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - - /* HELLO_RETRY_REQUEST allowed with tls1.3 */ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - /* Reset state machine */ - conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; - - /* HELLO_RETRY_REQUEST not allowed with tls1.2 */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS12)); - conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; - EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); - EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type does not set FULL_HANDSHAKE if - * a pre-shared key has been chosen. */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); - struct s2n_psk *psk = NULL; - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); - - conn->psk_params.chosen_psk = psk; - EXPECT_NOT_NULL(conn->psk_params.chosen_psk); - EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); - EXPECT_FALSE(conn->handshake.handshake_type & FULL_HANDSHAKE); - - conn->psk_params.chosen_psk = NULL; - EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); - EXPECT_TRUE(conn->handshake.handshake_type & FULL_HANDSHAKE); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is - * chosen and s2n is a client. */ - { - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); - client_conn->psk_params.chosen_psk = psk; - EXPECT_NOT_NULL(client_conn->psk_params.chosen_psk); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); - - EXPECT_OK(s2n_conn_set_tls13_handshake_type(client_conn)); - EXPECT_FALSE(client_conn->handshake.handshake_type & CLIENT_AUTH); - EXPECT_FALSE(client_conn->handshake.handshake_type & FULL_HANDSHAKE); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is - * chosen and s2n is a server. */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - struct s2n_psk *psk = NULL; - - EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &psk)); - server_conn->psk_params.chosen_psk = psk; - EXPECT_NOT_NULL(server_conn->psk_params.chosen_psk); - - EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); - - EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); - EXPECT_FALSE(server_conn->handshake.handshake_type & CLIENT_AUTH); - EXPECT_FALSE(server_conn->handshake.handshake_type & FULL_HANDSHAKE); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type sets WITH_EARLY_DATA */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - server_conn->actual_protocol_version = S2N_TLS13; - - server_conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; - EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); - EXPECT_TRUE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); - EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test: s2n_conn_set_tls13_handshake_type does not set WITH_EARLY_DATA if wrong state */ - { - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - server_conn->actual_protocol_version = S2N_TLS13; - - server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; - EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); - EXPECT_FALSE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); - EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - }; - - /* Test: TLS1.3 handshake type name maximum size is set correctly. - * The maximum size is the size of a name with all flags set. */ - { - size_t correct_size = 0; - for (size_t i = 0; i < s2n_array_len(tls13_handshake_type_names); i++) { - correct_size += strlen(tls13_handshake_type_names[i]); - } - if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { - fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); - FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.3 handshakes"); - } - }; - - /* Test: TLS 1.3 handshake types are all properly printed */ - { - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - - conn->handshake.handshake_type = INITIAL; - EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); - - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); - - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; - EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST", s2n_connection_get_handshake_type_name(conn)); - - const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT" - "|MIDDLEBOX_COMPAT|WITH_EARLY_DATA|EARLY_CLIENT_CCS"; - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT - | MIDDLEBOX_COMPAT | WITH_EARLY_DATA | EARLY_CLIENT_CCS; - EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - - const char *handshake_type_name = NULL; - for (int i = 0; i < valid_tls13_handshakes_size; i++) { - conn->handshake.handshake_type = valid_tls13_handshakes[i]; - - handshake_type_name = s2n_connection_get_handshake_type_name(conn); - - /* The handshake type names must be unique */ - for (int j = 0; j < valid_tls13_handshakes_size; j++) { - conn->handshake.handshake_type = valid_tls13_handshakes[j]; - if (i == j) { - EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } else { - EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); - } - } - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS 1.3 message types are all properly printed */ - { - uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; - const char *expected[] = { "CLIENT_HELLO", "SERVER_HELLO", "SERVER_CHANGE_CIPHER_SPEC", - "ENCRYPTED_EXTENSIONS", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", - "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_FINISHED", "APPLICATION_DATA" }; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - conn->handshake.handshake_type = test_handshake_type; - - for (int i = 0; i < s2n_array_len(expected); i++) { - conn->handshake.message_number = i; - EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: TLS 1.3 message types are all properly printed for client auth */ - { - uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH; - const char *expected[] = { "CLIENT_HELLO", - "SERVER_HELLO", "ENCRYPTED_EXTENSIONS", "SERVER_CERT_REQ", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", - "CLIENT_CERT", "CLIENT_CERT_VERIFY", "CLIENT_FINISHED", - "APPLICATION_DATA" }; - - struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); - - conn->handshake.handshake_type = test_handshake_type; - - for (int i = 0; i < s2n_array_len(expected); i++) { - conn->handshake.message_number = i; - EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* Test: Make sure not to miss out populating any message names */ - { - for (int i = CLIENT_HELLO; i <= APPLICATION_DATA; i++) { - EXPECT_NOT_NULL(message_names[i]); - } - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls13.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" + +static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +static int expected_handler_called; +static int unexpected_handler_called; + +static int s2n_test_handler(struct s2n_connection *conn) +{ + unexpected_handler_called = 1; + return 0; +} + +static int s2n_test_expected_handler(struct s2n_connection *conn) +{ + expected_handler_called = 1; + return 0; +} + +static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) +{ + for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[0] = s2n_test_handler; + tls13_state_machine[i].handler[1] = s2n_test_handler; + } + + tls13_state_machine[expected].handler[direction] = s2n_test_expected_handler; + + expected_handler_called = 0; + unexpected_handler_called = 0; + + return 0; +} + +static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); + + /* TLS1.2 protocol version */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + + if (record_type == TLS_HANDSHAKE) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); + + POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); + + /* Handshake message data size */ + POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); + return 0; + } + + if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); + + /* change spec is always just 0x01 */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); + return 0; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Construct an array of all valid tls1.3 handshake_types */ + uint16_t valid_tls13_handshakes[S2N_HANDSHAKES_COUNT]; + int valid_tls13_handshakes_size = 0; + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + if (memcmp(tls13_handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { + valid_tls13_handshakes[valid_tls13_handshakes_size] = i; + valid_tls13_handshakes_size++; + } + } + EXPECT_TRUE(valid_tls13_handshakes_size > 0); + EXPECT_TRUE(valid_tls13_handshakes_size < S2N_HANDSHAKES_COUNT); + + /* Use handler stubs to avoid errors in handler implementation */ + for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[0] = s2n_test_handler; + tls13_state_machine[i].handler[1] = s2n_test_handler; + } + + /* Test: A WITH_EARLY_DATA form of every non-full, non-retry handshake exists + * and matches the non-WITH_EARLY_DATA form EXCEPT for the END_OF_EARLY_DATA + * message and client CCS messages. + */ + { + uint32_t original_handshake_type = 0, early_data_handshake_type = 0; + message_type_t *original_messages = NULL, *early_data_messages = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + original_handshake_type = valid_tls13_handshakes[i]; + original_messages = tls13_handshakes[original_handshake_type]; + + /* WITH_EARLY_DATA form of the handshake */ + early_data_handshake_type = original_handshake_type | WITH_EARLY_DATA; + early_data_messages = tls13_handshakes[early_data_handshake_type]; + + /* No handshake exists for INITIAL, FULL_HANDSHAKE, or HELLO_RETRY_REQUEST handshakes */ + if (!(original_handshake_type & NEGOTIATED) || (original_handshake_type & FULL_HANDSHAKE) + || (original_handshake_type & HELLO_RETRY_REQUEST)) { + EXPECT_BYTEARRAY_EQUAL(early_data_messages, invalid_handshake, sizeof(invalid_handshake)); + continue; + } + + /* Ignore identical handshakes */ + if (original_handshake_type == early_data_handshake_type) { + continue; + } + + size_t end_of_early_data_messages = 0; + size_t j = 0, j_ed = 0; + while (j < S2N_MAX_HANDSHAKE_LENGTH && j_ed < S2N_MAX_HANDSHAKE_LENGTH) { + /* The original handshake must NOT contain END_OF_EARLY_DATA messages */ + EXPECT_NOT_EQUAL(original_messages[j], END_OF_EARLY_DATA); + + /* Count END_OF_EARLY_DATA messages in the WITH_EARLY_DATA handshake */ + if (early_data_messages[j_ed] == END_OF_EARLY_DATA) { + end_of_early_data_messages++; + j_ed++; + continue; + } + + /* Skip client CCS message in both handshakes - they won't appear at the same point */ + if (early_data_messages[j_ed] == CLIENT_CHANGE_CIPHER_SPEC) { + j_ed++; + continue; + } + if (original_messages[j] == CLIENT_CHANGE_CIPHER_SPEC) { + j++; + continue; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(original_messages[j], early_data_messages[j_ed]); + j++; + j_ed++; + } + if (original_handshake_type & NEGOTIATED) { + EXPECT_EQUAL(end_of_early_data_messages, 1); + } else { + EXPECT_EQUAL(end_of_early_data_messages, 0); + } + } + }; + + /* Test: A MIDDLEBOX_COMPAT form of every valid, negotiated handshake exists + * and matches the non-MIDDLEBOX_COMPAT form EXCEPT for CCS messages */ + { + uint32_t handshake_type_original = 0, handshake_type_mc = 0; + message_type_t *messages_original = NULL, *messages_mc = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore INITIAL and MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & MIDDLEBOX_COMPAT)) { + continue; + } + + /* MIDDLEBOX_COMPAT form of the handshake */ + handshake_type_mc = handshake_type_original | MIDDLEBOX_COMPAT; + messages_mc = tls13_handshakes[handshake_type_mc]; + + for (size_t j = 0, j_mc = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_mc < S2N_MAX_HANDSHAKE_LENGTH; j++, j_mc++) { + /* The original handshake cannot contain CCS messages */ + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CHANGE_CIPHER_SPEC); + EXPECT_NOT_EQUAL(messages_original[j], CLIENT_CHANGE_CIPHER_SPEC); + + /* Skip CCS messages in the MIDDLEBOX_COMPAT handshake */ + while (messages_mc[j_mc] == SERVER_CHANGE_CIPHER_SPEC + || messages_mc[j_mc] == CLIENT_CHANGE_CIPHER_SPEC) { + j_mc++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_mc[j_mc]); + } + } + }; + + /* Test: A non-FULL_HANDSHAKE form of every valid, negotiated handshake exists */ + { + uint32_t handshake_type_original = 0, handshake_type_fh = 0; + message_type_t *messages_original = NULL, *messages_fh = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore INITIAL and FULL_HANDSHAKE handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & FULL_HANDSHAKE)) { + continue; + } + + /* FULL_HANDSHAKE form of the handshake */ + handshake_type_fh = handshake_type_original | FULL_HANDSHAKE; + messages_fh = tls13_handshakes[handshake_type_fh]; + + /* No FULL handshake exists for INITIAL or WITH_EARLY_DATA handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_EARLY_DATA)) { + EXPECT_BYTEARRAY_EQUAL(messages_fh, invalid_handshake, sizeof(invalid_handshake)); + continue; + } + + /* Ignore identical handshakes */ + if (handshake_type_original == handshake_type_fh) { + continue; + } + + for (size_t j = 0, j_fh = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_fh < S2N_MAX_HANDSHAKE_LENGTH; j++, j_fh++) { + /* The original handshake cannot contain authentication messages */ + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT); + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT_VERIFY); + + /* Skip authentication messages in the FULL_HANDSHAKE handshake */ + while (messages_fh[j_fh] == SERVER_CERT || messages_fh[j_fh] == SERVER_CERT_VERIFY) { + j_fh++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_fh[j_fh]); + } + } + }; + + /* Test: A EARLY_CLIENT_CCS form of every middlebox compatible handshake exists. + * Any handshake could start with early data, even if that early data is later rejected. */ + { + uint32_t handshake_type_original = 0, handshake_type_test = 0; + message_type_t *messages_original = NULL, *messages_test = NULL; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type_original & MIDDLEBOX_COMPAT)) { + continue; + } + + /* EARLY_CLIENT_CCS form of the handshake */ + handshake_type_test = handshake_type_original | EARLY_CLIENT_CCS; + messages_test = tls13_handshakes[handshake_type_test]; + + /* Ignore identical handshakes */ + if (handshake_type_original == handshake_type_test) { + continue; + } + + for (size_t j = 0, j_test = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_test < S2N_MAX_HANDSHAKE_LENGTH; j++, j_test++) { + /* Skip Client CCS messages */ + while (messages_original[j] == CLIENT_CHANGE_CIPHER_SPEC) { + j++; + } + while (messages_test[j_test] == CLIENT_CHANGE_CIPHER_SPEC) { + j_test++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_test[j_test]); + } + } + }; + + /* Test: When using TLS 1.3, use the new state machine and handshakes */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_EQUAL(&ACTIVE_STATE_MACHINE(conn)[0], &tls13_state_machine[0]); + EXPECT_EQUAL(&ACTIVE_HANDSHAKES(conn)[0], &tls13_handshakes[0]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server does not wait for client cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server does not skip server cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client does not wait for server cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client does not skip client cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client can receive a server cipher change spec at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + conn->in_status = ENCRYPTED; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + conn->handshake.message_number = j; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + } else { + EXPECT_EQUAL(conn->handshake.message_number, j); + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server can receive a client cipher change request at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + conn->in_status = ENCRYPTED; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + conn->handshake.message_number = j; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + } else { + EXPECT_EQUAL(conn->handshake.message_number, j); + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 s2n_handshake_read_io should accept only the expected message */ + { + /* TLS1.3 should accept the expected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, 1); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an unexpected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an expected message from the wrong writer */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an expected message from the wrong record type */ + { + /* Unfortunately, all our non-handshake record types have a message type of 0, + * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) + * which can appear at any point in a TLS1.2 handshake. + * + * To test, temporarily modify the actions table. + * We MUST restore this after this test. + */ + uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + uint8_t server_css_message_number = 2; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; + conn->handshake.message_number = server_css_message_number; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; + }; + + /* Error if a client receives a client cert request in non-FULL_HANDSHAKE mode */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + POSIX_GUARD(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERT_REQ)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: TLS 1.3 MIDDLEBOX_COMPAT handshakes all follow CCS middlebox compatibility rules. + * + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# Field measurements [Ben17a] [Ben17b] [Res17a] [Res17b] have found + *# that a significant number of middleboxes misbehave when a TLS + *# client/server pair negotiates TLS 1.3. Implementations can increase + *# the chance of making connections through those middleboxes by making + *# the TLS 1.3 handshake look more like a TLS 1.2 handshake: + */ + { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# If not offering early data, the client sends a dummy + *# change_cipher_spec record (see the third paragraph of Section 5) + *# immediately before its second flight. This may either be before + *# its second ClientHello or before its encrypted handshake flight. + **/ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + bool change_cipher_spec_found = false; + uint32_t handshake_type = valid_tls13_handshakes[i]; + message_type_t *messages = tls13_handshakes[handshake_type]; + + /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type & NEGOTIATED) + || !(handshake_type & MIDDLEBOX_COMPAT) + || (handshake_type & EARLY_CLIENT_CCS)) { + continue; + } + + for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + /* Is it the second client flight? + * Have we switched from the server sending to the client sending? */ + if (tls13_state_machine[messages[j]].writer != 'C' + || tls13_state_machine[messages[j - 1]].writer != 'S') { + continue; + } + + EXPECT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_EQUAL(tls13_state_machine[messages[j + 1]].writer, 'C'); + + /* CCS message found. We are done with this handshake. */ + change_cipher_spec_found = true; + break; + } + + EXPECT_TRUE(change_cipher_spec_found); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# If offering early data, the record is placed immediately after the + *# first ClientHello. + */ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + uint32_t handshake_type = valid_tls13_handshakes[i]; + message_type_t *messages = tls13_handshakes[handshake_type]; + + /* Ignore handshakes where early data did not trigger the change in CCS behavior */ + if (!(handshake_type & EARLY_CLIENT_CCS)) { + continue; + } + + EXPECT_EQUAL(messages[0], CLIENT_HELLO); + EXPECT_EQUAL(messages[1], CLIENT_CHANGE_CIPHER_SPEC); + for (size_t j = 2; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_NOT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); + } + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# The server sends a dummy change_cipher_spec record immediately + *# after its first handshake message. This may either be after a + *# ServerHello or a HelloRetryRequest. + **/ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + bool change_cipher_spec_found = false; + uint32_t handshake_type = valid_tls13_handshakes[i]; + message_type_t *messages = tls13_handshakes[handshake_type]; + + /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type & NEGOTIATED) || !(handshake_type & MIDDLEBOX_COMPAT)) { + continue; + } + + for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + /* Is it the first server flight? + * Have we switched from the client sending to the server sending? */ + if (tls13_state_machine[messages[j]].writer != 'S' + || tls13_state_machine[messages[j - 1]].writer != 'C') { + continue; + } + + EXPECT_EQUAL(messages[j + 1], SERVER_CHANGE_CIPHER_SPEC); + EXPECT_TRUE(messages[j] == SERVER_HELLO || messages[j] == HELLO_RETRY_MSG); + + /* CCS message found. We are done with this handshake. */ + change_cipher_spec_found = true; + break; + } + + EXPECT_TRUE(change_cipher_spec_found); + } + }; + + /* Test: TLS1.3 s2n_conn_set_handshake_type sets only handshake flags allowed by TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* Ensure WITH_SESSION_TICKETS is set */ + conn->config->use_tickets = 1; + conn->session_ticket_status = S2N_NEW_TICKET; + + /* Ensure CLIENT_AUTH is set */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_REQUIRED)); + + /* Ensure TLS12_PERFECT_FORWARD_SECRECY is set by choosing a cipher suite with is_ephemeral=1 on the kex */ + conn->secure->cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256; + + /* Ensure OCSP_STATUS is set by setting the connection status_type */ + conn->status_type = S2N_STATUS_REQUEST_OCSP; + + /* Verify that tls1.2 DOES set the flags allowed by tls1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & TLS12_PERFECT_FORWARD_SECRECY); + EXPECT_TRUE(conn->handshake.handshake_type & OCSP_STATUS); + EXPECT_TRUE(conn->handshake.handshake_type & WITH_SESSION_TICKET); + EXPECT_TRUE(conn->handshake.handshake_type & CLIENT_AUTH); + + /* Reset which state machine we're on */ + EXPECT_OK(s2n_handshake_type_reset(conn)); + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + /* Verify that tls1.3 ONLY sets the flags allowed by tls1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_handshake_type only allows HELLO_RETRY_REQUEST with TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* HELLO_RETRY_REQUEST allowed with tls1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + /* Reset state machine */ + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + /* HELLO_RETRY_REQUEST not allowed with tls1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS12)); + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type does not set FULL_HANDSHAKE if + * a pre-shared key has been chosen. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + struct s2n_psk *psk = NULL; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + + conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); + EXPECT_FALSE(conn->handshake.handshake_type & FULL_HANDSHAKE); + + conn->psk_params.chosen_psk = NULL; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is + * chosen and s2n is a client. */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + client_conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(client_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_OK(s2n_conn_set_tls13_handshake_type(client_conn)); + EXPECT_FALSE(client_conn->handshake.handshake_type & CLIENT_AUTH); + EXPECT_FALSE(client_conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is + * chosen and s2n is a server. */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + struct s2n_psk *psk = NULL; + + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &psk)); + server_conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_FALSE(server_conn->handshake.handshake_type & CLIENT_AUTH); + EXPECT_FALSE(server_conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type sets WITH_EARLY_DATA */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + server_conn->actual_protocol_version = S2N_TLS13; + + server_conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); + EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type does not set WITH_EARLY_DATA if wrong state */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + server_conn->actual_protocol_version = S2N_TLS13; + + server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_FALSE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); + EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: TLS1.3 handshake type name maximum size is set correctly. + * The maximum size is the size of a name with all flags set. */ + { + size_t correct_size = 0; + for (size_t i = 0; i < s2n_array_len(tls13_handshake_type_names); i++) { + correct_size += strlen(tls13_handshake_type_names[i]); + } + if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { + fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); + FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.3 handshakes"); + } + }; + + /* Test: TLS 1.3 handshake types are all properly printed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + + conn->handshake.handshake_type = INITIAL; + EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST", s2n_connection_get_handshake_type_name(conn)); + + const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT" + "|MIDDLEBOX_COMPAT|WITH_EARLY_DATA|EARLY_CLIENT_CCS"; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT + | MIDDLEBOX_COMPAT | WITH_EARLY_DATA | EARLY_CLIENT_CCS; + EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + + const char *handshake_type_name = NULL; + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + conn->handshake.handshake_type = valid_tls13_handshakes[i]; + + handshake_type_name = s2n_connection_get_handshake_type_name(conn); + + /* The handshake type names must be unique */ + for (int j = 0; j < valid_tls13_handshakes_size; j++) { + conn->handshake.handshake_type = valid_tls13_handshakes[j]; + if (i == j) { + EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } else { + EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.3 message types are all properly printed */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; + const char *expected[] = { "CLIENT_HELLO", "SERVER_HELLO", "SERVER_CHANGE_CIPHER_SPEC", + "ENCRYPTED_EXTENSIONS", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", + "CLIENT_CHANGE_CIPHER_SPEC", "CLIENT_FINISHED", "APPLICATION_DATA" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_handshake_type; + + for (int i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.3 message types are all properly printed for client auth */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH; + const char *expected[] = { "CLIENT_HELLO", + "SERVER_HELLO", "ENCRYPTED_EXTENSIONS", "SERVER_CERT_REQ", "SERVER_CERT", "SERVER_CERT_VERIFY", "SERVER_FINISHED", + "CLIENT_CERT", "CLIENT_CERT_VERIFY", "CLIENT_FINISHED", + "APPLICATION_DATA" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_handshake_type; + + for (int i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: Make sure not to miss out populating any message names */ + { + for (int i = CLIENT_HELLO; i <= APPLICATION_DATA; i++) { + EXPECT_NOT_NULL(message_names[i]); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_test.c b/tests/unit/s2n_tls13_handshake_test.c index 17d550e3ff4..792aaa4c1c0 100644 --- a/tests/unit/s2n_tls13_handshake_test.c +++ b/tests/unit/s2n_tls13_handshake_test.c @@ -1,332 +1,332 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -#include "tls/s2n_tls13_handshake.h" - -#include -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/extensions/s2n_client_key_share.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_quic_support.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* Just to get access to the static functions / variables we need to test */ -#include "tls/s2n_handshake_io.c" -#include "tls/s2n_handshake_transcript.c" -#include "tls/s2n_tls13_handshake.c" - -#define S2N_SECRET_TYPE_COUNT 5 -#define S2N_TEST_PSK_COUNT 10 - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* Test wiping PSKs after use */ - { - /* PSKs are wiped when chosen PSK is NULL */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - EXPECT_NOT_NULL(ecc_preferences); - - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - const uint8_t psk_data[] = "test identity data"; - const uint8_t secret_data[] = "test secret data"; - for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); - EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); - EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NOT_EQUAL(psk->identity.data, NULL); - EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); - EXPECT_NOT_EQUAL(psk->secret.size, 0); - EXPECT_NOT_EQUAL(psk->secret.data, NULL); - } - - EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); - EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); - EXPECT_NULL(conn->psk_params.chosen_psk); - - DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); - - /* Verify secrets are wiped */ - for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NULL(psk->secret.data); - EXPECT_EQUAL(psk->secret.size, 0); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - - /* PSKs are wiped when chosen PSK is NOT NULL */ - { - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - EXPECT_NOT_NULL(ecc_preferences); - - conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); - conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; - EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); - - const uint8_t psk_data[] = "test identity data"; - const uint8_t secret_data[] = "test secret data"; - const uint8_t early_secret_data[SHA256_DIGEST_LENGTH] = "test early secret data"; - for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); - EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); - EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NOT_EQUAL(psk->identity.data, NULL); - EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); - EXPECT_NOT_EQUAL(psk->secret.size, 0); - EXPECT_NOT_EQUAL(psk->secret.data, NULL); - EXPECT_SUCCESS(s2n_realloc(&psk->early_secret, sizeof(early_secret_data))); - POSIX_CHECKED_MEMCPY(psk->early_secret.data, early_secret_data, sizeof(early_secret_data)); - EXPECT_NOT_EQUAL(psk->early_secret.size, 0); - EXPECT_NOT_EQUAL(psk->early_secret.data, NULL); - } - - /* Set chosen PSK */ - struct s2n_psk *chosen_psk = NULL; - EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &chosen_psk)); - EXPECT_NOT_NULL(chosen_psk); - conn->psk_params.chosen_psk = chosen_psk; - conn->psk_params.chosen_psk_wire_index = 0; - - EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); - EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); - - DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); - - /* Verify secrets are wiped */ - for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { - struct s2n_psk *psk = NULL; - EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); - EXPECT_NOT_EQUAL(psk->identity.size, 0); - EXPECT_NULL(psk->secret.data); - EXPECT_EQUAL(psk->secret.size, 0); - EXPECT_NULL(psk->early_secret.data); - EXPECT_EQUAL(psk->early_secret.size, 0); - } - - EXPECT_SUCCESS(s2n_connection_free(conn)); - }; - }; - - /* Test: Handshake self-talks using s2n_handshake_write_io and s2n_handshake_read_io */ - { - struct s2n_connection *client_conn = NULL; - struct s2n_connection *server_conn = NULL; - - struct s2n_config *server_config = NULL, *client_config = NULL; - EXPECT_NOT_NULL(server_config = s2n_config_new()); - EXPECT_NOT_NULL(client_config = s2n_config_new()); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); - - uint8_t *cert_chain = NULL; - uint8_t *private_key = NULL; - uint32_t cert_chain_len = 0; - uint32_t private_key_len = 0; - - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); - - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_cert_chain_and_key *default_cert = NULL; - EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); - EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); - - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); - - struct s2n_stuffer client_to_server = { 0 }; - struct s2n_stuffer server_to_client = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - struct s2n_blob server_seq = { .data = server_conn->secure->server_sequence_number, .size = sizeof(server_conn->secure->server_sequence_number) }; - S2N_BLOB_FROM_HEX(seq_0, "0000000000000000"); - S2N_BLOB_FROM_HEX(seq_1, "0000000000000001"); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); - - /* Client sends ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); - - s2n_tls13_connection_keys(server_secrets_0, server_conn); - EXPECT_EQUAL(server_secrets_0.size, 0); - - EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); - - /* Server reads ClientHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ - EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT); - - s2n_tls13_connection_keys(server_secrets, server_conn); - EXPECT_EQUAL(server_secrets.size, SHA256_DIGEST_LENGTH); - - EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); - - /* Server sends ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - S2N_BLOB_EXPECT_EQUAL(server_seq, seq_0); - - /* Server sends EncryptedExtensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - S2N_BLOB_EXPECT_EQUAL(server_seq, seq_1); - - /* Server sends ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Server sends CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Client reads ServerHello */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - s2n_tls13_connection_keys(client_secrets, client_conn); - EXPECT_EQUAL(client_secrets.size, SHA256_DIGEST_LENGTH); - - /* Verify that derive and extract secrets match */ - S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); - S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); - - /* Client reads Encrypted extensions */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads ServerCert */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client reads CertVerify */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Server sends ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); - - /* Client reads ServerFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); - - /* Client sends CCS */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Client sends ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - /* Server reads CCS - * The CCS message does not affect its place in the state machine. */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - /* Server reads ClientFinished */ - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); - EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); - - /* Verify that derive and extract secrets match */ - S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); - S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); - EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); - EXPECT_SUCCESS(s2n_config_free(server_config)); - EXPECT_SUCCESS(s2n_config_free(client_config)); - - free(private_key); - free(cert_chain); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include "tls/s2n_tls13_handshake.h" + +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_handshake_transcript.c" +#include "tls/s2n_tls13_handshake.c" + +#define S2N_SECRET_TYPE_COUNT 5 +#define S2N_TEST_PSK_COUNT 10 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test wiping PSKs after use */ + { + /* PSKs are wiped when chosen PSK is NULL */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + const uint8_t psk_data[] = "test identity data"; + const uint8_t secret_data[] = "test secret data"; + for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NOT_EQUAL(psk->identity.data, NULL); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); + EXPECT_NOT_EQUAL(psk->secret.size, 0); + EXPECT_NOT_EQUAL(psk->secret.data, NULL); + } + + EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); + EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); + EXPECT_NULL(conn->psk_params.chosen_psk); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* PSKs are wiped when chosen PSK is NOT NULL */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + const uint8_t psk_data[] = "test identity data"; + const uint8_t secret_data[] = "test secret data"; + const uint8_t early_secret_data[SHA256_DIGEST_LENGTH] = "test early secret data"; + for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NOT_EQUAL(psk->identity.data, NULL); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); + EXPECT_NOT_EQUAL(psk->secret.size, 0); + EXPECT_NOT_EQUAL(psk->secret.data, NULL); + EXPECT_SUCCESS(s2n_realloc(&psk->early_secret, sizeof(early_secret_data))); + POSIX_CHECKED_MEMCPY(psk->early_secret.data, early_secret_data, sizeof(early_secret_data)); + EXPECT_NOT_EQUAL(psk->early_secret.size, 0); + EXPECT_NOT_EQUAL(psk->early_secret.data, NULL); + } + + /* Set chosen PSK */ + struct s2n_psk *chosen_psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &chosen_psk)); + EXPECT_NOT_NULL(chosen_psk); + conn->psk_params.chosen_psk = chosen_psk; + conn->psk_params.chosen_psk_wire_index = 0; + + EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); + EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + EXPECT_NULL(psk->early_secret.data); + EXPECT_EQUAL(psk->early_secret.size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: Handshake self-talks using s2n_handshake_write_io and s2n_handshake_read_io */ + { + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + + struct s2n_config *server_config = NULL, *client_config = NULL; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + uint8_t *cert_chain = NULL; + uint8_t *private_key = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_cert_chain_and_key *default_cert = NULL; + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + struct s2n_blob server_seq = { .data = server_conn->secure->server_sequence_number, .size = sizeof(server_conn->secure->server_sequence_number) }; + S2N_BLOB_FROM_HEX(seq_0, "0000000000000000"); + S2N_BLOB_FROM_HEX(seq_1, "0000000000000001"); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Client sends ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + s2n_tls13_connection_keys(server_secrets_0, server_conn); + EXPECT_EQUAL(server_secrets_0.size, 0); + + EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT); + + s2n_tls13_connection_keys(server_secrets, server_conn); + EXPECT_EQUAL(server_secrets.size, SHA256_DIGEST_LENGTH); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); + + /* Server sends ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + S2N_BLOB_EXPECT_EQUAL(server_seq, seq_0); + + /* Server sends EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + S2N_BLOB_EXPECT_EQUAL(server_seq, seq_1); + + /* Server sends ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + s2n_tls13_connection_keys(client_secrets, client_conn); + EXPECT_EQUAL(client_secrets.size, SHA256_DIGEST_LENGTH); + + /* Verify that derive and extract secrets match */ + S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); + S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); + + /* Client reads Encrypted extensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Server sends ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Client sends ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Server reads ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + /* Verify that derive and extract secrets match */ + S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); + S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(private_key); + free(cert_chain); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_parse_record_type_test.c b/tests/unit/s2n_tls13_parse_record_type_test.c index 42f06ae14b2..0f896e08a9c 100644 --- a/tests/unit/s2n_tls13_parse_record_type_test.c +++ b/tests/unit/s2n_tls13_parse_record_type_test.c @@ -1,257 +1,257 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_record.h" - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - uint8_t record_type = 0; - - /* In tls13 the true record type is inserted in the last byte of the encrypted payload. This - * test creates a fake unencrypted payload and checks that the helper function - * s2n_tls13_parse_record_type() correctly parses the type. - */ - { - uint16_t plaintext = 0xdaf3; - struct s2n_stuffer plaintext_stuffer = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0xf3); - EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); - } - - /* Test for failure when stuffer is completely empty */ - { - struct s2n_stuffer empty_stuffer = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_alloc(&empty_stuffer, 0)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&empty_stuffer, &record_type)); - }; - - /* Test for case where there is a record type in the stuffer but no content */ - { - uint16_t plaintext = 0xf3; - struct s2n_stuffer plaintext_stuffer = { 0 }; - - EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0xf3); - EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); - - /* Clean up */ - EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); - }; - - /* Test for record padding handling */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* no padding */ - S2N_BLOB_FROM_HEX(padding_0, "16"); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_0)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0x16); - - /* 1 byte padding */ - S2N_BLOB_FROM_HEX(padding_1, "1600"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_1)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0x16); - - /* 2 byte padding */ - S2N_BLOB_FROM_HEX(padding_2, "160000"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_2)); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, 0x16); - - /** test: padding without record type should fail - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.4 - *= type=test - *# If a receiving implementation does not - *# find a non-zero octet in the cleartext, it MUST terminate the - *# connection with an "unexpected_message" alert. - **/ - S2N_BLOB_FROM_HEX(no_type, "00"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); - - /* multiple padding without record type should fail */ - S2N_BLOB_FROM_HEX(no_type2, "0000"); - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type2)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); - - /* empty stuffer should fail */ - EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); - EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - }; - - /* Defining these here as variables as they aren't used in prior tests. */ - const uint8_t padding_value = 0x00; - const uint8_t not_padding_value = 0x16; - - /* Test maximum record length size (empty data) */ - { - EXPECT_EQUAL(S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH, 16385); - - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - - /* fill up stuffer to before the limit */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); - - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* There was no data before the record type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test maximum record length size (maximum data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* fill up stuffer to before the limit */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); - - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* The last byte is stripped as the content type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Certain versions of Java can generate inner plaintexts with lengths up to - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) - * However, after the padding is stripped, the result will always be no more than - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 - */ - { - const size_t extra_length_tolerated = 16; - /* Test slightly overlarge record for compatibility (empty data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - /* fill up stuffer the limit + 16 */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* There was no data before the record type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test slightly overlarge record for compatibility (maximum data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* fill up stuffer to before the limit */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - } - /* pad up stuffer the limit + 16 */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); - - EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); - EXPECT_EQUAL(record_type, not_padding_value); - /* The last byte is stripped as the content type */ - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test slightly overlarge record for compatibility (with too much data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - /* Finally, do this with an overall length which should pass, but too much data before the padding */ - /* fill up stuffer to the maximum amount of data */ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); /* Record type */ - /* 16 bytes of padding*/ - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - - /* Test slightly overlarge + 1 record for compatibility (empty data) */ - { - struct s2n_stuffer stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); - while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1) { - EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); - } - EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1); - - EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); - } - } - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t record_type = 0; + + /* In tls13 the true record type is inserted in the last byte of the encrypted payload. This + * test creates a fake unencrypted payload and checks that the helper function + * s2n_tls13_parse_record_type() correctly parses the type. + */ + { + uint16_t plaintext = 0xdaf3; + struct s2n_stuffer plaintext_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0xf3); + EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); + } + + /* Test for failure when stuffer is completely empty */ + { + struct s2n_stuffer empty_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&empty_stuffer, 0)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&empty_stuffer, &record_type)); + }; + + /* Test for case where there is a record type in the stuffer but no content */ + { + uint16_t plaintext = 0xf3; + struct s2n_stuffer plaintext_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0xf3); + EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); + }; + + /* Test for record padding handling */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* no padding */ + S2N_BLOB_FROM_HEX(padding_0, "16"); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_0)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /* 1 byte padding */ + S2N_BLOB_FROM_HEX(padding_1, "1600"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_1)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /* 2 byte padding */ + S2N_BLOB_FROM_HEX(padding_2, "160000"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_2)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /** test: padding without record type should fail + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.4 + *= type=test + *# If a receiving implementation does not + *# find a non-zero octet in the cleartext, it MUST terminate the + *# connection with an "unexpected_message" alert. + **/ + S2N_BLOB_FROM_HEX(no_type, "00"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + /* multiple padding without record type should fail */ + S2N_BLOB_FROM_HEX(no_type2, "0000"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type2)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + /* empty stuffer should fail */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Defining these here as variables as they aren't used in prior tests. */ + const uint8_t padding_value = 0x00; + const uint8_t not_padding_value = 0x16; + + /* Test maximum record length size (empty data) */ + { + EXPECT_EQUAL(S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH, 16385); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* There was no data before the record type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test maximum record length size (maximum data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* The last byte is stripped as the content type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Certain versions of Java can generate inner plaintexts with lengths up to + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) + * However, after the padding is stripped, the result will always be no more than + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 + */ + { + const size_t extra_length_tolerated = 16; + /* Test slightly overlarge record for compatibility (empty data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + /* fill up stuffer the limit + 16 */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* There was no data before the record type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge record for compatibility (maximum data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + } + /* pad up stuffer the limit + 16 */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* The last byte is stripped as the content type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge record for compatibility (with too much data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* Finally, do this with an overall length which should pass, but too much data before the padding */ + /* fill up stuffer to the maximum amount of data */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); /* Record type */ + /* 16 bytes of padding*/ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge + 1 record for compatibility (empty data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + } + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_pq_handshake_test.c b/tests/unit/s2n_tls13_pq_handshake_test.c index 354970d1b3b..17819ea6df9 100644 --- a/tests/unit/s2n_tls13_pq_handshake_test.c +++ b/tests/unit/s2n_tls13_pq_handshake_test.c @@ -1,715 +1,715 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_pq.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/policy/s2n_policy_feature.h" -#include "tls/s2n_ecc_preferences.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_kem_preferences.h" -#include "tls/s2n_security_policies.h" - -/* Include C file directly to access static functions */ -#include "tls/s2n_handshake_io.c" - -const struct s2n_kem_group *s2n_get_predicted_negotiated_kem_group(const struct s2n_security_policy *client_policy, const struct s2n_security_policy *server_policy) -{ - PTR_ENSURE_REF(client_policy); - PTR_ENSURE_REF(server_policy); - - const struct s2n_kem_preferences *client_prefs = client_policy->kem_preferences; - const struct s2n_kem_preferences *server_prefs = server_policy->kem_preferences; - - PTR_ENSURE_REF(client_prefs); - PTR_ENSURE_REF(server_prefs); - - /* Client will offer their highest priority PQ KeyShare in their ClientHello. This PQ KeyShare - * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually - * supported PQ KeyShares that the server would prefer over this one but would require 2-RTT's). - */ - const struct s2n_kem_group *client_default = client_prefs->tls13_kem_groups[0]; - PTR_ENSURE_REF(client_default); - - for (int i = 0; server_policy->strongly_preferred_groups != NULL && i < server_policy->strongly_preferred_groups->count; i++) { - for (int j = 0; j < client_policy->kem_preferences->tls13_kem_group_count; j++) { - if (server_policy->strongly_preferred_groups->iana_ids[i] == client_policy->kem_preferences->tls13_kem_groups[j]->iana_id - && s2n_kem_group_is_available(client_policy->kem_preferences->tls13_kem_groups[j])) { - return client_policy->kem_preferences->tls13_kem_groups[j]; - } - } - } - - for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { - const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; - PTR_ENSURE_REF(server_group); - if (s2n_kem_group_is_available(client_default) && s2n_kem_group_is_available(server_group) - && client_default->iana_id == server_group->iana_id - && s2n_kem_group_is_available(client_default)) { - return client_default; - } - } - - /* Otherwise, if the client's default isn't supported, and a 2-RTT PQ handshake is required, the server will choose - * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ - for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { - const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; - - /* j starts at 1 since we already checked client_prefs->tls13_kem_groups[0] above */ - for (int j = 1; j < client_prefs->tls13_kem_group_count; j++) { - const struct s2n_kem_group *client_group = client_prefs->tls13_kem_groups[j]; - PTR_ENSURE_REF(client_group); - PTR_ENSURE_REF(server_group); - if (s2n_kem_group_is_available(client_group) && s2n_kem_group_is_available(server_group) - && client_group->iana_id == server_group->iana_id - && s2n_kem_group_is_available(client_group)) { - return client_group; - } - } - } - - return NULL; -} - -const struct s2n_ecc_named_curve *s2n_get_predicted_negotiated_ecdhe_curve(const struct s2n_security_policy *client_sec_policy, - const struct s2n_security_policy *server_sec_policy) -{ - PTR_ENSURE_REF(client_sec_policy); - PTR_ENSURE_REF(server_sec_policy); - - /* Client will offer their highest priority ECDHE KeyShare in their ClientHello. This KeyShare - * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually - * supported ECDHE KeyShares that the server would prefer over this one but would require 2-RTT's). - */ - const struct s2n_ecc_named_curve *client_default = client_sec_policy->ecc_preferences->ecc_curves[0]; - PTR_ENSURE_REF(client_default); - - for (int i = 0; server_sec_policy->strongly_preferred_groups != NULL && i < server_sec_policy->strongly_preferred_groups->count; i++) { - for (int j = 0; j < client_sec_policy->ecc_preferences->count; j++) { - if (server_sec_policy->strongly_preferred_groups->iana_ids[i] == client_sec_policy->ecc_preferences->ecc_curves[j]->iana_id) { - return client_sec_policy->ecc_preferences->ecc_curves[j]; - } - } - } - - for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { - const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; - PTR_ENSURE_REF(server_curve); - if (server_curve->iana_id == client_default->iana_id) { - return client_default; - } - } - - /* Otherwise, if the client's default isn't supported, and a 2-RTT handshake is required, the server will choose - * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ - for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { - const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; - - /* j starts at 1 since we already checked client_sec_policy->ecc_preferences->ecc_curves[0] above */ - for (int j = 1; j < client_sec_policy->ecc_preferences->count; j++) { - const struct s2n_ecc_named_curve *client_curve = client_sec_policy->ecc_preferences->ecc_curves[j]; - PTR_ENSURE_REF(client_curve); - PTR_ENSURE_REF(server_curve); - if (client_curve->iana_id == server_curve->iana_id) { - return client_curve; - } - } - } - - return NULL; -} - -int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_policy, - const struct s2n_security_policy *server_sec_policy, const struct s2n_kem_group *expected_kem_group, - const struct s2n_ecc_named_curve *expected_curve, bool hrr_expected, bool len_prefix_expected) -{ - /* XOR check: can expect to negotiate either a KEM group, or a classic EC curve, but not both/neither */ - POSIX_ENSURE((expected_kem_group == NULL) != (expected_curve == NULL), S2N_ERR_SAFETY); - - /* Set up connections */ - struct s2n_connection *client_conn = NULL, *server_conn = NULL; - POSIX_ENSURE_REF(client_conn = s2n_connection_new(S2N_CLIENT)); - POSIX_ENSURE_REF(server_conn = s2n_connection_new(S2N_SERVER)); - - struct s2n_config *client_config = NULL, *server_config = NULL; - POSIX_ENSURE_REF(client_config = s2n_config_new()); - POSIX_ENSURE_REF(server_config = s2n_config_new()); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }, private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - struct s2n_cert_chain_and_key *chain_and_key = NULL; - POSIX_ENSURE_REF(chain_and_key = s2n_cert_chain_and_key_new()); - POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - - POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); - POSIX_GUARD(s2n_connection_set_config(server_conn, server_config)); - - struct s2n_stuffer client_to_server = { 0 }, server_to_client = { 0 }; - POSIX_GUARD(s2n_stuffer_growable_alloc(&client_to_server, 2048)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&server_to_client, 2048)); - - POSIX_GUARD(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - POSIX_GUARD(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - client_conn->security_policy_override = client_sec_policy; - server_conn->security_policy_override = server_sec_policy; - - /* Client sends ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_write_io(client_conn)); - - POSIX_ENSURE_EQ(client_conn->actual_protocol_version, S2N_TLS13); - POSIX_ENSURE_EQ(server_conn->actual_protocol_version, 0); /* Won't get set until after server reads ClientHello */ - POSIX_ENSURE_EQ(client_conn->handshake.handshake_type, INITIAL); - - /* Server reads ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_read_io(server_conn)); - - POSIX_ENSURE_EQ(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ - - /* Assert that the server chose the correct group */ - if (expected_kem_group) { - /* Client should always determine whether the KEM group used len_prefixed format, and server should match client's behavior. */ - POSIX_ENSURE_EQ(len_prefix_expected, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); - POSIX_ENSURE_EQ(len_prefix_expected, s2n_tls13_client_must_use_hybrid_kem_length_prefix(client_sec_policy->kem_preferences)); - POSIX_ENSURE_EQ(server_conn->kex_params.client_kem_group_params.kem_params.len_prefixed, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); - - POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); - if (expected_kem_group->curve != &s2n_ecc_curve_none) { - POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - } - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - } else { - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - if (expected_curve != server_conn->kex_params.server_ecc_evp_params.negotiated_curve) { - const char *expected_name = "NULL"; - const char *actual_name = "NULL"; - if (expected_curve != NULL && expected_curve->name != NULL) { - expected_name = expected_curve->name; - } - if (server_conn->kex_params.server_ecc_evp_params.negotiated_curve != NULL && server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name != NULL) { - actual_name = server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name; - } - - fprintf(stderr, "\n\nError: Unexpected curve was negotiated. Expected: %s, Actual: %s\n", expected_name, actual_name); - fflush(stderr); - } - - POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - } - - /* Server sends ServerHello or HRR */ - POSIX_GUARD(s2n_conn_set_handshake_type(server_conn)); - POSIX_ENSURE_EQ(hrr_expected, s2n_handshake_type_check_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); - POSIX_GUARD(s2n_handshake_write_io(server_conn)); - - /* Server sends CCS */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); - POSIX_GUARD(s2n_handshake_write_io(server_conn)); - - if (hrr_expected) { - /* Client reads HRR */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - POSIX_GUARD(s2n_handshake_read_io(client_conn)); - POSIX_GUARD(s2n_conn_set_handshake_type(client_conn)); - POSIX_ENSURE_NE(0, client_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); - - /* Client reads CCS */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - POSIX_GUARD(s2n_handshake_read_io(client_conn)); - - /* Client sends CCS and new ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); - POSIX_GUARD(s2n_handshake_write_io(client_conn)); - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_write_io(client_conn)); - - /* Server reads CCS (doesn't change state machine) */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_read_io(server_conn)); - - /* Server reads new ClientHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); - POSIX_GUARD(s2n_handshake_read_io(server_conn)); - - /* Server sends ServerHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - POSIX_GUARD(s2n_handshake_write_io(server_conn)); - } - - /* Client reads ServerHello */ - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); - POSIX_GUARD(s2n_handshake_read_io(client_conn)); - - /* We've gotten far enough in the handshake that both client and server should have - * derived the shared secrets, so we don't send/receive any more messages. */ - - /* Assert that the correct group was negotiated (we re-check the server group to assert that - * nothing unexpected changed between then and now while e.g. processing HRR) */ - if (expected_kem_group) { - POSIX_ENSURE_EQ(expected_kem_group, client_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(expected_kem_group->kem, client_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - if (expected_kem_group->curve != &s2n_ecc_curve_none) { - POSIX_ENSURE_EQ(expected_kem_group->curve, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - } - - /* Ensure s2n_connection_get_kem_group_name() gives the correct answer for both client and server */ - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(server_conn))); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(server_conn), strlen(expected_kem_group->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(client_conn))); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(client_conn), strlen(expected_kem_group->name)), 0); - - /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ - const char *server_group_name = NULL; - const char *client_group_name = NULL; - POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); - POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(server_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, server_group_name, strlen(expected_kem_group->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(client_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, client_group_name, strlen(expected_kem_group->name)), 0); - } else { - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - POSIX_ENSURE_EQ(expected_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); - POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); - POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); - - /* Ensure s2n_connection_get_curve() gives the correct answer for both client and server */ - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(server_conn))); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(server_conn), strlen(expected_curve->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(client_conn))); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(client_conn), strlen(expected_curve->name)), 0); - - /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ - const char *server_group_name = NULL; - const char *client_group_name = NULL; - POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); - POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(server_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, server_group_name, strlen(expected_curve->name)), 0); - POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(client_group_name)); - POSIX_ENSURE_EQ(memcmp(expected_curve->name, client_group_name, strlen(expected_curve->name)), 0); - } - - /* Verify basic properties of secrets */ - s2n_tls13_connection_keys(server_secret_info, server_conn); - s2n_tls13_connection_keys(client_secret_info, client_conn); - POSIX_ENSURE_EQ(server_conn->secure->cipher_suite, client_conn->secure->cipher_suite); - if (server_conn->secure->cipher_suite == &s2n_tls13_aes_256_gcm_sha384) { - POSIX_ENSURE_EQ(server_secret_info.size, 48); - POSIX_ENSURE_EQ(client_secret_info.size, 48); - } else { - POSIX_ENSURE_EQ(server_secret_info.size, 32); - POSIX_ENSURE_EQ(client_secret_info.size, 32); - } - - /* Verify secrets aren't just zero'ed memory */ - uint8_t all_zeros[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - POSIX_CHECKED_MEMSET((void *) all_zeros, 0, S2N_TLS13_SECRET_MAX_LEN); - struct s2n_tls13_secrets *client_secrets = &client_conn->secrets.version.tls13; - struct s2n_tls13_secrets *server_secrets = &server_conn->secrets.version.tls13; - POSIX_ENSURE_EQ(server_secret_info.size, client_secret_info.size); - uint8_t size = server_secret_info.size; - POSIX_ENSURE_EQ(client_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); - POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->extract_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->client_handshake_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->server_handshake_secret, size)); - POSIX_ENSURE_EQ(server_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); - POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->extract_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->client_handshake_secret, size)); - POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->server_handshake_secret, size)); - - /* Verify client and server secrets are equal to each other */ - POSIX_ENSURE_EQ(0, memcmp(server_secrets->extract_secret, client_secrets->extract_secret, size)); - POSIX_ENSURE_EQ(0, memcmp(server_secrets->client_handshake_secret, client_secrets->client_handshake_secret, size)); - POSIX_ENSURE_EQ(0, memcmp(server_secrets->server_handshake_secret, client_secrets->server_handshake_secret, size)); - - /* Clean up */ - POSIX_GUARD(s2n_stuffer_free(&client_to_server)); - POSIX_GUARD(s2n_stuffer_free(&server_to_client)); - - POSIX_GUARD(s2n_connection_free(client_conn)); - POSIX_GUARD(s2n_connection_free(server_conn)); - - POSIX_GUARD(s2n_cert_chain_and_key_free(chain_and_key)); - POSIX_GUARD(s2n_config_free(server_config)); - POSIX_GUARD(s2n_config_free(client_config)); - - return S2N_SUCCESS; -} - -int main() -{ - BEGIN_TEST(); - - if (!s2n_is_tls13_fully_supported()) { - END_TEST(); - } - - /* Additional KEM preferences/security policies to test against. These policies can only be used - * as the server's policy in this test: when generating the ClientHello, the client relies on - * the security_policy_selection[] array (in s2n_security_policies.c) to determine if it should - * write the supported_groups extension. Because these unofficial policies don't exist in that - * array, the supported_groups extension won't get sent and the handshake won't complete as expected. */ - - const struct s2n_kem_group *mlkem768_test_groups[] = { - &s2n_x25519_mlkem_768, - &s2n_secp256r1_mlkem_768, - }; - - const struct s2n_kem_preferences mlkem768_test_prefs = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = s2n_array_len(mlkem768_test_groups), - .tls13_kem_groups = mlkem768_test_groups, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy mlkem768_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &mlkem768_test_prefs, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct s2n_kem_group *mlkem1024_test_groups[] = { - &s2n_secp384r1_mlkem_1024, - }; - - const struct s2n_kem_preferences mlkem1024_test_prefs = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = s2n_array_len(mlkem1024_test_groups), - .tls13_kem_groups = mlkem1024_test_groups, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy mlkem1024_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &mlkem1024_test_prefs, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct s2n_kem_group *pure_mlkem1024_test_groups[] = { - &s2n_pure_mlkem_1024, - }; - - const struct s2n_kem_preferences pure_mlkem1024_test_prefs = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = s2n_array_len(pure_mlkem1024_test_groups), - .tls13_kem_groups = pure_mlkem1024_test_groups, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy pure_mlkem1024_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &pure_mlkem1024_test_prefs, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct s2n_ecc_named_curve *default_curve = &s2n_ecc_curve_x25519; - - if (!s2n_is_evp_apis_supported()) { - default_curve = &s2n_ecc_curve_secp256r1; - } - - struct pq_handshake_test_vector { - const struct s2n_security_policy *client_policy; - const struct s2n_security_policy *server_policy; - const struct s2n_kem_group *expected_kem_group; - const struct s2n_ecc_named_curve *expected_curve; - bool hrr_expected; - bool len_prefix_expected; - }; - - /* Self talk test with each TLS 1.3 KemGroup we support */ - for (size_t i = 0; i < S2N_KEM_GROUPS_COUNT; i++) { - const struct s2n_kem_group *kem_group = ALL_SUPPORTED_KEM_GROUPS[i]; - - if (kem_group == NULL || !s2n_kem_group_is_available(kem_group)) { - continue; - } - - const struct s2n_kem_preferences singleton_test_pref = { - .kem_count = 0, - .kems = NULL, - .tls13_kem_group_count = 1, - .tls13_kem_groups = &kem_group, - .tls13_pq_hybrid_draft_revision = 5 - }; - - const struct s2n_security_policy singleton_test_policy = { - .minimum_protocol_version = S2N_TLS13, - .cipher_preferences = &cipher_preferences_20190801, - .kem_preferences = &singleton_test_pref, - .signature_preferences = &s2n_signature_preferences_20200207, - .ecc_preferences = &s2n_ecc_preferences_20240603, - }; - - const struct pq_handshake_test_vector test_vec = { - .client_policy = &singleton_test_policy, - .server_policy = &singleton_test_policy, - .expected_kem_group = kem_group, - .expected_curve = NULL, - .hrr_expected = false, - .len_prefix_expected = false, - }; - - EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(test_vec.client_policy, test_vec.server_policy, - test_vec.expected_kem_group, test_vec.expected_curve, test_vec.hrr_expected, test_vec.len_prefix_expected)); - } - - /* ML-KEM is only available on newer versions of AWS-LC. If it's - * unavailable, we must downgrade the assertions to EC. */ - const struct s2n_kem_group *null_if_no_mlkem_768 = &s2n_x25519_mlkem_768; - const struct s2n_kem_group *null_if_no_mlkem_1024 = &s2n_secp384r1_mlkem_1024; - const struct s2n_kem_group *null_if_no_pure_mlkem_1024 = &s2n_pure_mlkem_1024; - const struct s2n_ecc_named_curve *ec_if_no_mlkem = NULL; - bool hrr_expected_if_mlkem = true; - if (!s2n_libcrypto_supports_mlkem()) { - null_if_no_mlkem_768 = NULL; - null_if_no_mlkem_1024 = NULL; - null_if_no_pure_mlkem_1024 = NULL; - ec_if_no_mlkem = default_curve; - hrr_expected_if_mlkem = false; - } - - /* Test vectors that expect to negotiate PQ assume that PQ is enabled in s2n. - * If PQ is disabled, the expected negotiation outcome is overridden below - * before performing the handshake test. */ - const struct pq_handshake_test_vector test_vectors[] = { - /* Server does not support PQ; client sends a PQ key share and an EC key share; - * server should negotiate EC without HRR. */ - { - .client_policy = &security_policy_pq_tls_1_2_2024_10_09, - .server_policy = &security_policy_test_all_tls13, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = false, - .len_prefix_expected = true, - }, - - /* Server does not support PQ; client sends a PQ key share, but no EC shares; - * server should negotiate EC and send HRR. */ - { - .client_policy = &security_policy_test_tls13_retry_with_pq, - .server_policy = &security_policy_test_all_tls13, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = true, - .len_prefix_expected = true, - }, - - /* Server supports PQ, but client does not. Client sent an EC share, - * EC should be negotiated without HRR */ - { - .client_policy = &security_policy_test_all_tls13, - .server_policy = &security_policy_pq_tls_1_2_2024_10_09, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = false, - .len_prefix_expected = true, - }, - - /* Server supports PQ, but client does not. Client did not send any EC shares, - * EC should be negotiated after exchanging HRR */ - { - .client_policy = &security_policy_test_tls13_retry, - .server_policy = &security_policy_pq_tls_1_2_2024_10_09, - .expected_kem_group = NULL, - .expected_curve = default_curve, - .hrr_expected = true, - .len_prefix_expected = true, - }, - - /* Confirm that MLKEM768 is negotiable */ - { - .client_policy = &mlkem768_test_policy, - .server_policy = &mlkem768_test_policy, - .expected_kem_group = null_if_no_mlkem_768, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = false, - .len_prefix_expected = false, - }, - - /* Confirm that MLKEM1024 is negotiable */ - { - .client_policy = &mlkem1024_test_policy, - .server_policy = &mlkem1024_test_policy, - .expected_kem_group = null_if_no_mlkem_1024, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = false, - .len_prefix_expected = false, - }, - - /* Confirm that pure MLKEM1024 is negotiable; fall back to EC when MLKEM is not supported. */ - { - .client_policy = &pure_mlkem1024_test_policy, - .server_policy = &pure_mlkem1024_test_policy, - .expected_kem_group = null_if_no_pure_mlkem_1024, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = false, - .len_prefix_expected = false, - }, - - /* Client supports pure MLKEM but did not send that key share. Pure MLKEM should be negotiated after exchanging HRR. - * If ML-KEM is not supported, EC should be negotiated without HRR. */ - { - .client_policy = &security_policy_test_all, - .server_policy = &pure_mlkem1024_test_policy, - .expected_kem_group = null_if_no_pure_mlkem_1024, - .expected_curve = ec_if_no_mlkem, - .hrr_expected = hrr_expected_if_mlkem, - .len_prefix_expected = false, - }, - - /* Client supports p384 but did not send that key share when connecting to server that strongly prefers p384. */ - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251113, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251114, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251115, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - { - .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ - .server_policy = &security_policy_20251116, /* Strongly prefers p384 */ - .expected_kem_group = NULL, - .expected_curve = &s2n_ecc_curve_secp384r1, - .hrr_expected = true, - .len_prefix_expected = false, - }, - }; - - for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { - const struct pq_handshake_test_vector *vector = &test_vectors[i]; - const struct s2n_security_policy *client_policy = vector->client_policy; - const struct s2n_security_policy *server_policy = vector->server_policy; - const struct s2n_kem_group *kem_group = vector->expected_kem_group; - const struct s2n_ecc_named_curve *curve = vector->expected_curve; - bool hrr_expected = vector->hrr_expected; - bool len_prefix_expected = vector->len_prefix_expected; - - /* Print Test Vector Info for easier debugging on failure. */ - { - const char *kem_group_str = (kem_group == NULL) ? "NULL" : kem_group->name; - const char *curve_str = (curve == NULL) ? "NULL" : curve->name; - fprintf(stderr, "\n\nRunning test (%zu/%zu)...\n", i + 1, s2n_array_len(test_vectors)); - fflush(stderr); - fprintf(stderr, "Test Vector: kem_group: %s, curve: %s, hrr_expected: %d, len_prefix_expected: %d\n", kem_group_str, curve_str, hrr_expected, len_prefix_expected); - fflush(stderr); - uint32_t output_size = 0; - fprintf(stderr, "\nClient Security Policy: \n"); - EXPECT_SUCCESS(s2n_security_policy_write_fd(client_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); - - fprintf(stderr, "\nServer Security Policy: \n"); - EXPECT_SUCCESS(s2n_security_policy_write_fd(server_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); - fflush(stderr); - } - - /* Check if we need to override the test vector. This may need to be done due to some LibCryptos not supporting PQ. */ - if (!s2n_kem_group_is_available(kem_group)) { - kem_group = NULL; - if (curve == NULL) { - curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); - } - } - - if (!s2n_pq_is_enabled()) { - EXPECT_TRUE(client_policy->ecc_preferences->count > 0); - const struct s2n_ecc_named_curve *client_default = client_policy->ecc_preferences->ecc_curves[0]; - const struct s2n_ecc_named_curve *predicted_curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); - - /* If the default curve is x25519, and the test vector is predicting the default, but neither policy supports x25519, - * fall back to predict p256 as it should be in common with every ECC preference list. */ - if (default_curve->iana_id == s2n_ecc_curve_x25519.iana_id - && curve->iana_id == default_curve->iana_id - && (!s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id) - || !s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id))) { - EXPECT_TRUE(s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); - EXPECT_TRUE(s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); - curve = &s2n_ecc_curve_secp256r1; - } - - /* The client's preferred curve will be a higher priority than the default if both sides - * support TLS 1.3, and if the client's default can be chosen by the server in 1-RTT. */ - if (s2n_security_policy_supports_tls13(client_policy) && s2n_security_policy_supports_tls13(server_policy) - && (server_policy->strongly_preferred_groups == NULL || server_policy->strongly_preferred_groups->count == 0) - && s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, client_default->iana_id)) { - curve = client_default; - } - - /* Finally, confirm that the expected curve listed in the test vector matches the output of s2n_get_predicted_negotiated_ecdhe_curve() */ - EXPECT_EQUAL(curve->iana_id, predicted_curve->iana_id); - } - - if (kem_group != NULL) { - const struct s2n_kem_group *predicted_kem_group = s2n_get_predicted_negotiated_kem_group(client_policy, server_policy); - POSIX_ENSURE_REF(predicted_kem_group); - - /* Confirm that the expected KEM Group listed in the test vector matches the output of - * s2n_get_predicted_negotiated_kem_group() */ - POSIX_ENSURE_EQ(kem_group->iana_id, predicted_kem_group->iana_id); - } - - EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, kem_group, curve, hrr_expected, len_prefix_expected)); - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/policy/s2n_policy_feature.h" +#include "tls/s2n_ecc_preferences.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem_preferences.h" +#include "tls/s2n_security_policies.h" + +/* Include C file directly to access static functions */ +#include "tls/s2n_handshake_io.c" + +const struct s2n_kem_group *s2n_get_predicted_negotiated_kem_group(const struct s2n_security_policy *client_policy, const struct s2n_security_policy *server_policy) +{ + PTR_ENSURE_REF(client_policy); + PTR_ENSURE_REF(server_policy); + + const struct s2n_kem_preferences *client_prefs = client_policy->kem_preferences; + const struct s2n_kem_preferences *server_prefs = server_policy->kem_preferences; + + PTR_ENSURE_REF(client_prefs); + PTR_ENSURE_REF(server_prefs); + + /* Client will offer their highest priority PQ KeyShare in their ClientHello. This PQ KeyShare + * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually + * supported PQ KeyShares that the server would prefer over this one but would require 2-RTT's). + */ + const struct s2n_kem_group *client_default = client_prefs->tls13_kem_groups[0]; + PTR_ENSURE_REF(client_default); + + for (int i = 0; server_policy->strongly_preferred_groups != NULL && i < server_policy->strongly_preferred_groups->count; i++) { + for (int j = 0; j < client_policy->kem_preferences->tls13_kem_group_count; j++) { + if (server_policy->strongly_preferred_groups->iana_ids[i] == client_policy->kem_preferences->tls13_kem_groups[j]->iana_id + && s2n_kem_group_is_available(client_policy->kem_preferences->tls13_kem_groups[j])) { + return client_policy->kem_preferences->tls13_kem_groups[j]; + } + } + } + + for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { + const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; + PTR_ENSURE_REF(server_group); + if (s2n_kem_group_is_available(client_default) && s2n_kem_group_is_available(server_group) + && client_default->iana_id == server_group->iana_id + && s2n_kem_group_is_available(client_default)) { + return client_default; + } + } + + /* Otherwise, if the client's default isn't supported, and a 2-RTT PQ handshake is required, the server will choose + * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ + for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { + const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; + + /* j starts at 1 since we already checked client_prefs->tls13_kem_groups[0] above */ + for (int j = 1; j < client_prefs->tls13_kem_group_count; j++) { + const struct s2n_kem_group *client_group = client_prefs->tls13_kem_groups[j]; + PTR_ENSURE_REF(client_group); + PTR_ENSURE_REF(server_group); + if (s2n_kem_group_is_available(client_group) && s2n_kem_group_is_available(server_group) + && client_group->iana_id == server_group->iana_id + && s2n_kem_group_is_available(client_group)) { + return client_group; + } + } + } + + return NULL; +} + +const struct s2n_ecc_named_curve *s2n_get_predicted_negotiated_ecdhe_curve(const struct s2n_security_policy *client_sec_policy, + const struct s2n_security_policy *server_sec_policy) +{ + PTR_ENSURE_REF(client_sec_policy); + PTR_ENSURE_REF(server_sec_policy); + + /* Client will offer their highest priority ECDHE KeyShare in their ClientHello. This KeyShare + * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually + * supported ECDHE KeyShares that the server would prefer over this one but would require 2-RTT's). + */ + const struct s2n_ecc_named_curve *client_default = client_sec_policy->ecc_preferences->ecc_curves[0]; + PTR_ENSURE_REF(client_default); + + for (int i = 0; server_sec_policy->strongly_preferred_groups != NULL && i < server_sec_policy->strongly_preferred_groups->count; i++) { + for (int j = 0; j < client_sec_policy->ecc_preferences->count; j++) { + if (server_sec_policy->strongly_preferred_groups->iana_ids[i] == client_sec_policy->ecc_preferences->ecc_curves[j]->iana_id) { + return client_sec_policy->ecc_preferences->ecc_curves[j]; + } + } + } + + for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; + PTR_ENSURE_REF(server_curve); + if (server_curve->iana_id == client_default->iana_id) { + return client_default; + } + } + + /* Otherwise, if the client's default isn't supported, and a 2-RTT handshake is required, the server will choose + * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ + for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; + + /* j starts at 1 since we already checked client_sec_policy->ecc_preferences->ecc_curves[0] above */ + for (int j = 1; j < client_sec_policy->ecc_preferences->count; j++) { + const struct s2n_ecc_named_curve *client_curve = client_sec_policy->ecc_preferences->ecc_curves[j]; + PTR_ENSURE_REF(client_curve); + PTR_ENSURE_REF(server_curve); + if (client_curve->iana_id == server_curve->iana_id) { + return client_curve; + } + } + } + + return NULL; +} + +int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_policy, + const struct s2n_security_policy *server_sec_policy, const struct s2n_kem_group *expected_kem_group, + const struct s2n_ecc_named_curve *expected_curve, bool hrr_expected, bool len_prefix_expected) +{ + /* XOR check: can expect to negotiate either a KEM group, or a classic EC curve, but not both/neither */ + POSIX_ENSURE((expected_kem_group == NULL) != (expected_curve == NULL), S2N_ERR_SAFETY); + + /* Set up connections */ + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + POSIX_ENSURE_REF(client_conn = s2n_connection_new(S2N_CLIENT)); + POSIX_ENSURE_REF(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_config *client_config = NULL, *server_config = NULL; + POSIX_ENSURE_REF(client_config = s2n_config_new()); + POSIX_ENSURE_REF(server_config = s2n_config_new()); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }, private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + POSIX_ENSURE_REF(chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_connection_set_config(server_conn, server_config)); + + struct s2n_stuffer client_to_server = { 0 }, server_to_client = { 0 }; + POSIX_GUARD(s2n_stuffer_growable_alloc(&client_to_server, 2048)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&server_to_client, 2048)); + + POSIX_GUARD(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + POSIX_GUARD(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + client_conn->security_policy_override = client_sec_policy; + server_conn->security_policy_override = server_sec_policy; + + /* Client sends ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + + POSIX_ENSURE_EQ(client_conn->actual_protocol_version, S2N_TLS13); + POSIX_ENSURE_EQ(server_conn->actual_protocol_version, 0); /* Won't get set until after server reads ClientHello */ + POSIX_ENSURE_EQ(client_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + POSIX_ENSURE_EQ(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + + /* Assert that the server chose the correct group */ + if (expected_kem_group) { + /* Client should always determine whether the KEM group used len_prefixed format, and server should match client's behavior. */ + POSIX_ENSURE_EQ(len_prefix_expected, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); + POSIX_ENSURE_EQ(len_prefix_expected, s2n_tls13_client_must_use_hybrid_kem_length_prefix(client_sec_policy->kem_preferences)); + POSIX_ENSURE_EQ(server_conn->kex_params.client_kem_group_params.kem_params.len_prefixed, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); + + POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); + if (expected_kem_group->curve != &s2n_ecc_curve_none) { + POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + } + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } else { + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + if (expected_curve != server_conn->kex_params.server_ecc_evp_params.negotiated_curve) { + const char *expected_name = "NULL"; + const char *actual_name = "NULL"; + if (expected_curve != NULL && expected_curve->name != NULL) { + expected_name = expected_curve->name; + } + if (server_conn->kex_params.server_ecc_evp_params.negotiated_curve != NULL && server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name != NULL) { + actual_name = server_conn->kex_params.server_ecc_evp_params.negotiated_curve->name; + } + + fprintf(stderr, "\n\nError: Unexpected curve was negotiated. Expected: %s, Actual: %s\n", expected_name, actual_name); + fflush(stderr); + } + + POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } + + /* Server sends ServerHello or HRR */ + POSIX_GUARD(s2n_conn_set_handshake_type(server_conn)); + POSIX_ENSURE_EQ(hrr_expected, s2n_handshake_type_check_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + + if (hrr_expected) { + /* Client reads HRR */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + POSIX_GUARD(s2n_conn_set_handshake_type(client_conn)); + POSIX_ENSURE_NE(0, client_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + /* Client reads CCS */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS and new ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS (doesn't change state machine) */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + /* Server reads new ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + /* Server sends ServerHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + } + + /* Client reads ServerHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + + /* We've gotten far enough in the handshake that both client and server should have + * derived the shared secrets, so we don't send/receive any more messages. */ + + /* Assert that the correct group was negotiated (we re-check the server group to assert that + * nothing unexpected changed between then and now while e.g. processing HRR) */ + if (expected_kem_group) { + POSIX_ENSURE_EQ(expected_kem_group, client_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, client_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + if (expected_kem_group->curve != &s2n_ecc_curve_none) { + POSIX_ENSURE_EQ(expected_kem_group->curve, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + } + + /* Ensure s2n_connection_get_kem_group_name() gives the correct answer for both client and server */ + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(server_conn))); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(server_conn), strlen(expected_kem_group->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(s2n_connection_get_kem_group_name(client_conn))); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, s2n_connection_get_kem_group_name(client_conn), strlen(expected_kem_group->name)), 0); + + /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ + const char *server_group_name = NULL; + const char *client_group_name = NULL; + POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); + POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(server_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, server_group_name, strlen(expected_kem_group->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_kem_group->name), strlen(client_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_kem_group->name, client_group_name, strlen(expected_kem_group->name)), 0); + } else { + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + /* Ensure s2n_connection_get_curve() gives the correct answer for both client and server */ + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(server_conn))); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(server_conn), strlen(expected_curve->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(s2n_connection_get_curve(client_conn))); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, s2n_connection_get_curve(client_conn), strlen(expected_curve->name)), 0); + + /* Ensure s2n_connection_get_key_exchange_group() gives the correct answer for both client and server */ + const char *server_group_name = NULL; + const char *client_group_name = NULL; + POSIX_GUARD(s2n_connection_get_key_exchange_group(server_conn, &server_group_name)); + POSIX_GUARD(s2n_connection_get_key_exchange_group(client_conn, &client_group_name)); + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(server_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, server_group_name, strlen(expected_curve->name)), 0); + POSIX_ENSURE_EQ(strlen(expected_curve->name), strlen(client_group_name)); + POSIX_ENSURE_EQ(memcmp(expected_curve->name, client_group_name, strlen(expected_curve->name)), 0); + } + + /* Verify basic properties of secrets */ + s2n_tls13_connection_keys(server_secret_info, server_conn); + s2n_tls13_connection_keys(client_secret_info, client_conn); + POSIX_ENSURE_EQ(server_conn->secure->cipher_suite, client_conn->secure->cipher_suite); + if (server_conn->secure->cipher_suite == &s2n_tls13_aes_256_gcm_sha384) { + POSIX_ENSURE_EQ(server_secret_info.size, 48); + POSIX_ENSURE_EQ(client_secret_info.size, 48); + } else { + POSIX_ENSURE_EQ(server_secret_info.size, 32); + POSIX_ENSURE_EQ(client_secret_info.size, 32); + } + + /* Verify secrets aren't just zero'ed memory */ + uint8_t all_zeros[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + POSIX_CHECKED_MEMSET((void *) all_zeros, 0, S2N_TLS13_SECRET_MAX_LEN); + struct s2n_tls13_secrets *client_secrets = &client_conn->secrets.version.tls13; + struct s2n_tls13_secrets *server_secrets = &server_conn->secrets.version.tls13; + POSIX_ENSURE_EQ(server_secret_info.size, client_secret_info.size); + uint8_t size = server_secret_info.size; + POSIX_ENSURE_EQ(client_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->extract_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->client_handshake_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->server_handshake_secret, size)); + POSIX_ENSURE_EQ(server_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->extract_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->client_handshake_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->server_handshake_secret, size)); + + /* Verify client and server secrets are equal to each other */ + POSIX_ENSURE_EQ(0, memcmp(server_secrets->extract_secret, client_secrets->extract_secret, size)); + POSIX_ENSURE_EQ(0, memcmp(server_secrets->client_handshake_secret, client_secrets->client_handshake_secret, size)); + POSIX_ENSURE_EQ(0, memcmp(server_secrets->server_handshake_secret, client_secrets->server_handshake_secret, size)); + + /* Clean up */ + POSIX_GUARD(s2n_stuffer_free(&client_to_server)); + POSIX_GUARD(s2n_stuffer_free(&server_to_client)); + + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_connection_free(server_conn)); + + POSIX_GUARD(s2n_cert_chain_and_key_free(chain_and_key)); + POSIX_GUARD(s2n_config_free(server_config)); + POSIX_GUARD(s2n_config_free(client_config)); + + return S2N_SUCCESS; +} + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Additional KEM preferences/security policies to test against. These policies can only be used + * as the server's policy in this test: when generating the ClientHello, the client relies on + * the security_policy_selection[] array (in s2n_security_policies.c) to determine if it should + * write the supported_groups extension. Because these unofficial policies don't exist in that + * array, the supported_groups extension won't get sent and the handshake won't complete as expected. */ + + const struct s2n_kem_group *mlkem768_test_groups[] = { + &s2n_x25519_mlkem_768, + &s2n_secp256r1_mlkem_768, + }; + + const struct s2n_kem_preferences mlkem768_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(mlkem768_test_groups), + .tls13_kem_groups = mlkem768_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy mlkem768_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &mlkem768_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct s2n_kem_group *mlkem1024_test_groups[] = { + &s2n_secp384r1_mlkem_1024, + }; + + const struct s2n_kem_preferences mlkem1024_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(mlkem1024_test_groups), + .tls13_kem_groups = mlkem1024_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy mlkem1024_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &mlkem1024_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct s2n_kem_group *pure_mlkem1024_test_groups[] = { + &s2n_pure_mlkem_1024, + }; + + const struct s2n_kem_preferences pure_mlkem1024_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(pure_mlkem1024_test_groups), + .tls13_kem_groups = pure_mlkem1024_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy pure_mlkem1024_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &pure_mlkem1024_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct s2n_ecc_named_curve *default_curve = &s2n_ecc_curve_x25519; + + if (!s2n_is_evp_apis_supported()) { + default_curve = &s2n_ecc_curve_secp256r1; + } + + struct pq_handshake_test_vector { + const struct s2n_security_policy *client_policy; + const struct s2n_security_policy *server_policy; + const struct s2n_kem_group *expected_kem_group; + const struct s2n_ecc_named_curve *expected_curve; + bool hrr_expected; + bool len_prefix_expected; + }; + + /* Self talk test with each TLS 1.3 KemGroup we support */ + for (size_t i = 0; i < S2N_KEM_GROUPS_COUNT; i++) { + const struct s2n_kem_group *kem_group = ALL_SUPPORTED_KEM_GROUPS[i]; + + if (kem_group == NULL || !s2n_kem_group_is_available(kem_group)) { + continue; + } + + const struct s2n_kem_preferences singleton_test_pref = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = 1, + .tls13_kem_groups = &kem_group, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy singleton_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &singleton_test_pref, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20240603, + }; + + const struct pq_handshake_test_vector test_vec = { + .client_policy = &singleton_test_policy, + .server_policy = &singleton_test_policy, + .expected_kem_group = kem_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }; + + EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(test_vec.client_policy, test_vec.server_policy, + test_vec.expected_kem_group, test_vec.expected_curve, test_vec.hrr_expected, test_vec.len_prefix_expected)); + } + + /* ML-KEM is only available on newer versions of AWS-LC. If it's + * unavailable, we must downgrade the assertions to EC. */ + const struct s2n_kem_group *null_if_no_mlkem_768 = &s2n_x25519_mlkem_768; + const struct s2n_kem_group *null_if_no_mlkem_1024 = &s2n_secp384r1_mlkem_1024; + const struct s2n_kem_group *null_if_no_pure_mlkem_1024 = &s2n_pure_mlkem_1024; + const struct s2n_ecc_named_curve *ec_if_no_mlkem = NULL; + bool hrr_expected_if_mlkem = true; + if (!s2n_libcrypto_supports_mlkem()) { + null_if_no_mlkem_768 = NULL; + null_if_no_mlkem_1024 = NULL; + null_if_no_pure_mlkem_1024 = NULL; + ec_if_no_mlkem = default_curve; + hrr_expected_if_mlkem = false; + } + + /* Test vectors that expect to negotiate PQ assume that PQ is enabled in s2n. + * If PQ is disabled, the expected negotiation outcome is overridden below + * before performing the handshake test. */ + const struct pq_handshake_test_vector test_vectors[] = { + /* Server does not support PQ; client sends a PQ key share and an EC key share; + * server should negotiate EC without HRR. */ + { + .client_policy = &security_policy_pq_tls_1_2_2024_10_09, + .server_policy = &security_policy_test_all_tls13, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server does not support PQ; client sends a PQ key share, but no EC shares; + * server should negotiate EC and send HRR. */ + { + .client_policy = &security_policy_test_tls13_retry_with_pq, + .server_policy = &security_policy_test_all_tls13, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = true, + .len_prefix_expected = true, + }, + + /* Server supports PQ, but client does not. Client sent an EC share, + * EC should be negotiated without HRR */ + { + .client_policy = &security_policy_test_all_tls13, + .server_policy = &security_policy_pq_tls_1_2_2024_10_09, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server supports PQ, but client does not. Client did not send any EC shares, + * EC should be negotiated after exchanging HRR */ + { + .client_policy = &security_policy_test_tls13_retry, + .server_policy = &security_policy_pq_tls_1_2_2024_10_09, + .expected_kem_group = NULL, + .expected_curve = default_curve, + .hrr_expected = true, + .len_prefix_expected = true, + }, + + /* Confirm that MLKEM768 is negotiable */ + { + .client_policy = &mlkem768_test_policy, + .server_policy = &mlkem768_test_policy, + .expected_kem_group = null_if_no_mlkem_768, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Confirm that MLKEM1024 is negotiable */ + { + .client_policy = &mlkem1024_test_policy, + .server_policy = &mlkem1024_test_policy, + .expected_kem_group = null_if_no_mlkem_1024, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Confirm that pure MLKEM1024 is negotiable; fall back to EC when MLKEM is not supported. */ + { + .client_policy = &pure_mlkem1024_test_policy, + .server_policy = &pure_mlkem1024_test_policy, + .expected_kem_group = null_if_no_pure_mlkem_1024, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Client supports pure MLKEM but did not send that key share. Pure MLKEM should be negotiated after exchanging HRR. + * If ML-KEM is not supported, EC should be negotiated without HRR. */ + { + .client_policy = &security_policy_test_all, + .server_policy = &pure_mlkem1024_test_policy, + .expected_kem_group = null_if_no_pure_mlkem_1024, + .expected_curve = ec_if_no_mlkem, + .hrr_expected = hrr_expected_if_mlkem, + .len_prefix_expected = false, + }, + + /* Client supports p384 but did not send that key share when connecting to server that strongly prefers p384. */ + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251113, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251114, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251115, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_20240503, /* Will send p256 KeyShare, but also supports p384 at lower priority */ + .server_policy = &security_policy_20251116, /* Strongly prefers p384 */ + .expected_kem_group = NULL, + .expected_curve = &s2n_ecc_curve_secp384r1, + .hrr_expected = true, + .len_prefix_expected = false, + }, + }; + + for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { + const struct pq_handshake_test_vector *vector = &test_vectors[i]; + const struct s2n_security_policy *client_policy = vector->client_policy; + const struct s2n_security_policy *server_policy = vector->server_policy; + const struct s2n_kem_group *kem_group = vector->expected_kem_group; + const struct s2n_ecc_named_curve *curve = vector->expected_curve; + bool hrr_expected = vector->hrr_expected; + bool len_prefix_expected = vector->len_prefix_expected; + + /* Print Test Vector Info for easier debugging on failure. */ + { + const char *kem_group_str = (kem_group == NULL) ? "NULL" : kem_group->name; + const char *curve_str = (curve == NULL) ? "NULL" : curve->name; + fprintf(stderr, "\n\nRunning test (%zu/%zu)...\n", i + 1, s2n_array_len(test_vectors)); + fflush(stderr); + fprintf(stderr, "Test Vector: kem_group: %s, curve: %s, hrr_expected: %d, len_prefix_expected: %d\n", kem_group_str, curve_str, hrr_expected, len_prefix_expected); + fflush(stderr); + uint32_t output_size = 0; + fprintf(stderr, "\nClient Security Policy: \n"); + EXPECT_SUCCESS(s2n_security_policy_write_fd(client_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); + + fprintf(stderr, "\nServer Security Policy: \n"); + EXPECT_SUCCESS(s2n_security_policy_write_fd(server_policy, S2N_POLICY_FORMAT_DEBUG_V1, STDERR_FILENO, &output_size)); + fflush(stderr); + } + + /* Check if we need to override the test vector. This may need to be done due to some LibCryptos not supporting PQ. */ + if (!s2n_kem_group_is_available(kem_group)) { + kem_group = NULL; + if (curve == NULL) { + curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); + } + } + + if (!s2n_pq_is_enabled()) { + EXPECT_TRUE(client_policy->ecc_preferences->count > 0); + const struct s2n_ecc_named_curve *client_default = client_policy->ecc_preferences->ecc_curves[0]; + const struct s2n_ecc_named_curve *predicted_curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); + + /* If the default curve is x25519, and the test vector is predicting the default, but neither policy supports x25519, + * fall back to predict p256 as it should be in common with every ECC preference list. */ + if (default_curve->iana_id == s2n_ecc_curve_x25519.iana_id + && curve->iana_id == default_curve->iana_id + && (!s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id) + || !s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_x25519.iana_id))) { + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); + curve = &s2n_ecc_curve_secp256r1; + } + + /* The client's preferred curve will be a higher priority than the default if both sides + * support TLS 1.3, and if the client's default can be chosen by the server in 1-RTT. */ + if (s2n_security_policy_supports_tls13(client_policy) && s2n_security_policy_supports_tls13(server_policy) + && (server_policy->strongly_preferred_groups == NULL || server_policy->strongly_preferred_groups->count == 0) + && s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, client_default->iana_id)) { + curve = client_default; + } + + /* Finally, confirm that the expected curve listed in the test vector matches the output of s2n_get_predicted_negotiated_ecdhe_curve() */ + EXPECT_EQUAL(curve->iana_id, predicted_curve->iana_id); + } + + if (kem_group != NULL) { + const struct s2n_kem_group *predicted_kem_group = s2n_get_predicted_negotiated_kem_group(client_policy, server_policy); + POSIX_ENSURE_REF(predicted_kem_group); + + /* Confirm that the expected KEM Group listed in the test vector matches the output of + * s2n_get_predicted_negotiated_kem_group() */ + POSIX_ENSURE_EQ(kem_group->iana_id, predicted_kem_group->iana_id); + } + + EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, kem_group, curve, hrr_expected, len_prefix_expected)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_server_cert_test.c b/tests/unit/s2n_tls13_server_cert_test.c index 046852b2f81..58d3692d9ae 100644 --- a/tests/unit/s2n_tls13_server_cert_test.c +++ b/tests/unit/s2n_tls13_server_cert_test.c @@ -1,200 +1,200 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_safety.h" - -/* Test vectors from https://tools.ietf.org/html/rfc8448#section-3 */ - -/* whole cert message without 0b0001b9 header */ -const char tls13_cert_message_hex[] = - "000001b50001b03082" - "01ac30820115a003020102020102300d06092a8648" - "86f70d01010b0500300e310c300a06035504031303" - "727361301e170d3136303733303031323335395a17" - "0d3236303733303031323335395a300e310c300a06" - "03550403130372736130819f300d06092a864886f7" - "0d010101050003818d0030818902818100b4bb498f" - "8279303d980836399b36c6988c0c68de55e1bdb826" - "d3901a2461eafd2de49a91d015abbc9a95137ace6c" - "1af19eaa6af98c7ced43120998e187a80ee0ccb052" - "4b1b018c3e0b63264d449a6d38e22a5fda43084674" - "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" - "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" - "010001a31a301830090603551d1304023000300b06" - "03551d0f0404030205a0300d06092a864886f70d01" - "010b05000381810085aad2a0e5b9276b908c65f73a" - "7267170618a54c5f8a7b337d2df7a594365417f2ea" - "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" - "5156726096fd335e5e67f2dbf102702e608ccae6be" - "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" - "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" - "961229ac9187b42b4de10000"; - -/* cert only */ -const char tls13_cert_hex[] = - "3082" /* without certificate chain header */ - "01ac30820115a003020102020102300d06092a8648" - "86f70d01010b0500300e310c300a06035504031303" - "727361301e170d3136303733303031323335395a17" - "0d3236303733303031323335395a300e310c300a06" - "03550403130372736130819f300d06092a864886f7" - "0d010101050003818d0030818902818100b4bb498f" - "8279303d980836399b36c6988c0c68de55e1bdb826" - "d3901a2461eafd2de49a91d015abbc9a95137ace6c" - "1af19eaa6af98c7ced43120998e187a80ee0ccb052" - "4b1b018c3e0b63264d449a6d38e22a5fda43084674" - "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" - "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" - "010001a31a301830090603551d1304023000300b06" - "03551d0f0404030205a0300d06092a864886f70d01" - "010b05000381810085aad2a0e5b9276b908c65f73a" - "7267170618a54c5f8a7b337d2df7a594365417f2ea" - "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" - "5156726096fd335e5e67f2dbf102702e608ccae6be" - "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" - "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" - "961229ac9187b42b4de1"; - -/* certificate chain header. It contains - 1. Request Context length (00) - 2. Cert chain length (0001b5) - 3. Cert length (0001b0) - */ -const char tls13_cert_chain_header_hex[] = - "000001b50001b0"; - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* Test s2n_server_cert_recv() parses tls13 certificate */ - { - S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_message_hex); - struct s2n_connection *conn = NULL; - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - - conn->x509_validator.skip_cert_validation = 1; - - /* success case in tls13 parsing mode */ - conn->actual_protocol_version = S2N_TLS13; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); - EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); - EXPECT_SUCCESS(s2n_server_cert_recv(conn)); - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), 0); - - /* failure case in tls12 parsing mode */ - conn->actual_protocol_version = S2N_TLS12; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); - EXPECT_FAILURE(s2n_server_cert_recv(conn)); - - EXPECT_SUCCESS(s2n_connection_free(conn)); - } - - /* Test s2n_server_cert_send() verify server's certificate */ - { - char *tls13_cert_chain_hex = NULL; - /* creating a certificate chain by concatenating - 1. chain header - 2. certificate - */ - EXPECT_NOT_NULL(tls13_cert_chain_hex = malloc(S2N_MAX_TEST_PEM_SIZE)); - strcpy(tls13_cert_chain_hex, tls13_cert_chain_header_hex); - strcat(tls13_cert_chain_hex, tls13_cert_hex); - - S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_chain_hex); - S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); - - struct s2n_connection *conn = NULL; - uint8_t certificate_request_context_len = 0; - - struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; - /* .chain_size is size of cert + 3 for the 3 bytes to express the length */ - struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; - struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; - - /* tls13 mode */ - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - conn->actual_protocol_version = S2N_TLS13; - conn->handshake_params.our_chain_and_key = &cert_chain_and_key; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); - EXPECT_SUCCESS(s2n_server_cert_send(conn)); - - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size + 2); - EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->handshake.io, &certificate_request_context_len)); - - /* server's certificate request context should always be of zero length */ - EXPECT_EQUAL(certificate_request_context_len, 0); - EXPECT_SUCCESS(s2n_connection_free(conn)); - - /* tls12 mode */ - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); - conn->actual_protocol_version = S2N_TLS12; - conn->handshake_params.our_chain_and_key = &cert_chain_and_key; - EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); - EXPECT_SUCCESS(s2n_server_cert_send(conn)); - /* In tls1.2 there is no certificate request context. - TLS1.2 Cert length = TLS1.3 Cert length -1 (server's request context)*/ - EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size - 1); - EXPECT_SUCCESS(s2n_connection_free(conn)); - - free(tls13_cert_chain_hex); - } - - /* Test server sends cert and client receives cert for tls 1.3 */ - { - EXPECT_SUCCESS(s2n_enable_tls13_in_test()); - - struct s2n_connection *server_conn = NULL; - struct s2n_connection *client_conn = NULL; - EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); - EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); - server_conn->actual_protocol_version = S2N_TLS13; - client_conn->actual_protocol_version = S2N_TLS13; - client_conn->x509_validator.skip_cert_validation = 1; - - S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); - S2N_BLOB_FROM_HEX(tls13_cert_message, tls13_cert_message_hex); - - struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; - struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; - struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; - server_conn->handshake_params.our_chain_and_key = &cert_chain_and_key; - - EXPECT_SUCCESS(s2n_server_cert_send(server_conn)); - EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), tls13_cert_message.size); - EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); - EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), tls13_cert_message.size); - EXPECT_SUCCESS(s2n_server_cert_recv(client_conn)); - - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* Test vectors from https://tools.ietf.org/html/rfc8448#section-3 */ + +/* whole cert message without 0b0001b9 header */ +const char tls13_cert_message_hex[] = + "000001b50001b03082" + "01ac30820115a003020102020102300d06092a8648" + "86f70d01010b0500300e310c300a06035504031303" + "727361301e170d3136303733303031323335395a17" + "0d3236303733303031323335395a300e310c300a06" + "03550403130372736130819f300d06092a864886f7" + "0d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826" + "d3901a2461eafd2de49a91d015abbc9a95137ace6c" + "1af19eaa6af98c7ced43120998e187a80ee0ccb052" + "4b1b018c3e0b63264d449a6d38e22a5fda43084674" + "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" + "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" + "010001a31a301830090603551d1304023000300b06" + "03551d0f0404030205a0300d06092a864886f70d01" + "010b05000381810085aad2a0e5b9276b908c65f73a" + "7267170618a54c5f8a7b337d2df7a594365417f2ea" + "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" + "5156726096fd335e5e67f2dbf102702e608ccae6be" + "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" + "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" + "961229ac9187b42b4de10000"; + +/* cert only */ +const char tls13_cert_hex[] = + "3082" /* without certificate chain header */ + "01ac30820115a003020102020102300d06092a8648" + "86f70d01010b0500300e310c300a06035504031303" + "727361301e170d3136303733303031323335395a17" + "0d3236303733303031323335395a300e310c300a06" + "03550403130372736130819f300d06092a864886f7" + "0d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826" + "d3901a2461eafd2de49a91d015abbc9a95137ace6c" + "1af19eaa6af98c7ced43120998e187a80ee0ccb052" + "4b1b018c3e0b63264d449a6d38e22a5fda43084674" + "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" + "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" + "010001a31a301830090603551d1304023000300b06" + "03551d0f0404030205a0300d06092a864886f70d01" + "010b05000381810085aad2a0e5b9276b908c65f73a" + "7267170618a54c5f8a7b337d2df7a594365417f2ea" + "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" + "5156726096fd335e5e67f2dbf102702e608ccae6be" + "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" + "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" + "961229ac9187b42b4de1"; + +/* certificate chain header. It contains + 1. Request Context length (00) + 2. Cert chain length (0001b5) + 3. Cert length (0001b0) + */ +const char tls13_cert_chain_header_hex[] = + "000001b50001b0"; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_server_cert_recv() parses tls13 certificate */ + { + S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_message_hex); + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + conn->x509_validator.skip_cert_validation = 1; + + /* success case in tls13 parsing mode */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); + EXPECT_SUCCESS(s2n_server_cert_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), 0); + + /* failure case in tls12 parsing mode */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); + EXPECT_FAILURE(s2n_server_cert_recv(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test s2n_server_cert_send() verify server's certificate */ + { + char *tls13_cert_chain_hex = NULL; + /* creating a certificate chain by concatenating + 1. chain header + 2. certificate + */ + EXPECT_NOT_NULL(tls13_cert_chain_hex = malloc(S2N_MAX_TEST_PEM_SIZE)); + strcpy(tls13_cert_chain_hex, tls13_cert_chain_header_hex); + strcat(tls13_cert_chain_hex, tls13_cert_hex); + + S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_chain_hex); + S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); + + struct s2n_connection *conn = NULL; + uint8_t certificate_request_context_len = 0; + + struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; + /* .chain_size is size of cert + 3 for the 3 bytes to express the length */ + struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; + struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; + + /* tls13 mode */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_server_cert_send(conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size + 2); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->handshake.io, &certificate_request_context_len)); + + /* server's certificate request context should always be of zero length */ + EXPECT_EQUAL(certificate_request_context_len, 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* tls12 mode */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_SUCCESS(s2n_server_cert_send(conn)); + /* In tls1.2 there is no certificate request context. + TLS1.2 Cert length = TLS1.3 Cert length -1 (server's request context)*/ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size - 1); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + free(tls13_cert_chain_hex); + } + + /* Test server sends cert and client receives cert for tls 1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_connection *server_conn = NULL; + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->x509_validator.skip_cert_validation = 1; + + S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); + S2N_BLOB_FROM_HEX(tls13_cert_message, tls13_cert_message_hex); + + struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; + struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; + struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; + server_conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + + EXPECT_SUCCESS(s2n_server_cert_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), tls13_cert_message.size); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), tls13_cert_message.size); + EXPECT_SUCCESS(s2n_server_cert_recv(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls_prf_test.c b/tests/unit/s2n_tls_prf_test.c index 02eef7d02d0..187d2160095 100644 --- a/tests/unit/s2n_tls_prf_test.c +++ b/tests/unit/s2n_tls_prf_test.c @@ -1,529 +1,529 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_libcrypto.h" -#include "s2n_test.h" -#include "stuffer/s2n_stuffer.h" -#include "testlib/s2n_testlib.h" -/* To gain access to handshake_read and handshake_write */ -#include "tls/s2n_handshake_io.c" - -#define TEST_BLOB_SIZE 64 - -int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, - struct s2n_blob *label, struct s2n_blob *seed_a, - struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out); -S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, - struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output); -S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, - struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); -int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret); - -/* - * Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0 - * - * |<9>| INT: PREMASTER SECRET[48]: 0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748 - * |<9>| INT: CLIENT RANDOM[32]: 537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286 - * |<9>| INT: SERVER RANDOM[32]: 537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821 - * |<9>| INT: MASTER SECRET: c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120 - */ -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - S2N_BLOB_FROM_HEX(premaster_secret_in, - "0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748"); - S2N_BLOB_FROM_HEX(client_random_in, - "537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286"); - S2N_BLOB_FROM_HEX(server_random_in, - "537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821"); - S2N_BLOB_FROM_HEX(master_secret_in, - "c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120"); - - /* s2n_prf tests */ - { - uint8_t secret_bytes[TEST_BLOB_SIZE] = "secret"; - struct s2n_blob secret = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&secret, secret_bytes, sizeof(secret_bytes))); - - uint8_t label_bytes[TEST_BLOB_SIZE] = "label"; - struct s2n_blob label = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&label, label_bytes, sizeof(label_bytes))); - - uint8_t seed_a_bytes[TEST_BLOB_SIZE] = "seed a"; - struct s2n_blob seed_a = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seed_a, seed_a_bytes, sizeof(seed_a_bytes))); - - uint8_t seed_b_bytes[TEST_BLOB_SIZE] = "seed b"; - struct s2n_blob seed_b = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seed_b, seed_b_bytes, sizeof(seed_b_bytes))); - - uint8_t seed_c_bytes[TEST_BLOB_SIZE] = "seed c"; - struct s2n_blob seed_c = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&seed_c, seed_c_bytes, sizeof(seed_c_bytes))); - - /* Safety */ - { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(NULL, &secret, &label, &seed_a, &seed_b, &seed_c, &out), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, NULL, &label, &seed_a, &seed_b, &seed_c, &out), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, NULL, &seed_a, &seed_b, &seed_c, &out), - S2N_ERR_NULL); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, NULL), - S2N_ERR_NULL); - - /* seed_a is required */ - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, NULL, &seed_b, &seed_c, &out), - S2N_ERR_PRF_INVALID_SEED); - - /* seed_b and seed_c are optional */ - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, NULL, NULL, &out)); - - /* seed_b is required if seed_c is provided */ - EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, NULL, &seed_c, &out), - S2N_ERR_PRF_INVALID_SEED); - - /* seed_c is optional */ - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, NULL, &out)); - } - - /* Test: secret and label */ - { - struct s2n_blob empty = { 0 }; - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "d6 4d 7e e1 f3 ca ff a0 d4 7e 84 18 ff 64 97 d8" - "56 4d d1 99 5e ea 53 0d 29 b0 42 68 89 45 f3 58 86" - "f5 4b 12 ff b3 83 87 80 d5 ba 7d f6 26 10 a5 cc 39" - "d9 e7 37 6e eb 28 8e 21 29 53 13 54 f5 f6"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &empty, NULL, NULL, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: secret, label, and seed_a */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "4e f2 13 65 22 07 f1 e5 86 dc 03 aa 60 a2 c9 5c" - "ab 05 5e 43 f0 c8 3a 70 6c f4 be 21 44 9e b8 45 70" - "d9 e8 e1 83 3b f7 65 dd e9 d1 ea ae 14 f7 73 c5 47" - "57 da 0a a8 5f cc 75 ff b2 9e 3d 02 01 e9"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, NULL, NULL, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: secret, label, seed_a, and seed_b */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "45 0b b0 da 24 d5 23 05 b6 00 f9 1c 71 6f ca 40" - "54 ba 1b 6c 91 43 77 9c 0f 6f 2c e6 e4 24 30 ee b3" - "fc 83 85 3a 02 60 1e 37 2c 2c ee d3 b1 8a 6a cc 75" - "d9 84 87 42 d8 b6 a9 9c 1b f8 72 8c 6b 71"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, NULL, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: all inputs */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - const char *expected_hex = - "4f b6 e2 ac d0 68 dc 55 70 43 ab 98 f6 23 a8 93" - "6b f2 0f 48 1c 74 50 7e a0 f2 ef 49 53 a6 b0 56 84" - "fe 2e a9 76 31 50 44 f7 8d e7 3d 52 97 ce 36 82 a8" - "d7 27 59 f7 7a 73 19 06 6c 0a 1a df 68 c6"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: cipher specific digest */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS12; - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - const char *expected_hex = - "5b 54 71 ec b9 8a 49 ce f1 6a d9 31 cf c9 76 be" - "5a e6 25 bd a3 45 69 45 8c 6c 1a a1 98 06 d9 0d cc" - "c8 cd 7c aa d7 e2 59 25 4b 36 ff f7 01 a6 7e 89 22" - "0f bd 06 15 bf 9d 7e d1 53 45 a3 1b 36 da"; - S2N_BLOB_FROM_HEX(expected, expected_hex); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - EXPECT_EQUAL(out.size, expected.size); - EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); - } - - /* Test: large seeds fail for openssl-3.0-fips */ - if (s2n_libcrypto_is_openssl_fips()) { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - conn->actual_protocol_version = S2N_TLS11; - - DEFER_CLEANUP(struct s2n_blob small_seed = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&small_seed, 30)); - - DEFER_CLEANUP(struct s2n_blob medium_seed = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&medium_seed, 300)); - - DEFER_CLEANUP(struct s2n_blob large_seed = { 0 }, s2n_free); - EXPECT_SUCCESS(s2n_alloc(&large_seed, 3000)); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_FAILURE_WITH_ERRNO( - s2n_prf(conn, &secret, &small_seed, &small_seed, &small_seed, &large_seed, &out), - S2N_ERR_PRF_INVALID_SEED); - EXPECT_FAILURE_WITH_ERRNO( - s2n_prf(conn, &secret, &medium_seed, &medium_seed, &medium_seed, &medium_seed, &out), - S2N_ERR_PRF_INVALID_SEED); - EXPECT_SUCCESS(s2n_prf(conn, &secret, - &small_seed, &small_seed, &small_seed, &small_seed, &out)); - } - - /* The custom PRF implementation is used when s2n-tls is not operating in FIPS mode */ - if (!s2n_is_in_fips_mode()) { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - - uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - - /* The custom PRF implementation should modify the digest fields in the prf_space */ - EXPECT_NOT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - } - - /* The libcrypto PRF implementation is used when s2n-tls is in FIPS mode */ - if (s2n_is_in_fips_mode()) { - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - - uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; - EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - - s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); - EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); - - /* The libcrypto PRF implementation will not modify the digest fields in the prf_space */ - EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); - } - } - - /* s2n_tls_prf_master_secret */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - /* Check the most common PRF */ - conn->actual_protocol_version = S2N_TLS11; - - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - - struct s2n_blob pms = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); - EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - }; - - /* s2n_tls_prf_extended_master_secret */ - { - /* The test premaster secret, hash digest, and resulting - * extended master secret were pulled from an OpenSSL TLS1.2 EMS session - * using the s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384 ciphersuite. - */ - S2N_BLOB_FROM_HEX(premaster_secret, - "05e12675c9264d82b53fa15d589c829af9be1ae3d881ab0b023b7b8cad8bc058"); - - S2N_BLOB_FROM_HEX(hash_digest, - "e6cbbaa03909ea387714fe70c07546086dedfcee086fd2985dfdd50924393619" - "009115758e490e2e3b0c13bebdad5fbb"); - - S2N_BLOB_FROM_HEX(extended_master_secret, - "aef116e65e2cd77d4e96b1ceeadb7912ddd9aaf3a907aa3344ec3a2de6cc3b69" - "9ca768fe389eab3b53c98d8ccd830b06"); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-4 - *= type=test - *# When the extended master secret extension is negotiated in a full - *# handshake, the "master_secret" is computed as - *# - *# master_secret = PRF(pre_master_secret, "extended master secret", - *# session_hash) - *# [0..47]; - */ - EXPECT_OK(s2n_prf_tls_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL)); - EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); - }; - - /* s2n_prf_calculate_master_secret */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; - - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - - struct s2n_blob pms = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); - - /* Errors when handshake is not at the Client Key Exchange message */ - EXPECT_FAILURE_WITH_ERRNO(s2n_prf_calculate_master_secret(conn, &pms), S2N_ERR_SAFETY); - - /* Advance handshake to Client Key Exchange message */ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - while (ACTIVE_MESSAGE(conn) != CLIENT_KEY) { - conn->handshake.message_number++; - } - - /* Master secret is calculated when handshake is at Client Key Exchange message*/ - EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - /* s2n_prf_calculate_master_secret will produce the same master secret if given the same inputs */ - EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - conn->ems_negotiated = true; - EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); - - /* Extended master secret calculated is different than the master secret calculated */ - EXPECT_NOT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - }; - - /* s2n_prf_get_digest_for_ems calculates the correct digest to generate an extended master secret. - * Here we test that the retrieved digest is the same as the digest after the Client Key Exchange - * message is added to the transcript hash. - * - *= https://www.rfc-editor.org/rfc/rfc7627#section-3 - *= type=test - *# When a full TLS handshake takes place, we define - *# - *# session_hash = Hash(handshake_messages) - *# - *# where "handshake_messages" refers to all handshake messages sent or - *# received, starting at the ClientHello up to and including the - *# ClientKeyExchange message, including the type and length fields of - *# the handshake messages. - */ - { - struct s2n_cert_chain_and_key *tls12_chain_and_key = NULL; - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls12_chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, - S2N_DEFAULT_TEST_PRIVATE_KEY)); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls12_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); - - struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(client_conn); - - struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); - EXPECT_NOT_NULL(server_conn); - - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); - EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); - - EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_KEY)); - - /* Client writes Client Key Exchange message */ - EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); - - uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob digest_for_ems = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&digest_for_ems, data, sizeof(data))); - - /* Get the Client Key transcript */ - EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH)); - uint8_t client_key_message_length = s2n_stuffer_data_available(&client_to_server); - uint8_t *client_key_message = s2n_stuffer_raw_read(&client_to_server, client_key_message_length); - struct s2n_blob message = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&message, client_key_message, client_key_message_length)); - - s2n_hmac_algorithm prf_alg = server_conn->secure->cipher_suite->prf_alg; - s2n_hash_algorithm hash_alg = 0; - POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); - EXPECT_OK(s2n_prf_get_digest_for_ems(server_conn, &message, hash_alg, &digest_for_ems)); - - /* Server reads Client Key Exchange message */ - EXPECT_SUCCESS(s2n_stuffer_rewind_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH + client_key_message_length)); - EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); - - /* Calculate the digest message after the Server read the Client Key message */ - DEFER_CLEANUP(struct s2n_hash_state current_hash_state = { 0 }, s2n_hash_free); - uint8_t server_digest[S2N_MAX_DIGEST_LEN] = { 0 }; - uint8_t digest_size = 0; - EXPECT_SUCCESS(s2n_hash_digest_size(hash_alg, &digest_size)); - EXPECT_SUCCESS(s2n_hash_new(¤t_hash_state)); - EXPECT_OK(s2n_handshake_copy_hash_state(server_conn, hash_alg, ¤t_hash_state)); - EXPECT_SUCCESS(s2n_hash_digest(¤t_hash_state, server_digest, digest_size)); - - /* Digest for generating the EMS and digest after reading the Client Key message - * should be the same. */ - EXPECT_BYTEARRAY_EQUAL(server_digest, digest_for_ems.data, digest_size); - - EXPECT_SUCCESS(s2n_connection_free(client_conn)); - EXPECT_SUCCESS(s2n_connection_free(server_conn)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls12_chain_and_key)); - } - - /* PRF lifecyle */ - { - /* Safety */ - { - EXPECT_ERROR_WITH_ERRNO(s2n_prf_new(NULL), S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(NULL), S2N_ERR_NULL); - EXPECT_ERROR_WITH_ERRNO(s2n_prf_free(NULL), S2N_ERR_NULL); - - struct s2n_connection conn_with_null_prf_space = { 0 }; - EXPECT_NULL(conn_with_null_prf_space.prf_space); - - EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(&conn_with_null_prf_space), S2N_ERR_NULL); - EXPECT_OK(s2n_prf_free(&conn_with_null_prf_space)); - }; - - /* Basic lifecyle */ - { - struct s2n_connection connection = { 0 }; - EXPECT_NULL(connection.prf_space); - - EXPECT_OK(s2n_prf_new(&connection)); - EXPECT_NOT_NULL(connection.prf_space); - - EXPECT_OK(s2n_prf_wipe(&connection)); - EXPECT_NOT_NULL(connection.prf_space); - - EXPECT_OK(s2n_prf_free(&connection)); - EXPECT_NULL(connection.prf_space); - }; - - /* PRF freed by s2n_connection_free_handshake */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_NOT_NULL(conn->prf_space); - - EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); - EXPECT_NULL(conn->prf_space); - }; - - /* Freed PRF restored by s2n_connection_wipe */ - { - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_prf_free(conn)); - EXPECT_NULL(conn->prf_space); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_NOT_NULL(conn->prf_space); - }; - - /* PRF usable throughout connection lifecycle */ - { - struct s2n_blob pms = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&pms, premaster_secret_in.data, premaster_secret_in.size)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - - EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - EXPECT_FAILURE_WITH_ERRNO(s2n_prf_tls_master_secret(conn, &pms), S2N_ERR_NULL); - - EXPECT_SUCCESS(s2n_connection_wipe(conn)); - EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); - EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); - EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); - EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); - EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); - }; - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_libcrypto.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +/* To gain access to handshake_read and handshake_write */ +#include "tls/s2n_handshake_io.c" + +#define TEST_BLOB_SIZE 64 + +int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, + struct s2n_blob *label, struct s2n_blob *seed_a, + struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out); +S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, + struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output); +S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, + struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); +int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret); + +/* + * Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0 + * + * |<9>| INT: PREMASTER SECRET[48]: 0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748 + * |<9>| INT: CLIENT RANDOM[32]: 537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286 + * |<9>| INT: SERVER RANDOM[32]: 537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821 + * |<9>| INT: MASTER SECRET: c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120 + */ +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + S2N_BLOB_FROM_HEX(premaster_secret_in, + "0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748"); + S2N_BLOB_FROM_HEX(client_random_in, + "537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286"); + S2N_BLOB_FROM_HEX(server_random_in, + "537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821"); + S2N_BLOB_FROM_HEX(master_secret_in, + "c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120"); + + /* s2n_prf tests */ + { + uint8_t secret_bytes[TEST_BLOB_SIZE] = "secret"; + struct s2n_blob secret = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&secret, secret_bytes, sizeof(secret_bytes))); + + uint8_t label_bytes[TEST_BLOB_SIZE] = "label"; + struct s2n_blob label = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&label, label_bytes, sizeof(label_bytes))); + + uint8_t seed_a_bytes[TEST_BLOB_SIZE] = "seed a"; + struct s2n_blob seed_a = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_a, seed_a_bytes, sizeof(seed_a_bytes))); + + uint8_t seed_b_bytes[TEST_BLOB_SIZE] = "seed b"; + struct s2n_blob seed_b = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_b, seed_b_bytes, sizeof(seed_b_bytes))); + + uint8_t seed_c_bytes[TEST_BLOB_SIZE] = "seed c"; + struct s2n_blob seed_c = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seed_c, seed_c_bytes, sizeof(seed_c_bytes))); + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(NULL, &secret, &label, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, NULL, &label, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, NULL, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, NULL), + S2N_ERR_NULL); + + /* seed_a is required */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, NULL, &seed_b, &seed_c, &out), + S2N_ERR_PRF_INVALID_SEED); + + /* seed_b and seed_c are optional */ + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, NULL, NULL, &out)); + + /* seed_b is required if seed_c is provided */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, NULL, &seed_c, &out), + S2N_ERR_PRF_INVALID_SEED); + + /* seed_c is optional */ + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, NULL, &out)); + } + + /* Test: secret and label */ + { + struct s2n_blob empty = { 0 }; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "d6 4d 7e e1 f3 ca ff a0 d4 7e 84 18 ff 64 97 d8" + "56 4d d1 99 5e ea 53 0d 29 b0 42 68 89 45 f3 58 86" + "f5 4b 12 ff b3 83 87 80 d5 ba 7d f6 26 10 a5 cc 39" + "d9 e7 37 6e eb 28 8e 21 29 53 13 54 f5 f6"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &empty, NULL, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: secret, label, and seed_a */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "4e f2 13 65 22 07 f1 e5 86 dc 03 aa 60 a2 c9 5c" + "ab 05 5e 43 f0 c8 3a 70 6c f4 be 21 44 9e b8 45 70" + "d9 e8 e1 83 3b f7 65 dd e9 d1 ea ae 14 f7 73 c5 47" + "57 da 0a a8 5f cc 75 ff b2 9e 3d 02 01 e9"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, NULL, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: secret, label, seed_a, and seed_b */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "45 0b b0 da 24 d5 23 05 b6 00 f9 1c 71 6f ca 40" + "54 ba 1b 6c 91 43 77 9c 0f 6f 2c e6 e4 24 30 ee b3" + "fc 83 85 3a 02 60 1e 37 2c 2c ee d3 b1 8a 6a cc 75" + "d9 84 87 42 d8 b6 a9 9c 1b f8 72 8c 6b 71"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, NULL, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: all inputs */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + const char *expected_hex = + "4f b6 e2 ac d0 68 dc 55 70 43 ab 98 f6 23 a8 93" + "6b f2 0f 48 1c 74 50 7e a0 f2 ef 49 53 a6 b0 56 84" + "fe 2e a9 76 31 50 44 f7 8d e7 3d 52 97 ce 36 82 a8" + "d7 27 59 f7 7a 73 19 06 6c 0a 1a df 68 c6"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: cipher specific digest */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + const char *expected_hex = + "5b 54 71 ec b9 8a 49 ce f1 6a d9 31 cf c9 76 be" + "5a e6 25 bd a3 45 69 45 8c 6c 1a a1 98 06 d9 0d cc" + "c8 cd 7c aa d7 e2 59 25 4b 36 ff f7 01 a6 7e 89 22" + "0f bd 06 15 bf 9d 7e d1 53 45 a3 1b 36 da"; + S2N_BLOB_FROM_HEX(expected, expected_hex); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(conn, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + EXPECT_EQUAL(out.size, expected.size); + EXPECT_BYTEARRAY_EQUAL(out.data, expected.data, expected.size); + } + + /* Test: large seeds fail for openssl-3.0-fips */ + if (s2n_libcrypto_is_openssl_fips()) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS11; + + DEFER_CLEANUP(struct s2n_blob small_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&small_seed, 30)); + + DEFER_CLEANUP(struct s2n_blob medium_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&medium_seed, 300)); + + DEFER_CLEANUP(struct s2n_blob large_seed = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&large_seed, 3000)); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_FAILURE_WITH_ERRNO( + s2n_prf(conn, &secret, &small_seed, &small_seed, &small_seed, &large_seed, &out), + S2N_ERR_PRF_INVALID_SEED); + EXPECT_FAILURE_WITH_ERRNO( + s2n_prf(conn, &secret, &medium_seed, &medium_seed, &medium_seed, &medium_seed, &out), + S2N_ERR_PRF_INVALID_SEED); + EXPECT_SUCCESS(s2n_prf(conn, &secret, + &small_seed, &small_seed, &small_seed, &small_seed, &out)); + } + + /* The custom PRF implementation is used when s2n-tls is not operating in FIPS mode */ + if (!s2n_is_in_fips_mode()) { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + + /* The custom PRF implementation should modify the digest fields in the prf_space */ + EXPECT_NOT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + } + + /* The libcrypto PRF implementation is used when s2n-tls is in FIPS mode */ + if (s2n_is_in_fips_mode()) { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + + /* The libcrypto PRF implementation will not modify the digest fields in the prf_space */ + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + } + } + + /* s2n_tls_prf_master_secret */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Check the most common PRF */ + conn->actual_protocol_version = S2N_TLS11; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); + EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + }; + + /* s2n_tls_prf_extended_master_secret */ + { + /* The test premaster secret, hash digest, and resulting + * extended master secret were pulled from an OpenSSL TLS1.2 EMS session + * using the s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384 ciphersuite. + */ + S2N_BLOB_FROM_HEX(premaster_secret, + "05e12675c9264d82b53fa15d589c829af9be1ae3d881ab0b023b7b8cad8bc058"); + + S2N_BLOB_FROM_HEX(hash_digest, + "e6cbbaa03909ea387714fe70c07546086dedfcee086fd2985dfdd50924393619" + "009115758e490e2e3b0c13bebdad5fbb"); + + S2N_BLOB_FROM_HEX(extended_master_secret, + "aef116e65e2cd77d4e96b1ceeadb7912ddd9aaf3a907aa3344ec3a2de6cc3b69" + "9ca768fe389eab3b53c98d8ccd830b06"); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-4 + *= type=test + *# When the extended master secret extension is negotiated in a full + *# handshake, the "master_secret" is computed as + *# + *# master_secret = PRF(pre_master_secret, "extended master secret", + *# session_hash) + *# [0..47]; + */ + EXPECT_OK(s2n_prf_tls_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL)); + EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); + }; + + /* s2n_prf_calculate_master_secret */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); + + /* Errors when handshake is not at the Client Key Exchange message */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf_calculate_master_secret(conn, &pms), S2N_ERR_SAFETY); + + /* Advance handshake to Client Key Exchange message */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + while (ACTIVE_MESSAGE(conn) != CLIENT_KEY) { + conn->handshake.message_number++; + } + + /* Master secret is calculated when handshake is at Client Key Exchange message*/ + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + /* s2n_prf_calculate_master_secret will produce the same master secret if given the same inputs */ + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + conn->ems_negotiated = true; + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + + /* Extended master secret calculated is different than the master secret calculated */ + EXPECT_NOT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + }; + + /* s2n_prf_get_digest_for_ems calculates the correct digest to generate an extended master secret. + * Here we test that the retrieved digest is the same as the digest after the Client Key Exchange + * message is added to the transcript hash. + * + *= https://www.rfc-editor.org/rfc/rfc7627#section-3 + *= type=test + *# When a full TLS handshake takes place, we define + *# + *# session_hash = Hash(handshake_messages) + *# + *# where "handshake_messages" refers to all handshake messages sent or + *# received, starting at the ClientHello up to and including the + *# ClientKeyExchange message, including the type and length fields of + *# the handshake messages. + */ + { + struct s2n_cert_chain_and_key *tls12_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls12_chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, + S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls12_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_KEY)); + + /* Client writes Client Key Exchange message */ + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest_for_ems = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&digest_for_ems, data, sizeof(data))); + + /* Get the Client Key transcript */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH)); + uint8_t client_key_message_length = s2n_stuffer_data_available(&client_to_server); + uint8_t *client_key_message = s2n_stuffer_raw_read(&client_to_server, client_key_message_length); + struct s2n_blob message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&message, client_key_message, client_key_message_length)); + + s2n_hmac_algorithm prf_alg = server_conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg = 0; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + EXPECT_OK(s2n_prf_get_digest_for_ems(server_conn, &message, hash_alg, &digest_for_ems)); + + /* Server reads Client Key Exchange message */ + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH + client_key_message_length)); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Calculate the digest message after the Server read the Client Key message */ + DEFER_CLEANUP(struct s2n_hash_state current_hash_state = { 0 }, s2n_hash_free); + uint8_t server_digest[S2N_MAX_DIGEST_LEN] = { 0 }; + uint8_t digest_size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(hash_alg, &digest_size)); + EXPECT_SUCCESS(s2n_hash_new(¤t_hash_state)); + EXPECT_OK(s2n_handshake_copy_hash_state(server_conn, hash_alg, ¤t_hash_state)); + EXPECT_SUCCESS(s2n_hash_digest(¤t_hash_state, server_digest, digest_size)); + + /* Digest for generating the EMS and digest after reading the Client Key message + * should be the same. */ + EXPECT_BYTEARRAY_EQUAL(server_digest, digest_for_ems.data, digest_size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls12_chain_and_key)); + } + + /* PRF lifecyle */ + { + /* Safety */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_prf_new(NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_prf_free(NULL), S2N_ERR_NULL); + + struct s2n_connection conn_with_null_prf_space = { 0 }; + EXPECT_NULL(conn_with_null_prf_space.prf_space); + + EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(&conn_with_null_prf_space), S2N_ERR_NULL); + EXPECT_OK(s2n_prf_free(&conn_with_null_prf_space)); + }; + + /* Basic lifecyle */ + { + struct s2n_connection connection = { 0 }; + EXPECT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_new(&connection)); + EXPECT_NOT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_wipe(&connection)); + EXPECT_NOT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_free(&connection)); + EXPECT_NULL(connection.prf_space); + }; + + /* PRF freed by s2n_connection_free_handshake */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_NULL(conn->prf_space); + }; + + /* Freed PRF restored by s2n_connection_wipe */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_prf_free(conn)); + EXPECT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_NOT_NULL(conn->prf_space); + }; + + /* PRF usable throughout connection lifecycle */ + { + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, premaster_secret_in.data, premaster_secret_in.size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf_tls_master_secret(conn, &pms), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->client_hello.random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_SUCCESS(s2n_prf_tls_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_wildcard_hostname_test.c b/tests/unit/s2n_wildcard_hostname_test.c index 936e45ff3a7..236c0e52fc1 100644 --- a/tests/unit/s2n_wildcard_hostname_test.c +++ b/tests/unit/s2n_wildcard_hostname_test.c @@ -1,213 +1,213 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "api/s2n.h" -#include "crypto/s2n_certificate.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "utils/s2n_safety.h" - -struct wildcardify_test_case { - const char *hostname; - const char *output; -}; - -struct wildcardify_test_case wildcardify_test_cases[] = { - { .hostname = "foo.bar.com", .output = "*.bar.com" }, - { .hostname = "localhost", .output = NULL }, - { .hostname = "one.com", .output = "*.com" }, - { .hostname = "foo*.bar*.com*", .output = "*.bar*.com*" }, - { .hostname = "foo.bar.com.", .output = "*.bar.com." }, - { .hostname = "*.a.c", .output = "*.a.c" }, - { .hostname = "*", .output = NULL }, - { .hostname = "foo.", .output = "*." }, -}; - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - const int num_wildcardify_tests = s2n_array_len(wildcardify_test_cases); - for (size_t i = 0; i < num_wildcardify_tests; i++) { - const char *hostname = wildcardify_test_cases[i].hostname; - struct s2n_blob hostname_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) hostname, strlen(hostname))); - uint8_t output[S2N_MAX_SERVER_NAME] = { 0 }; - struct s2n_blob output_blob = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&output_blob, (uint8_t *) (uintptr_t) output, sizeof(output))); - struct s2n_stuffer hostname_stuffer = { 0 }; - struct s2n_stuffer output_stuffer = { 0 }; - EXPECT_SUCCESS(s2n_stuffer_init(&hostname_stuffer, &hostname_blob)); - EXPECT_SUCCESS(s2n_stuffer_skip_write(&hostname_stuffer, hostname_blob.size)); - EXPECT_SUCCESS(s2n_stuffer_init(&output_stuffer, &output_blob)); - EXPECT_SUCCESS(s2n_create_wildcard_hostname(&hostname_stuffer, &output_stuffer)); - - /* Make sure the wildcard generated matches the output we expect. */ - const uint32_t wildcard_len = s2n_stuffer_data_available(&output_stuffer); - const char *expected_output = wildcardify_test_cases[i].output; - if (wildcard_len > 0) { - EXPECT_EQUAL(wildcard_len, strlen(expected_output)); - EXPECT_SUCCESS(memcmp(output, expected_output, wildcard_len)); - } else { - EXPECT_EQUAL(expected_output, NULL); - } - } - - /* s2n_connection_get_certificate_match */ - { - /* Safety checks */ - { - s2n_cert_sni_match match_status = 0; - struct s2n_connection *conn = NULL; - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(NULL, &match_status), - S2N_ERR_INVALID_ARGUMENT); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(conn, NULL), - S2N_ERR_INVALID_ARGUMENT); - - /* This API does not work on a client connection */ - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(client_conn, &match_status), - S2N_ERR_CLIENT_MODE); - - /* This API will not work if a certificate isn't selected yet. */ - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(server_conn, &match_status), - S2N_ERR_NO_CERT_FOUND); - } - - const struct { - const char *cert_path; - const char *key_path; - const char *server_name; - s2n_cert_sni_match expected_match_status; - } test_cases[] = { - /* Client does not send an SNI extension */ - { - .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, - .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, - .server_name = NULL, - .expected_match_status = S2N_SNI_NONE, - }, - /* Server has a certificate that matches the client's SNI extension */ - { - .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, - .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, - .server_name = "localhost", - .expected_match_status = S2N_SNI_EXACT_MATCH, - }, - /* Server has a certificate with a domain name containing a wildcard character - * which can be matched to the client's SNI extension */ - { - .cert_path = S2N_RSA_2048_SHA256_WILDCARD_CERT, - .key_path = S2N_RSA_2048_SHA256_WILDCARD_KEY, - .server_name = "alligator.localhost", - .expected_match_status = S2N_SNI_WILDCARD_MATCH, - }, - /* Server does not have a certificate that can be matched to the client's - * SNI extension. */ - { - .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, - .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, - .server_name = "This cert name is unlikely to exist.", - .expected_match_status = S2N_SNI_NO_MATCH, - }, - }; - - for (size_t i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[i].cert_path, test_cases[i].key_path)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); - - const char *server_name = test_cases[i].server_name; - if (server_name) { - EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); - } - - EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); - - s2n_cert_sni_match match_status = 0; - EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); - EXPECT_EQUAL(match_status, test_cases[i].expected_match_status); - } - - /* Test that cert info can still be retrieved in the case of a failed handshake */ - { - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_OK(s2n_config_set_tls12_security_policy(config)); - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); - EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); - EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); - - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "This cert name is unlikely to exist.")); - EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), - S2N_ERR_CERT_INVALID_HOSTNAME); - - s2n_cert_sni_match match_status = 0; - EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); - EXPECT_EQUAL(match_status, S2N_SNI_NO_MATCH); - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "api/s2n.h" +#include "crypto/s2n_certificate.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +struct wildcardify_test_case { + const char *hostname; + const char *output; +}; + +struct wildcardify_test_case wildcardify_test_cases[] = { + { .hostname = "foo.bar.com", .output = "*.bar.com" }, + { .hostname = "localhost", .output = NULL }, + { .hostname = "one.com", .output = "*.com" }, + { .hostname = "foo*.bar*.com*", .output = "*.bar*.com*" }, + { .hostname = "foo.bar.com.", .output = "*.bar.com." }, + { .hostname = "*.a.c", .output = "*.a.c" }, + { .hostname = "*", .output = NULL }, + { .hostname = "foo.", .output = "*." }, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const int num_wildcardify_tests = s2n_array_len(wildcardify_test_cases); + for (size_t i = 0; i < num_wildcardify_tests; i++) { + const char *hostname = wildcardify_test_cases[i].hostname; + struct s2n_blob hostname_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) hostname, strlen(hostname))); + uint8_t output[S2N_MAX_SERVER_NAME] = { 0 }; + struct s2n_blob output_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output_blob, (uint8_t *) (uintptr_t) output, sizeof(output))); + struct s2n_stuffer hostname_stuffer = { 0 }; + struct s2n_stuffer output_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&hostname_stuffer, &hostname_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&hostname_stuffer, hostname_blob.size)); + EXPECT_SUCCESS(s2n_stuffer_init(&output_stuffer, &output_blob)); + EXPECT_SUCCESS(s2n_create_wildcard_hostname(&hostname_stuffer, &output_stuffer)); + + /* Make sure the wildcard generated matches the output we expect. */ + const uint32_t wildcard_len = s2n_stuffer_data_available(&output_stuffer); + const char *expected_output = wildcardify_test_cases[i].output; + if (wildcard_len > 0) { + EXPECT_EQUAL(wildcard_len, strlen(expected_output)); + EXPECT_SUCCESS(memcmp(output, expected_output, wildcard_len)); + } else { + EXPECT_EQUAL(expected_output, NULL); + } + } + + /* s2n_connection_get_certificate_match */ + { + /* Safety checks */ + { + s2n_cert_sni_match match_status = 0; + struct s2n_connection *conn = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(NULL, &match_status), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(conn, NULL), + S2N_ERR_INVALID_ARGUMENT); + + /* This API does not work on a client connection */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(client_conn, &match_status), + S2N_ERR_CLIENT_MODE); + + /* This API will not work if a certificate isn't selected yet. */ + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_certificate_match(server_conn, &match_status), + S2N_ERR_NO_CERT_FOUND); + } + + const struct { + const char *cert_path; + const char *key_path; + const char *server_name; + s2n_cert_sni_match expected_match_status; + } test_cases[] = { + /* Client does not send an SNI extension */ + { + .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, + .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, + .server_name = NULL, + .expected_match_status = S2N_SNI_NONE, + }, + /* Server has a certificate that matches the client's SNI extension */ + { + .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, + .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, + .server_name = "localhost", + .expected_match_status = S2N_SNI_EXACT_MATCH, + }, + /* Server has a certificate with a domain name containing a wildcard character + * which can be matched to the client's SNI extension */ + { + .cert_path = S2N_RSA_2048_SHA256_WILDCARD_CERT, + .key_path = S2N_RSA_2048_SHA256_WILDCARD_KEY, + .server_name = "alligator.localhost", + .expected_match_status = S2N_SNI_WILDCARD_MATCH, + }, + /* Server does not have a certificate that can be matched to the client's + * SNI extension. */ + { + .cert_path = S2N_DEFAULT_TEST_CERT_CHAIN, + .key_path = S2N_DEFAULT_TEST_PRIVATE_KEY, + .server_name = "This cert name is unlikely to exist.", + .expected_match_status = S2N_SNI_NO_MATCH, + }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_path, test_cases[i].key_path)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); + + const char *server_name = test_cases[i].server_name; + if (server_name) { + EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); + } + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + s2n_cert_sni_match match_status = 0; + EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); + EXPECT_EQUAL(match_status, test_cases[i].expected_match_status); + } + + /* Test that cert info can still be retrieved in the case of a failed handshake */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_connection_set_tls12_security_policy(client_conn)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_set_tls12_security_policy(config)); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(server_conn, client_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "This cert name is unlikely to exist.")); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_INVALID_HOSTNAME); + + s2n_cert_sni_match match_status = 0; + EXPECT_SUCCESS(s2n_connection_get_certificate_match(server_conn, &match_status)); + EXPECT_EQUAL(match_status, S2N_SNI_NO_MATCH); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_intent_verification_test.c b/tests/unit/s2n_x509_intent_verification_test.c index fc74f0c39fe..df7f0c93b2e 100644 --- a/tests/unit/s2n_x509_intent_verification_test.c +++ b/tests/unit/s2n_x509_intent_verification_test.c @@ -1,637 +1,637 @@ -#include "utils/s2n_prelude.h" -/* -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* -* Licensed under the Apache License, Version 2.0 (the "License"). -* You may not use this file except in compliance with the License. -* A copy of the License is located at -* -* http://aws.amazon.com/apache2.0 -* -* or in the "license" file accompanying this file. This file is distributed -* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -* express or implied. See the License for the specific language governing -* permissions and limitations under the License. -*/ - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -static S2N_RESULT s2n_test_pem_paths_from_intent_dir(const char *intent_cert_dir, char *cert_chain_path, - char *leaf_key_path, char *root_cert_path) -{ - sprintf(cert_chain_path, "%s/cert-chain.pem", intent_cert_dir); - sprintf(leaf_key_path, "%s/leaf-key.pem", intent_cert_dir); - sprintf(root_cert_path, "%s/root-cert.pem", intent_cert_dir); - return S2N_RESULT_OK; -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* Test s2n_config_disable_x509_intent_verification(). */ - { - /* Safety */ - { - EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_intent_verification(NULL), S2N_ERR_INVALID_ARGUMENT); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); - } - - /* The verification is enabled by default. */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - EXPECT_FALSE(config->disable_x509_intent_verification); - } - - /* Disabling the verification on the config updates the proper flags. */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - - EXPECT_FALSE(config->disable_x509_intent_verification); - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); - EXPECT_TRUE(config->disable_x509_intent_verification); - } - } - - /* Test certificate intent verification. */ - { - struct { - const char *cert_chain_dir; - s2n_error expected_client_error; - s2n_error expected_server_error; - } test_cases[] = { - { - /* A certificate chain with no optional intent extensions specified. This - * certificate chain includes the mandatory BasicConstraints extension for CA - * certificates, but doesn't include the KeyUsage or ExtendedKeyUsage - * extensions. - */ - .cert_chain_dir = "../pems/intent/cert_chains/no_intent", - /* The KeyUsage and ExtendedKeyUsage extensions are optional, so validation - * should succeed. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with digitalSignature set in the leaf KeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_leaf", - /* Setting digitalSignature is valid for both client and server leaf - * certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, -#if !defined(LIBRESSL_VERSION_NUMBER) - /* This test is skipped for LibreSSL since LibreSSL doesn't consider keyAgreement to be - * valid for client or server leaf certificates: - * https://github.com/libressl/openbsd/blob/8bb14039f52469491bf0058b1efdf0c75db6befc/src/lib/libcrypto/x509/x509_purp.c#L662-L691 - */ - { - /* A certificate chain with keyAgreement set in the leaf KeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_agreement_leaf", - /* Setting keyAgreement is valid for both client and server leaf certificates. */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, -#endif - { - /* A certificate chain with digitalSignature and keyAgreement set in the leaf - * KeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_key_agreement_leaf", - /* Setting digitalSignature OR keyAgreement is valid for both client and server - * leaf certificates. Setting both fields is also valid. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with digitalSignature and contentCommitment set in the leaf - * KeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_content_commitment_leaf", - /* Setting digitalSignature is valid for both client and server leaf - * certificates. contentCommitment is not relevant for client or server - * certificates, but since digitalSignature is set, validation should succeed. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with keyEncipherment set in the leaf KeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_encipherment_leaf", - /* Setting keyEncipherment is valid for server leaf certificates, but is NOT - * valid for client leaf certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with keyCertSign set in the leaf KeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_leaf", - /* Setting keyCertSign is only valid for CA certificates, NOT for leaf - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with keyCertSign set in an intermediate KeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_intermediate", - /* Setting keyCertSign is valid for CA certificates. */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with keyCertSign and contentCommitment set in an - * intermediate KeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_and_content_commitment_intermediate", - /* Setting keyCertSign is valid for CA certificates. The irrelevant - * contentCommitment field should not impact validation. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth and serverAuth set in the leaf - * ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_leaf", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth and serverAuth set in an intermediate - * ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_intermediate", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth, serverAuth, and emailProtection set in - * the leaf ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_leaf", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. The irrelevant emailProtection field should - * not impact validation. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth, serverAuth, and emailProtection set in - * an intermediate ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_intermediate", - /* A certificate that sets both clientAuth and serverAuth is valid for both - * client and server certificates. The irrelevant emailProtection field should - * not impact validation. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth set in the leaf ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_leaf", - /* A certificate that sets clientAuth is valid for client certificates, but NOT - * for server certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with clientAuth set in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_intermediate", - /* A certificate that sets clientAuth is valid for client certificates, but NOT - * for server certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_OK, - }, - { - /* A certificate chain with serverAuth set in the leaf ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_leaf", - /* A certificate that sets serverAuth is valid for server certificates but NOT - * for client certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with serverAuth set in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_intermediate", - /* A certificate that sets serverAuth is valid for server certificates but NOT - * for client certificates. - */ - .expected_client_error = S2N_ERR_OK, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_leaf", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A long certificate chain with codeSigning set in the fourth intermediate - * ExtendedKeyUsage extension. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate_long", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with crlSign set in the leaf KeyUsage extension, but the - * extension is marked as non-critical. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_crl_sign_leaf_non_critical", - /* A certificate that sets crlSign is NOT valid for client or server - * certificates. This should be validated regardless of the extension - * criticality. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning set in the leaf ExtendedKeyUsage - * extension, but the extension is marked as non-critical. - */ - .cert_chain_dir = "../pems/intent/cert_chains/eku_code_signing_intermediate_non_critical", - /* A certificate that sets codeSigning is NOT valid for client or server - * certificates. This should be validated regardless of the extension - * criticality. - */ - .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, - .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, - }, - }; - - for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { - char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, - leaf_key_path, root_cert_path)); - - /* Intent is verified for server certificates received by the client. */ - for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - cert_chain_path, leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - if (disable_intent_verification) { - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); - } - - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[test_idx].expected_client_error; - if (disable_intent_verification) { - EXPECT_SUCCESS(ret); - } else { - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - } - - /* Intent is verified for client certificates received by the server. */ - for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, - cert_chain_path, leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, root_cert_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - if (disable_intent_verification) { - EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); - } - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - 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, client_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[test_idx].expected_server_error; - if (disable_intent_verification) { - EXPECT_SUCCESS(ret); - } else { - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - - /* Ensure that a client certificate was received. In the case of an expected error, - * this ensures that the error occurred on the server side, after the client - * successfully validated the server's certificate. - */ - 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); - } - } - } - - /* Test default s2n-tls intent verification. - * - * s2n-tls already verifies intent fields for intermediate certificates in the call to - * X509_verify_cert. So this verification should continue to be performed whether certificate - * intent verification is enabled or not. - */ - { - struct { - const char *cert_chain_dir; - } test_cases[] = { - { - /* A certificate chain with the CA field set to false in an intermediate - * BasicConstraints extension. - * - * CA certificates MUST set the CA field to true. - */ - .cert_chain_dir = "../pems/intent/cert_chains/bc_non_ca_intermediate", - }, - { - /* A certificate chain with digitalSignature set in an intermediate KeyUsage - * extension. - * - * The digitalSignature field is valid only for leaf certificates, NOT CA - * certificates. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate", - }, - { - /* A long certificate chain with digitalSignature set in the fourth - * intermediate KeyUsage extension. - * - * The digitalSignature field is valid only for leaf certificates, NOT CA - * certificates. - */ - .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate_long", - }, - }; - - for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { - char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; - EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, - leaf_key_path, root_cert_path)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, cert_chain_path, leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - - 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 intent verification doesn't apply to trust anchors. - * - * Despite certificate intent fields that may indicate otherwise, it is assumed that trust - * anchors were intended to be used for a TLS purpose, given that they were included in the - * s2n-tls trust store. - */ - { - struct { - const char *cert_chain_path; - const char *leaf_key_path; - const char *trust_anchor_path; - s2n_error expected_error; - } test_cases[] = { - { - /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. When the invalid - * leaf certificate is not a trust anchor, validation should fail. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/root-cert.pem", - .expected_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. However, this is - * acceptable if the invalid leaf certificate is a trust anchor. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-cert.pem", - .expected_error = S2N_ERR_OK, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. When the invalid - * intermediate certificate is not a trust anchor, validation should fail. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/root-cert.pem", - .expected_error = S2N_ERR_CERT_INTENT_INVALID, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. However, this is - * acceptable if the invalid intermediate certificate is a trust anchor. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/intermediate_1-cert.pem", - .expected_error = S2N_ERR_OK, - }, - { - /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage - * extension. - */ - .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", - /* A certificate that sets codeSigning is not valid for TLS. However, this is - * acceptable if the leaf certificate is a trust anchor, since the invalid - * intermediate certificate won't be in the chain of trust. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-cert.pem", - .expected_error = S2N_ERR_OK, - }, - { - /* A certificate that sets emailProtection in the root ExtendedKeyUsage extension. */ - .cert_chain_path = "../pems/intent/cert_chains/eku_email_protection_root/cert-chain.pem", - .leaf_key_path = "../pems/intent/cert_chains/eku_email_protection_root/leaf-key.pem", - /* A certificate that sets emailProtection is not valid for TLS. However, this is - * acceptable if the certificate is a trust anchor. - */ - .trust_anchor_path = "../pems/intent/cert_chains/eku_email_protection_root/root-cert.pem", - .expected_error = S2N_ERR_OK, - }, - }; - - for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[test_idx].cert_chain_path, test_cases[test_idx].leaf_key_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, - test_cases[test_idx].trust_anchor_path, NULL)); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[test_idx].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +static S2N_RESULT s2n_test_pem_paths_from_intent_dir(const char *intent_cert_dir, char *cert_chain_path, + char *leaf_key_path, char *root_cert_path) +{ + sprintf(cert_chain_path, "%s/cert-chain.pem", intent_cert_dir); + sprintf(leaf_key_path, "%s/leaf-key.pem", intent_cert_dir); + sprintf(root_cert_path, "%s/root-cert.pem", intent_cert_dir); + return S2N_RESULT_OK; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Test s2n_config_disable_x509_intent_verification(). */ + { + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_intent_verification(NULL), S2N_ERR_INVALID_ARGUMENT); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); + } + + /* The verification is enabled by default. */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(config->disable_x509_intent_verification); + } + + /* Disabling the verification on the config updates the proper flags. */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(config->disable_x509_intent_verification); + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); + EXPECT_TRUE(config->disable_x509_intent_verification); + } + } + + /* Test certificate intent verification. */ + { + struct { + const char *cert_chain_dir; + s2n_error expected_client_error; + s2n_error expected_server_error; + } test_cases[] = { + { + /* A certificate chain with no optional intent extensions specified. This + * certificate chain includes the mandatory BasicConstraints extension for CA + * certificates, but doesn't include the KeyUsage or ExtendedKeyUsage + * extensions. + */ + .cert_chain_dir = "../pems/intent/cert_chains/no_intent", + /* The KeyUsage and ExtendedKeyUsage extensions are optional, so validation + * should succeed. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with digitalSignature set in the leaf KeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_leaf", + /* Setting digitalSignature is valid for both client and server leaf + * certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, +#if !defined(LIBRESSL_VERSION_NUMBER) + /* This test is skipped for LibreSSL since LibreSSL doesn't consider keyAgreement to be + * valid for client or server leaf certificates: + * https://github.com/libressl/openbsd/blob/8bb14039f52469491bf0058b1efdf0c75db6befc/src/lib/libcrypto/x509/x509_purp.c#L662-L691 + */ + { + /* A certificate chain with keyAgreement set in the leaf KeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_agreement_leaf", + /* Setting keyAgreement is valid for both client and server leaf certificates. */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, +#endif + { + /* A certificate chain with digitalSignature and keyAgreement set in the leaf + * KeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_key_agreement_leaf", + /* Setting digitalSignature OR keyAgreement is valid for both client and server + * leaf certificates. Setting both fields is also valid. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with digitalSignature and contentCommitment set in the leaf + * KeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_and_content_commitment_leaf", + /* Setting digitalSignature is valid for both client and server leaf + * certificates. contentCommitment is not relevant for client or server + * certificates, but since digitalSignature is set, validation should succeed. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with keyEncipherment set in the leaf KeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_encipherment_leaf", + /* Setting keyEncipherment is valid for server leaf certificates, but is NOT + * valid for client leaf certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with keyCertSign set in the leaf KeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_leaf", + /* Setting keyCertSign is only valid for CA certificates, NOT for leaf + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with keyCertSign set in an intermediate KeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_intermediate", + /* Setting keyCertSign is valid for CA certificates. */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with keyCertSign and contentCommitment set in an + * intermediate KeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_key_cert_sign_and_content_commitment_intermediate", + /* Setting keyCertSign is valid for CA certificates. The irrelevant + * contentCommitment field should not impact validation. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth and serverAuth set in the leaf + * ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_leaf", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth and serverAuth set in an intermediate + * ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_intermediate", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth, serverAuth, and emailProtection set in + * the leaf ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_leaf", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. The irrelevant emailProtection field should + * not impact validation. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth, serverAuth, and emailProtection set in + * an intermediate ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_and_serverAuth_and_emailProtection_intermediate", + /* A certificate that sets both clientAuth and serverAuth is valid for both + * client and server certificates. The irrelevant emailProtection field should + * not impact validation. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth set in the leaf ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_leaf", + /* A certificate that sets clientAuth is valid for client certificates, but NOT + * for server certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with clientAuth set in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_clientAuth_intermediate", + /* A certificate that sets clientAuth is valid for client certificates, but NOT + * for server certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_OK, + }, + { + /* A certificate chain with serverAuth set in the leaf ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_leaf", + /* A certificate that sets serverAuth is valid for server certificates but NOT + * for client certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with serverAuth set in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_serverAuth_intermediate", + /* A certificate that sets serverAuth is valid for server certificates but NOT + * for client certificates. + */ + .expected_client_error = S2N_ERR_OK, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_leaf", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A long certificate chain with codeSigning set in the fourth intermediate + * ExtendedKeyUsage extension. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_codeSigning_intermediate_long", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with crlSign set in the leaf KeyUsage extension, but the + * extension is marked as non-critical. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_crl_sign_leaf_non_critical", + /* A certificate that sets crlSign is NOT valid for client or server + * certificates. This should be validated regardless of the extension + * criticality. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning set in the leaf ExtendedKeyUsage + * extension, but the extension is marked as non-critical. + */ + .cert_chain_dir = "../pems/intent/cert_chains/eku_code_signing_intermediate_non_critical", + /* A certificate that sets codeSigning is NOT valid for client or server + * certificates. This should be validated regardless of the extension + * criticality. + */ + .expected_client_error = S2N_ERR_CERT_INTENT_INVALID, + .expected_server_error = S2N_ERR_CERT_INTENT_INVALID, + }, + }; + + for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { + char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, + leaf_key_path, root_cert_path)); + + /* Intent is verified for server certificates received by the client. */ + for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + cert_chain_path, leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + if (disable_intent_verification) { + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(config)); + } + + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[test_idx].expected_client_error; + if (disable_intent_verification) { + EXPECT_SUCCESS(ret); + } else { + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + /* Intent is verified for client certificates received by the server. */ + for (size_t disable_intent_verification = 0; disable_intent_verification <= 1; disable_intent_verification++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + cert_chain_path, leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, root_cert_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + if (disable_intent_verification) { + EXPECT_SUCCESS(s2n_config_disable_x509_intent_verification(server_config)); + } + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + 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, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[test_idx].expected_server_error; + if (disable_intent_verification) { + EXPECT_SUCCESS(ret); + } else { + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + + /* Ensure that a client certificate was received. In the case of an expected error, + * this ensures that the error occurred on the server side, after the client + * successfully validated the server's certificate. + */ + 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); + } + } + } + + /* Test default s2n-tls intent verification. + * + * s2n-tls already verifies intent fields for intermediate certificates in the call to + * X509_verify_cert. So this verification should continue to be performed whether certificate + * intent verification is enabled or not. + */ + { + struct { + const char *cert_chain_dir; + } test_cases[] = { + { + /* A certificate chain with the CA field set to false in an intermediate + * BasicConstraints extension. + * + * CA certificates MUST set the CA field to true. + */ + .cert_chain_dir = "../pems/intent/cert_chains/bc_non_ca_intermediate", + }, + { + /* A certificate chain with digitalSignature set in an intermediate KeyUsage + * extension. + * + * The digitalSignature field is valid only for leaf certificates, NOT CA + * certificates. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate", + }, + { + /* A long certificate chain with digitalSignature set in the fourth + * intermediate KeyUsage extension. + * + * The digitalSignature field is valid only for leaf certificates, NOT CA + * certificates. + */ + .cert_chain_dir = "../pems/intent/cert_chains/ku_digital_signature_intermediate_long", + }, + }; + + for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { + char cert_chain_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char leaf_key_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + char root_cert_path[S2N_MAX_TEST_PEM_PATH_LENGTH] = { 0 }; + EXPECT_OK(s2n_test_pem_paths_from_intent_dir(test_cases[test_idx].cert_chain_dir, cert_chain_path, + leaf_key_path, root_cert_path)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, cert_chain_path, leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, root_cert_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + 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 intent verification doesn't apply to trust anchors. + * + * Despite certificate intent fields that may indicate otherwise, it is assumed that trust + * anchors were intended to be used for a TLS purpose, given that they were included in the + * s2n-tls trust store. + */ + { + struct { + const char *cert_chain_path; + const char *leaf_key_path; + const char *trust_anchor_path; + s2n_error expected_error; + } test_cases[] = { + { + /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. When the invalid + * leaf certificate is not a trust anchor, validation should fail. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/root-cert.pem", + .expected_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in the leaf ExtendedKeyUsage extension. */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. However, this is + * acceptable if the invalid leaf certificate is a trust anchor. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_leaf/leaf-cert.pem", + .expected_error = S2N_ERR_OK, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. When the invalid + * intermediate certificate is not a trust anchor, validation should fail. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/root-cert.pem", + .expected_error = S2N_ERR_CERT_INTENT_INVALID, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. However, this is + * acceptable if the invalid intermediate certificate is a trust anchor. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/intermediate_1-cert.pem", + .expected_error = S2N_ERR_OK, + }, + { + /* A certificate chain with codeSigning in an intermediate ExtendedKeyUsage + * extension. + */ + .cert_chain_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-key.pem", + /* A certificate that sets codeSigning is not valid for TLS. However, this is + * acceptable if the leaf certificate is a trust anchor, since the invalid + * intermediate certificate won't be in the chain of trust. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_codeSigning_intermediate/leaf-cert.pem", + .expected_error = S2N_ERR_OK, + }, + { + /* A certificate that sets emailProtection in the root ExtendedKeyUsage extension. */ + .cert_chain_path = "../pems/intent/cert_chains/eku_email_protection_root/cert-chain.pem", + .leaf_key_path = "../pems/intent/cert_chains/eku_email_protection_root/leaf-key.pem", + /* A certificate that sets emailProtection is not valid for TLS. However, this is + * acceptable if the certificate is a trust anchor. + */ + .trust_anchor_path = "../pems/intent/cert_chains/eku_email_protection_root/root-cert.pem", + .expected_error = S2N_ERR_OK, + }, + }; + + for (size_t test_idx = 0; test_idx < s2n_array_len(test_cases); test_idx++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[test_idx].cert_chain_path, test_cases[test_idx].leaf_key_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, + test_cases[test_idx].trust_anchor_path, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(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_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[test_idx].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_validator_certificate_signatures_test.c b/tests/unit/s2n_x509_validator_certificate_signatures_test.c index a83f70f5619..1554d1240c0 100644 --- a/tests/unit/s2n_x509_validator_certificate_signatures_test.c +++ b/tests/unit/s2n_x509_validator_certificate_signatures_test.c @@ -1,220 +1,220 @@ -#include "utils/s2n_prelude.h" -/* - * 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" -#include "crypto/s2n_openssl_x509.h" -#include "error/s2n_errno.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_signature_scheme.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_x509_validator.h" -#include "utils/s2n_safety.h" - -/* forward declaration */ -S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert); - -DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - X509 *cert = NULL; - BIO *certBio = NULL; - size_t certLen = 0; - - const struct s2n_signature_scheme *const test_sig_scheme_list[] = { - &s2n_ecdsa_sha256, - &s2n_rsa_pkcs1_sha1, - }; - - const struct s2n_signature_preferences test_certificate_signature_preferences = { - .count = s2n_array_len(test_sig_scheme_list), - .signature_schemes = test_sig_scheme_list, - }; - - const struct s2n_security_policy test_sp = { - .minimum_protocol_version = S2N_TLS12, - .certificate_signature_preferences = &test_certificate_signature_preferences, - }; - - /* s2n_x509_validator_check_cert_preferences */ - { - /* Connection using a security policy with no certificate_signature_preferences allows SHA-1 signatures in certificates */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *config = s2n_config_new(); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - /* 20140601 is a security policy with no certificate_signature_preferences list */ - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - certLen = strlen((const char *) cert_file); - - /* Read the test certificates into an Openssl X509 struct */ - EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); - EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); - - EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(BIO_free(certBio)); - X509_free(cert); - }; - - /* Connection using the default_tls13 security policy does not validate SHA-1 signatures in certificates */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *config = s2n_config_new(); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - certLen = strlen((const char *) cert_file); - - /* Read the test certificates into an Openssl X509 struct */ - EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); - EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, cert), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(BIO_free(certBio)); - X509_free(cert); - }; - - /* Connection using the default_tls13 security policy ignores a SHA-1 signature on a root certificate */ - { - struct s2n_connection *conn = NULL; - struct s2n_config *config = s2n_config_new(); - - EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - EXPECT_SUCCESS(s2n_read_test_pem(S2N_SHA1_ROOT_SIGNATURE_CA_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); - certLen = strlen((const char *) cert_file); - - /* Read the test certificates into an Openssl X509 struct */ - EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); - EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); - EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); - - EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); - - EXPECT_SUCCESS(s2n_config_free(config)); - EXPECT_SUCCESS(s2n_connection_free(conn)); - EXPECT_SUCCESS(BIO_free(certBio)); - X509_free(cert); - }; - - /* When the certificate signature algorithm is not in the preferences list, then an S2N_ERR_CERT_UNTRUSTED err - * is returned. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = &test_sp; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_p384_sha256 = NULL, - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&ecdsa_p384_sha256, "ec", - "ecdsa", "p384", "sha384")); - DEFER_CLEANUP(X509 *test_cert = NULL, X509_free_pointer); - EXPECT_OK(s2n_openssl_x509_parse(&ecdsa_p384_sha256->cert_chain->head->raw, &test_cert)); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - }; - - /* Certificate signature algorithm is in the test certificate signature preferences list but signature is SHA-1 - * and TLS 1.3 has been negotiated. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = &test_sp; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint32_t cert_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, - &cert_len, S2N_MAX_TEST_PEM_SIZE)); - - /* Read the test certificates into an Openssl X509 struct */ - DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); - EXPECT_NOT_NULL(cert_bio); - EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); - DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); - EXPECT_NOT_NULL(test_cert); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_CERT_UNTRUSTED); - }; - - /* Certificate signature algorithm is in the test certificate signature preferences list and signature is SHA-1 - * and TLS 1.2 has been negotiated. - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - config->security_policy = &test_sp; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - - uint32_t cert_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, - &cert_len, S2N_MAX_TEST_PEM_SIZE)); - - /* Read the test certificates into an Openssl X509 struct */ - DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); - EXPECT_NOT_NULL(cert_bio); - EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); - DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); - EXPECT_NOT_NULL(test_cert); - - EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, test_cert)); - }; - }; - END_TEST(); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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" +#include "crypto/s2n_openssl_x509.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_signature_scheme.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_x509_validator.h" +#include "utils/s2n_safety.h" + +/* forward declaration */ +S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert); + +DEFINE_POINTER_CLEANUP_FUNC(BIO *, BIO_free); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + X509 *cert = NULL; + BIO *certBio = NULL; + size_t certLen = 0; + + const struct s2n_signature_scheme *const test_sig_scheme_list[] = { + &s2n_ecdsa_sha256, + &s2n_rsa_pkcs1_sha1, + }; + + const struct s2n_signature_preferences test_certificate_signature_preferences = { + .count = s2n_array_len(test_sig_scheme_list), + .signature_schemes = test_sig_scheme_list, + }; + + const struct s2n_security_policy test_sp = { + .minimum_protocol_version = S2N_TLS12, + .certificate_signature_preferences = &test_certificate_signature_preferences, + }; + + /* s2n_x509_validator_check_cert_preferences */ + { + /* Connection using a security policy with no certificate_signature_preferences allows SHA-1 signatures in certificates */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + /* 20140601 is a security policy with no certificate_signature_preferences list */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Connection using the default_tls13 security policy does not validate SHA-1 signatures in certificates */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, cert), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Connection using the default_tls13 security policy ignores a SHA-1 signature on a root certificate */ + { + struct s2n_connection *conn = NULL; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_SHA1_ROOT_SIGNATURE_CA_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, cert)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* When the certificate signature algorithm is not in the preferences list, then an S2N_ERR_CERT_UNTRUSTED err + * is returned. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = &test_sp; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_p384_sha256 = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_permutation_load_server_chain(&ecdsa_p384_sha256, "ec", + "ecdsa", "p384", "sha384")); + DEFER_CLEANUP(X509 *test_cert = NULL, X509_free_pointer); + EXPECT_OK(s2n_openssl_x509_parse(&ecdsa_p384_sha256->cert_chain->head->raw, &test_cert)); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + }; + + /* Certificate signature algorithm is in the test certificate signature preferences list but signature is SHA-1 + * and TLS 1.3 has been negotiated. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = &test_sp; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint32_t cert_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, + &cert_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Read the test certificates into an Openssl X509 struct */ + DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); + EXPECT_NOT_NULL(cert_bio); + EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); + DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); + EXPECT_NOT_NULL(test_cert); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_check_cert_preferences(conn, test_cert), S2N_ERR_CERT_UNTRUSTED); + }; + + /* Certificate signature algorithm is in the test certificate signature preferences list and signature is SHA-1 + * and TLS 1.2 has been negotiated. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->security_policy = &test_sp; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint32_t cert_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_file, + &cert_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Read the test certificates into an Openssl X509 struct */ + DEFER_CLEANUP(BIO *cert_bio = BIO_new(BIO_s_mem()), BIO_free_pointer); + EXPECT_NOT_NULL(cert_bio); + EXPECT_TRUE(BIO_write(cert_bio, cert_file, cert_len) > 0); + DEFER_CLEANUP(X509 *test_cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL), X509_free_pointer); + EXPECT_NOT_NULL(test_cert); + + EXPECT_OK(s2n_x509_validator_check_cert_preferences(conn, test_cert)); + }; + }; + END_TEST(); + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_x509_validator_test.c b/tests/unit/s2n_x509_validator_test.c index 2a3aa8dff7e..a00e7a74bbb 100644 --- a/tests/unit/s2n_x509_validator_test.c +++ b/tests/unit/s2n_x509_validator_test.c @@ -1,2309 +1,2309 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_libcrypto.h" -#include "crypto/s2n_openssl.h" -#include "crypto/s2n_openssl_x509.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" -#include "tls/s2n_certificate_keys.h" - -static int fetch_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2250-01-01 */ - *timestamp = 8835984000000000000; - return 0; -} - -static int fetch_early_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2038-01-01 */ - *timestamp = 2145920461000000000; - return 0; -} - -#if S2N_OCSP_STAPLING_SUPPORTED -static int mock_time(void *data, uint64_t *timestamp) -{ - *timestamp = *(uint64_t *) data; - return 0; -} -static int fetch_invalid_before_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2015-02-27 */ - *timestamp = 1425019604000000000; - return 0; -} - -static int fetch_not_expired_ocsp_timestamp(void *data, uint64_t *timestamp) -{ - /* 2019-03-17 */ - *timestamp = 1552824239000000000; - return 0; -} -#endif /* S2N_OCSP_STAPLING_SUPPORTED */ - -static int read_file(struct s2n_stuffer *file_output, const char *path, uint32_t max_len) -{ - FILE *fd = fopen(path, "rb"); - POSIX_GUARD(s2n_stuffer_alloc(file_output, max_len)); - - if (fd) { - char data[1024]; - size_t r = 0; - while ((r = fread(data, 1, sizeof(data), fd)) > 0) { - POSIX_GUARD(s2n_stuffer_write_bytes(file_output, (const uint8_t *) data, (const uint32_t) r)); - } - fclose(fd); - return s2n_stuffer_data_available(file_output) > 0; - } - - return -1; -} - -struct host_verify_data { - const char *name; - uint8_t found_name; - uint8_t callback_invoked; -}; - -static uint8_t verify_host_reject_everything(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return 0; -} - -static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - verify_data->callback_invoked = 1; - return 1; -} - -static uint8_t verify_host_verify_alt(const char *host_name, size_t host_name_len, void *data) -{ - struct host_verify_data *verify_data = (struct host_verify_data *) data; - - verify_data->callback_invoked = 1; - if (!strcmp(host_name, verify_data->name)) { - verify_data->found_name = 1; - return 1; - } - - return 0; -} - -/* some tests try to mock the system time to a date post 2038. If this test is - * run on a platform where time_t is 32 bits, the time_t will overflow, so we - * only run these tests on platforms with a 64 bit time_t. - */ -static bool s2n_supports_large_time_t() -{ - return sizeof(time_t) == 8; -} - -/* Early versions of Openssl (Openssl-1.0.2k confirmed) included a bug where UTCTime - * formatted dates in certificates could not be compared to dates after the year 2050, - * because Openssl would assume that the validation date was also UTCTime formatted - * and therefore reject any date with a year after 2050. - * This is an issue because RFC5280 requires that dates in certificates be in - * UTCTime format for years before 2050. - * Affected tests are modified to account for this bug. - * See https://github.com/openssl/openssl/blob/OpenSSL_1_0_2k/crypto/x509/x509_vfy.c#L2027C1-L2027C26 - */ -static bool s2n_libcrypto_supports_2050() -{ - ASN1_TIME *utc_time = ASN1_UTCTIME_set(NULL, 0); - if (!utc_time) { - return false; - } - - /* The `32BitBuildAndUnit` job in s2nGeneralBatch runs on a 32-bit system - * where time_t cannot represent the year 2050 (2524608000) and triggers - * -Wconstant-conversion (treated as an error). - * - * The libcrypto used by the job (i386-linux-gnu) does support year 2050. - * Return true to skip the `X509_cmp_time` call. - */ - if (sizeof(time_t) < 8) { - ASN1_STRING_free(utc_time); - return true; - } - - time_t time_2050 = (time_t) 2524608000LL; - int result = X509_cmp_time(utc_time, &time_2050); - ASN1_STRING_free(utc_time); - return (result != 0); -} - -int main(int argc, char **argv) -{ - BEGIN_TEST(); - EXPECT_SUCCESS(s2n_disable_tls13_in_test()); - - /* The issues with 2050 only affected openssl-1.0.2 */ - if (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { - EXPECT_TRUE(s2n_libcrypto_supports_2050()); - } - - /* test empty trust store */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); - }; - - /* test trust store from PEM file */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL); - EXPECT_EQUAL(0, err_code); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test trust store from PEM */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - EXPECT_EQUAL(0, err_code); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); - - /* s2n_x509_trust_store_add_pem returns success when trying to add a - * certificate that already exists in the trust store */ - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - free(cert_chain); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test trust store from non-existent PEM file */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, "dskfjasdklfjsdkl", NULL); - EXPECT_EQUAL(-1, err_code); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test trust store from invalid PEM file */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_INVALID_HEADER_KEY, NULL); - EXPECT_EQUAL(-1, err_code); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in unsafe mode */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* test validator in unsafe mode, make sure max depth is honored on the read, but not an error condition */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* test validator in safe mode, but no configured trust store */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - EXPECT_FAILURE_WITH_ERRNO(s2n_x509_validator_set_max_chain_depth(&validator, 0), S2N_ERR_INVALID_ARGUMENT); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, but no configured trust store */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - - EXPECT_NOT_NULL(connection); - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - EXPECT_EQUAL(0, verify_data.callback_invoked); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store and test that SAN URI callback is invoked. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_URI_SANS_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "foo://bar" }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_URI_SANS_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(1, verify_data.found_name); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, using s2n PEM Parser. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, but max chain depth is exceeded*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); - - EXPECT_EQUAL(0, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test post-2038 certificate expiration. - * - * The expired certificate should fail as untrusted. This test fails on - * platforms where time_t is 4 bytes because representing dates past 2038 as - * unix seconds overflows the time_t. - */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - int expected_errno = S2N_ERR_CERT_EXPIRED; - /* In some cases validation may fail with a less specific error due to - * issues with large dates, but validation does always fail. */ - if (!s2n_supports_large_time_t()) { - expected_errno = S2N_ERR_SAFETY; - } else if (!s2n_libcrypto_supports_2050()) { - expected_errno = S2N_ERR_CERT_UNTRUSTED; - } - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, connection, - chain_data, chain_len, &pkey_type, &public_key_out), - expected_errno); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test pre-2038 certificate expiration - * - * After the expiration date, the certificate should fail as untrusted. This - * test uses pre-2038 dates for 32 bit time_t concerns - */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, connection, - chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_EXPIRED); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); - }; - - /* test validator in safe mode, with properly configured trust store, but the server's end-entity cert is invalid. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - /* alter a random byte in the certificate to make it invalid */ - size_t corrupt_index = 200; - EXPECT_TRUE(chain_len > corrupt_index); - chain_data[corrupt_index] = (uint8_t) (chain_data[corrupt_index] << 2); - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, but host isn't trusted*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store, but host isn't trusted, using s2n PEM Parser */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_connection_free(connection); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name validation succeeds */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name validation succeeds, using s2n PEM Parser */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_pkey_free(&public_key_out); - - s2n_connection_free(connection); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name via alternative name validation succeeds - * note: in this case, we don't have valid certs but it's enough to make sure we are properly pulling alternative names - * from the certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - struct host_verify_data verify_data = { - .name = "127.0.0.1", - .found_name = 0, - .callback_invoked = 0, - }; - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_EQUAL(1, verify_data.found_name); - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name via alternative name validation fails, and - * no Common Name validation happens as DNS alternative name is present. note: in this case, we don't have valid certs but - * it's enough to make sure we are properly validating alternative names and common name.*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Name matches CN on certificate (CN=localhost), but no match in alternative names */ - struct host_verify_data verify_data = { - .name = "localhost", - .found_name = 0, - .callback_invoked = 0, - }; - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(0, verify_data.found_name); - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with properly configured trust store. host name via common name validation succeeds, - * non-dns alternative names are ignored. note: in this case, we don't have valid certs but it's enough to make sure - * we are properly validating alternative names and common name.*/ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - - /* Name matches CN on certificate (CN=localhost) */ - struct host_verify_data verify_data = { - .name = "localhost", - .found_name = 0, - .callback_invoked = 0, - }; - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_NO_DNS_SANS_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - EXPECT_EQUAL(1, verify_data.found_name); - EXPECT_EQUAL(1, verify_data.callback_invoked); - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; -#if S2N_OCSP_STAPLING_SUPPORTED - /* Test valid OCSP date range */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test valid OCSP date range without nextUpdate field */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_NO_NEXT_UPDATE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_not_expired_ocsp_timestamp, NULL); - - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test valid OCSP date range, but with s2n PEM Parser */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); - free(cert_chain); - EXPECT_EQUAL(0, err_code); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - s2n_pkey_free(&public_key_out); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /** - * Test invalid OCSP date range post-2038 - * - * After the "Next Update" time in the OCSP response, the certificate should - * fail as expired. - */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); - - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - if (s2n_supports_large_time_t()) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_EXPIRED); - } else { - /* fetch_expired_after_ocsp_timestamp is in 2200 which is not - * representable for 32 bit time_t's. - */ - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_SAFETY); - } - - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /** - * Test invalid OCSP date range pre-2038 - * - * This test sets the clock time to be after the expiration date of the cert - * and after the "Next Update" field of the OCSP response. - */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); - - DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_EXPIRED); - - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); - } - - /* Test invalid OCSP date range (thisupdate is off) */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - s2n_config_set_wall_clock(connection->config, fetch_invalid_before_ocsp_timestamp, NULL); - - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_INVALID); - - s2n_config_set_wall_clock(connection->config, old_clock, NULL); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /* Test valid OCSP date range, but the data itself is untrusted */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - /* flip a byte right in the middle of the cert */ - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - raw_data[800] = (uint8_t) (raw_data[800] + 1); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test valid OCSP date range and data, but the stapled response was signed with an issuer not in the chain of trust */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test OCSP response signed by the correct responder certificate, but not for the requested certificate. - * (So this would be a completely valid response to a different OCSP request for the other certificate.) */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /* Test OCSP response signed by the wrong responder certificate, but the requested certificate was signed. - * (however this incorrect OCSP responder certificate is a valid OCSP responder for some other case and chains - * to a trusted root). Thus, this response is not valid for any request. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_WRONG_SIGNER_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - raw_data, ocsp_data_len), - S2N_ERR_CERT_UNTRUSTED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - } - - /* Test OCSP response status is revoked */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - EXPECT_EQUAL(1, verify_data.callback_invoked); - struct s2n_stuffer ocsp_data_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_REVOKED_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - S2N_ERR_CERT_REVOKED); - - s2n_stuffer_free(&ocsp_data_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /** - * Test OCSP validation at various offsets from update times. - * - * libcrypto ASN1 comparison calculates differences in terms of days and seconds, - * so try mocking the system time to a collection of more than day & less - * than day differences. - * The T's in the below diagram represent test cases that should fail - * The F's represent test cases that should succeed - * S2N_ERR_CERT_INVALID S2N_ERR_CERT_EXPIRED - * | | | | - * v v v v - * F F T T T T F F - * v v v v v v v v - * <----------|---|---|----------------------------|---|---|---> - * ^ ^ - * this update next update - * |---| - * one day - * - * If this test is failing make sure that the this_update_timestamp_nanoseconds - * matches the actual timestamp of ocsp_response_early_expire.der - * - * openssl ocsp -respin ocsp_response_early_expire.der -text -noverify | grep "This Update" - */ - { - /* Apr 28 22:11:56 2023 GMT */ - uint64_t this_update_timestamp_nanoseconds = (uint64_t) 1682719916 * ONE_SEC_IN_NANOS; - - /* Apr 28 22:11:56 2023 GMT */ - uint64_t next_update_timestamp_nanoseconds = (uint64_t) 2082838316 * ONE_SEC_IN_NANOS; - - uint64_t one_hour_nanoseconds = (uint64_t) 60 * 60 * ONE_SEC_IN_NANOS; - uint64_t one_day_nanoseconds = 24 * one_hour_nanoseconds; - - struct { - uint64_t time; - int result; - } test_cases[] = { - { - .time = this_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_CERT_INVALID, - }, - { - .time = this_update_timestamp_nanoseconds - one_hour_nanoseconds, - .result = S2N_ERR_CERT_INVALID, - }, - { - .time = this_update_timestamp_nanoseconds + one_hour_nanoseconds, - .result = S2N_ERR_OK, - }, - { - .time = this_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_OK, - }, - { - .time = next_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_OK, - }, - { - .time = next_update_timestamp_nanoseconds - one_hour_nanoseconds, - .result = S2N_ERR_OK, - }, - { - .time = next_update_timestamp_nanoseconds + one_hour_nanoseconds, - .result = S2N_ERR_CERT_EXPIRED, - }, - { - .time = next_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), - .result = S2N_ERR_CERT_EXPIRED, - } - }; - - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - /** - * keep track of the old clock, because we want cert validation to happen - * with the default system clock, and not the "mock_time" clock. - */ - s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; - uint64_t timestamp_nanoseconds = test_cases[i].time; - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, mock_time, ×tamp_nanoseconds)); - - DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); - uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); - EXPECT_TRUE(ocsp_data_len > 0); - - if (test_cases[i].result != S2N_ERR_OK) { - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), - test_cases[i].result); - } else { - EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, - s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); - } - - EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); - }; - }; -#endif /* S2N_OCSP_STAPLING_SUPPORTED */ - /* test validator in safe mode, with default host name validator. Connection server name matches alternative name on a certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with default host name validator. Connection server name matches wildcard alternative name on a certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - EXPECT_SUCCESS(s2n_set_server_name(connection, "test.localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with default host name validator. Connection server does not match alternative names on a certificate. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - EXPECT_SUCCESS(s2n_set_server_name(connection, "127.0.0.1")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* test validator in safe mode, with default host name validator. Connection server matches the IPv6 address on the certificate. */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_IP_V6_LO_RSA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - s2n_x509_validator_init(&validator, &trust_store, 1); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - /* the provided hostname should be an empty string */ - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "::1" }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem( - connection, - S2N_IP_V6_LO_RSA_CERT, - &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - }; - - /* Server matches the empty string when there are no usable identifiers in the cert. */ - { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_WITHOUT_CN_RSA_CERT, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - s2n_x509_validator_init(&validator, &trust_store, 1); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - /* the provided hostname should be an empty string */ - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "" }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem( - connection, - S2N_WITHOUT_CN_RSA_CERT, - &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - }; - - /* test validator in safe mode, with default host name validator. No connection server name supplied. */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK( - s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_CERT_INVALID_HOSTNAME); - - EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test trust store in a configuration can handle invalid PEM without crashing */ - { - struct s2n_config *cfg = s2n_config_new(); - s2n_config_add_pem_to_trust_store(cfg, ""); - s2n_config_free(cfg); - /* Expect no crash. */ - }; - - /* Test one trailing byte in cert validator */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct s2n_stuffer chain_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - s2n_stuffer_free(&chain_stuffer); - EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* Test more trailing bytes in cert validator for negative case */ - { - struct s2n_x509_validator validator; - s2n_x509_validator_init_no_x509_validation(&validator); - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct s2n_stuffer chain_stuffer = { 0 }; - EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, - connection, chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_DECODE_CERTIFICATE); - - s2n_stuffer_free(&chain_stuffer); - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - s2n_x509_validator_wipe(&validator); - }; - - /* Test unknown curve in cert validator for negative case */ - if (s2n_libcrypto_is_awslc()) { - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); - - char pem_str[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_BRAINPOOL_CURVE_CERT, pem_str, sizeof(pem_str))); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, pem_str)); - - DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); - EXPECT_SUCCESS(s2n_send_cert_chain(conn, &message, chain_and_key)); - EXPECT_SUCCESS(s2n_stuffer_skip_read(&message, 3)); - - uint32_t chain_len = s2n_stuffer_data_available(&message); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&message, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO( - s2n_x509_validator_validate_cert_chain(&validator, - conn, chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_DECODE_CERTIFICATE); - }; - - /* Ensure that certs after the leaf cert can have an arbitrary number of trailing bytes */ - { - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - DEFER_CLEANUP(struct s2n_stuffer one_trailing_byte_chain = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&one_trailing_byte_chain, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t one_trailing_byte_chain_len = s2n_stuffer_data_available(&one_trailing_byte_chain); - uint8_t *one_trailing_byte_chain_data = s2n_stuffer_raw_read(&one_trailing_byte_chain, - one_trailing_byte_chain_len); - - DEFER_CLEANUP(struct s2n_stuffer four_trailing_bytes_chain = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(read_file(&four_trailing_bytes_chain, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); - uint32_t four_trailing_bytes_chain_len = s2n_stuffer_data_available(&four_trailing_bytes_chain); - uint8_t *four_trailing_bytes_chain_data = s2n_stuffer_raw_read(&four_trailing_bytes_chain, - four_trailing_bytes_chain_len); - - DEFER_CLEANUP(struct s2n_stuffer chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_alloc(&chain_stuffer, S2N_MAX_TEST_PEM_SIZE * 2)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, one_trailing_byte_chain_data, - one_trailing_byte_chain_len)); - EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, four_trailing_bytes_chain_data, - four_trailing_bytes_chain_len)); - - uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); - EXPECT_TRUE(chain_len > 0); - uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); - - DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key)); - - EXPECT_EQUAL(sk_X509_num(validator.cert_chain_from_wire), 2); - }; - - /* Test validator trusts a SHA-1 signature in a certificate chain if certificate validation is off */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - - EXPECT_NOT_NULL(connection); - connection->actual_protocol_version = S2N_TLS13; - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - /* This cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - validator.skip_cert_validation = 1; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_config_free(config); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Test validator does not trust a SHA-1 signature in a certificate chain */ - { - struct s2n_x509_trust_store trust_store; - s2n_x509_trust_store_init_empty(&trust_store); - EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); - - struct s2n_x509_validator validator; - s2n_x509_validator_init(&validator, &trust_store, 1); - - struct s2n_config *config = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - - struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); - EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); - - EXPECT_NOT_NULL(connection); - connection->actual_protocol_version = S2N_TLS13; - - struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; - EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - struct s2n_pkey public_key_out; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, - chain_data, chain_len, &pkey_type, &public_key_out), - S2N_ERR_CERT_UNTRUSTED); - - s2n_connection_free(connection); - s2n_pkey_free(&public_key_out); - - s2n_config_free(config); - s2n_x509_validator_wipe(&validator); - s2n_x509_trust_store_wipe(&trust_store); - }; - - /* Validator fails if cert chain is empty */ - { - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); - - struct s2n_pkey public_key = { 0 }; - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, NULL, 0, &pkey_type, - &public_key), - S2N_ERR_NO_CERT_FOUND); - } - - /* Test trust store can be wiped */ - { - /* Wipe new s2n_config, which is initialized with certs from the system default locations. */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Wipe repeatedly without crash */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Wipe after setting verification location */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Set verification location after wipe */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - s2n_config_free(cfg); - }; - - /* Wipe after adding PEM */ - { - struct s2n_config *cfg = s2n_config_new(); - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - free(cert_chain); - s2n_config_free(cfg); - }; - - /* Add PEM after wipe */ - { - struct s2n_config *cfg = s2n_config_new(); - EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); - EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - - char *cert_chain = NULL; - EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); - EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); - free(cert_chain); - s2n_config_free(cfg); - }; - }; - - /* Ensure that non-root certificates added to the trust store are trusted */ - { - const char *non_root_cert_path = S2N_RSA_2048_PKCS1_LEAF_CERT; - -#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) - /* Ensure that the test certificate isn't self-signed, and is therefore not a root. - * - * The X509_get_extension_flags API wasn't added to OpenSSL until 1.1.0. - */ - { - const char *non_root_key_path = S2N_RSA_2048_PKCS1_KEY; - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, non_root_cert_path, non_root_key_path)); - struct s2n_cert *cert = NULL; - EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &cert, 0)); - EXPECT_NOT_NULL(cert); - - /* Use the s2n_cert to convert the PEM to ASN.1. */ - const uint8_t *asn1_data = NULL; - uint32_t asn1_len = 0; - EXPECT_SUCCESS(s2n_cert_get_der(cert, &asn1_data, &asn1_len)); - EXPECT_NOT_NULL(asn1_data); - - /* Parse the ASN.1 data with the libcrypto */ - DEFER_CLEANUP(X509 *x509 = d2i_X509(NULL, &asn1_data, asn1_len), X509_free_pointer); - EXPECT_NOT_NULL(x509); - - /* Ensure that the self-signed flag isn't set */ - uint32_t extension_flags = X509_get_extension_flags(x509); - EXPECT_EQUAL(extension_flags & EXFLAG_SS, 0); - } -#endif - - /* Test s2n_config_set_verification_ca_location */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, non_root_cert_path, NULL)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - } - - /* Test s2n_config_add_pem_to_trust_store */ - { - char non_root_cert_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(non_root_cert_path, non_root_cert_pem, S2N_MAX_TEST_PEM_SIZE)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, non_root_cert_pem)); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - } - - /* Test system trust store - * - * This test uses the SSL_CERT_FILE environment variable to override the system trust store - * location, which isn't supported by LibreSSL. - */ - if (!s2n_libcrypto_is_libressl()) { - /* Override the system cert file with the non-root test cert. */ - EXPECT_SUCCESS(setenv("SSL_CERT_FILE", non_root_cert_path, 1)); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(connection); - EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); - EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, - &public_key_out)); - - EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); - } - } - - /* Test that CAs must comply with cert preferences */ - { - uint8_t invalid_root_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t root_pem_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CA, &invalid_root_pem[0], &root_pem_len, - S2N_MAX_TEST_PEM_SIZE)); - - uint8_t chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - uint32_t chain_pem_len = 0; - EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CERTS, &chain_pem[0], &chain_pem_len, - S2N_MAX_TEST_PEM_SIZE)); - - struct s2n_security_policy security_policy_not_local = security_policy_20250429; - security_policy_not_local.certificate_preferences_apply_locally = false; - - /* when the peer sends the full chain with a non-compliant CA, verification fails when reading in the certs */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->security_policy_override = &security_policy_not_local; - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - - EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - /* Failed while processing/reading in the cert chain */ - EXPECT_TRUE(validator.state == INIT); - }; - - /* when the peer sends only compliant certs from a non-compliant CA, - * validation fails on the local trust store - */ - { - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); - EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); - - DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - conn->security_policy_override = &security_policy_not_local; - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - - /* `chain_pem_len - root_pem_len`: only use the first two certs in the chain (leaf and intermediate) to - * ensure we are correctly failing on the local trust store and not the chain that the peer sent. - */ - EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len - root_pem_len, - &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, - &pkey_type, &public_key_out), - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); - /* X509_verify_cert finished successfully */ - EXPECT_TRUE(validator.state == VALIDATED); - }; - }; - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_libcrypto.h" +#include "crypto/s2n_openssl.h" +#include "crypto/s2n_openssl_x509.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_certificate_keys.h" + +static int fetch_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2250-01-01 */ + *timestamp = 8835984000000000000; + return 0; +} + +static int fetch_early_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2038-01-01 */ + *timestamp = 2145920461000000000; + return 0; +} + +#if S2N_OCSP_STAPLING_SUPPORTED +static int mock_time(void *data, uint64_t *timestamp) +{ + *timestamp = *(uint64_t *) data; + return 0; +} +static int fetch_invalid_before_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2015-02-27 */ + *timestamp = 1425019604000000000; + return 0; +} + +static int fetch_not_expired_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2019-03-17 */ + *timestamp = 1552824239000000000; + return 0; +} +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ + +static int read_file(struct s2n_stuffer *file_output, const char *path, uint32_t max_len) +{ + FILE *fd = fopen(path, "rb"); + POSIX_GUARD(s2n_stuffer_alloc(file_output, max_len)); + + if (fd) { + char data[1024]; + size_t r = 0; + while ((r = fread(data, 1, sizeof(data), fd)) > 0) { + POSIX_GUARD(s2n_stuffer_write_bytes(file_output, (const uint8_t *) data, (const uint32_t) r)); + } + fclose(fd); + return s2n_stuffer_data_available(file_output) > 0; + } + + return -1; +} + +struct host_verify_data { + const char *name; + uint8_t found_name; + uint8_t callback_invoked; +}; + +static uint8_t verify_host_reject_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 0; +} + +static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 1; +} + +static uint8_t verify_host_verify_alt(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + + verify_data->callback_invoked = 1; + if (!strcmp(host_name, verify_data->name)) { + verify_data->found_name = 1; + return 1; + } + + return 0; +} + +/* some tests try to mock the system time to a date post 2038. If this test is + * run on a platform where time_t is 32 bits, the time_t will overflow, so we + * only run these tests on platforms with a 64 bit time_t. + */ +static bool s2n_supports_large_time_t() +{ + return sizeof(time_t) == 8; +} + +/* Early versions of Openssl (Openssl-1.0.2k confirmed) included a bug where UTCTime + * formatted dates in certificates could not be compared to dates after the year 2050, + * because Openssl would assume that the validation date was also UTCTime formatted + * and therefore reject any date with a year after 2050. + * This is an issue because RFC5280 requires that dates in certificates be in + * UTCTime format for years before 2050. + * Affected tests are modified to account for this bug. + * See https://github.com/openssl/openssl/blob/OpenSSL_1_0_2k/crypto/x509/x509_vfy.c#L2027C1-L2027C26 + */ +static bool s2n_libcrypto_supports_2050() +{ + ASN1_TIME *utc_time = ASN1_UTCTIME_set(NULL, 0); + if (!utc_time) { + return false; + } + + /* The `32BitBuildAndUnit` job in s2nGeneralBatch runs on a 32-bit system + * where time_t cannot represent the year 2050 (2524608000) and triggers + * -Wconstant-conversion (treated as an error). + * + * The libcrypto used by the job (i386-linux-gnu) does support year 2050. + * Return true to skip the `X509_cmp_time` call. + */ + if (sizeof(time_t) < 8) { + ASN1_STRING_free(utc_time); + return true; + } + + time_t time_2050 = (time_t) 2524608000LL; + int result = X509_cmp_time(utc_time, &time_2050); + ASN1_STRING_free(utc_time); + return (result != 0); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* The issues with 2050 only affected openssl-1.0.2 */ + if (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { + EXPECT_TRUE(s2n_libcrypto_supports_2050()); + } + + /* test empty trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + }; + + /* test trust store from PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL); + EXPECT_EQUAL(0, err_code); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from PEM */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + EXPECT_EQUAL(0, err_code); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); + + /* s2n_x509_trust_store_add_pem returns success when trying to add a + * certificate that already exists in the trust store */ + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + free(cert_chain); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from non-existent PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, "dskfjasdklfjsdkl", NULL); + EXPECT_EQUAL(-1, err_code); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from invalid PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_INVALID_HEADER_KEY, NULL); + EXPECT_EQUAL(-1, err_code); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in unsafe mode */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* test validator in unsafe mode, make sure max depth is honored on the read, but not an error condition */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* test validator in safe mode, but no configured trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + EXPECT_FAILURE_WITH_ERRNO(s2n_x509_validator_set_max_chain_depth(&validator, 0), S2N_ERR_INVALID_ARGUMENT); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, but no configured trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + + EXPECT_NOT_NULL(connection); + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + EXPECT_EQUAL(0, verify_data.callback_invoked); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store and test that SAN URI callback is invoked. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_URI_SANS_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "foo://bar" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_URI_SANS_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, using s2n PEM Parser. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but max chain depth is exceeded*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); + + EXPECT_EQUAL(0, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test post-2038 certificate expiration. + * + * The expired certificate should fail as untrusted. This test fails on + * platforms where time_t is 4 bytes because representing dates past 2038 as + * unix seconds overflows the time_t. + */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + int expected_errno = S2N_ERR_CERT_EXPIRED; + /* In some cases validation may fail with a less specific error due to + * issues with large dates, but validation does always fail. */ + if (!s2n_supports_large_time_t()) { + expected_errno = S2N_ERR_SAFETY; + } else if (!s2n_libcrypto_supports_2050()) { + expected_errno = S2N_ERR_CERT_UNTRUSTED; + } + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + expected_errno); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test pre-2038 certificate expiration + * + * After the expiration date, the certificate should fail as untrusted. This + * test uses pre-2038 dates for 32 bit time_t concerns + */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_EXPIRED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + }; + + /* test validator in safe mode, with properly configured trust store, but the server's end-entity cert is invalid. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + /* alter a random byte in the certificate to make it invalid */ + size_t corrupt_index = 200; + EXPECT_TRUE(chain_len > corrupt_index); + chain_data[corrupt_index] = (uint8_t) (chain_data[corrupt_index] << 2); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but host isn't trusted*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but host isn't trusted, using s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name validation succeeds */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name validation succeeds, using s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_pkey_free(&public_key_out); + + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via alternative name validation succeeds + * note: in this case, we don't have valid certs but it's enough to make sure we are properly pulling alternative names + * from the certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via alternative name validation fails, and + * no Common Name validation happens as DNS alternative name is present. note: in this case, we don't have valid certs but + * it's enough to make sure we are properly validating alternative names and common name.*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Name matches CN on certificate (CN=localhost), but no match in alternative names */ + struct host_verify_data verify_data = { + .name = "localhost", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(0, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via common name validation succeeds, + * non-dns alternative names are ignored. note: in this case, we don't have valid certs but it's enough to make sure + * we are properly validating alternative names and common name.*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Name matches CN on certificate (CN=localhost) */ + struct host_verify_data verify_data = { + .name = "localhost", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_NO_DNS_SANS_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; +#if S2N_OCSP_STAPLING_SUPPORTED + /* Test valid OCSP date range */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range without nextUpdate field */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_NO_NEXT_UPDATE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_not_expired_ocsp_timestamp, NULL); + + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range, but with s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + s2n_pkey_free(&public_key_out); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /** + * Test invalid OCSP date range post-2038 + * + * After the "Next Update" time in the OCSP response, the certificate should + * fail as expired. + */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + if (s2n_supports_large_time_t()) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_EXPIRED); + } else { + /* fetch_expired_after_ocsp_timestamp is in 2200 which is not + * representable for 32 bit time_t's. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_SAFETY); + } + + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /** + * Test invalid OCSP date range pre-2038 + * + * This test sets the clock time to be after the expiration date of the cert + * and after the "Next Update" field of the OCSP response. + */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); + + DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_EXPIRED); + + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + } + + /* Test invalid OCSP date range (thisupdate is off) */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_invalid_before_ocsp_timestamp, NULL); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_INVALID); + + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test valid OCSP date range, but the data itself is untrusted */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + /* flip a byte right in the middle of the cert */ + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + raw_data[800] = (uint8_t) (raw_data[800] + 1); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range and data, but the stapled response was signed with an issuer not in the chain of trust */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test OCSP response signed by the correct responder certificate, but not for the requested certificate. + * (So this would be a completely valid response to a different OCSP request for the other certificate.) */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test OCSP response signed by the wrong responder certificate, but the requested certificate was signed. + * (however this incorrect OCSP responder certificate is a valid OCSP responder for some other case and chains + * to a trusted root). Thus, this response is not valid for any request. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_WRONG_SIGNER_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test OCSP response status is revoked */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_REVOKED_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_REVOKED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /** + * Test OCSP validation at various offsets from update times. + * + * libcrypto ASN1 comparison calculates differences in terms of days and seconds, + * so try mocking the system time to a collection of more than day & less + * than day differences. + * The T's in the below diagram represent test cases that should fail + * The F's represent test cases that should succeed + * S2N_ERR_CERT_INVALID S2N_ERR_CERT_EXPIRED + * | | | | + * v v v v + * F F T T T T F F + * v v v v v v v v + * <----------|---|---|----------------------------|---|---|---> + * ^ ^ + * this update next update + * |---| + * one day + * + * If this test is failing make sure that the this_update_timestamp_nanoseconds + * matches the actual timestamp of ocsp_response_early_expire.der + * + * openssl ocsp -respin ocsp_response_early_expire.der -text -noverify | grep "This Update" + */ + { + /* Apr 28 22:11:56 2023 GMT */ + uint64_t this_update_timestamp_nanoseconds = (uint64_t) 1682719916 * ONE_SEC_IN_NANOS; + + /* Apr 28 22:11:56 2023 GMT */ + uint64_t next_update_timestamp_nanoseconds = (uint64_t) 2082838316 * ONE_SEC_IN_NANOS; + + uint64_t one_hour_nanoseconds = (uint64_t) 60 * 60 * ONE_SEC_IN_NANOS; + uint64_t one_day_nanoseconds = 24 * one_hour_nanoseconds; + + struct { + uint64_t time; + int result; + } test_cases[] = { + { + .time = this_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_CERT_INVALID, + }, + { + .time = this_update_timestamp_nanoseconds - one_hour_nanoseconds, + .result = S2N_ERR_CERT_INVALID, + }, + { + .time = this_update_timestamp_nanoseconds + one_hour_nanoseconds, + .result = S2N_ERR_OK, + }, + { + .time = this_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds - one_hour_nanoseconds, + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds + one_hour_nanoseconds, + .result = S2N_ERR_CERT_EXPIRED, + }, + { + .time = next_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_CERT_EXPIRED, + } + }; + + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + /** + * keep track of the old clock, because we want cert validation to happen + * with the default system clock, and not the "mock_time" clock. + */ + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + uint64_t timestamp_nanoseconds = test_cases[i].time; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, mock_time, ×tamp_nanoseconds)); + + DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + if (test_cases[i].result != S2N_ERR_OK) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + test_cases[i].result); + } else { + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + } + + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + }; + }; +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ + /* test validator in safe mode, with default host name validator. Connection server name matches alternative name on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server name matches wildcard alternative name on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "test.localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server does not match alternative names on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "127.0.0.1")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server matches the IPv6 address on the certificate. */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_IP_V6_LO_RSA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + /* the provided hostname should be an empty string */ + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "::1" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem( + connection, + S2N_IP_V6_LO_RSA_CERT, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + }; + + /* Server matches the empty string when there are no usable identifiers in the cert. */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_WITHOUT_CN_RSA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + /* the provided hostname should be an empty string */ + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem( + connection, + S2N_WITHOUT_CN_RSA_CERT, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + }; + + /* test validator in safe mode, with default host name validator. No connection server name supplied. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_INVALID_HOSTNAME); + + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test trust store in a configuration can handle invalid PEM without crashing */ + { + struct s2n_config *cfg = s2n_config_new(); + s2n_config_add_pem_to_trust_store(cfg, ""); + s2n_config_free(cfg); + /* Expect no crash. */ + }; + + /* Test one trailing byte in cert validator */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct s2n_stuffer chain_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + s2n_stuffer_free(&chain_stuffer); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* Test more trailing bytes in cert validator for negative case */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct s2n_stuffer chain_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, + connection, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_DECODE_CERTIFICATE); + + s2n_stuffer_free(&chain_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* Test unknown curve in cert validator for negative case */ + if (s2n_libcrypto_is_awslc()) { + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_tls12_security_policy(conn)); + + char pem_str[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_BRAINPOOL_CURVE_CERT, pem_str, sizeof(pem_str))); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, pem_str)); + + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &message, chain_and_key)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&message, 3)); + + uint32_t chain_len = s2n_stuffer_data_available(&message); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&message, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, + conn, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_DECODE_CERTIFICATE); + }; + + /* Ensure that certs after the leaf cert can have an arbitrary number of trailing bytes */ + { + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + DEFER_CLEANUP(struct s2n_stuffer one_trailing_byte_chain = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&one_trailing_byte_chain, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t one_trailing_byte_chain_len = s2n_stuffer_data_available(&one_trailing_byte_chain); + uint8_t *one_trailing_byte_chain_data = s2n_stuffer_raw_read(&one_trailing_byte_chain, + one_trailing_byte_chain_len); + + DEFER_CLEANUP(struct s2n_stuffer four_trailing_bytes_chain = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&four_trailing_bytes_chain, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t four_trailing_bytes_chain_len = s2n_stuffer_data_available(&four_trailing_bytes_chain); + uint8_t *four_trailing_bytes_chain_data = s2n_stuffer_raw_read(&four_trailing_bytes_chain, + four_trailing_bytes_chain_len); + + DEFER_CLEANUP(struct s2n_stuffer chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&chain_stuffer, S2N_MAX_TEST_PEM_SIZE * 2)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, one_trailing_byte_chain_data, + one_trailing_byte_chain_len)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&chain_stuffer, four_trailing_bytes_chain_data, + four_trailing_bytes_chain_len)); + + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key)); + + EXPECT_EQUAL(sk_X509_num(validator.cert_chain_from_wire), 2); + }; + + /* Test validator trusts a SHA-1 signature in a certificate chain if certificate validation is off */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + + EXPECT_NOT_NULL(connection); + connection->actual_protocol_version = S2N_TLS13; + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* This cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + validator.skip_cert_validation = 1; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_config_free(config); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test validator does not trust a SHA-1 signature in a certificate chain */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + + EXPECT_NOT_NULL(connection); + connection->actual_protocol_version = S2N_TLS13; + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_config_free(config); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Validator fails if cert chain is empty */ + { + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_OK(s2n_connection_set_tls12_security_policy(connection)); + + struct s2n_pkey public_key = { 0 }; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, NULL, 0, &pkey_type, + &public_key), + S2N_ERR_NO_CERT_FOUND); + } + + /* Test trust store can be wiped */ + { + /* Wipe new s2n_config, which is initialized with certs from the system default locations. */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe repeatedly without crash */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe after setting verification location */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Set verification location after wipe */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe after adding PEM */ + { + struct s2n_config *cfg = s2n_config_new(); + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + free(cert_chain); + s2n_config_free(cfg); + }; + + /* Add PEM after wipe */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + free(cert_chain); + s2n_config_free(cfg); + }; + }; + + /* Ensure that non-root certificates added to the trust store are trusted */ + { + const char *non_root_cert_path = S2N_RSA_2048_PKCS1_LEAF_CERT; + +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + /* Ensure that the test certificate isn't self-signed, and is therefore not a root. + * + * The X509_get_extension_flags API wasn't added to OpenSSL until 1.1.0. + */ + { + const char *non_root_key_path = S2N_RSA_2048_PKCS1_KEY; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, non_root_cert_path, non_root_key_path)); + struct s2n_cert *cert = NULL; + EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &cert, 0)); + EXPECT_NOT_NULL(cert); + + /* Use the s2n_cert to convert the PEM to ASN.1. */ + const uint8_t *asn1_data = NULL; + uint32_t asn1_len = 0; + EXPECT_SUCCESS(s2n_cert_get_der(cert, &asn1_data, &asn1_len)); + EXPECT_NOT_NULL(asn1_data); + + /* Parse the ASN.1 data with the libcrypto */ + DEFER_CLEANUP(X509 *x509 = d2i_X509(NULL, &asn1_data, asn1_len), X509_free_pointer); + EXPECT_NOT_NULL(x509); + + /* Ensure that the self-signed flag isn't set */ + uint32_t extension_flags = X509_get_extension_flags(x509); + EXPECT_EQUAL(extension_flags & EXFLAG_SS, 0); + } +#endif + + /* Test s2n_config_set_verification_ca_location */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, non_root_cert_path, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + } + + /* Test s2n_config_add_pem_to_trust_store */ + { + char non_root_cert_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(non_root_cert_path, non_root_cert_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, non_root_cert_pem)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + } + + /* Test system trust store + * + * This test uses the SSL_CERT_FILE environment variable to override the system trust store + * location, which isn't supported by LibreSSL. + */ + if (!s2n_libcrypto_is_libressl()) { + /* Override the system cert file with the non-root test cert. */ + EXPECT_SUCCESS(setenv("SSL_CERT_FILE", non_root_cert_path, 1)); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "20240501")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + + EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); + } + } + + /* Test that CAs must comply with cert preferences */ + { + uint8_t invalid_root_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t root_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CA, &invalid_root_pem[0], &root_pem_len, + S2N_MAX_TEST_PEM_SIZE)); + + uint8_t chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t chain_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_MIXED_CHAIN_CERTS, &chain_pem[0], &chain_pem_len, + S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_security_policy security_policy_not_local = security_policy_20250429; + security_policy_not_local.certificate_preferences_apply_locally = false; + + /* when the peer sends the full chain with a non-compliant CA, verification fails when reading in the certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &security_policy_not_local; + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + /* Failed while processing/reading in the cert chain */ + EXPECT_TRUE(validator.state == INIT); + }; + + /* when the peer sends only compliant certs from a non-compliant CA, + * validation fails on the local trust store + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, (char *) &invalid_root_pem[0])); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &security_policy_not_local; + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + + /* `chain_pem_len - root_pem_len`: only use the first two certs in the chain (leaf and intermediate) to + * ensure we are correctly failing on the local trust store and not the chain that the peer sent. + */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(conn, &chain_pem[0], chain_pem_len - root_pem_len, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT); + /* X509_verify_cert finished successfully */ + EXPECT_TRUE(validator.state == VALIDATED); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_validator_time_verification_test.c b/tests/unit/s2n_x509_validator_time_verification_test.c index dfc1e246839..a96b79aaa45 100644 --- a/tests/unit/s2n_x509_validator_time_verification_test.c +++ b/tests/unit/s2n_x509_validator_time_verification_test.c @@ -1,287 +1,287 @@ -#include "utils/s2n_prelude.h" -/* -* 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 "crypto/s2n_libcrypto.h" -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -bool s2n_libcrypto_supports_flag_no_check_time(); -uint64_t s2n_libcrypto_awslc_api_version(void); - -static uint8_t s2n_verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) -{ - return 1; -} - -int main(int argc, char *argv[]) -{ - BEGIN_TEST(); - - /* Test the NO_CHECK_TIME flag feature probe. The flag was added to AWS-LC in API version 19. */ - if (s2n_libcrypto_is_awslc() && s2n_libcrypto_awslc_api_version() > 19) { - EXPECT_TRUE(s2n_libcrypto_supports_flag_no_check_time()); - } - - /* Test disabling x509 time validation. - * - * By default, validation should fail for certificates with invalid timestamps. However, if - * x509 time validation is disabled, validation should succeed. - * - * When time validation is disabled, s2n_config_set_wall_clock() will not set a custom time on - * the libcrypto, so this function cannot be used to set a fake time for testing. Instead, the - * test certificates themselves contain invalid timestamps. - */ - { - /* clang-format off */ - struct { - const char *cert_pem_path; - const char *key_pem_path; - bool disable_x509_time_validation; - s2n_error expected_error; - } test_cases[] = { - /* Validation should fail for a certificate that is not yet valid. */ - { - .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, - .key_pem_path = S2N_NOT_YET_VALID_KEY, - .disable_x509_time_validation = false, - .expected_error = S2N_ERR_CERT_NOT_YET_VALID, - }, - - /* Validation should succeed for a certificate that is not yet valid when time - * validation is disabled. - */ - { - .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, - .key_pem_path = S2N_NOT_YET_VALID_KEY, - .disable_x509_time_validation = true, - .expected_error = S2N_ERR_OK, - }, - - /* Validation should fail for an expired certificate. */ - { - .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, - .key_pem_path = S2N_EXPIRED_KEY, - .disable_x509_time_validation = false, - .expected_error = S2N_ERR_CERT_EXPIRED, - }, - - /* Validation should succeed for an expired certificate when time validation is - * disabled. - */ - { - .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, - .key_pem_path = S2N_EXPIRED_KEY, - .disable_x509_time_validation = true, - .expected_error = S2N_ERR_OK, - }, - }; - /* clang-format on */ - - /* s2n_x509_validator test */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(test_cases[i].cert_pem_path, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - if (test_cases[i].disable_x509_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - } - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, test_cases[i].cert_pem_path, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, - &public_key_out); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_OK(ret); - } else { - EXPECT_ERROR_WITH_ERRNO(ret, expected_error); - } - } - - /* Self-talk: Disable validity period validation on client for server auth */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, test_cases[i].cert_pem_path, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - - if (test_cases[i].disable_x509_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - } - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); - - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(client_conn); - EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - - /* Self-talk: Disable validity period validation on server for client auth */ - for (int i = 0; i < s2n_array_len(test_cases); i++) { - DEFER_CLEANUP(struct s2n_cert_chain_and_key *default_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(server_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, test_cases[i].cert_pem_path, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); - - if (test_cases[i].disable_x509_time_validation) { - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(server_config)); - } - - /* Disable verify host validation for client auth */ - EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, s2n_verify_host_accept_everything, NULL)); - - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); - EXPECT_NOT_NULL(server_conn); - EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); - EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, - test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); - - DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(client_config); - EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); - EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); - EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); - - 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)); - - int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); - - s2n_error expected_error = test_cases[i].expected_error; - if (expected_error == S2N_ERR_OK) { - EXPECT_SUCCESS(ret); - } else { - EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); - } - } - } - - /* Ensure that certificate validation can fail for reasons other than time validation when time - * validation is disabled. - */ - for (int trust_cert = 0; trust_cert <= 1; trust_cert += 1) { - DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); - s2n_x509_trust_store_init_empty(&trust_store); - - if (trust_cert) { - char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; - EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); - EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); - } - - DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); - EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); - - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); - EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); - EXPECT_NOT_NULL(conn); - EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); - EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); - - DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); - EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); - uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); - uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); - EXPECT_NOT_NULL(chain_data); - - DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); - EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); - s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; - - s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, - &public_key_out); - - if (trust_cert) { - EXPECT_OK(ret); - } else { - /* If the certificate was not added to the trust store, validation should fail even - * though time validation was disabled. - */ - EXPECT_ERROR_WITH_ERRNO(ret, S2N_ERR_CERT_UNTRUSTED); - } - } - - END_TEST(); -} +#include "utils/s2n_prelude.h" +/* +* 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 "crypto/s2n_libcrypto.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +bool s2n_libcrypto_supports_flag_no_check_time(); +uint64_t s2n_libcrypto_awslc_api_version(void); + +static uint8_t s2n_verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Test the NO_CHECK_TIME flag feature probe. The flag was added to AWS-LC in API version 19. */ + if (s2n_libcrypto_is_awslc() && s2n_libcrypto_awslc_api_version() > 19) { + EXPECT_TRUE(s2n_libcrypto_supports_flag_no_check_time()); + } + + /* Test disabling x509 time validation. + * + * By default, validation should fail for certificates with invalid timestamps. However, if + * x509 time validation is disabled, validation should succeed. + * + * When time validation is disabled, s2n_config_set_wall_clock() will not set a custom time on + * the libcrypto, so this function cannot be used to set a fake time for testing. Instead, the + * test certificates themselves contain invalid timestamps. + */ + { + /* clang-format off */ + struct { + const char *cert_pem_path; + const char *key_pem_path; + bool disable_x509_time_validation; + s2n_error expected_error; + } test_cases[] = { + /* Validation should fail for a certificate that is not yet valid. */ + { + .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, + .key_pem_path = S2N_NOT_YET_VALID_KEY, + .disable_x509_time_validation = false, + .expected_error = S2N_ERR_CERT_NOT_YET_VALID, + }, + + /* Validation should succeed for a certificate that is not yet valid when time + * validation is disabled. + */ + { + .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, + .key_pem_path = S2N_NOT_YET_VALID_KEY, + .disable_x509_time_validation = true, + .expected_error = S2N_ERR_OK, + }, + + /* Validation should fail for an expired certificate. */ + { + .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, + .key_pem_path = S2N_EXPIRED_KEY, + .disable_x509_time_validation = false, + .expected_error = S2N_ERR_CERT_EXPIRED, + }, + + /* Validation should succeed for an expired certificate when time validation is + * disabled. + */ + { + .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, + .key_pem_path = S2N_EXPIRED_KEY, + .disable_x509_time_validation = true, + .expected_error = S2N_ERR_OK, + }, + }; + /* clang-format on */ + + /* s2n_x509_validator test */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(test_cases[i].cert_pem_path, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, test_cases[i].cert_pem_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, + &public_key_out); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(ret); + } else { + EXPECT_ERROR_WITH_ERRNO(ret, expected_error); + } + } + + /* Self-talk: Disable validity period validation on client for server auth */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, test_cases[i].cert_pem_path, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + + /* Self-talk: Disable validity period validation on server for client auth */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *default_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, test_cases[i].cert_pem_path, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(server_config)); + } + + /* Disable verify host validation for client auth */ + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, s2n_verify_host_accept_everything, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20240501")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + 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)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + /* Ensure that certificate validation can fail for reasons other than time validation when time + * validation is disabled. + */ + for (int trust_cert = 0; trust_cert <= 1; trust_cert += 1) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + if (trust_cert) { + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + } + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20240501")); + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, + &public_key_out); + + if (trust_cert) { + EXPECT_OK(ret); + } else { + /* If the certificate was not added to the trust store, validation should fail even + * though time validation was disabled. + */ + EXPECT_ERROR_WITH_ERRNO(ret, S2N_ERR_CERT_UNTRUSTED); + } + } + + END_TEST(); +} diff --git a/tests/viz/s2n_state_machine_viz.c b/tests/viz/s2n_state_machine_viz.c index 826d69a4627..e138cc6b081 100644 --- a/tests/viz/s2n_state_machine_viz.c +++ b/tests/viz/s2n_state_machine_viz.c @@ -1,115 +1,115 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "tls/s2n_handshake_io.c" - -#define MAX_STATE_TYPE (APPLICATION_DATA + 1) - -struct state { - const char *name; - int children[MAX_STATE_TYPE]; -}; - -int traverse_handshakes(message_type_t hs_table[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH], const char *version, const char *destination) -{ - FILE *out; - char cmd[255]; - const char *dot = "dot -Tsvg > %s"; - snprintf(cmd, sizeof(cmd), dot, destination); - - out = popen(cmd, "w"); - if (!out) { - fprintf(stdout, "Failed to run graphviz. Check if you have graphviz installed?\n"); - return 1; - } - - struct state states[MAX_STATE_TYPE] = { 0 }; - - /* generate struct for all states */ - struct state initial = { .name = "INITIAL" }; - - for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { - struct state node = { .name = message_names[i] }; - states[i] = node; - } - - /* traverse handshakes */ - for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { - /* to detect client_hello from empty 0-init value, we check for the following value */ - if (!hs_table[i][1]) - continue; - - for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { - message_type_t msg = hs_table[i][j]; - if (j > 0 && !msg) - continue; - - /* register oneself as parent's child */ - if (j == 0) { - initial.children[msg] = 1; - } else { - states[hs_table[i][j - 1]].children[msg] = 1; - } - } - } - - /* find associated descendents of this node */ - #define print_children(state) \ - for (int c = 0; c < MAX_STATE_TYPE; c++) { \ - if (!state.children[c]) continue; \ - fprintf(out, " %s -> %s\n", state.name, states[c].name); \ - } - - /* produce dot format header */ - fprintf(out, "digraph G {\n"); - fprintf(out, " labelloc=\"t\";\n"); - fprintf(out, " label=<s2n TLS %s State Machine>\n", version); - - /* output initial root node */ - print_children(initial); - - /* iterate thru all possible nodes */ - for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { - print_children(states[i]); - } - - /* produce dot format footer */ - fprintf(out, " INITIAL [shape=diamond];\n"); - fprintf(out, " APPLICATION_DATA [shape=square];\n"); - fprintf(out, "}"); - - pclose(out); - - return 0; -} - -/* - * This program generates a visualization of the s2n TLS state machine. - * It does so by generating a directed acyclic graph, before piping - * a dot graph format output to graphviz to generate svg files in the - * document image directory. - */ - -int main(int argc, char **argv) -{ - fprintf(stdout, "Generating graphs for s2n TLS state machine...\n"); - traverse_handshakes(handshakes, "1.2", "../../docs/images/tls12_state_machine.svg"); - traverse_handshakes(tls13_handshakes, "1.3", "../../docs/images/tls13_state_machine.svg"); - fprintf(stdout, "Done.\n"); -} +#include "utils/s2n_prelude.h" +/* + * 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 "tls/s2n_handshake_io.c" + +#define MAX_STATE_TYPE (APPLICATION_DATA + 1) + +struct state { + const char *name; + int children[MAX_STATE_TYPE]; +}; + +int traverse_handshakes(message_type_t hs_table[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH], const char *version, const char *destination) +{ + FILE *out; + char cmd[255]; + const char *dot = "dot -Tsvg > %s"; + snprintf(cmd, sizeof(cmd), dot, destination); + + out = popen(cmd, "w"); + if (!out) { + fprintf(stdout, "Failed to run graphviz. Check if you have graphviz installed?\n"); + return 1; + } + + struct state states[MAX_STATE_TYPE] = { 0 }; + + /* generate struct for all states */ + struct state initial = { .name = "INITIAL" }; + + for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { + struct state node = { .name = message_names[i] }; + states[i] = node; + } + + /* traverse handshakes */ + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + /* to detect client_hello from empty 0-init value, we check for the following value */ + if (!hs_table[i][1]) + continue; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + message_type_t msg = hs_table[i][j]; + if (j > 0 && !msg) + continue; + + /* register oneself as parent's child */ + if (j == 0) { + initial.children[msg] = 1; + } else { + states[hs_table[i][j - 1]].children[msg] = 1; + } + } + } + + /* find associated descendents of this node */ + #define print_children(state) \ + for (int c = 0; c < MAX_STATE_TYPE; c++) { \ + if (!state.children[c]) continue; \ + fprintf(out, " %s -> %s\n", state.name, states[c].name); \ + } + + /* produce dot format header */ + fprintf(out, "digraph G {\n"); + fprintf(out, " labelloc=\"t\";\n"); + fprintf(out, " label=<s2n TLS %s State Machine>\n", version); + + /* output initial root node */ + print_children(initial); + + /* iterate thru all possible nodes */ + for (int i = CLIENT_HELLO; i < MAX_STATE_TYPE; i++) { + print_children(states[i]); + } + + /* produce dot format footer */ + fprintf(out, " INITIAL [shape=diamond];\n"); + fprintf(out, " APPLICATION_DATA [shape=square];\n"); + fprintf(out, "}"); + + pclose(out); + + return 0; +} + +/* + * This program generates a visualization of the s2n TLS state machine. + * It does so by generating a directed acyclic graph, before piping + * a dot graph format output to graphviz to generate svg files in the + * document image directory. + */ + +int main(int argc, char **argv) +{ + fprintf(stdout, "Generating graphs for s2n TLS state machine...\n"); + traverse_handshakes(handshakes, "1.2", "../../docs/images/tls12_state_machine.svg"); + traverse_handshakes(tls13_handshakes, "1.3", "../../docs/images/tls13_state_machine.svg"); + fprintf(stdout, "Done.\n"); +} diff --git a/tls/extensions/s2n_client_psk.c b/tls/extensions/s2n_client_psk.c index b61718cea89..8e8a29b8917 100644 --- a/tls/extensions/s2n_client_psk.c +++ b/tls/extensions/s2n_client_psk.c @@ -1,422 +1,422 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_client_psk.h" - -#include - -#include "crypto/s2n_hash.h" -#include "tls/s2n_psk.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define SIZE_OF_BINDER_SIZE sizeof(uint8_t) -#define SIZE_OF_BINDER_LIST_SIZE sizeof(uint16_t) - -/* To avoid a DoS attack triggered by decrypting too many session tickets, - * set a limit on the number of tickets we will attempt to decrypt before giving up. - * We may want to make this configurable someday, but just set a reasonable maximum for now. */ -#define MAX_REJECTED_TICKETS 3 - -static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out); -static int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); -static int s2n_client_psk_is_missing(struct s2n_connection *conn); - -const s2n_extension_type s2n_client_psk_extension = { - .iana_value = TLS_EXTENSION_PRE_SHARED_KEY, - .minimum_version = S2N_TLS13, - .is_response = false, - .send = s2n_client_psk_send, - .recv = s2n_client_psk_recv, - .should_send = s2n_client_psk_should_send, - .if_missing = s2n_client_psk_is_missing, -}; - -int s2n_client_psk_is_missing(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* If the PSK extension is missing, we must not have received - * a request for early data. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# When a PSK is used and early data is allowed for that PSK, the client - *# can send Application Data in its first flight of messages. If the - *# client opts to do so, it MUST supply both the "pre_shared_key" and - *# "early_data" extensions. - */ - POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_UNSUPPORTED_EXTENSION); - return S2N_SUCCESS; -} - -bool s2n_client_psk_should_send(struct s2n_connection *conn) -{ - if (!conn || !conn->secure) { - return false; - } - - /* If this is NOT the second ClientHello after a retry, then all PSKs are viable. - * Send the extension if any PSKs are configured. - */ - if (!s2n_is_hello_retry_handshake(conn)) { - return conn->psk_params.psk_list.len > 0; - } - - /* If this is the second ClientHello after a retry, then only PSKs that match the cipher suite - * are viable. Only send the extension if at least one configured PSK matches the cipher suite. - */ - for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { - struct s2n_psk *psk = NULL; - if (s2n_result_is_ok(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)) - && psk != NULL - && conn->secure->cipher_suite->prf_alg == psk->hmac_alg) { - return true; - } - } - return false; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11.1 - *# The "obfuscated_ticket_age" - *# field of each PskIdentity contains an obfuscated version of the - *# ticket age formed by taking the age in milliseconds and adding the - *# "ticket_age_add" value that was included with the ticket (see - *# Section 4.6.1), modulo 2^32. -*/ -static S2N_RESULT s2n_generate_obfuscated_ticket_age(struct s2n_psk *psk, uint64_t current_time, uint32_t *output) -{ - RESULT_ENSURE_REF(psk); - RESULT_ENSURE_MUT(output); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# For identities - *# established externally, an obfuscated_ticket_age of 0 SHOULD be - *# used, - **/ - if (psk->type == S2N_PSK_TYPE_EXTERNAL) { - *output = 0; - return S2N_RESULT_OK; - } - - RESULT_ENSURE(current_time >= psk->ticket_issue_time, S2N_ERR_SAFETY); - - /* Calculate ticket age */ - uint64_t ticket_age_in_nanos = current_time - psk->ticket_issue_time; - - /* Convert ticket age to milliseconds */ - uint64_t ticket_age_in_millis = ticket_age_in_nanos / ONE_MILLISEC_IN_NANOS; - RESULT_ENSURE(ticket_age_in_millis <= UINT32_MAX, S2N_ERR_SAFETY); - - /* Add the ticket_age_add value to the ticket age in milliseconds. The resulting uint32_t value - * may wrap, resulting in the modulo 2^32 operation. */ - *output = ticket_age_in_millis + psk->ticket_age_add; - - return S2N_RESULT_OK; -} - -static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - struct s2n_psk_parameters *psk_params = &conn->psk_params; - struct s2n_array *psk_list = &psk_params->psk_list; - - struct s2n_stuffer_reservation identity_list_size; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &identity_list_size)); - - uint16_t binder_list_size = SIZE_OF_BINDER_LIST_SIZE; - - for (size_t i = 0; i < psk_list->len; i++) { - struct s2n_psk *psk = NULL; - POSIX_GUARD_RESULT(s2n_array_get(psk_list, i, (void **) &psk)); - POSIX_ENSURE_REF(psk); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 - *# In addition, in its updated ClientHello, the client SHOULD NOT offer - *# any pre-shared keys associated with a hash other than that of the - *# selected cipher suite. - */ - if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { - continue; - } - - /* Write the identity */ - POSIX_GUARD(s2n_stuffer_write_uint16(out, psk->identity.size)); - POSIX_GUARD(s2n_stuffer_write(out, &psk->identity)); - - /* Write obfuscated ticket age */ - uint32_t obfuscated_ticket_age = 0; - uint64_t current_time = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, ¤t_time)); - POSIX_GUARD_RESULT(s2n_generate_obfuscated_ticket_age(psk, current_time, &obfuscated_ticket_age)); - POSIX_GUARD(s2n_stuffer_write_uint32(out, obfuscated_ticket_age)); - - /* Calculate binder size */ - uint8_t hash_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); - binder_list_size += hash_size + SIZE_OF_BINDER_SIZE; - } - - POSIX_GUARD(s2n_stuffer_write_vector_size(&identity_list_size)); - - /* Calculating the binders requires a complete ClientHello, and at this point - * the extension size, extension list size, and message size are all blank. - * - * We'll write placeholder data to ensure the extension and extension list sizes - * are calculated correctly, then rewrite the binders with real data later. */ - psk_params->binder_list_size = binder_list_size; - POSIX_GUARD(s2n_stuffer_skip_write(out, binder_list_size)); - - return S2N_SUCCESS; -} - -/* Find the first of the server's PSK identities that matches the client's identities. - * This method compares all server identities to all client identities. - * - * While both the client's identities and whether a match was found are public, we should make an attempt - * to keep the server's identities a secret. We will make comparisons to the server's identities constant - * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). - * - * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, - * and an attacker could probably guess the server's known identities just by observing the public identities - * sent by clients. - */ -static S2N_RESULT s2n_select_external_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(client_identity_list); - - struct s2n_array *server_psks = &conn->psk_params.psk_list; - conn->psk_params.chosen_psk = NULL; - - for (size_t i = 0; i < server_psks->len; i++) { - struct s2n_psk *server_psk = NULL; - RESULT_GUARD(s2n_array_get(server_psks, i, (void **) &server_psk)); - RESULT_ENSURE_REF(server_psk); - - struct s2n_offered_psk client_psk = { 0 }; - uint16_t wire_index = 0; - - RESULT_GUARD_POSIX(s2n_offered_psk_list_reread(client_identity_list)); - while (s2n_offered_psk_list_has_next(client_identity_list)) { - RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); - uint16_t compare_size = S2N_MIN(client_psk.identity.size, server_psk->identity.size); - if (s2n_constant_time_equals(client_psk.identity.data, server_psk->identity.data, compare_size) - & (client_psk.identity.size == server_psk->identity.size) - & (conn->psk_params.chosen_psk == NULL)) { - conn->psk_params.chosen_psk = server_psk; - conn->psk_params.chosen_psk_wire_index = wire_index; - } - wire_index++; - }; - } - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_select_resumption_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(client_identity_list); - - struct s2n_offered_psk client_psk = { 0 }; - conn->psk_params.chosen_psk = NULL; - - uint8_t rejected_count = 0; - while (s2n_offered_psk_list_has_next(client_identity_list) && (rejected_count < MAX_REJECTED_TICKETS)) { - RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); - /* Select the first resumption PSK that can be decrypted */ - if (s2n_offered_psk_list_choose_psk(client_identity_list, &client_psk) == S2N_SUCCESS) { - return S2N_RESULT_OK; - } - rejected_count++; - } - - RESULT_BAIL(S2N_ERR_INVALID_SESSION_TICKET); -} - -static S2N_RESULT s2n_client_psk_recv_identity_list(struct s2n_connection *conn, struct s2n_stuffer *wire_identities_in) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_REF(wire_identities_in); - - struct s2n_offered_psk_list identity_list = { - .conn = conn, - .wire_data = *wire_identities_in, - }; - - if (conn->config->psk_selection_cb) { - RESULT_GUARD_POSIX(conn->config->psk_selection_cb(conn, conn->config->psk_selection_ctx, &identity_list)); - } else if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { - RESULT_GUARD(s2n_select_external_psk(conn, &identity_list)); - } else if (conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION) { - RESULT_GUARD(s2n_select_resumption_psk(conn, &identity_list)); - } - - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_client_psk_recv_binder_list(struct s2n_connection *conn, struct s2n_blob *partial_client_hello, - struct s2n_stuffer *wire_binders_in) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(wire_binders_in); - - uint16_t wire_index = 0; - while (s2n_stuffer_data_available(wire_binders_in) > 0) { - uint8_t wire_binder_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(wire_binders_in, &wire_binder_size)); - - uint8_t *wire_binder_data = NULL; - RESULT_ENSURE_REF(wire_binder_data = s2n_stuffer_raw_read(wire_binders_in, wire_binder_size)); - - struct s2n_blob wire_binder = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&wire_binder, wire_binder_data, wire_binder_size)); - - if (wire_index == conn->psk_params.chosen_psk_wire_index) { - RESULT_GUARD_POSIX(s2n_psk_verify_binder(conn, conn->psk_params.chosen_psk, - partial_client_hello, &wire_binder)); - return S2N_RESULT_OK; - } - wire_index++; - } - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); -} - -static S2N_RESULT s2n_client_psk_recv_identities(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - RESULT_ENSURE_REF(conn); - - uint16_t identity_list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &identity_list_size)); - - uint8_t *identity_list_data = NULL; - RESULT_ENSURE_REF(identity_list_data = s2n_stuffer_raw_read(extension, identity_list_size)); - - struct s2n_blob identity_list_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&identity_list_blob, identity_list_data, identity_list_size)); - - struct s2n_stuffer identity_list = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&identity_list, &identity_list_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&identity_list, identity_list_blob.size)); - - return s2n_client_psk_recv_identity_list(conn, &identity_list); -} - -static S2N_RESULT s2n_client_psk_recv_binders(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - RESULT_ENSURE_REF(conn); - - uint16_t binder_list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &binder_list_size)); - - uint8_t *binder_list_data = NULL; - RESULT_ENSURE_REF(binder_list_data = s2n_stuffer_raw_read(extension, binder_list_size)); - - struct s2n_blob binder_list_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&binder_list_blob, binder_list_data, binder_list_size)); - - struct s2n_stuffer binder_list = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&binder_list, &binder_list_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&binder_list, binder_list_blob.size)); - - /* Record the ClientHello message up to but not including the binder list. - * This is required to calculate the binder for the chosen PSK. */ - struct s2n_blob partial_client_hello = { 0 }; - const struct s2n_stuffer *client_hello = &conn->handshake.io; - uint32_t binders_size = binder_list_blob.size + SIZE_OF_BINDER_LIST_SIZE; - RESULT_ENSURE_GTE(client_hello->write_cursor, binders_size); - uint32_t partial_client_hello_size = client_hello->write_cursor - binders_size; - RESULT_GUARD_POSIX(s2n_blob_slice(&client_hello->blob, &partial_client_hello, 0, partial_client_hello_size)); - - return s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &binder_list); -} - -int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - POSIX_ENSURE_REF(conn); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# The "pre_shared_key" extension MUST be the last extension in the - *# ClientHello (this facilitates implementation as described below). - *# Servers MUST check that it is the last extension and otherwise fail - *# the handshake with an "illegal_parameter" alert. - */ - s2n_extension_type_id psk_ext_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); - POSIX_ENSURE_NE(conn->client_hello.extensions.count, 0); - uint16_t last_wire_index = conn->client_hello.extensions.count - 1; - uint16_t extension_wire_index = conn->client_hello.extensions.parsed_extensions[psk_ext_id].wire_index; - POSIX_ENSURE(extension_wire_index == last_wire_index, S2N_ERR_UNSUPPORTED_EXTENSION); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.9 - *# If clients offer "pre_shared_key" without a "psk_key_exchange_modes" extension, - *# servers MUST abort the handshake. - * - * We can safely do this check here because s2n_client_psk is - * required to be the last extension sent in the list. - */ - s2n_extension_type_id psk_ke_mode_ext_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &psk_ke_mode_ext_id)); - POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, psk_ke_mode_ext_id), S2N_ERR_MISSING_EXTENSION); - - if (conn->psk_params.psk_ke_mode == S2N_PSK_DHE_KE) { - s2n_extension_type_id key_share_ext_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_ext_id)); - /* A key_share extension must have been received in order to use a pre-shared key - * in (EC)DHE key exchange mode. - */ - POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, key_share_ext_id), S2N_ERR_MISSING_EXTENSION); - } else { - /* s2n currently only supports pre-shared keys in (EC)DHE key exchange mode. If we receive keys with any other - * exchange mode we fall back to a full handshake. - */ - return S2N_SUCCESS; - } - - if (s2n_result_is_error(s2n_client_psk_recv_identities(conn, extension))) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# If no acceptable PSKs are found, the server SHOULD perform a non-PSK - *# handshake if possible. - */ - conn->psk_params.chosen_psk = NULL; - } - - if (conn->psk_params.chosen_psk) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# Prior to accepting PSK key establishment, the server MUST validate - *# the corresponding binder value (see Section 4.2.11.2 below). If this - *# value is not present or does not validate, the server MUST abort the - *# handshake. - */ - POSIX_GUARD_RESULT(s2n_client_psk_recv_binders(conn, extension)); - } - - /* At this point, we have either chosen a PSK or fallen back to a full handshake. */ - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_client_psk.h" + +#include + +#include "crypto/s2n_hash.h" +#include "tls/s2n_psk.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define SIZE_OF_BINDER_SIZE sizeof(uint8_t) +#define SIZE_OF_BINDER_LIST_SIZE sizeof(uint16_t) + +/* To avoid a DoS attack triggered by decrypting too many session tickets, + * set a limit on the number of tickets we will attempt to decrypt before giving up. + * We may want to make this configurable someday, but just set a reasonable maximum for now. */ +#define MAX_REJECTED_TICKETS 3 + +static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out); +static int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); +static int s2n_client_psk_is_missing(struct s2n_connection *conn); + +const s2n_extension_type s2n_client_psk_extension = { + .iana_value = TLS_EXTENSION_PRE_SHARED_KEY, + .minimum_version = S2N_TLS13, + .is_response = false, + .send = s2n_client_psk_send, + .recv = s2n_client_psk_recv, + .should_send = s2n_client_psk_should_send, + .if_missing = s2n_client_psk_is_missing, +}; + +int s2n_client_psk_is_missing(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* If the PSK extension is missing, we must not have received + * a request for early data. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# When a PSK is used and early data is allowed for that PSK, the client + *# can send Application Data in its first flight of messages. If the + *# client opts to do so, it MUST supply both the "pre_shared_key" and + *# "early_data" extensions. + */ + POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_UNSUPPORTED_EXTENSION); + return S2N_SUCCESS; +} + +bool s2n_client_psk_should_send(struct s2n_connection *conn) +{ + if (!conn || !conn->secure) { + return false; + } + + /* If this is NOT the second ClientHello after a retry, then all PSKs are viable. + * Send the extension if any PSKs are configured. + */ + if (!s2n_is_hello_retry_handshake(conn)) { + return conn->psk_params.psk_list.len > 0; + } + + /* If this is the second ClientHello after a retry, then only PSKs that match the cipher suite + * are viable. Only send the extension if at least one configured PSK matches the cipher suite. + */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + if (s2n_result_is_ok(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)) + && psk != NULL + && conn->secure->cipher_suite->prf_alg == psk->hmac_alg) { + return true; + } + } + return false; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11.1 + *# The "obfuscated_ticket_age" + *# field of each PskIdentity contains an obfuscated version of the + *# ticket age formed by taking the age in milliseconds and adding the + *# "ticket_age_add" value that was included with the ticket (see + *# Section 4.6.1), modulo 2^32. +*/ +static S2N_RESULT s2n_generate_obfuscated_ticket_age(struct s2n_psk *psk, uint64_t current_time, uint32_t *output) +{ + RESULT_ENSURE_REF(psk); + RESULT_ENSURE_MUT(output); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# For identities + *# established externally, an obfuscated_ticket_age of 0 SHOULD be + *# used, + **/ + if (psk->type == S2N_PSK_TYPE_EXTERNAL) { + *output = 0; + return S2N_RESULT_OK; + } + + RESULT_ENSURE(current_time >= psk->ticket_issue_time, S2N_ERR_SAFETY); + + /* Calculate ticket age */ + uint64_t ticket_age_in_nanos = current_time - psk->ticket_issue_time; + + /* Convert ticket age to milliseconds */ + uint64_t ticket_age_in_millis = ticket_age_in_nanos / ONE_MILLISEC_IN_NANOS; + RESULT_ENSURE(ticket_age_in_millis <= UINT32_MAX, S2N_ERR_SAFETY); + + /* Add the ticket_age_add value to the ticket age in milliseconds. The resulting uint32_t value + * may wrap, resulting in the modulo 2^32 operation. */ + *output = ticket_age_in_millis + psk->ticket_age_add; + + return S2N_RESULT_OK; +} + +static int s2n_client_psk_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + struct s2n_psk_parameters *psk_params = &conn->psk_params; + struct s2n_array *psk_list = &psk_params->psk_list; + + struct s2n_stuffer_reservation identity_list_size; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &identity_list_size)); + + uint16_t binder_list_size = SIZE_OF_BINDER_LIST_SIZE; + + for (size_t i = 0; i < psk_list->len; i++) { + struct s2n_psk *psk = NULL; + POSIX_GUARD_RESULT(s2n_array_get(psk_list, i, (void **) &psk)); + POSIX_ENSURE_REF(psk); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. + */ + if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { + continue; + } + + /* Write the identity */ + POSIX_GUARD(s2n_stuffer_write_uint16(out, psk->identity.size)); + POSIX_GUARD(s2n_stuffer_write(out, &psk->identity)); + + /* Write obfuscated ticket age */ + uint32_t obfuscated_ticket_age = 0; + uint64_t current_time = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, ¤t_time)); + POSIX_GUARD_RESULT(s2n_generate_obfuscated_ticket_age(psk, current_time, &obfuscated_ticket_age)); + POSIX_GUARD(s2n_stuffer_write_uint32(out, obfuscated_ticket_age)); + + /* Calculate binder size */ + uint8_t hash_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); + binder_list_size += hash_size + SIZE_OF_BINDER_SIZE; + } + + POSIX_GUARD(s2n_stuffer_write_vector_size(&identity_list_size)); + + /* Calculating the binders requires a complete ClientHello, and at this point + * the extension size, extension list size, and message size are all blank. + * + * We'll write placeholder data to ensure the extension and extension list sizes + * are calculated correctly, then rewrite the binders with real data later. */ + psk_params->binder_list_size = binder_list_size; + POSIX_GUARD(s2n_stuffer_skip_write(out, binder_list_size)); + + return S2N_SUCCESS; +} + +/* Find the first of the server's PSK identities that matches the client's identities. + * This method compares all server identities to all client identities. + * + * While both the client's identities and whether a match was found are public, we should make an attempt + * to keep the server's identities a secret. We will make comparisons to the server's identities constant + * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). + * + * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, + * and an attacker could probably guess the server's known identities just by observing the public identities + * sent by clients. + */ +static S2N_RESULT s2n_select_external_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(client_identity_list); + + struct s2n_array *server_psks = &conn->psk_params.psk_list; + conn->psk_params.chosen_psk = NULL; + + for (size_t i = 0; i < server_psks->len; i++) { + struct s2n_psk *server_psk = NULL; + RESULT_GUARD(s2n_array_get(server_psks, i, (void **) &server_psk)); + RESULT_ENSURE_REF(server_psk); + + struct s2n_offered_psk client_psk = { 0 }; + uint16_t wire_index = 0; + + RESULT_GUARD_POSIX(s2n_offered_psk_list_reread(client_identity_list)); + while (s2n_offered_psk_list_has_next(client_identity_list)) { + RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); + uint16_t compare_size = S2N_MIN(client_psk.identity.size, server_psk->identity.size); + if (s2n_constant_time_equals(client_psk.identity.data, server_psk->identity.data, compare_size) + & (client_psk.identity.size == server_psk->identity.size) + & (conn->psk_params.chosen_psk == NULL)) { + conn->psk_params.chosen_psk = server_psk; + conn->psk_params.chosen_psk_wire_index = wire_index; + } + wire_index++; + }; + } + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_select_resumption_psk(struct s2n_connection *conn, struct s2n_offered_psk_list *client_identity_list) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(client_identity_list); + + struct s2n_offered_psk client_psk = { 0 }; + conn->psk_params.chosen_psk = NULL; + + uint8_t rejected_count = 0; + while (s2n_offered_psk_list_has_next(client_identity_list) && (rejected_count < MAX_REJECTED_TICKETS)) { + RESULT_GUARD_POSIX(s2n_offered_psk_list_next(client_identity_list, &client_psk)); + /* Select the first resumption PSK that can be decrypted */ + if (s2n_offered_psk_list_choose_psk(client_identity_list, &client_psk) == S2N_SUCCESS) { + return S2N_RESULT_OK; + } + rejected_count++; + } + + RESULT_BAIL(S2N_ERR_INVALID_SESSION_TICKET); +} + +static S2N_RESULT s2n_client_psk_recv_identity_list(struct s2n_connection *conn, struct s2n_stuffer *wire_identities_in) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_REF(wire_identities_in); + + struct s2n_offered_psk_list identity_list = { + .conn = conn, + .wire_data = *wire_identities_in, + }; + + if (conn->config->psk_selection_cb) { + RESULT_GUARD_POSIX(conn->config->psk_selection_cb(conn, conn->config->psk_selection_ctx, &identity_list)); + } else if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { + RESULT_GUARD(s2n_select_external_psk(conn, &identity_list)); + } else if (conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION) { + RESULT_GUARD(s2n_select_resumption_psk(conn, &identity_list)); + } + + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_client_psk_recv_binder_list(struct s2n_connection *conn, struct s2n_blob *partial_client_hello, + struct s2n_stuffer *wire_binders_in) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(wire_binders_in); + + uint16_t wire_index = 0; + while (s2n_stuffer_data_available(wire_binders_in) > 0) { + uint8_t wire_binder_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(wire_binders_in, &wire_binder_size)); + + uint8_t *wire_binder_data = NULL; + RESULT_ENSURE_REF(wire_binder_data = s2n_stuffer_raw_read(wire_binders_in, wire_binder_size)); + + struct s2n_blob wire_binder = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&wire_binder, wire_binder_data, wire_binder_size)); + + if (wire_index == conn->psk_params.chosen_psk_wire_index) { + RESULT_GUARD_POSIX(s2n_psk_verify_binder(conn, conn->psk_params.chosen_psk, + partial_client_hello, &wire_binder)); + return S2N_RESULT_OK; + } + wire_index++; + } + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); +} + +static S2N_RESULT s2n_client_psk_recv_identities(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + RESULT_ENSURE_REF(conn); + + uint16_t identity_list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &identity_list_size)); + + uint8_t *identity_list_data = NULL; + RESULT_ENSURE_REF(identity_list_data = s2n_stuffer_raw_read(extension, identity_list_size)); + + struct s2n_blob identity_list_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&identity_list_blob, identity_list_data, identity_list_size)); + + struct s2n_stuffer identity_list = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&identity_list, &identity_list_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&identity_list, identity_list_blob.size)); + + return s2n_client_psk_recv_identity_list(conn, &identity_list); +} + +static S2N_RESULT s2n_client_psk_recv_binders(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + RESULT_ENSURE_REF(conn); + + uint16_t binder_list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &binder_list_size)); + + uint8_t *binder_list_data = NULL; + RESULT_ENSURE_REF(binder_list_data = s2n_stuffer_raw_read(extension, binder_list_size)); + + struct s2n_blob binder_list_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&binder_list_blob, binder_list_data, binder_list_size)); + + struct s2n_stuffer binder_list = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&binder_list, &binder_list_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&binder_list, binder_list_blob.size)); + + /* Record the ClientHello message up to but not including the binder list. + * This is required to calculate the binder for the chosen PSK. */ + struct s2n_blob partial_client_hello = { 0 }; + const struct s2n_stuffer *client_hello = &conn->handshake.io; + uint32_t binders_size = binder_list_blob.size + SIZE_OF_BINDER_LIST_SIZE; + RESULT_ENSURE_GTE(client_hello->write_cursor, binders_size); + uint32_t partial_client_hello_size = client_hello->write_cursor - binders_size; + RESULT_GUARD_POSIX(s2n_blob_slice(&client_hello->blob, &partial_client_hello, 0, partial_client_hello_size)); + + return s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &binder_list); +} + +int s2n_client_psk_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + POSIX_ENSURE_REF(conn); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# The "pre_shared_key" extension MUST be the last extension in the + *# ClientHello (this facilitates implementation as described below). + *# Servers MUST check that it is the last extension and otherwise fail + *# the handshake with an "illegal_parameter" alert. + */ + s2n_extension_type_id psk_ext_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); + POSIX_ENSURE_NE(conn->client_hello.extensions.count, 0); + uint16_t last_wire_index = conn->client_hello.extensions.count - 1; + uint16_t extension_wire_index = conn->client_hello.extensions.parsed_extensions[psk_ext_id].wire_index; + POSIX_ENSURE(extension_wire_index == last_wire_index, S2N_ERR_UNSUPPORTED_EXTENSION); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.9 + *# If clients offer "pre_shared_key" without a "psk_key_exchange_modes" extension, + *# servers MUST abort the handshake. + * + * We can safely do this check here because s2n_client_psk is + * required to be the last extension sent in the list. + */ + s2n_extension_type_id psk_ke_mode_ext_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &psk_ke_mode_ext_id)); + POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, psk_ke_mode_ext_id), S2N_ERR_MISSING_EXTENSION); + + if (conn->psk_params.psk_ke_mode == S2N_PSK_DHE_KE) { + s2n_extension_type_id key_share_ext_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_ext_id)); + /* A key_share extension must have been received in order to use a pre-shared key + * in (EC)DHE key exchange mode. + */ + POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_requests_received, key_share_ext_id), S2N_ERR_MISSING_EXTENSION); + } else { + /* s2n currently only supports pre-shared keys in (EC)DHE key exchange mode. If we receive keys with any other + * exchange mode we fall back to a full handshake. + */ + return S2N_SUCCESS; + } + + if (s2n_result_is_error(s2n_client_psk_recv_identities(conn, extension))) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# If no acceptable PSKs are found, the server SHOULD perform a non-PSK + *# handshake if possible. + */ + conn->psk_params.chosen_psk = NULL; + } + + if (conn->psk_params.chosen_psk) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# Prior to accepting PSK key establishment, the server MUST validate + *# the corresponding binder value (see Section 4.2.11.2 below). If this + *# value is not present or does not validate, the server MUST abort the + *# handshake. + */ + POSIX_GUARD_RESULT(s2n_client_psk_recv_binders(conn, extension)); + } + + /* At this point, we have either chosen a PSK or fallen back to a full handshake. */ + return S2N_SUCCESS; +} diff --git a/tls/extensions/s2n_client_server_name.c b/tls/extensions/s2n_client_server_name.c index c9c98d3faff..999c2384895 100644 --- a/tls/extensions/s2n_client_server_name.c +++ b/tls/extensions/s2n_client_server_name.c @@ -1,103 +1,103 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_client_server_name.h" - -#include - -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" - -#define S2N_NAME_TYPE_HOST_NAME 0 - -static bool s2n_client_server_name_should_send(struct s2n_connection *conn); -static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out); -static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); - -const s2n_extension_type s2n_client_server_name_extension = { - .iana_value = TLS_EXTENSION_SERVER_NAME, - .is_response = false, - .send = s2n_client_server_name_send, - .recv = s2n_client_server_name_recv, - .should_send = s2n_client_server_name_should_send, - .if_missing = s2n_extension_noop_if_missing, -}; - -static bool s2n_client_server_name_should_send(struct s2n_connection *conn) -{ - return conn && conn->server_name[0] != '\0'; -} - -static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - struct s2n_stuffer_reservation server_name_list_size = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &server_name_list_size)); - - /* NameType, as described by RFC6066. - * host_name is currently the only possible NameType defined. */ - POSIX_GUARD(s2n_stuffer_write_uint8(out, S2N_NAME_TYPE_HOST_NAME)); - - POSIX_GUARD(s2n_stuffer_write_uint16(out, strlen(conn->server_name))); - POSIX_GUARD(s2n_stuffer_write_bytes(out, (const uint8_t *) conn->server_name, strlen(conn->server_name))); - - POSIX_GUARD(s2n_stuffer_write_vector_size(&server_name_list_size)); - return S2N_SUCCESS; -} - -/* Read the extension up to the first item in ServerNameList. Instantiates the server_name blob to - * point to the first entry. For now s2n ignores all subsequent items in ServerNameList. - */ -S2N_RESULT s2n_client_server_name_parse(struct s2n_stuffer *extension, struct s2n_blob *server_name) -{ - uint16_t list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &list_size)); - RESULT_ENSURE_LTE(list_size, s2n_stuffer_data_available(extension)); - - uint8_t server_name_type = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(extension, &server_name_type)); - RESULT_ENSURE_EQ(server_name_type, S2N_NAME_TYPE_HOST_NAME); - - uint16_t length = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &length)); - RESULT_ENSURE_LTE(length, s2n_stuffer_data_available(extension)); - - uint8_t *data = s2n_stuffer_raw_read(extension, length); - RESULT_ENSURE_REF(data); - RESULT_GUARD_POSIX(s2n_blob_init(server_name, data, length)); - - return S2N_RESULT_OK; -} - -static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - POSIX_ENSURE_REF(conn); - - /* Exit early if we've already parsed the server name */ - if (conn->server_name[0]) { - return S2N_SUCCESS; - } - - /* Ignore if malformed or we don't have enough space to store it. We just won't use the server name. */ - struct s2n_blob server_name = { 0 }; - if (!s2n_result_is_ok(s2n_client_server_name_parse(extension, &server_name)) || server_name.size > S2N_MAX_SERVER_NAME) { - return S2N_SUCCESS; - } - - POSIX_CHECKED_MEMCPY(conn->server_name, server_name.data, server_name.size); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_client_server_name.h" + +#include + +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +#define S2N_NAME_TYPE_HOST_NAME 0 + +static bool s2n_client_server_name_should_send(struct s2n_connection *conn); +static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out); +static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); + +const s2n_extension_type s2n_client_server_name_extension = { + .iana_value = TLS_EXTENSION_SERVER_NAME, + .is_response = false, + .send = s2n_client_server_name_send, + .recv = s2n_client_server_name_recv, + .should_send = s2n_client_server_name_should_send, + .if_missing = s2n_extension_noop_if_missing, +}; + +static bool s2n_client_server_name_should_send(struct s2n_connection *conn) +{ + return conn && conn->server_name[0] != '\0'; +} + +static int s2n_client_server_name_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + struct s2n_stuffer_reservation server_name_list_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &server_name_list_size)); + + /* NameType, as described by RFC6066. + * host_name is currently the only possible NameType defined. */ + POSIX_GUARD(s2n_stuffer_write_uint8(out, S2N_NAME_TYPE_HOST_NAME)); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, strlen(conn->server_name))); + POSIX_GUARD(s2n_stuffer_write_bytes(out, (const uint8_t *) conn->server_name, strlen(conn->server_name))); + + POSIX_GUARD(s2n_stuffer_write_vector_size(&server_name_list_size)); + return S2N_SUCCESS; +} + +/* Read the extension up to the first item in ServerNameList. Instantiates the server_name blob to + * point to the first entry. For now s2n ignores all subsequent items in ServerNameList. + */ +S2N_RESULT s2n_client_server_name_parse(struct s2n_stuffer *extension, struct s2n_blob *server_name) +{ + uint16_t list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &list_size)); + RESULT_ENSURE_LTE(list_size, s2n_stuffer_data_available(extension)); + + uint8_t server_name_type = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(extension, &server_name_type)); + RESULT_ENSURE_EQ(server_name_type, S2N_NAME_TYPE_HOST_NAME); + + uint16_t length = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &length)); + RESULT_ENSURE_LTE(length, s2n_stuffer_data_available(extension)); + + uint8_t *data = s2n_stuffer_raw_read(extension, length); + RESULT_ENSURE_REF(data); + RESULT_GUARD_POSIX(s2n_blob_init(server_name, data, length)); + + return S2N_RESULT_OK; +} + +static int s2n_client_server_name_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + POSIX_ENSURE_REF(conn); + + /* Exit early if we've already parsed the server name */ + if (conn->server_name[0]) { + return S2N_SUCCESS; + } + + /* Ignore if malformed or we don't have enough space to store it. We just won't use the server name. */ + struct s2n_blob server_name = { 0 }; + if (!s2n_result_is_ok(s2n_client_server_name_parse(extension, &server_name)) || server_name.size > S2N_MAX_SERVER_NAME) { + return S2N_SUCCESS; + } + + POSIX_CHECKED_MEMCPY(conn->server_name, server_name.data, server_name.size); + + return S2N_SUCCESS; +} diff --git a/tls/extensions/s2n_client_supported_versions.c b/tls/extensions/s2n_client_supported_versions.c index e9e943bcd96..157ea5444de 100644 --- a/tls/extensions/s2n_client_supported_versions.c +++ b/tls/extensions/s2n_client_supported_versions.c @@ -1,204 +1,204 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_client_supported_versions.h" - -#include - -#include "tls/extensions/s2n_supported_versions.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_safety.h" - -/** - * Specified in https://tools.ietf.org/html/rfc8446#section-4.2.1 - * - * "The "supported_versions" extension is used by the client to indicate - * which versions of TLS it supports and by the server to indicate which - * version it is using. The extension contains a list of supported - * versions in preference order, with the most preferred version first." - * - * Structure: - * Extension type (2 bytes) - * Extension size (2 bytes) - * Version list length (1 byte) - * Version list (number of versions * 2 bytes) - * - * Note: We assume in these functions that the supported version numbers - * are consecutive. This is true because S2N does not support SSLv2, and - * is already an assumption made in the old client hello version handling. - **/ - -static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out); -static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *in); - -const s2n_extension_type s2n_client_supported_versions_extension = { - .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, - .is_response = false, - .send = s2n_client_supported_versions_send, - .recv = s2n_client_supported_versions_recv, - .should_send = s2n_extension_send_if_tls13_connection, - .if_missing = s2n_extension_noop_if_missing, -}; - -static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - uint8_t highest_supported_version = conn->client_protocol_version; - uint8_t minimum_supported_version = s2n_unknown_protocol_version; - POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); - POSIX_ENSURE(highest_supported_version >= minimum_supported_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; - POSIX_GUARD(s2n_stuffer_write_uint8(out, version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); - - for (uint8_t i = highest_supported_version; i >= minimum_supported_version; i--) { - POSIX_GUARD(s2n_stuffer_write_uint8(out, i / 10)); - POSIX_GUARD(s2n_stuffer_write_uint8(out, i % 10)); - } - - return S2N_SUCCESS; -} - -int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension, - uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out) -{ - uint8_t highest_supported_version = conn->server_protocol_version; - uint8_t minimum_supported_version = s2n_unknown_protocol_version; - POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); - - uint8_t size_of_version_list = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(extension, &size_of_version_list)); - S2N_ERROR_IF(size_of_version_list != s2n_stuffer_data_available(extension), S2N_ERR_BAD_MESSAGE); - S2N_ERROR_IF(size_of_version_list % S2N_TLS_PROTOCOL_VERSION_LEN != 0, S2N_ERR_BAD_MESSAGE); - - uint8_t client_protocol_version = s2n_unknown_protocol_version; - uint8_t actual_protocol_version = s2n_unknown_protocol_version; - - for (int i = 0; i < size_of_version_list; i += S2N_TLS_PROTOCOL_VERSION_LEN) { - uint8_t client_version_parts[S2N_TLS_PROTOCOL_VERSION_LEN]; - POSIX_GUARD(s2n_stuffer_read_bytes(extension, client_version_parts, S2N_TLS_PROTOCOL_VERSION_LEN)); - - /* If the client version is outside of our supported versions, then ignore the value. - * S2N does not support SSLv2 except for upgrading connections. Since this extension is - * a TLS1.3 extension, we will skip any SSLv2 values. */ - if (client_version_parts[0] != 3 || client_version_parts[1] > 4) { - continue; - } - - uint16_t client_version = (client_version_parts[0] * 10) + client_version_parts[1]; - - client_protocol_version = S2N_MAX(client_version, client_protocol_version); - - if (client_version > highest_supported_version) { - continue; - } - - if (client_version < minimum_supported_version) { - continue; - } - - /* We ignore the client's preferred order and instead choose - * the highest version that both client and server support. */ - actual_protocol_version = S2N_MAX(client_version, actual_protocol_version); - } - - *client_protocol_version_out = client_protocol_version; - *actual_protocol_version_out = actual_protocol_version; - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_client_supported_versions_recv_impl(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(extension); - - RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, extension, &conn->client_protocol_version, - &conn->actual_protocol_version)); - - RESULT_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION); - RESULT_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - return S2N_RESULT_OK; -} - -static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - /* For backwards compatibility, the supported versions extension isn't used for protocol - * version selection if the server doesn't support TLS 1.3. This ensures that TLS 1.2 servers - * experience no behavior change due to processing the TLS 1.3 extension. See - * https://github.com/aws/s2n-tls/issues/4240. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.1 - *= type=exception - *= reason=The client hello legacy version is used for version selection on TLS 1.2 servers for backwards compatibility - *# If this extension is present in the ClientHello, servers MUST NOT use - *# the ClientHello.legacy_version value for version negotiation and MUST - *# use only the "supported_versions" extension to determine client - *# preferences. - */ - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - return S2N_SUCCESS; - } - - /* A TLS 1.3 state machine flag is used to determine if a HelloRetryRequest is negotiated. A - * protocol version of TLS 1.3 must be set in order to query the TLS 1.3 state machine, so - * it must be queried before the protocol version is potentially reset due to processing the - * extension. - */ - bool is_hrr_handshake = s2n_is_hello_retry_handshake(conn); - - s2n_result result = s2n_client_supported_versions_recv_impl(conn, extension); - if (s2n_result_is_error(result)) { - conn->client_protocol_version = s2n_unknown_protocol_version; - conn->actual_protocol_version = s2n_unknown_protocol_version; - - s2n_queue_reader_unsupported_protocol_version_alert(conn); - POSIX_ENSURE(s2n_errno != S2N_ERR_SAFETY, S2N_ERR_BAD_MESSAGE); - } - POSIX_GUARD_RESULT(result); - - /* When the supported versions extension is received in a ClientHello sent in response to a - * HelloRetryRequest, ensure that TLS 1.3 is selected as the protocol version. - */ - if (is_hrr_handshake && conn->handshake.message_number > 0) { - POSIX_ENSURE(conn->client_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - POSIX_ENSURE(conn->actual_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - return S2N_SUCCESS; -} - -/* Old-style extension functions -- remove after extensions refactor is complete */ - -int s2n_extensions_client_supported_versions_size(struct s2n_connection *conn) -{ - uint8_t minimum_supported_version = s2n_unknown_protocol_version; - POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); - uint8_t highest_supported_version = conn->client_protocol_version; - - uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; - - return version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 5; -} - -/* still used in fuzz test */ -int s2n_extensions_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) -{ - return s2n_extension_recv(&s2n_client_supported_versions_extension, conn, extension); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_client_supported_versions.h" + +#include + +#include "tls/extensions/s2n_supported_versions.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +/** + * Specified in https://tools.ietf.org/html/rfc8446#section-4.2.1 + * + * "The "supported_versions" extension is used by the client to indicate + * which versions of TLS it supports and by the server to indicate which + * version it is using. The extension contains a list of supported + * versions in preference order, with the most preferred version first." + * + * Structure: + * Extension type (2 bytes) + * Extension size (2 bytes) + * Version list length (1 byte) + * Version list (number of versions * 2 bytes) + * + * Note: We assume in these functions that the supported version numbers + * are consecutive. This is true because S2N does not support SSLv2, and + * is already an assumption made in the old client hello version handling. + **/ + +static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out); +static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *in); + +const s2n_extension_type s2n_client_supported_versions_extension = { + .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, + .is_response = false, + .send = s2n_client_supported_versions_send, + .recv = s2n_client_supported_versions_recv, + .should_send = s2n_extension_send_if_tls13_connection, + .if_missing = s2n_extension_noop_if_missing, +}; + +static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + uint8_t highest_supported_version = conn->client_protocol_version; + uint8_t minimum_supported_version = s2n_unknown_protocol_version; + POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); + POSIX_ENSURE(highest_supported_version >= minimum_supported_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; + POSIX_GUARD(s2n_stuffer_write_uint8(out, version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); + + for (uint8_t i = highest_supported_version; i >= minimum_supported_version; i--) { + POSIX_GUARD(s2n_stuffer_write_uint8(out, i / 10)); + POSIX_GUARD(s2n_stuffer_write_uint8(out, i % 10)); + } + + return S2N_SUCCESS; +} + +int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension, + uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out) +{ + uint8_t highest_supported_version = conn->server_protocol_version; + uint8_t minimum_supported_version = s2n_unknown_protocol_version; + POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); + + uint8_t size_of_version_list = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(extension, &size_of_version_list)); + S2N_ERROR_IF(size_of_version_list != s2n_stuffer_data_available(extension), S2N_ERR_BAD_MESSAGE); + S2N_ERROR_IF(size_of_version_list % S2N_TLS_PROTOCOL_VERSION_LEN != 0, S2N_ERR_BAD_MESSAGE); + + uint8_t client_protocol_version = s2n_unknown_protocol_version; + uint8_t actual_protocol_version = s2n_unknown_protocol_version; + + for (int i = 0; i < size_of_version_list; i += S2N_TLS_PROTOCOL_VERSION_LEN) { + uint8_t client_version_parts[S2N_TLS_PROTOCOL_VERSION_LEN]; + POSIX_GUARD(s2n_stuffer_read_bytes(extension, client_version_parts, S2N_TLS_PROTOCOL_VERSION_LEN)); + + /* If the client version is outside of our supported versions, then ignore the value. + * S2N does not support SSLv2 except for upgrading connections. Since this extension is + * a TLS1.3 extension, we will skip any SSLv2 values. */ + if (client_version_parts[0] != 3 || client_version_parts[1] > 4) { + continue; + } + + uint16_t client_version = (client_version_parts[0] * 10) + client_version_parts[1]; + + client_protocol_version = S2N_MAX(client_version, client_protocol_version); + + if (client_version > highest_supported_version) { + continue; + } + + if (client_version < minimum_supported_version) { + continue; + } + + /* We ignore the client's preferred order and instead choose + * the highest version that both client and server support. */ + actual_protocol_version = S2N_MAX(client_version, actual_protocol_version); + } + + *client_protocol_version_out = client_protocol_version; + *actual_protocol_version_out = actual_protocol_version; + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_client_supported_versions_recv_impl(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(extension); + + RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, extension, &conn->client_protocol_version, + &conn->actual_protocol_version)); + + RESULT_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION); + RESULT_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + return S2N_RESULT_OK; +} + +static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + /* For backwards compatibility, the supported versions extension isn't used for protocol + * version selection if the server doesn't support TLS 1.3. This ensures that TLS 1.2 servers + * experience no behavior change due to processing the TLS 1.3 extension. See + * https://github.com/aws/s2n-tls/issues/4240. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.1 + *= type=exception + *= reason=The client hello legacy version is used for version selection on TLS 1.2 servers for backwards compatibility + *# If this extension is present in the ClientHello, servers MUST NOT use + *# the ClientHello.legacy_version value for version negotiation and MUST + *# use only the "supported_versions" extension to determine client + *# preferences. + */ + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + return S2N_SUCCESS; + } + + /* A TLS 1.3 state machine flag is used to determine if a HelloRetryRequest is negotiated. A + * protocol version of TLS 1.3 must be set in order to query the TLS 1.3 state machine, so + * it must be queried before the protocol version is potentially reset due to processing the + * extension. + */ + bool is_hrr_handshake = s2n_is_hello_retry_handshake(conn); + + s2n_result result = s2n_client_supported_versions_recv_impl(conn, extension); + if (s2n_result_is_error(result)) { + conn->client_protocol_version = s2n_unknown_protocol_version; + conn->actual_protocol_version = s2n_unknown_protocol_version; + + s2n_queue_reader_unsupported_protocol_version_alert(conn); + POSIX_ENSURE(s2n_errno != S2N_ERR_SAFETY, S2N_ERR_BAD_MESSAGE); + } + POSIX_GUARD_RESULT(result); + + /* When the supported versions extension is received in a ClientHello sent in response to a + * HelloRetryRequest, ensure that TLS 1.3 is selected as the protocol version. + */ + if (is_hrr_handshake && conn->handshake.message_number > 0) { + POSIX_ENSURE(conn->client_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + POSIX_ENSURE(conn->actual_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + return S2N_SUCCESS; +} + +/* Old-style extension functions -- remove after extensions refactor is complete */ + +int s2n_extensions_client_supported_versions_size(struct s2n_connection *conn) +{ + uint8_t minimum_supported_version = s2n_unknown_protocol_version; + POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version)); + uint8_t highest_supported_version = conn->client_protocol_version; + + uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1; + + return version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 5; +} + +/* still used in fuzz test */ +int s2n_extensions_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + return s2n_extension_recv(&s2n_client_supported_versions_extension, conn, extension); +} diff --git a/tls/extensions/s2n_extension_type.c b/tls/extensions/s2n_extension_type.c index 01ab6e02242..ea29c1b6be5 100644 --- a/tls/extensions/s2n_extension_type.c +++ b/tls/extensions/s2n_extension_type.c @@ -1,258 +1,258 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_extension_type.h" - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_safety.h" - -#define TLS_EXTENSION_DATA_LENGTH_BYTES 2 - -/* Because there are 65536 possible extension IANAs, we will only - * put the lowest (and most common) in a lookup table to conserve space. */ -#define S2N_MAX_INDEXED_EXTENSION_IANA 60 - -const s2n_extension_type_id s2n_unsupported_extension = S2N_SUPPORTED_EXTENSIONS_COUNT; -s2n_extension_type_id s2n_extension_ianas_to_ids[S2N_MAX_INDEXED_EXTENSION_IANA]; - -int s2n_extension_type_init() -{ - /* Initialize to s2n_unsupported_extension */ - for (size_t i = 0; i < S2N_MAX_INDEXED_EXTENSION_IANA; i++) { - s2n_extension_ianas_to_ids[i] = s2n_unsupported_extension; - } - - /* Reverse the mapping */ - for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { - uint16_t iana_value = s2n_supported_extensions[i]; - if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { - s2n_extension_ianas_to_ids[iana_value] = i; - } - } - - return S2N_SUCCESS; -} - -/* Convert the IANA value (which ranges from 0->65535) to an id with a more - * constrained range. That id can be used for bitfields, array indexes, etc. - * to avoid allocating too much memory. */ -s2n_extension_type_id s2n_extension_iana_value_to_id(const uint16_t iana_value) -{ - /* Check the lookup table */ - if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { - return s2n_extension_ianas_to_ids[iana_value]; - } - - /* Fall back to the full list. We can handle this more - * efficiently later if our extension list gets long. */ - for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { - if (s2n_supported_extensions[i] == iana_value) { - return i; - } - } - - return s2n_unsupported_extension; -} - -int s2n_extension_supported_iana_value_to_id(const uint16_t iana_value, s2n_extension_type_id *internal_id) -{ - POSIX_ENSURE_REF(internal_id); - - *internal_id = s2n_extension_iana_value_to_id(iana_value); - S2N_ERROR_IF(*internal_id == s2n_unsupported_extension, S2N_ERR_UNRECOGNIZED_EXTENSION); - return S2N_SUCCESS; -} - -int s2n_extension_send(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *out) -{ - POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(extension_type->should_send); - POSIX_ENSURE_REF(extension_type->send); - POSIX_ENSURE_REF(conn); - - s2n_extension_type_id extension_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - - /* Do not send response if request not received. */ - if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_received, extension_id)) { - return S2N_SUCCESS; - } - - /* Do not send an extension that is not valid for the protocol version */ - if (extension_type->minimum_version > conn->actual_protocol_version) { - return S2N_SUCCESS; - } - - /* Check if we need to send. Some extensions are only sent if specific conditions are met. */ - if (!extension_type->should_send(conn)) { - return S2N_SUCCESS; - } - - /* Write extension type */ - POSIX_GUARD(s2n_stuffer_write_uint16(out, extension_type->iana_value)); - - /* Reserve space for extension size */ - struct s2n_stuffer_reservation extension_size_bytes = { 0 }; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &extension_size_bytes)); - - /* Write extension data */ - POSIX_GUARD(extension_type->send(conn, out)); - - /** - * Reset the tainted flag in the out stuffer (handshake.io stuffer). - * - * Some extension send functions call s2n_stuffer_raw_write(), which - * makes the stuffer tainted, preventing further resizing of the handshake.io stuffer. - * We need to reset this flag after each extension is sent, because handshake.io - * might need to be resized to send subsequent extensions. - * - * This is safe because the outstanding pointer from s2n_stuffer_raw_write() is scoped to - * the send function of each extension, and it is never stored in s2n_connection. - */ - out->tainted = false; - - /* Record extension size */ - POSIX_GUARD(s2n_stuffer_write_vector_size(&extension_size_bytes)); - - /* Set request bit flag */ - if (!extension_type->is_response) { - S2N_CBIT_SET(conn->extension_requests_sent, extension_id); - } - - return S2N_SUCCESS; -} - -int s2n_extension_recv(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *in) -{ - POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(extension_type->recv); - POSIX_ENSURE_REF(conn); - - s2n_extension_type_id extension_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2 - *# Implementations MUST NOT send extension responses if the remote - *# endpoint did not send the corresponding extension requests, with the - *# exception of the "cookie" extension in the HelloRetryRequest. Upon - *# receiving such an extension, an endpoint MUST abort the handshake - *# with an "unsupported_extension" alert. - * - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *# If the original session did not use the "extended_master_secret" - *# extension but the new ServerHello contains the extension, the - *# client MUST abort the handshake. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any - *# extensions that were not first offered by the client in its - *# ClientHello, with the exception of optionally the "cookie" (see - *# Section 4.2.2) extension. - **/ - if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { - POSIX_BAIL(S2N_ERR_UNSUPPORTED_EXTENSION); - } - - /* Do not process an extension not valid for the protocol version */ - if (extension_type->minimum_version > conn->actual_protocol_version) { - return S2N_SUCCESS; - } - - POSIX_GUARD(extension_type->recv(conn, in)); - - /* Set request bit flag */ - if (extension_type->is_response) { - S2N_CBIT_SET(conn->extension_responses_received, extension_id); - } else { - S2N_CBIT_SET(conn->extension_requests_received, extension_id); - } - - return S2N_SUCCESS; -} - -int s2n_extension_is_missing(const s2n_extension_type *extension_type, struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(extension_type); - POSIX_ENSURE_REF(extension_type->if_missing); - POSIX_ENSURE_REF(conn); - - s2n_extension_type_id extension_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); - - /* Do not consider an extension missing if we did not send a request */ - if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { - return S2N_SUCCESS; - } - - /* Do not consider an extension missing if it is not valid for the protocol version */ - if (extension_type->minimum_version > conn->actual_protocol_version) { - return S2N_SUCCESS; - } - - POSIX_GUARD(extension_type->if_missing(conn)); - - return S2N_SUCCESS; -} - -int s2n_extension_send_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_extension_recv_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *in) -{ - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -int s2n_extension_send_noop(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - return S2N_SUCCESS; -} - -int s2n_extension_recv_noop(struct s2n_connection *conn, struct s2n_stuffer *in) -{ - return S2N_SUCCESS; -} - -bool s2n_extension_always_send(struct s2n_connection *conn) -{ - return true; -} - -bool s2n_extension_never_send(struct s2n_connection *conn) -{ - return false; -} - -bool s2n_extension_send_if_tls13_connection(struct s2n_connection *conn) -{ - return s2n_connection_get_protocol_version(conn) >= S2N_TLS13; -} - -int s2n_extension_error_if_missing(struct s2n_connection *conn) -{ - POSIX_BAIL(S2N_ERR_MISSING_EXTENSION); -} - -int s2n_extension_noop_if_missing(struct s2n_connection *conn) -{ - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_extension_type.h" + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define TLS_EXTENSION_DATA_LENGTH_BYTES 2 + +/* Because there are 65536 possible extension IANAs, we will only + * put the lowest (and most common) in a lookup table to conserve space. */ +#define S2N_MAX_INDEXED_EXTENSION_IANA 60 + +const s2n_extension_type_id s2n_unsupported_extension = S2N_SUPPORTED_EXTENSIONS_COUNT; +s2n_extension_type_id s2n_extension_ianas_to_ids[S2N_MAX_INDEXED_EXTENSION_IANA]; + +int s2n_extension_type_init() +{ + /* Initialize to s2n_unsupported_extension */ + for (size_t i = 0; i < S2N_MAX_INDEXED_EXTENSION_IANA; i++) { + s2n_extension_ianas_to_ids[i] = s2n_unsupported_extension; + } + + /* Reverse the mapping */ + for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { + uint16_t iana_value = s2n_supported_extensions[i]; + if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { + s2n_extension_ianas_to_ids[iana_value] = i; + } + } + + return S2N_SUCCESS; +} + +/* Convert the IANA value (which ranges from 0->65535) to an id with a more + * constrained range. That id can be used for bitfields, array indexes, etc. + * to avoid allocating too much memory. */ +s2n_extension_type_id s2n_extension_iana_value_to_id(const uint16_t iana_value) +{ + /* Check the lookup table */ + if (iana_value < S2N_MAX_INDEXED_EXTENSION_IANA) { + return s2n_extension_ianas_to_ids[iana_value]; + } + + /* Fall back to the full list. We can handle this more + * efficiently later if our extension list gets long. */ + for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { + if (s2n_supported_extensions[i] == iana_value) { + return i; + } + } + + return s2n_unsupported_extension; +} + +int s2n_extension_supported_iana_value_to_id(const uint16_t iana_value, s2n_extension_type_id *internal_id) +{ + POSIX_ENSURE_REF(internal_id); + + *internal_id = s2n_extension_iana_value_to_id(iana_value); + S2N_ERROR_IF(*internal_id == s2n_unsupported_extension, S2N_ERR_UNRECOGNIZED_EXTENSION); + return S2N_SUCCESS; +} + +int s2n_extension_send(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(extension_type); + POSIX_ENSURE_REF(extension_type->should_send); + POSIX_ENSURE_REF(extension_type->send); + POSIX_ENSURE_REF(conn); + + s2n_extension_type_id extension_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); + + /* Do not send response if request not received. */ + if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_received, extension_id)) { + return S2N_SUCCESS; + } + + /* Do not send an extension that is not valid for the protocol version */ + if (extension_type->minimum_version > conn->actual_protocol_version) { + return S2N_SUCCESS; + } + + /* Check if we need to send. Some extensions are only sent if specific conditions are met. */ + if (!extension_type->should_send(conn)) { + return S2N_SUCCESS; + } + + /* Write extension type */ + POSIX_GUARD(s2n_stuffer_write_uint16(out, extension_type->iana_value)); + + /* Reserve space for extension size */ + struct s2n_stuffer_reservation extension_size_bytes = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &extension_size_bytes)); + + /* Write extension data */ + POSIX_GUARD(extension_type->send(conn, out)); + + /** + * Reset the tainted flag in the out stuffer (handshake.io stuffer). + * + * Some extension send functions call s2n_stuffer_raw_write(), which + * makes the stuffer tainted, preventing further resizing of the handshake.io stuffer. + * We need to reset this flag after each extension is sent, because handshake.io + * might need to be resized to send subsequent extensions. + * + * This is safe because the outstanding pointer from s2n_stuffer_raw_write() is scoped to + * the send function of each extension, and it is never stored in s2n_connection. + */ + out->tainted = false; + + /* Record extension size */ + POSIX_GUARD(s2n_stuffer_write_vector_size(&extension_size_bytes)); + + /* Set request bit flag */ + if (!extension_type->is_response) { + S2N_CBIT_SET(conn->extension_requests_sent, extension_id); + } + + return S2N_SUCCESS; +} + +int s2n_extension_recv(const s2n_extension_type *extension_type, struct s2n_connection *conn, struct s2n_stuffer *in) +{ + POSIX_ENSURE_REF(extension_type); + POSIX_ENSURE_REF(extension_type->recv); + POSIX_ENSURE_REF(conn); + + s2n_extension_type_id extension_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2 + *# Implementations MUST NOT send extension responses if the remote + *# endpoint did not send the corresponding extension requests, with the + *# exception of the "cookie" extension in the HelloRetryRequest. Upon + *# receiving such an extension, an endpoint MUST abort the handshake + *# with an "unsupported_extension" alert. + * + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *# If the original session did not use the "extended_master_secret" + *# extension but the new ServerHello contains the extension, the + *# client MUST abort the handshake. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { + POSIX_BAIL(S2N_ERR_UNSUPPORTED_EXTENSION); + } + + /* Do not process an extension not valid for the protocol version */ + if (extension_type->minimum_version > conn->actual_protocol_version) { + return S2N_SUCCESS; + } + + POSIX_GUARD(extension_type->recv(conn, in)); + + /* Set request bit flag */ + if (extension_type->is_response) { + S2N_CBIT_SET(conn->extension_responses_received, extension_id); + } else { + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + } + + return S2N_SUCCESS; +} + +int s2n_extension_is_missing(const s2n_extension_type *extension_type, struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(extension_type); + POSIX_ENSURE_REF(extension_type->if_missing); + POSIX_ENSURE_REF(conn); + + s2n_extension_type_id extension_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type->iana_value, &extension_id)); + + /* Do not consider an extension missing if we did not send a request */ + if (extension_type->is_response && !S2N_CBIT_TEST(conn->extension_requests_sent, extension_id)) { + return S2N_SUCCESS; + } + + /* Do not consider an extension missing if it is not valid for the protocol version */ + if (extension_type->minimum_version > conn->actual_protocol_version) { + return S2N_SUCCESS; + } + + POSIX_GUARD(extension_type->if_missing(conn)); + + return S2N_SUCCESS; +} + +int s2n_extension_send_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_extension_recv_unimplemented(struct s2n_connection *conn, struct s2n_stuffer *in) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +int s2n_extension_send_noop(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + return S2N_SUCCESS; +} + +int s2n_extension_recv_noop(struct s2n_connection *conn, struct s2n_stuffer *in) +{ + return S2N_SUCCESS; +} + +bool s2n_extension_always_send(struct s2n_connection *conn) +{ + return true; +} + +bool s2n_extension_never_send(struct s2n_connection *conn) +{ + return false; +} + +bool s2n_extension_send_if_tls13_connection(struct s2n_connection *conn) +{ + return s2n_connection_get_protocol_version(conn) >= S2N_TLS13; +} + +int s2n_extension_error_if_missing(struct s2n_connection *conn) +{ + POSIX_BAIL(S2N_ERR_MISSING_EXTENSION); +} + +int s2n_extension_noop_if_missing(struct s2n_connection *conn) +{ + return S2N_SUCCESS; +} diff --git a/tls/extensions/s2n_supported_versions.c b/tls/extensions/s2n_supported_versions.c index 0f2d41b3c1d..46541676520 100644 --- a/tls/extensions/s2n_supported_versions.c +++ b/tls/extensions/s2n_supported_versions.c @@ -1,39 +1,39 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/extensions/s2n_supported_versions.h" - -#include - -#include "tls/s2n_security_policies.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_connection_get_minimum_supported_version(struct s2n_connection *conn, uint8_t *min_version) -{ - RESULT_ENSURE_REF(min_version); - - const struct s2n_security_policy *security_policy = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); - RESULT_ENSURE_REF(security_policy); - *min_version = security_policy->minimum_protocol_version; - - /* QUIC requires >= TLS1.3 */ - if (s2n_connection_is_quic_enabled(conn)) { - *min_version = S2N_MAX(*min_version, S2N_TLS13); - } - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_supported_versions.h" + +#include + +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_connection_get_minimum_supported_version(struct s2n_connection *conn, uint8_t *min_version) +{ + RESULT_ENSURE_REF(min_version); + + const struct s2n_security_policy *security_policy = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); + RESULT_ENSURE_REF(security_policy); + *min_version = security_policy->minimum_protocol_version; + + /* QUIC requires >= TLS1.3 */ + if (s2n_connection_is_quic_enabled(conn)) { + *min_version = S2N_MAX(*min_version, S2N_TLS13); + } + + return S2N_RESULT_OK; +} diff --git a/tls/policy/s2n_policy_defaults.c b/tls/policy/s2n_policy_defaults.c index c4dc7d07a76..26d98b9b3ba 100644 --- a/tls/policy/s2n_policy_defaults.c +++ b/tls/policy/s2n_policy_defaults.c @@ -1,129 +1,129 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/policy/s2n_policy_defaults.h" - -#include "tls/s2n_security_policies.h" - -/* clang-format off */ -S2N_INLINE_SECURITY_POLICY_V1( - default_policy_strict, - S2N_TLS13, - S2N_CIPHER_PREF_LIST( - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - ), - S2N_SIG_PREF_LIST( - &s2n_mldsa44, - &s2n_mldsa65, - &s2n_mldsa87, - &s2n_ecdsa_sha256, - &s2n_ecdsa_sha384, - &s2n_ecdsa_sha512, - &s2n_rsa_pss_pss_sha256, - &s2n_rsa_pss_pss_sha384, - &s2n_rsa_pss_pss_sha512, - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_rsae_sha384, - &s2n_rsa_pss_rsae_sha512, - ), - S2N_CURVE_PREF_LIST( - &s2n_ecc_curve_secp256r1, - &s2n_ecc_curve_secp384r1, - &s2n_ecc_curve_secp521r1, - ), - S2N_KEM_PREF_LIST( - &s2n_secp256r1_mlkem_768, - &s2n_x25519_mlkem_768, - &s2n_secp384r1_mlkem_1024, - ) -); -/* clang-format on */ - -/* clang-format off */ -S2N_INLINE_SECURITY_POLICY_V1( - default_policy_compat, - S2N_TLS12, - S2N_CIPHER_PREF_LIST( - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - ), - S2N_SIG_PREF_LIST( - &s2n_mldsa44, - &s2n_mldsa65, - &s2n_mldsa87, - &s2n_ecdsa_sha256, - &s2n_ecdsa_sha384, - &s2n_ecdsa_sha512, - &s2n_rsa_pss_pss_sha256, - &s2n_rsa_pss_pss_sha384, - &s2n_rsa_pss_pss_sha512, - &s2n_rsa_pss_rsae_sha256, - &s2n_rsa_pss_rsae_sha384, - &s2n_rsa_pss_rsae_sha512, - &s2n_rsa_pkcs1_sha256, - &s2n_rsa_pkcs1_sha384, - &s2n_rsa_pkcs1_sha512, - ), - S2N_CURVE_PREF_LIST( - &s2n_ecc_curve_secp256r1, - &s2n_ecc_curve_x25519, - &s2n_ecc_curve_secp384r1, - &s2n_ecc_curve_secp521r1, - ), - S2N_KEM_PREF_LIST( - &s2n_secp256r1_mlkem_768, - &s2n_x25519_mlkem_768, - &s2n_secp384r1_mlkem_1024, - ) -); -/* clang-format on */ - -const struct s2n_security_policy *default_policies[S2N_MAX_DEFAULT_POLICIES][S2N_MAX_POLICY_VERSIONS] = { - [S2N_POLICY_STRICT] = { - [S2N_STRICT_2025_08_20] = &default_policy_strict, - }, - [S2N_POLICY_COMPATIBLE] = { - [S2N_COMPAT_2025_08_20] = &default_policy_compat, - }, -}; - -const struct s2n_security_policy *s2n_security_policy_get(s2n_policy_name policy, uint64_t version) -{ - /* The uint64_t cast here is required for some older compilers to avoid a - * "tautological-constant-out-of-range-compare" error. That error assumes - * "policy" will be a valid s2n_default_policy, but that is not guaranteed by - * the standard. - */ - PTR_ENSURE((uint64_t) policy < S2N_MAX_DEFAULT_POLICIES, S2N_ERR_INVALID_SECURITY_POLICY); - PTR_ENSURE(version < S2N_MAX_POLICY_VERSIONS, S2N_ERR_INVALID_SECURITY_POLICY); - - const struct s2n_security_policy *match = default_policies[policy][version]; - PTR_ENSURE(match, S2N_ERR_INVALID_SECURITY_POLICY); - - return match; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/policy/s2n_policy_defaults.h" + +#include "tls/s2n_security_policies.h" + +/* clang-format off */ +S2N_INLINE_SECURITY_POLICY_V1( + default_policy_strict, + S2N_TLS13, + S2N_CIPHER_PREF_LIST( + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + ), + S2N_SIG_PREF_LIST( + &s2n_mldsa44, + &s2n_mldsa65, + &s2n_mldsa87, + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, + &s2n_ecdsa_sha512, + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + ), + S2N_CURVE_PREF_LIST( + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_secp384r1, + &s2n_ecc_curve_secp521r1, + ), + S2N_KEM_PREF_LIST( + &s2n_secp256r1_mlkem_768, + &s2n_x25519_mlkem_768, + &s2n_secp384r1_mlkem_1024, + ) +); +/* clang-format on */ + +/* clang-format off */ +S2N_INLINE_SECURITY_POLICY_V1( + default_policy_compat, + S2N_TLS12, + S2N_CIPHER_PREF_LIST( + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + ), + S2N_SIG_PREF_LIST( + &s2n_mldsa44, + &s2n_mldsa65, + &s2n_mldsa87, + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, + &s2n_ecdsa_sha512, + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + &s2n_rsa_pkcs1_sha256, + &s2n_rsa_pkcs1_sha384, + &s2n_rsa_pkcs1_sha512, + ), + S2N_CURVE_PREF_LIST( + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_x25519, + &s2n_ecc_curve_secp384r1, + &s2n_ecc_curve_secp521r1, + ), + S2N_KEM_PREF_LIST( + &s2n_secp256r1_mlkem_768, + &s2n_x25519_mlkem_768, + &s2n_secp384r1_mlkem_1024, + ) +); +/* clang-format on */ + +const struct s2n_security_policy *default_policies[S2N_MAX_DEFAULT_POLICIES][S2N_MAX_POLICY_VERSIONS] = { + [S2N_POLICY_STRICT] = { + [S2N_STRICT_2025_08_20] = &default_policy_strict, + }, + [S2N_POLICY_COMPATIBLE] = { + [S2N_COMPAT_2025_08_20] = &default_policy_compat, + }, +}; + +const struct s2n_security_policy *s2n_security_policy_get(s2n_policy_name policy, uint64_t version) +{ + /* The uint64_t cast here is required for some older compilers to avoid a + * "tautological-constant-out-of-range-compare" error. That error assumes + * "policy" will be a valid s2n_default_policy, but that is not guaranteed by + * the standard. + */ + PTR_ENSURE((uint64_t) policy < S2N_MAX_DEFAULT_POLICIES, S2N_ERR_INVALID_SECURITY_POLICY); + PTR_ENSURE(version < S2N_MAX_POLICY_VERSIONS, S2N_ERR_INVALID_SECURITY_POLICY); + + const struct s2n_security_policy *match = default_policies[policy][version]; + PTR_ENSURE(match, S2N_ERR_INVALID_SECURITY_POLICY); + + return match; +} diff --git a/tls/policy/s2n_policy_writer.c b/tls/policy/s2n_policy_writer.c index 6e9d48901cd..46c06b20ab6 100644 --- a/tls/policy/s2n_policy_writer.c +++ b/tls/policy/s2n_policy_writer.c @@ -1,211 +1,211 @@ -/* - * 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 -#if !defined(_MSC_VER) -#include -#endif - -#include "stuffer/s2n_stuffer.h" -#include "tls/policy/s2n_policy_feature.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_security_rules.h" -#include "utils/s2n_safety.h" - -#define BOOL_STR(b) ((b) ? "yes" : "no") - -extern const struct s2n_security_rule security_rule_definitions[S2N_SECURITY_RULES_COUNT]; - -static const char *version_strs[] = { - [S2N_SSLv2] = "SSLv2", - [S2N_SSLv3] = "SSLv3", - [S2N_TLS10] = "TLS1.0", - [S2N_TLS11] = "TLS1.1", - [S2N_TLS12] = "TLS1.2", - [S2N_TLS13] = "TLS1.3", -}; - -static S2N_RESULT s2n_security_policy_write_format_v1_to_stuffer(const struct s2n_security_policy *policy, struct s2n_stuffer *stuffer) -{ - RESULT_ENSURE_REF(policy); - RESULT_ENSURE_REF(stuffer); - - const char *version_str = NULL; - if (policy->minimum_protocol_version <= S2N_TLS13) { - version_str = version_strs[policy->minimum_protocol_version]; - } - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "min version: %s\n", version_str ? version_str : "None")); - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "rules:\n")); - for (size_t i = 0; i < S2N_SECURITY_RULES_COUNT; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s: %s\n", - security_rule_definitions[i].name, BOOL_STR(policy->rules[i]))); - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "cipher suites:\n")); - if (policy->cipher_preferences->allow_chacha20_boosting) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- chacha20 boosting enabled\n")); - } - for (size_t i = 0; i < policy->cipher_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->cipher_preferences->suites[i]->iana_name)); - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "signature schemes:\n")); - for (size_t i = 0; i < policy->signature_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->signature_preferences->signature_schemes[i]->name)); - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "curves:\n")); - for (size_t i = 0; i < policy->ecc_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->ecc_preferences->ecc_curves[i]->name)); - } - - for (size_t i = 0; policy->strongly_preferred_groups != NULL && i < policy->strongly_preferred_groups->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "strongly preferred groups:\n")); - const struct s2n_ecc_named_curve *strongly_preferred_curve = NULL; - const struct s2n_kem_group *strongly_preferred_kem_group = NULL; - bool found = false; - RESULT_GUARD_POSIX(s2n_find_ecc_curve_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_curve, &found)); - RESULT_GUARD_POSIX(s2n_find_kem_group_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_kem_group, &found)); - RESULT_ENSURE((strongly_preferred_curve == NULL) != (strongly_preferred_kem_group == NULL), S2N_ERR_INVALID_SUPPORTED_GROUP_STATE); - - if (strongly_preferred_curve != NULL) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_curve->name)); - } - - if (strongly_preferred_kem_group != NULL) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_kem_group->name)); - } - } - - if (policy->certificate_signature_preferences) { - if (policy->certificate_preferences_apply_locally) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate preferences apply locally\n")); - } - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate signature schemes:\n")); - for (size_t i = 0; i < policy->certificate_signature_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", - policy->certificate_signature_preferences->signature_schemes[i]->name)); - } - } - - if (policy->certificate_key_preferences) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate keys:\n")); - for (size_t i = 0; i < policy->certificate_key_preferences->count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", - policy->certificate_key_preferences->certificate_keys[i]->name)); - } - } - - if (policy->kem_preferences && policy->kem_preferences != &kem_preferences_null) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "pq:\n")); - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- revision: %i\n", - policy->kem_preferences->tls13_pq_hybrid_draft_revision)); - - if (policy->kem_preferences->kem_count > 0) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kems:\n")); - for (size_t i = 0; i < policy->kem_preferences->kem_count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", - policy->kem_preferences->kems[i]->name)); - } - } - - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kem groups:\n")); - for (size_t i = 0; i < policy->kem_preferences->tls13_kem_group_count; i++) { - RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", - policy->kem_preferences->tls13_kem_groups[i]->name)); - } - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_security_policy_write_to_stuffer(const struct s2n_security_policy *policy, - s2n_policy_format format, struct s2n_stuffer *stuffer) -{ - RESULT_ENSURE_REF(policy); - RESULT_ENSURE_REF(stuffer); - - switch (format) { - case S2N_POLICY_FORMAT_DEBUG_V1: - RESULT_GUARD(s2n_security_policy_write_format_v1_to_stuffer(policy, stuffer)); - break; - default: - RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); - } - - return S2N_RESULT_OK; -} - -int s2n_security_policy_write_length(const struct s2n_security_policy *policy, - s2n_policy_format format, uint32_t *length) -{ - POSIX_ENSURE_REF(policy); - POSIX_ENSURE_REF(length); - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); - - *length = s2n_stuffer_data_available(&stuffer); - - return S2N_SUCCESS; -} - -int s2n_security_policy_write_bytes(const struct s2n_security_policy *policy, - s2n_policy_format format, uint8_t *buffer, uint32_t buffer_length, uint32_t *output_size) -{ - POSIX_ENSURE_REF(policy); - POSIX_ENSURE_REF(buffer); - POSIX_ENSURE_REF(output_size); - *output_size = 0; - - /* Intermediate stuffer is needed because s2n_stuffer_printf requires temporary space for null - * terminators. We cannot write directly to application memory which may not have the extra byte - * available - */ - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); - POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); - uint32_t required_size = s2n_stuffer_data_available(&stuffer); - POSIX_ENSURE(buffer_length >= required_size, S2N_ERR_INSUFFICIENT_MEM_SIZE); - - POSIX_CHECKED_MEMCPY(buffer, stuffer.blob.data, required_size); - *output_size = s2n_stuffer_data_available(&stuffer); - return S2N_SUCCESS; -} - -int s2n_security_policy_write_fd(const struct s2n_security_policy *policy, - s2n_policy_format format, int fd, uint32_t *output_size) -{ - POSIX_ENSURE_REF(policy); - POSIX_ENSURE_REF(output_size); - POSIX_ENSURE(fd >= 0, S2N_ERR_INVALID_ARGUMENT); - *output_size = 0; - - DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); - - POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); - - uint32_t data_size = s2n_stuffer_data_available(&stuffer); - ssize_t written = write(fd, stuffer.blob.data, data_size); - POSIX_ENSURE(written == (ssize_t) data_size, S2N_ERR_IO); - - *output_size = (uint32_t) written; - return S2N_SUCCESS; -} +/* + * 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 +#if !defined(_MSC_VER) +#include +#endif + +#include "stuffer/s2n_stuffer.h" +#include "tls/policy/s2n_policy_feature.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_security_rules.h" +#include "utils/s2n_safety.h" + +#define BOOL_STR(b) ((b) ? "yes" : "no") + +extern const struct s2n_security_rule security_rule_definitions[S2N_SECURITY_RULES_COUNT]; + +static const char *version_strs[] = { + [S2N_SSLv2] = "SSLv2", + [S2N_SSLv3] = "SSLv3", + [S2N_TLS10] = "TLS1.0", + [S2N_TLS11] = "TLS1.1", + [S2N_TLS12] = "TLS1.2", + [S2N_TLS13] = "TLS1.3", +}; + +static S2N_RESULT s2n_security_policy_write_format_v1_to_stuffer(const struct s2n_security_policy *policy, struct s2n_stuffer *stuffer) +{ + RESULT_ENSURE_REF(policy); + RESULT_ENSURE_REF(stuffer); + + const char *version_str = NULL; + if (policy->minimum_protocol_version <= S2N_TLS13) { + version_str = version_strs[policy->minimum_protocol_version]; + } + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "min version: %s\n", version_str ? version_str : "None")); + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "rules:\n")); + for (size_t i = 0; i < S2N_SECURITY_RULES_COUNT; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s: %s\n", + security_rule_definitions[i].name, BOOL_STR(policy->rules[i]))); + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "cipher suites:\n")); + if (policy->cipher_preferences->allow_chacha20_boosting) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- chacha20 boosting enabled\n")); + } + for (size_t i = 0; i < policy->cipher_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->cipher_preferences->suites[i]->iana_name)); + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "signature schemes:\n")); + for (size_t i = 0; i < policy->signature_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->signature_preferences->signature_schemes[i]->name)); + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "curves:\n")); + for (size_t i = 0; i < policy->ecc_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", policy->ecc_preferences->ecc_curves[i]->name)); + } + + for (size_t i = 0; policy->strongly_preferred_groups != NULL && i < policy->strongly_preferred_groups->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "strongly preferred groups:\n")); + const struct s2n_ecc_named_curve *strongly_preferred_curve = NULL; + const struct s2n_kem_group *strongly_preferred_kem_group = NULL; + bool found = false; + RESULT_GUARD_POSIX(s2n_find_ecc_curve_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_curve, &found)); + RESULT_GUARD_POSIX(s2n_find_kem_group_from_iana_id(policy->strongly_preferred_groups->iana_ids[i], &strongly_preferred_kem_group, &found)); + RESULT_ENSURE((strongly_preferred_curve == NULL) != (strongly_preferred_kem_group == NULL), S2N_ERR_INVALID_SUPPORTED_GROUP_STATE); + + if (strongly_preferred_curve != NULL) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_curve->name)); + } + + if (strongly_preferred_kem_group != NULL) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", strongly_preferred_kem_group->name)); + } + } + + if (policy->certificate_signature_preferences) { + if (policy->certificate_preferences_apply_locally) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate preferences apply locally\n")); + } + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate signature schemes:\n")); + for (size_t i = 0; i < policy->certificate_signature_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", + policy->certificate_signature_preferences->signature_schemes[i]->name)); + } + } + + if (policy->certificate_key_preferences) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "certificate keys:\n")); + for (size_t i = 0; i < policy->certificate_key_preferences->count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- %s\n", + policy->certificate_key_preferences->certificate_keys[i]->name)); + } + } + + if (policy->kem_preferences && policy->kem_preferences != &kem_preferences_null) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "pq:\n")); + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- revision: %i\n", + policy->kem_preferences->tls13_pq_hybrid_draft_revision)); + + if (policy->kem_preferences->kem_count > 0) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kems:\n")); + for (size_t i = 0; i < policy->kem_preferences->kem_count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", + policy->kem_preferences->kems[i]->name)); + } + } + + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "- kem groups:\n")); + for (size_t i = 0; i < policy->kem_preferences->tls13_kem_group_count; i++) { + RESULT_GUARD_POSIX(s2n_stuffer_printf(stuffer, "-- %s\n", + policy->kem_preferences->tls13_kem_groups[i]->name)); + } + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_security_policy_write_to_stuffer(const struct s2n_security_policy *policy, + s2n_policy_format format, struct s2n_stuffer *stuffer) +{ + RESULT_ENSURE_REF(policy); + RESULT_ENSURE_REF(stuffer); + + switch (format) { + case S2N_POLICY_FORMAT_DEBUG_V1: + RESULT_GUARD(s2n_security_policy_write_format_v1_to_stuffer(policy, stuffer)); + break; + default: + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + + return S2N_RESULT_OK; +} + +int s2n_security_policy_write_length(const struct s2n_security_policy *policy, + s2n_policy_format format, uint32_t *length) +{ + POSIX_ENSURE_REF(policy); + POSIX_ENSURE_REF(length); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); + + *length = s2n_stuffer_data_available(&stuffer); + + return S2N_SUCCESS; +} + +int s2n_security_policy_write_bytes(const struct s2n_security_policy *policy, + s2n_policy_format format, uint8_t *buffer, uint32_t buffer_length, uint32_t *output_size) +{ + POSIX_ENSURE_REF(policy); + POSIX_ENSURE_REF(buffer); + POSIX_ENSURE_REF(output_size); + *output_size = 0; + + /* Intermediate stuffer is needed because s2n_stuffer_printf requires temporary space for null + * terminators. We cannot write directly to application memory which may not have the extra byte + * available + */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); + POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); + uint32_t required_size = s2n_stuffer_data_available(&stuffer); + POSIX_ENSURE(buffer_length >= required_size, S2N_ERR_INSUFFICIENT_MEM_SIZE); + + POSIX_CHECKED_MEMCPY(buffer, stuffer.blob.data, required_size); + *output_size = s2n_stuffer_data_available(&stuffer); + return S2N_SUCCESS; +} + +int s2n_security_policy_write_fd(const struct s2n_security_policy *policy, + s2n_policy_format format, int fd, uint32_t *output_size) +{ + POSIX_ENSURE_REF(policy); + POSIX_ENSURE_REF(output_size); + POSIX_ENSURE(fd >= 0, S2N_ERR_INVALID_ARGUMENT); + *output_size = 0; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + POSIX_GUARD_RESULT(s2n_security_policy_write_to_stuffer(policy, format, &stuffer)); + + uint32_t data_size = s2n_stuffer_data_available(&stuffer); + ssize_t written = write(fd, stuffer.blob.data, data_size); + POSIX_ENSURE(written == (ssize_t) data_size, S2N_ERR_IO); + + *output_size = (uint32_t) written; + return S2N_SUCCESS; +} diff --git a/tls/s2n_alerts.c b/tls/s2n_alerts.c index 4eb28888540..9342598f09c 100644 --- a/tls/s2n_alerts.c +++ b/tls/s2n_alerts.c @@ -1,393 +1,393 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_alerts.h" - -#include - -#include "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_atomic.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -#define S2N_ALERT_CASE(error, alert_code) \ - case (error): \ - *alert = (alert_code); \ - return S2N_RESULT_OK - -#define S2N_NO_ALERT(error) \ - case (error): \ - RESULT_BAIL(S2N_ERR_NO_ALERT) - -static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t *alert) -{ - RESULT_ENSURE_REF(alert); - - switch (error_code) { - S2N_ALERT_CASE(S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION); - S2N_ALERT_CASE(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, S2N_TLS_ALERT_HANDSHAKE_FAILURE); - S2N_ALERT_CASE(S2N_ERR_MISSING_CLIENT_CERT, S2N_TLS_ALERT_CERTIFICATE_REQUIRED); - - /* TODO: The ERR_BAD_MESSAGE -> ALERT_UNEXPECTED_MESSAGE mapping - * isn't always correct. Sometimes s2n-tls uses ERR_BAD_MESSAGE - * to indicate S2N_TLS_ALERT_ILLEGAL_PARAMETER instead. - * We'll want to add a new error to distinguish between the two usages: - * our errors should be equally or more specific than alerts, not less. - */ - S2N_ALERT_CASE(S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - S2N_ALERT_CASE(S2N_ERR_UNEXPECTED_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - S2N_ALERT_CASE(S2N_ERR_MISSING_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - - /* For errors involving secure renegotiation: - *= https://www.rfc-editor.org/rfc/rfc5746#3.4 - *# Note: later in Section 3, "abort the handshake" is used as - *# shorthand for "send a fatal handshake_failure alert and - *# terminate the connection". - */ - S2N_ALERT_CASE(S2N_ERR_NO_RENEGOTIATION, S2N_TLS_ALERT_HANDSHAKE_FAILURE); - - S2N_ALERT_CASE(S2N_ERR_KTLS_KEYUPDATE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); - - /* For errors involving certificates */ - - /* This error is used in several ways so make it a general certificate issue - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# certificate_unknown: Some other (unspecified) issue arose in - *# processing the certificate, rendering it unacceptable. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - S2N_ALERT_CASE(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - S2N_ALERT_CASE(S2N_ERR_CERT_INVALID_HOSTNAME, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# certificate_revoked: A certificate was revoked by its signer. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_REVOKED, S2N_TLS_ALERT_CERTIFICATE_REVOKED); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# certificate_expired: A certificate has expired or is not currently - *# valid. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_NOT_YET_VALID, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); - S2N_ALERT_CASE(S2N_ERR_CERT_EXPIRED, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# unsupported_certificate: A certificate was of an unsupported type. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_TYPE_UNSUPPORTED, S2N_TLS_ALERT_UNSUPPORTED_CERTIFICATE); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# access_denied: A valid certificate or PSK was received, but when - *# access control was applied, the sender decided not to proceed with - *# negotiation. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_REJECTED, S2N_TLS_ALERT_ACCESS_DENIED); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *# bad_certificate: A certificate was corrupt, contained signatures - *# that did not verify correctly, etc. - */ - S2N_ALERT_CASE(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, S2N_TLS_ALERT_BAD_CERTIFICATE); - S2N_ALERT_CASE(S2N_ERR_CERT_INVALID, S2N_TLS_ALERT_BAD_CERTIFICATE); - S2N_ALERT_CASE(S2N_ERR_DECODE_CERTIFICATE, S2N_TLS_ALERT_BAD_CERTIFICATE); - - /* TODO: Add mappings for other protocol errors. - */ - S2N_NO_ALERT(S2N_ERR_ENCRYPT); - S2N_NO_ALERT(S2N_ERR_DECRYPT); - S2N_NO_ALERT(S2N_ERR_KEY_INIT); - S2N_NO_ALERT(S2N_ERR_KEY_DESTROY); - S2N_NO_ALERT(S2N_ERR_DH_SERIALIZING); - S2N_NO_ALERT(S2N_ERR_DH_SHARED_SECRET); - S2N_NO_ALERT(S2N_ERR_DH_WRITING_PUBLIC_KEY); - S2N_NO_ALERT(S2N_ERR_DH_FAILED_SIGNING); - S2N_NO_ALERT(S2N_ERR_DH_COPYING_PARAMETERS); - S2N_NO_ALERT(S2N_ERR_DH_GENERATING_PARAMETERS); - S2N_NO_ALERT(S2N_ERR_CIPHER_NOT_SUPPORTED); - S2N_NO_ALERT(S2N_ERR_NO_APPLICATION_PROTOCOL); - S2N_NO_ALERT(S2N_ERR_FALLBACK_DETECTED); - S2N_NO_ALERT(S2N_ERR_HASH_DIGEST_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_INIT_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_UPDATE_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_COPY_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_WIPE_FAILED); - S2N_NO_ALERT(S2N_ERR_HASH_NOT_READY); - S2N_NO_ALERT(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED); - S2N_NO_ALERT(S2N_ERR_DECODE_PRIVATE_KEY); - S2N_NO_ALERT(S2N_ERR_INVALID_HELLO_RETRY); - S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_SCHEME); - S2N_NO_ALERT(S2N_ERR_CBC_VERIFY); - S2N_NO_ALERT(S2N_ERR_DH_COPYING_PUBLIC_KEY); - S2N_NO_ALERT(S2N_ERR_SIGN); - S2N_NO_ALERT(S2N_ERR_VERIFY_SIGNATURE); - S2N_NO_ALERT(S2N_ERR_ECDHE_GEN_KEY); - S2N_NO_ALERT(S2N_ERR_ECDHE_SHARED_SECRET); - S2N_NO_ALERT(S2N_ERR_ECDHE_UNSUPPORTED_CURVE); - S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY); - S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS); - S2N_NO_ALERT(S2N_ERR_ECDSA_UNSUPPORTED_CURVE); - S2N_NO_ALERT(S2N_ERR_ECDHE_SERIALIZING); - S2N_NO_ALERT(S2N_ERR_KEM_UNSUPPORTED_PARAMS); - S2N_NO_ALERT(S2N_ERR_SHUTDOWN_RECORD_TYPE); - S2N_NO_ALERT(S2N_ERR_SHUTDOWN_CLOSED); - S2N_NO_ALERT(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); - S2N_NO_ALERT(S2N_ERR_RECORD_LIMIT); - S2N_NO_ALERT(S2N_ERR_CERT_INTENT_INVALID); - S2N_NO_ALERT(S2N_ERR_CRL_LOOKUP_FAILED); - S2N_NO_ALERT(S2N_ERR_CRL_SIGNATURE); - S2N_NO_ALERT(S2N_ERR_CRL_ISSUER); - S2N_NO_ALERT(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); - S2N_NO_ALERT(S2N_ERR_CRL_INVALID_THIS_UPDATE); - S2N_NO_ALERT(S2N_ERR_CRL_INVALID_NEXT_UPDATE); - S2N_NO_ALERT(S2N_ERR_CRL_NOT_YET_VALID); - S2N_NO_ALERT(S2N_ERR_CRL_EXPIRED); - S2N_NO_ALERT(S2N_ERR_INVALID_MAX_FRAG_LEN); - S2N_NO_ALERT(S2N_ERR_MAX_FRAG_LEN_MISMATCH); - S2N_NO_ALERT(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - S2N_NO_ALERT(S2N_ERR_BAD_KEY_SHARE); - S2N_NO_ALERT(S2N_ERR_CANCELLED); - S2N_NO_ALERT(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - S2N_NO_ALERT(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_SIZE); - S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - S2N_NO_ALERT(S2N_ERR_UNSUPPORTED_EXTENSION); - S2N_NO_ALERT(S2N_ERR_DUPLICATE_EXTENSION); - S2N_NO_ALERT(S2N_ERR_MAX_EARLY_DATA_SIZE); - S2N_NO_ALERT(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); - } - - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -} - -static bool s2n_alerts_supported(struct s2n_connection *conn) -{ - /* If running in QUIC mode, QUIC handles alerting. - * S2N should not send or receive alerts. */ - return !s2n_connection_is_quic_enabled(conn); -} - -/* In TLS1.3 all Alerts - *= https://www.rfc-editor.org/rfc/rfc8446#section-6 - *# MUST be treated as error alerts when received - *# regardless of the AlertLevel in the message. - */ -static bool s2n_process_as_warning(struct s2n_connection *conn, uint8_t level, uint8_t type) -{ - /* Only TLS1.2 considers the alert level. The alert level field is - * considered deprecated in TLS1.3. If the protocol version has not - * been negotiated yet, we allow for warnings to avoid premature - * handshake failures before we know the protocol version. */ - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13 || !conn->actual_protocol_version_established) { - return level == S2N_TLS_ALERT_LEVEL_WARNING - && conn->config->alert_behavior == S2N_ALERT_IGNORE_WARNINGS; - } - - /* user_canceled is the only alert currently treated as a warning in TLS1.3. - * We need to treat it as a warning regardless of alert_behavior to avoid marking - * correctly-closed connections as failed. */ - return type == S2N_TLS_ALERT_USER_CANCELED; -} - -int s2n_error_get_alert(int error, uint8_t *alert) -{ - int error_type = s2n_error_get_type(error); - - POSIX_ENSURE_REF(alert); - - switch (error_type) { - case S2N_ERR_T_OK: - case S2N_ERR_T_CLOSED: - case S2N_ERR_T_BLOCKED: - case S2N_ERR_T_USAGE: - case S2N_ERR_T_ALERT: - POSIX_BAIL(S2N_ERR_NO_ALERT); - break; - case S2N_ERR_T_PROTO: - POSIX_GUARD_RESULT(s2n_translate_protocol_error_to_alert(error, alert)); - break; - case S2N_ERR_T_IO: - case S2N_ERR_T_INTERNAL: - *alert = S2N_TLS_ALERT_INTERNAL_ERROR; - break; - } - - return S2N_SUCCESS; -} - -int s2n_process_alert_fragment(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) == 2, S2N_ERR_ALERT_PRESENT); - POSIX_ENSURE(s2n_alerts_supported(conn), S2N_ERR_BAD_MESSAGE); - - while (s2n_stuffer_data_available(&conn->in)) { - uint8_t bytes_required = 2; - - /* Alerts are two bytes long, but can still be fragmented or coalesced */ - if (s2n_stuffer_data_available(&conn->alert_in) == 1) { - bytes_required = 1; - } - - int bytes_to_read = S2N_MIN(bytes_required, s2n_stuffer_data_available(&conn->in)); - - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->alert_in, bytes_to_read)); - - if (s2n_stuffer_data_available(&conn->alert_in) == 2) { - /* Close notifications are handled as shutdowns */ - if (conn->alert_in_data[1] == S2N_TLS_ALERT_CLOSE_NOTIFY) { - s2n_atomic_flag_set(&conn->read_closed); - s2n_atomic_flag_set(&conn->close_notify_received); - return 0; - } - - /* Ignore warning-level alerts if we're in warning-tolerant mode */ - if (s2n_process_as_warning(conn, conn->alert_in_data[0], conn->alert_in_data[1])) { - POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); - return 0; - } - - /* RFC 5077 5.1 - Expire any cached session on an error alert */ - if (s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { - conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); - } - - /* All other alerts are treated as fatal errors. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-6 - *# Unknown Alert types MUST be treated as error alerts. - */ - POSIX_GUARD_RESULT(s2n_connection_set_closed(conn)); - s2n_atomic_flag_set(&conn->error_alert_received); - POSIX_BAIL(S2N_ERR_ALERT); - } - } - - return 0; -} - -static S2N_RESULT s2n_queue_reader_alert(struct s2n_connection *conn, s2n_tls_alert_code code) -{ - RESULT_ENSURE_REF(conn); - if (!conn->reader_alert_out) { - conn->reader_alert_out = code; - } - return S2N_RESULT_OK; -} - -int s2n_queue_reader_unsupported_protocol_version_alert(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_PROTOCOL_VERSION)); - return S2N_SUCCESS; -} - -int s2n_queue_reader_handshake_failure_alert(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_HANDSHAKE_FAILURE)); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_queue_reader_no_renegotiation_alert(struct s2n_connection *conn) -{ - /** - *= https://www.rfc-editor.org/rfc/rfc5746#4.5 - *# SSLv3 does not define the "no_renegotiation" alert (and does - *# not offer a way to indicate a refusal to renegotiate at a "warning" - *# level). SSLv3 clients that refuse renegotiation SHOULD use a fatal - *# handshake_failure alert. - **/ - if (s2n_connection_get_protocol_version(conn) == S2N_SSLv3) { - RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); - } - - if (!conn->reader_warning_out) { - conn->reader_warning_out = S2N_TLS_ALERT_NO_RENEGOTIATION; - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_alerts_write_error_or_close_notify(struct s2n_connection *conn) -{ - if (!s2n_alerts_supported(conn)) { - return S2N_RESULT_OK; - } - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 - *= type=exception - *= reason=Specific alerts could expose a side-channel attack vector. - *# The phrases "terminate the connection with an X - *# alert" and "abort the handshake with an X alert" mean that the - *# implementation MUST send alert X if it sends any alert. - * - * By default, s2n-tls sends a generic close_notify alert, even in - * response to fatal errors. This is done to avoid potential - * side-channel attacks since specific alerts could reveal information - * about why the error occurred. - */ - uint8_t code = S2N_TLS_ALERT_CLOSE_NOTIFY; - uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; - - /* s2n-tls sends a very small subset of more specific error alerts. - * Since either the reader or the writer can produce one of these alerts, - * but only a single alert can be reported, we prioritize writer alerts. - */ - if (conn->writer_alert_out) { - code = conn->writer_alert_out; - level = S2N_TLS_ALERT_LEVEL_FATAL; - } else if (conn->reader_alert_out) { - code = conn->reader_alert_out; - level = S2N_TLS_ALERT_LEVEL_FATAL; - } - - struct s2n_blob alert = { 0 }; - uint8_t alert_bytes[] = { level, code }; - RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); - - RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); - conn->alert_sent = true; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_alerts_write_warning(struct s2n_connection *conn) -{ - if (!s2n_alerts_supported(conn)) { - return S2N_RESULT_OK; - } - - uint8_t code = conn->reader_warning_out; - uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; - - struct s2n_blob alert = { 0 }; - uint8_t alert_bytes[] = { level, code }; - RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); - - RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_alerts.h" + +#include + +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_atomic.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +#define S2N_ALERT_CASE(error, alert_code) \ + case (error): \ + *alert = (alert_code); \ + return S2N_RESULT_OK + +#define S2N_NO_ALERT(error) \ + case (error): \ + RESULT_BAIL(S2N_ERR_NO_ALERT) + +static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t *alert) +{ + RESULT_ENSURE_REF(alert); + + switch (error_code) { + S2N_ALERT_CASE(S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION); + S2N_ALERT_CASE(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + S2N_ALERT_CASE(S2N_ERR_MISSING_CLIENT_CERT, S2N_TLS_ALERT_CERTIFICATE_REQUIRED); + + /* TODO: The ERR_BAD_MESSAGE -> ALERT_UNEXPECTED_MESSAGE mapping + * isn't always correct. Sometimes s2n-tls uses ERR_BAD_MESSAGE + * to indicate S2N_TLS_ALERT_ILLEGAL_PARAMETER instead. + * We'll want to add a new error to distinguish between the two usages: + * our errors should be equally or more specific than alerts, not less. + */ + S2N_ALERT_CASE(S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + S2N_ALERT_CASE(S2N_ERR_UNEXPECTED_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + S2N_ALERT_CASE(S2N_ERR_MISSING_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + + /* For errors involving secure renegotiation: + *= https://www.rfc-editor.org/rfc/rfc5746#3.4 + *# Note: later in Section 3, "abort the handshake" is used as + *# shorthand for "send a fatal handshake_failure alert and + *# terminate the connection". + */ + S2N_ALERT_CASE(S2N_ERR_NO_RENEGOTIATION, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + + S2N_ALERT_CASE(S2N_ERR_KTLS_KEYUPDATE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + + /* For errors involving certificates */ + + /* This error is used in several ways so make it a general certificate issue + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# certificate_unknown: Some other (unspecified) issue arose in + *# processing the certificate, rendering it unacceptable. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + S2N_ALERT_CASE(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + S2N_ALERT_CASE(S2N_ERR_CERT_INVALID_HOSTNAME, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# certificate_revoked: A certificate was revoked by its signer. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_REVOKED, S2N_TLS_ALERT_CERTIFICATE_REVOKED); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# certificate_expired: A certificate has expired or is not currently + *# valid. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_NOT_YET_VALID, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); + S2N_ALERT_CASE(S2N_ERR_CERT_EXPIRED, S2N_TLS_ALERT_CERTIFICATE_EXPIRED); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# unsupported_certificate: A certificate was of an unsupported type. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_TYPE_UNSUPPORTED, S2N_TLS_ALERT_UNSUPPORTED_CERTIFICATE); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# access_denied: A valid certificate or PSK was received, but when + *# access control was applied, the sender decided not to proceed with + *# negotiation. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_REJECTED, S2N_TLS_ALERT_ACCESS_DENIED); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *# bad_certificate: A certificate was corrupt, contained signatures + *# that did not verify correctly, etc. + */ + S2N_ALERT_CASE(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, S2N_TLS_ALERT_BAD_CERTIFICATE); + S2N_ALERT_CASE(S2N_ERR_CERT_INVALID, S2N_TLS_ALERT_BAD_CERTIFICATE); + S2N_ALERT_CASE(S2N_ERR_DECODE_CERTIFICATE, S2N_TLS_ALERT_BAD_CERTIFICATE); + + /* TODO: Add mappings for other protocol errors. + */ + S2N_NO_ALERT(S2N_ERR_ENCRYPT); + S2N_NO_ALERT(S2N_ERR_DECRYPT); + S2N_NO_ALERT(S2N_ERR_KEY_INIT); + S2N_NO_ALERT(S2N_ERR_KEY_DESTROY); + S2N_NO_ALERT(S2N_ERR_DH_SERIALIZING); + S2N_NO_ALERT(S2N_ERR_DH_SHARED_SECRET); + S2N_NO_ALERT(S2N_ERR_DH_WRITING_PUBLIC_KEY); + S2N_NO_ALERT(S2N_ERR_DH_FAILED_SIGNING); + S2N_NO_ALERT(S2N_ERR_DH_COPYING_PARAMETERS); + S2N_NO_ALERT(S2N_ERR_DH_GENERATING_PARAMETERS); + S2N_NO_ALERT(S2N_ERR_CIPHER_NOT_SUPPORTED); + S2N_NO_ALERT(S2N_ERR_NO_APPLICATION_PROTOCOL); + S2N_NO_ALERT(S2N_ERR_FALLBACK_DETECTED); + S2N_NO_ALERT(S2N_ERR_HASH_DIGEST_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_INIT_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_UPDATE_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_COPY_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_WIPE_FAILED); + S2N_NO_ALERT(S2N_ERR_HASH_NOT_READY); + S2N_NO_ALERT(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED); + S2N_NO_ALERT(S2N_ERR_DECODE_PRIVATE_KEY); + S2N_NO_ALERT(S2N_ERR_INVALID_HELLO_RETRY); + S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_SCHEME); + S2N_NO_ALERT(S2N_ERR_CBC_VERIFY); + S2N_NO_ALERT(S2N_ERR_DH_COPYING_PUBLIC_KEY); + S2N_NO_ALERT(S2N_ERR_SIGN); + S2N_NO_ALERT(S2N_ERR_VERIFY_SIGNATURE); + S2N_NO_ALERT(S2N_ERR_ECDHE_GEN_KEY); + S2N_NO_ALERT(S2N_ERR_ECDHE_SHARED_SECRET); + S2N_NO_ALERT(S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY); + S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS); + S2N_NO_ALERT(S2N_ERR_ECDSA_UNSUPPORTED_CURVE); + S2N_NO_ALERT(S2N_ERR_ECDHE_SERIALIZING); + S2N_NO_ALERT(S2N_ERR_KEM_UNSUPPORTED_PARAMS); + S2N_NO_ALERT(S2N_ERR_SHUTDOWN_RECORD_TYPE); + S2N_NO_ALERT(S2N_ERR_SHUTDOWN_CLOSED); + S2N_NO_ALERT(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + S2N_NO_ALERT(S2N_ERR_RECORD_LIMIT); + S2N_NO_ALERT(S2N_ERR_CERT_INTENT_INVALID); + S2N_NO_ALERT(S2N_ERR_CRL_LOOKUP_FAILED); + S2N_NO_ALERT(S2N_ERR_CRL_SIGNATURE); + S2N_NO_ALERT(S2N_ERR_CRL_ISSUER); + S2N_NO_ALERT(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); + S2N_NO_ALERT(S2N_ERR_CRL_INVALID_THIS_UPDATE); + S2N_NO_ALERT(S2N_ERR_CRL_INVALID_NEXT_UPDATE); + S2N_NO_ALERT(S2N_ERR_CRL_NOT_YET_VALID); + S2N_NO_ALERT(S2N_ERR_CRL_EXPIRED); + S2N_NO_ALERT(S2N_ERR_INVALID_MAX_FRAG_LEN); + S2N_NO_ALERT(S2N_ERR_MAX_FRAG_LEN_MISMATCH); + S2N_NO_ALERT(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + S2N_NO_ALERT(S2N_ERR_BAD_KEY_SHARE); + S2N_NO_ALERT(S2N_ERR_CANCELLED); + S2N_NO_ALERT(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + S2N_NO_ALERT(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_SIZE); + S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + S2N_NO_ALERT(S2N_ERR_UNSUPPORTED_EXTENSION); + S2N_NO_ALERT(S2N_ERR_DUPLICATE_EXTENSION); + S2N_NO_ALERT(S2N_ERR_MAX_EARLY_DATA_SIZE); + S2N_NO_ALERT(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); + } + + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static bool s2n_alerts_supported(struct s2n_connection *conn) +{ + /* If running in QUIC mode, QUIC handles alerting. + * S2N should not send or receive alerts. */ + return !s2n_connection_is_quic_enabled(conn); +} + +/* In TLS1.3 all Alerts + *= https://www.rfc-editor.org/rfc/rfc8446#section-6 + *# MUST be treated as error alerts when received + *# regardless of the AlertLevel in the message. + */ +static bool s2n_process_as_warning(struct s2n_connection *conn, uint8_t level, uint8_t type) +{ + /* Only TLS1.2 considers the alert level. The alert level field is + * considered deprecated in TLS1.3. If the protocol version has not + * been negotiated yet, we allow for warnings to avoid premature + * handshake failures before we know the protocol version. */ + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13 || !conn->actual_protocol_version_established) { + return level == S2N_TLS_ALERT_LEVEL_WARNING + && conn->config->alert_behavior == S2N_ALERT_IGNORE_WARNINGS; + } + + /* user_canceled is the only alert currently treated as a warning in TLS1.3. + * We need to treat it as a warning regardless of alert_behavior to avoid marking + * correctly-closed connections as failed. */ + return type == S2N_TLS_ALERT_USER_CANCELED; +} + +int s2n_error_get_alert(int error, uint8_t *alert) +{ + int error_type = s2n_error_get_type(error); + + POSIX_ENSURE_REF(alert); + + switch (error_type) { + case S2N_ERR_T_OK: + case S2N_ERR_T_CLOSED: + case S2N_ERR_T_BLOCKED: + case S2N_ERR_T_USAGE: + case S2N_ERR_T_ALERT: + POSIX_BAIL(S2N_ERR_NO_ALERT); + break; + case S2N_ERR_T_PROTO: + POSIX_GUARD_RESULT(s2n_translate_protocol_error_to_alert(error, alert)); + break; + case S2N_ERR_T_IO: + case S2N_ERR_T_INTERNAL: + *alert = S2N_TLS_ALERT_INTERNAL_ERROR; + break; + } + + return S2N_SUCCESS; +} + +int s2n_process_alert_fragment(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) == 2, S2N_ERR_ALERT_PRESENT); + POSIX_ENSURE(s2n_alerts_supported(conn), S2N_ERR_BAD_MESSAGE); + + while (s2n_stuffer_data_available(&conn->in)) { + uint8_t bytes_required = 2; + + /* Alerts are two bytes long, but can still be fragmented or coalesced */ + if (s2n_stuffer_data_available(&conn->alert_in) == 1) { + bytes_required = 1; + } + + int bytes_to_read = S2N_MIN(bytes_required, s2n_stuffer_data_available(&conn->in)); + + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->alert_in, bytes_to_read)); + + if (s2n_stuffer_data_available(&conn->alert_in) == 2) { + /* Close notifications are handled as shutdowns */ + if (conn->alert_in_data[1] == S2N_TLS_ALERT_CLOSE_NOTIFY) { + s2n_atomic_flag_set(&conn->read_closed); + s2n_atomic_flag_set(&conn->close_notify_received); + return 0; + } + + /* Ignore warning-level alerts if we're in warning-tolerant mode */ + if (s2n_process_as_warning(conn, conn->alert_in_data[0], conn->alert_in_data[1])) { + POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); + return 0; + } + + /* RFC 5077 5.1 - Expire any cached session on an error alert */ + if (s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { + conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); + } + + /* All other alerts are treated as fatal errors. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-6 + *# Unknown Alert types MUST be treated as error alerts. + */ + POSIX_GUARD_RESULT(s2n_connection_set_closed(conn)); + s2n_atomic_flag_set(&conn->error_alert_received); + POSIX_BAIL(S2N_ERR_ALERT); + } + } + + return 0; +} + +static S2N_RESULT s2n_queue_reader_alert(struct s2n_connection *conn, s2n_tls_alert_code code) +{ + RESULT_ENSURE_REF(conn); + if (!conn->reader_alert_out) { + conn->reader_alert_out = code; + } + return S2N_RESULT_OK; +} + +int s2n_queue_reader_unsupported_protocol_version_alert(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_PROTOCOL_VERSION)); + return S2N_SUCCESS; +} + +int s2n_queue_reader_handshake_failure_alert(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_HANDSHAKE_FAILURE)); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_queue_reader_no_renegotiation_alert(struct s2n_connection *conn) +{ + /** + *= https://www.rfc-editor.org/rfc/rfc5746#4.5 + *# SSLv3 does not define the "no_renegotiation" alert (and does + *# not offer a way to indicate a refusal to renegotiate at a "warning" + *# level). SSLv3 clients that refuse renegotiation SHOULD use a fatal + *# handshake_failure alert. + **/ + if (s2n_connection_get_protocol_version(conn) == S2N_SSLv3) { + RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); + } + + if (!conn->reader_warning_out) { + conn->reader_warning_out = S2N_TLS_ALERT_NO_RENEGOTIATION; + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_alerts_write_error_or_close_notify(struct s2n_connection *conn) +{ + if (!s2n_alerts_supported(conn)) { + return S2N_RESULT_OK; + } + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.2 + *= type=exception + *= reason=Specific alerts could expose a side-channel attack vector. + *# The phrases "terminate the connection with an X + *# alert" and "abort the handshake with an X alert" mean that the + *# implementation MUST send alert X if it sends any alert. + * + * By default, s2n-tls sends a generic close_notify alert, even in + * response to fatal errors. This is done to avoid potential + * side-channel attacks since specific alerts could reveal information + * about why the error occurred. + */ + uint8_t code = S2N_TLS_ALERT_CLOSE_NOTIFY; + uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; + + /* s2n-tls sends a very small subset of more specific error alerts. + * Since either the reader or the writer can produce one of these alerts, + * but only a single alert can be reported, we prioritize writer alerts. + */ + if (conn->writer_alert_out) { + code = conn->writer_alert_out; + level = S2N_TLS_ALERT_LEVEL_FATAL; + } else if (conn->reader_alert_out) { + code = conn->reader_alert_out; + level = S2N_TLS_ALERT_LEVEL_FATAL; + } + + struct s2n_blob alert = { 0 }; + uint8_t alert_bytes[] = { level, code }; + RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); + + RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); + conn->alert_sent = true; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_alerts_write_warning(struct s2n_connection *conn) +{ + if (!s2n_alerts_supported(conn)) { + return S2N_RESULT_OK; + } + + uint8_t code = conn->reader_warning_out; + uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING; + + struct s2n_blob alert = { 0 }; + uint8_t alert_bytes[] = { level, code }; + RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes))); + + RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert)); + return S2N_RESULT_OK; +} diff --git a/tls/s2n_async_pkey.h b/tls/s2n_async_pkey.h index 19779cfba36..bd11490956b 100644 --- a/tls/s2n_async_pkey.h +++ b/tls/s2n_async_pkey.h @@ -1,107 +1,107 @@ -/* - * 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 - -#include "crypto/s2n_signature.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_result.h" - -struct s2n_connection; - -typedef int (*s2n_async_pkey_sign_complete)(struct s2n_connection *conn, struct s2n_blob *signature); -typedef int (*s2n_async_pkey_decrypt_complete)(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted); - -struct s2n_async_pkey_op; - -/* Guard to handle async states inside handler which uses async pkey operations. If async operation was not invoked - * it means that we enter this handler for the first time and handler may or may not use async operation, so we let it - * continue. If async operation is invoking or was invoked, but yet to be complete, we error out of the handler to let - * s2n_handle_retry_state try again. If async operation was complete we clear the state and let s2n_handle_retry_state - * proceed to the next handler */ -#if defined(_MSC_VER) -#define S2N_ASYNC_PKEY_GUARD(conn) \ - do { \ - POSIX_GUARD_PTR((conn)); \ - switch ((conn)->handshake.async_state) { \ - case S2N_ASYNC_NOT_INVOKED: \ - break; \ - \ - case S2N_ASYNC_INVOKED: \ - POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ - \ - case S2N_ASYNC_COMPLETE: \ - /* clean up state and return a success from handler */ \ - (conn)->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ - return S2N_SUCCESS; \ - } \ - } while (0) -#else -#define S2N_ASYNC_PKEY_GUARD(conn) \ - do { \ - __typeof(conn) __tmp_conn = (conn); \ - POSIX_GUARD_PTR(__tmp_conn); \ - switch (conn->handshake.async_state) { \ - case S2N_ASYNC_NOT_INVOKED: \ - break; \ - \ - case S2N_ASYNC_INVOKED: \ - POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ - \ - case S2N_ASYNC_COMPLETE: \ - /* clean up state and return a success from handler */ \ - __tmp_conn->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ - return S2N_SUCCESS; \ - } \ - } while (0) -#endif - -/* Macros for safe exection of async sign/decrypt. - * - * When operation is done asynchronously, we drop to s2n_negotiate loop with S2N_ERR_ASYNC_BLOCKED error and do not - * perform any of the operations to follow after s2n_async* call. To enforce that there are no operations after the - * call, we use a macro which directly returns the result of s2n_async* operation forcing compiler to error out on - * unreachable code and forcing developer to use on_complete function instead */ -#define S2N_ASYNC_PKEY_DECRYPT(conn, encrypted, init_decrypted, on_complete) \ - return s2n_result_is_ok(s2n_async_pkey_decrypt(conn, encrypted, init_decrypted, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; - -#define S2N_ASYNC_PKEY_SIGN(conn, sig_alg, digest, on_complete) \ - return s2n_result_is_ok(s2n_async_pkey_sign(conn, sig_alg, digest, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; - -int s2n_async_pkey_op_perform(struct s2n_async_pkey_op *op, s2n_cert_private_key *key); -int s2n_async_pkey_op_apply(struct s2n_async_pkey_op *op, struct s2n_connection *conn); -int s2n_async_pkey_op_free(struct s2n_async_pkey_op *op); - -int s2n_async_pkey_op_get_op_type(struct s2n_async_pkey_op *op, s2n_async_pkey_op_type *type); -int s2n_async_pkey_op_get_input_size(struct s2n_async_pkey_op *op, uint32_t *data_len); -int s2n_async_pkey_op_get_input(struct s2n_async_pkey_op *op, uint8_t *data, uint32_t data_len); -int s2n_async_pkey_op_set_output(struct s2n_async_pkey_op *op, const uint8_t *data, uint32_t data_len); -int s2n_async_pkey_op_set_validation_mode(struct s2n_async_pkey_op *op, s2n_async_pkey_validation_mode mode); - -S2N_RESULT s2n_async_pkey_verify_signature(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *digest, struct s2n_blob *signature); -S2N_RESULT s2n_async_pkey_decrypt(struct s2n_connection *conn, struct s2n_blob *encrypted, struct s2n_blob *init_decrypted, - s2n_async_pkey_decrypt_complete on_complete); -S2N_RESULT s2n_async_pkey_sign(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, struct s2n_hash_state *digest, - s2n_async_pkey_sign_complete on_complete); - -struct s2n_async_pkey_verify_data { - struct s2n_hash_state digest; - s2n_signature_algorithm sig_alg; - struct s2n_blob signature; -}; - -int s2n_async_pkey_verify(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, - struct s2n_hash_state *digest, struct s2n_blob *signature); +/* + * 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 + +#include "crypto/s2n_signature.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" + +struct s2n_connection; + +typedef int (*s2n_async_pkey_sign_complete)(struct s2n_connection *conn, struct s2n_blob *signature); +typedef int (*s2n_async_pkey_decrypt_complete)(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted); + +struct s2n_async_pkey_op; + +/* Guard to handle async states inside handler which uses async pkey operations. If async operation was not invoked + * it means that we enter this handler for the first time and handler may or may not use async operation, so we let it + * continue. If async operation is invoking or was invoked, but yet to be complete, we error out of the handler to let + * s2n_handle_retry_state try again. If async operation was complete we clear the state and let s2n_handle_retry_state + * proceed to the next handler */ +#if defined(_MSC_VER) +#define S2N_ASYNC_PKEY_GUARD(conn) \ + do { \ + POSIX_GUARD_PTR((conn)); \ + switch ((conn)->handshake.async_state) { \ + case S2N_ASYNC_NOT_INVOKED: \ + break; \ + \ + case S2N_ASYNC_INVOKED: \ + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ + \ + case S2N_ASYNC_COMPLETE: \ + /* clean up state and return a success from handler */ \ + (conn)->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ + return S2N_SUCCESS; \ + } \ + } while (0) +#else +#define S2N_ASYNC_PKEY_GUARD(conn) \ + do { \ + __typeof(conn) __tmp_conn = (conn); \ + POSIX_GUARD_PTR(__tmp_conn); \ + switch (conn->handshake.async_state) { \ + case S2N_ASYNC_NOT_INVOKED: \ + break; \ + \ + case S2N_ASYNC_INVOKED: \ + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); \ + \ + case S2N_ASYNC_COMPLETE: \ + /* clean up state and return a success from handler */ \ + __tmp_conn->handshake.async_state = S2N_ASYNC_NOT_INVOKED; \ + return S2N_SUCCESS; \ + } \ + } while (0) +#endif + +/* Macros for safe exection of async sign/decrypt. + * + * When operation is done asynchronously, we drop to s2n_negotiate loop with S2N_ERR_ASYNC_BLOCKED error and do not + * perform any of the operations to follow after s2n_async* call. To enforce that there are no operations after the + * call, we use a macro which directly returns the result of s2n_async* operation forcing compiler to error out on + * unreachable code and forcing developer to use on_complete function instead */ +#define S2N_ASYNC_PKEY_DECRYPT(conn, encrypted, init_decrypted, on_complete) \ + return s2n_result_is_ok(s2n_async_pkey_decrypt(conn, encrypted, init_decrypted, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; + +#define S2N_ASYNC_PKEY_SIGN(conn, sig_alg, digest, on_complete) \ + return s2n_result_is_ok(s2n_async_pkey_sign(conn, sig_alg, digest, on_complete)) ? S2N_SUCCESS : S2N_FAILURE; + +int s2n_async_pkey_op_perform(struct s2n_async_pkey_op *op, s2n_cert_private_key *key); +int s2n_async_pkey_op_apply(struct s2n_async_pkey_op *op, struct s2n_connection *conn); +int s2n_async_pkey_op_free(struct s2n_async_pkey_op *op); + +int s2n_async_pkey_op_get_op_type(struct s2n_async_pkey_op *op, s2n_async_pkey_op_type *type); +int s2n_async_pkey_op_get_input_size(struct s2n_async_pkey_op *op, uint32_t *data_len); +int s2n_async_pkey_op_get_input(struct s2n_async_pkey_op *op, uint8_t *data, uint32_t data_len); +int s2n_async_pkey_op_set_output(struct s2n_async_pkey_op *op, const uint8_t *data, uint32_t data_len); +int s2n_async_pkey_op_set_validation_mode(struct s2n_async_pkey_op *op, s2n_async_pkey_validation_mode mode); + +S2N_RESULT s2n_async_pkey_verify_signature(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature); +S2N_RESULT s2n_async_pkey_decrypt(struct s2n_connection *conn, struct s2n_blob *encrypted, struct s2n_blob *init_decrypted, + s2n_async_pkey_decrypt_complete on_complete); +S2N_RESULT s2n_async_pkey_sign(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, struct s2n_hash_state *digest, + s2n_async_pkey_sign_complete on_complete); + +struct s2n_async_pkey_verify_data { + struct s2n_hash_state digest; + s2n_signature_algorithm sig_alg; + struct s2n_blob signature; +}; + +int s2n_async_pkey_verify(struct s2n_connection *conn, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature); diff --git a/tls/s2n_cbc.c b/tls/s2n_cbc.c index e5d3d530f15..b2a8bf28d21 100644 --- a/tls/s2n_cbc.c +++ b/tls/s2n_cbc.c @@ -1,104 +1,104 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_hmac.h" -#include "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* A TLS CBC record looks like .. - * - * [ Payload data ] [ HMAC ] [ Padding ] [ Padding length byte ] - * - * Each byte in the padding is expected to be set to the same value - * as the padding length byte. So if the padding length byte is '2' - * then the padding will be [ '2', '2' ] (there'll be three bytes - * set to that value if you include the padding length byte). - * - * The goal of s2n_verify_cbc() is to verify that the padding and hmac - * are correct, without leaking (via timing) how much padding there - * actually is: as this is considered secret. - * - * In addition to our efforts here though, s2n also wraps any CBC - * verification error (or record parsing error in general) with - * a randomized delay of between 1ms and 10 seconds. See s2n_connection.c. - * This amount of delay randomization is sufficient to increase the - * complexity of attack for even a 1 microsecond timing leak (which - * is quite large) by a factor of around 83 trillion. - */ -int s2n_verify_cbc(struct s2n_connection *conn, struct s2n_hmac_state *hmac, struct s2n_blob *decrypted) -{ - uint8_t mac_digest_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(hmac->alg, &mac_digest_size)); - - /* The record has to be at least big enough to contain the MAC, - * plus the padding length byte */ - POSIX_ENSURE_GT(decrypted->size, mac_digest_size); - - int payload_and_padding_size = decrypted->size - mac_digest_size; - - /* Determine what the padding length is */ - uint8_t padding_length = decrypted->data[decrypted->size - 1]; - - int payload_length = S2N_MAX(payload_and_padding_size - padding_length - 1, 0); - - /* Update the MAC */ - POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, payload_length)); - int currently_in_hash_block = hmac->currently_in_hash_block; - - /* Check the MAC */ - uint8_t check_digest[S2N_MAX_DIGEST_LEN]; - POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); - POSIX_GUARD(s2n_hmac_digest_two_compression_rounds(hmac, check_digest, mac_digest_size)); - - int mismatches = s2n_constant_time_equals(decrypted->data + payload_length, check_digest, mac_digest_size) ^ 1; - - /* Compute a MAC on the rest of the data so that we perform the same number of hash operations. - * Include the partial hash block from the first MAC to ensure we use the same number of blocks. - */ - POSIX_GUARD(s2n_hmac_reset(hmac)); - POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, currently_in_hash_block)); - POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data + payload_length + mac_digest_size, decrypted->size - payload_length - mac_digest_size - 1)); - - /* SSLv3 doesn't specify what the padding should actually be, so - * padding bytes are not verified. This is the vector for the POODLE - * attack (CVE-2014-3566). SSLv3 is disabled by default and not - * recommended. If SSLv3 must be used, the blinding feature (enabled - * by default) helps mitigates this issue as well. - */ - if (conn->actual_protocol_version == S2N_SSLv3) { - return 0 - mismatches; - } - - /* Check the maximum amount that could theoretically be padding */ - uint32_t check = S2N_MIN(255, (payload_and_padding_size - 1)); - - POSIX_ENSURE_GTE(check, padding_length); - - uint32_t cutoff = check - padding_length; - for (size_t i = 0, j = decrypted->size - 1 - check; i < check && j < decrypted->size; i++, j++) { - uint8_t mask = ~(0xff << ((i >= cutoff) * 8)); - mismatches |= (decrypted->data[j] ^ padding_length) & mask; - } - - S2N_ERROR_IF(mismatches, S2N_ERR_CBC_VERIFY); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hmac.h" +#include "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* A TLS CBC record looks like .. + * + * [ Payload data ] [ HMAC ] [ Padding ] [ Padding length byte ] + * + * Each byte in the padding is expected to be set to the same value + * as the padding length byte. So if the padding length byte is '2' + * then the padding will be [ '2', '2' ] (there'll be three bytes + * set to that value if you include the padding length byte). + * + * The goal of s2n_verify_cbc() is to verify that the padding and hmac + * are correct, without leaking (via timing) how much padding there + * actually is: as this is considered secret. + * + * In addition to our efforts here though, s2n also wraps any CBC + * verification error (or record parsing error in general) with + * a randomized delay of between 1ms and 10 seconds. See s2n_connection.c. + * This amount of delay randomization is sufficient to increase the + * complexity of attack for even a 1 microsecond timing leak (which + * is quite large) by a factor of around 83 trillion. + */ +int s2n_verify_cbc(struct s2n_connection *conn, struct s2n_hmac_state *hmac, struct s2n_blob *decrypted) +{ + uint8_t mac_digest_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(hmac->alg, &mac_digest_size)); + + /* The record has to be at least big enough to contain the MAC, + * plus the padding length byte */ + POSIX_ENSURE_GT(decrypted->size, mac_digest_size); + + int payload_and_padding_size = decrypted->size - mac_digest_size; + + /* Determine what the padding length is */ + uint8_t padding_length = decrypted->data[decrypted->size - 1]; + + int payload_length = S2N_MAX(payload_and_padding_size - padding_length - 1, 0); + + /* Update the MAC */ + POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, payload_length)); + int currently_in_hash_block = hmac->currently_in_hash_block; + + /* Check the MAC */ + uint8_t check_digest[S2N_MAX_DIGEST_LEN]; + POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); + POSIX_GUARD(s2n_hmac_digest_two_compression_rounds(hmac, check_digest, mac_digest_size)); + + int mismatches = s2n_constant_time_equals(decrypted->data + payload_length, check_digest, mac_digest_size) ^ 1; + + /* Compute a MAC on the rest of the data so that we perform the same number of hash operations. + * Include the partial hash block from the first MAC to ensure we use the same number of blocks. + */ + POSIX_GUARD(s2n_hmac_reset(hmac)); + POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data, currently_in_hash_block)); + POSIX_GUARD(s2n_hmac_update(hmac, decrypted->data + payload_length + mac_digest_size, decrypted->size - payload_length - mac_digest_size - 1)); + + /* SSLv3 doesn't specify what the padding should actually be, so + * padding bytes are not verified. This is the vector for the POODLE + * attack (CVE-2014-3566). SSLv3 is disabled by default and not + * recommended. If SSLv3 must be used, the blinding feature (enabled + * by default) helps mitigates this issue as well. + */ + if (conn->actual_protocol_version == S2N_SSLv3) { + return 0 - mismatches; + } + + /* Check the maximum amount that could theoretically be padding */ + uint32_t check = S2N_MIN(255, (payload_and_padding_size - 1)); + + POSIX_ENSURE_GTE(check, padding_length); + + uint32_t cutoff = check - padding_length; + for (size_t i = 0, j = decrypted->size - 1 - check; i < check && j < decrypted->size; i++, j++) { + uint8_t mask = ~(0xff << ((i >= cutoff) * 8)); + mismatches |= (decrypted->data[j] ^ padding_length) & mask; + } + + S2N_ERROR_IF(mismatches, S2N_ERR_CBC_VERIFY); + + return 0; +} diff --git a/tls/s2n_cipher_preferences.c b/tls/s2n_cipher_preferences.c index b60d4f55695..ba1639ca5a3 100644 --- a/tls/s2n_cipher_preferences.c +++ b/tls/s2n_cipher_preferences.c @@ -1,2119 +1,2119 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_cipher_preferences.h" - -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "tls/s2n_config.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_kex.h" -#include "utils/s2n_safety.h" - -/* clang-format off */ -/* TLS 1.3 cipher suites, in order of preference. - * Can be added to other ciphers suite lists to enable - * TLS1.3 compatibility. */ -#define S2N_TLS13_CIPHER_SUITES_20190801 \ - &s2n_tls13_aes_256_gcm_sha384, \ - &s2n_tls13_aes_128_gcm_sha256, \ - &s2n_tls13_chacha20_poly1305_sha256 - -#define S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 \ - &s2n_tls13_aes_128_gcm_sha256, \ - &s2n_tls13_aes_256_gcm_sha384, \ - &s2n_tls13_chacha20_poly1305_sha256 - -/* s2n's list of cipher suites, in order of preferences, as of 2019-08-01 */ -struct s2n_cipher_suite *cipher_suites_20190801[] = { - S2N_TLS13_CIPHER_SUITES_20190801, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20190801 = { - .count = s2n_array_len(cipher_suites_20190801), - .suites = cipher_suites_20190801, - .allow_chacha20_boosting = false, -}; - -/* Same as 20190801, but with ECDSA for TLS 1.2 added */ -struct s2n_cipher_suite *cipher_suites_20210831[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20210831 = { - .count = s2n_array_len(cipher_suites_20210831), - .suites = cipher_suites_20210831, - .allow_chacha20_boosting = false, -}; - -/* - * These cipher suites were chosen based on the following specification: - * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf - */ -struct s2n_cipher_suite *cipher_suites_default_fips[] = { - /* tls1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - /* tls1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_default_fips = { - .count = s2n_array_len(cipher_suites_default_fips), - .suites = cipher_suites_default_fips, - .allow_chacha20_boosting = false, -}; - -/* s2n's list of cipher suites, in order of preference, as of 2014-06-01 */ -struct s2n_cipher_suite *cipher_suites_20140601[] = { - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_20140601 = { - .count = s2n_array_len(cipher_suites_20140601), - .suites = cipher_suites_20140601, - .allow_chacha20_boosting = false, -}; - -/* Disable SSLv3 due to POODLE */ -const struct s2n_cipher_preferences cipher_preferences_20141001 = { - .count = s2n_array_len(cipher_suites_20140601), - .suites = cipher_suites_20140601, - .allow_chacha20_boosting = false, -}; - -/* Disable RC4 */ -struct s2n_cipher_suite *cipher_suites_20150202[] = { - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20150202 = { - .count = s2n_array_len(cipher_suites_20150202), - .suites = cipher_suites_20150202, - .allow_chacha20_boosting = false, -}; - -/* Support AES-GCM modes */ -struct s2n_cipher_suite *cipher_suites_20150214[] = { - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20150214 = { - .count = s2n_array_len(cipher_suites_20150214), - .suites = cipher_suites_20150214, - .allow_chacha20_boosting = false, -}; - -/* Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients */ -struct s2n_cipher_suite *cipher_suites_20160411[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20160411 = { - .count = s2n_array_len(cipher_suites_20160411), - .suites = cipher_suites_20160411, - .allow_chacha20_boosting = false, -}; - -/* Use ECDHE instead of plain DHE. Prioritize ECDHE in favour of non ECDHE; GCM in favour of CBC; AES128 in favour of AES256. */ -struct s2n_cipher_suite *cipher_suites_20150306[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20150306 = { - .count = s2n_array_len(cipher_suites_20150306), - .suites = cipher_suites_20150306, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20160804[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20160804 = { - .count = s2n_array_len(cipher_suites_20160804), - .suites = cipher_suites_20160804, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20160824[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20160824 = { - .count = s2n_array_len(cipher_suites_20160824), - .suites = cipher_suites_20160824, - .allow_chacha20_boosting = false, -}; - -/* Add ChaCha20 suite */ -struct s2n_cipher_suite *cipher_suites_20170210[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20170210 = { - .count = s2n_array_len(cipher_suites_20170210), - .suites = cipher_suites_20170210, - .allow_chacha20_boosting = false, -}; - -/* - * TLS1.3 support. - * FIPS compliant. - * No DHE (would require extra setup with s2n_config_add_dhparams) - */ -struct s2n_cipher_suite *cipher_suites_20230317[] = { - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - - /* TLS1.3 */ - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20230317 = { - .count = s2n_array_len(cipher_suites_20230317), - .suites = cipher_suites_20230317, - .allow_chacha20_boosting = false, -}; - -/* - * TLS1.3 support. - */ -struct s2n_cipher_suite *cipher_suites_20251014[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251014 = { - .count = s2n_array_len(cipher_suites_20251014), - .suites = cipher_suites_20251014, - .allow_chacha20_boosting = false, -}; - -/* - * FIPS - * TLS1.3 support. - * Same as 20251014 but without chachapoly - */ -struct s2n_cipher_suite *cipher_suites_20251015[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251015 = { - .count = s2n_array_len(cipher_suites_20251015), - .suites = cipher_suites_20251015, - .allow_chacha20_boosting = false, -}; - -/* - * No TLS1.3 support. - * FIPS compliant. - * No DHE (would require extra setup with s2n_config_add_dhparams) - */ -struct s2n_cipher_suite *cipher_suites_20240331[] = { - /* TLS1.2 with ECDSA */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - - /* TLS1.2 with RSA */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20240331 = { - .count = s2n_array_len(cipher_suites_20240331), - .suites = cipher_suites_20240331, - .allow_chacha20_boosting = false, -}; - -/* Same as 20160411, but with ChaCha20 added as 1st in Preference List */ -struct s2n_cipher_suite *cipher_suites_20190122[] = { - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190122 = { - .count = s2n_array_len(cipher_suites_20190122), - .suites = cipher_suites_20190122, - .allow_chacha20_boosting = false, -}; - -/* Same as 20160804, but with ChaCha20 added as 2nd in Preference List */ -struct s2n_cipher_suite *cipher_suites_20190121[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_20190121 = { - .count = s2n_array_len(cipher_suites_20190121), - .suites = cipher_suites_20190121, - .allow_chacha20_boosting = false, -}; - -/* Same as 20160411, but with ChaCha20 in 3rd Place after CBC and GCM */ -struct s2n_cipher_suite *cipher_suites_20190120[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190120 = { - .count = s2n_array_len(cipher_suites_20190120), - .suites = cipher_suites_20190120, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for interop, includes ECDSA priortitized. DHE and 3DES are added(at the lowest preference). */ -struct s2n_cipher_suite *cipher_suites_20190214[] = { - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190214 = { - .count = s2n_array_len(cipher_suites_20190214), - .suites = cipher_suites_20190214, - .allow_chacha20_boosting = false, -}; - -/* 20190214 with aes-gcm prioritized above aes-cbc */ -struct s2n_cipher_suite *cipher_suites_20190214_gcm[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20190214_gcm = { - .count = s2n_array_len(cipher_suites_20190214_gcm), - .suites = cipher_suites_20190214_gcm, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20190214, but with TLS 1.3 Ciphers */ -struct s2n_cipher_suite *cipher_suites_20210825[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210825 = { - .count = s2n_array_len(cipher_suites_20210825), - .suites = cipher_suites_20210825, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20190214_gcm, but with TLS 1.3 Ciphers */ -struct s2n_cipher_suite *cipher_suites_20210825_gcm[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210825_gcm = { - .count = s2n_array_len(cipher_suites_20210825_gcm), - .suites = cipher_suites_20210825_gcm, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20210825, but with 3DES removed */ -struct s2n_cipher_suite *cipher_suites_20241008[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20241008 = { - .count = s2n_array_len(cipher_suites_20241008), - .suites = cipher_suites_20241008, - .allow_chacha20_boosting = false, -}; - -/* Same as cipher_suites_20210825_gcm and cipher_suites_pq_tls_1_0_2021_05_26, but with 3DES removed */ -struct s2n_cipher_suite *cipher_suites_20241008_gcm[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20241008_gcm = { - .count = s2n_array_len(cipher_suites_20241008_gcm), - .suites = cipher_suites_20241008_gcm, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20241009[] = { - S2N_TLS13_CIPHER_SUITES_20190801, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20241009 = { - .count = s2n_array_len(cipher_suites_20241009), - .suites = cipher_suites_20241009, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_null[] = { - &s2n_null_cipher_suite -}; - -const struct s2n_cipher_preferences cipher_preferences_null = { - .count = s2n_array_len(cipher_suites_null), - .suites = cipher_suites_null, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for interop. DHE and 3DES are added(at the lowest preference). */ -struct s2n_cipher_suite *cipher_suites_20170328[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170328 = { - .count = s2n_array_len(cipher_suites_20170328), - .suites = cipher_suites_20170328, - .allow_chacha20_boosting = false, -}; - -/* Equivalent to cipher_suites_20170328 with aes-gcm prioritized above aes-cbc */ -struct s2n_cipher_suite *cipher_suites_20170328_gcm[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170328_gcm = { - .count = s2n_array_len(cipher_suites_20170328_gcm), - .suites = cipher_suites_20170328_gcm, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for FIPS compatibility. */ -struct s2n_cipher_suite *cipher_suites_20170405[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170405 = { - .count = s2n_array_len(cipher_suites_20170405), - .suites = cipher_suites_20170405, - .allow_chacha20_boosting = false, -}; - -/* Preferences optimized for FIPS compatibility with GCM prioritized */ -struct s2n_cipher_suite *cipher_suites_20170405_gcm[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170405_gcm = { - .count = s2n_array_len(cipher_suites_20170405_gcm), - .suites = cipher_suites_20170405_gcm, - .allow_chacha20_boosting = false, -}; - -/* Equivalent to cipher_suite_20160411 with 3DES removed. - * Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients. */ -struct s2n_cipher_suite *cipher_suites_20170718[] = { - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170718 = { - .count = s2n_array_len(cipher_suites_20170718), - .suites = cipher_suites_20170718, - .allow_chacha20_boosting = false, -}; - -/* Equivalent to cipher_suites_20170718 with aes-gcm prioritized above aes-cbc */ -struct s2n_cipher_suite *cipher_suites_20170718_gcm[] = { - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20170718_gcm = { - .count = s2n_array_len(cipher_suites_20170718_gcm), - .suites = cipher_suites_20170718_gcm, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_2015_04[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_2015_04 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_2015_04), - .suites = cipher_suites_elb_security_policy_2015_04, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_2016_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_2016_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_2016_08), - .suites = cipher_suites_elb_security_policy_2016_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_2017_01[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls_1_2_2017_01 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_2017_01), - .suites = cipher_suites_elb_security_policy_tls_1_2_2017_01, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_1_2017_01[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls_1_1_2017_01 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_1_2017_01), - .suites = cipher_suites_elb_security_policy_tls_1_1_2017_01, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_ext_2018_06[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls_1_2_ext_2018_06 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_ext_2018_06), - .suites = cipher_suites_elb_security_policy_tls_1_2_ext_2018_06, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_2018_06[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_2018_06 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_2018_06), - .suites = cipher_suites_elb_security_policy_fs_2018_06, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_2019_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_1_2_2019_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_2019_08), - .suites = cipher_suites_elb_security_policy_fs_1_2_2019_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_1_2019_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_1_1_2019_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_1_2019_08), - .suites = cipher_suites_elb_security_policy_fs_1_1_2019_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_Res_2019_08[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences elb_security_policy_fs_1_2_Res_2019_08 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_Res_2019_08), - .suites = cipher_suites_elb_security_policy_fs_1_2_Res_2019_08, - .allow_chacha20_boosting = false, -}; - -/* - * S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 on top of cipher_suites_elb_security_policy_tls_1_2_ext_2018_06 -*/ -struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences elb_security_policy_tls13_1_2_Ext2_2021_06 = { - .count = s2n_array_len(cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06), - .suites = cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream), - .suites = cipher_suites_cloudfront_upstream, - .allow_chacha20_boosting = false, -}; - -/* CloudFront viewer facing (with TLS 1.3) */ -struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3 = { - .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3), - .suites = cipher_suites_cloudfront_ssl_v_3, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014), - .suites = cipher_suites_cloudfront_tls_1_0_2014, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_sha256[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_sha256 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_sha256), - .suites = cipher_suites_cloudfront_tls_1_0_2014_sha256, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016), - .suites = cipher_suites_cloudfront_tls_1_0_2016, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016), - .suites = cipher_suites_cloudfront_tls_1_1_2016, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2017[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2017 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2017), - .suites = cipher_suites_cloudfront_tls_1_2_2017, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018), - .suites = cipher_suites_cloudfront_tls_1_2_2018, - .allow_chacha20_boosting = false, -}; - -/* TLS 1.3 beta policy */ -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_beta[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_beta = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_beta), - .suites = cipher_suites_cloudfront_tls_1_2_2018_beta, -}; - -/* CloudFront viewer facing legacy TLS 1.2 policies */ -struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3_legacy), - .suites = cipher_suites_cloudfront_ssl_v_3_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_legacy), - .suites = cipher_suites_cloudfront_tls_1_0_2014_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016_legacy), - .suites = cipher_suites_cloudfront_tls_1_0_2016_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016_legacy), - .suites = cipher_suites_cloudfront_tls_1_1_2016_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_legacy), - .suites = cipher_suites_cloudfront_tls_1_2_2018_legacy, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019_legacy[] = { - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019_legacy = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019_legacy), - .suites = cipher_suites_cloudfront_tls_1_2_2019_legacy, - .allow_chacha20_boosting = false, -}; - -/* CloudFront upstream */ -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls10[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls10 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls10), - .suites = cipher_suites_cloudfront_upstream_tls10, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls11[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls11 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls11), - .suites = cipher_suites_cloudfront_upstream_tls11, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls12[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls12 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls12), - .suites = cipher_suites_cloudfront_upstream_tls12, - .allow_chacha20_boosting = false, -}; - -/* CloudFront upstream with TLS 1.3 enabled */ -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08), - .suites = cipher_suites_cloudfront_upstream_2025_08_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08_tls13[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08_tls13 = { - .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08_tls13), - .suites = cipher_suites_cloudfront_upstream_2025_08_08_tls13, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019), - .suites = cipher_suites_cloudfront_tls_1_2_2019, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2021[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), - .suites = cipher_suites_cloudfront_tls_1_2_2021, - .allow_chacha20_boosting = false, -}; - -/* Duplicate of cipher_preferences_cloudfront_tls_1_2_2021 but with allow_chacha20_boosting enabled */ -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021_chacha20_boosted = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), - .suites = cipher_suites_cloudfront_tls_1_2_2021, - .allow_chacha20_boosting = true, -}; - -/* FIPS 140-3 compliant version of cipher_preferences_cloudfront_tls_1_2_2021 */ -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2025[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2025 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2025), - .suites = cipher_suites_cloudfront_tls_1_2_2025, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_3_2025[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 -}; - -const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_3_2025 = { - .count = s2n_array_len(cipher_suites_cloudfront_tls_1_3_2025), - .suites = cipher_suites_cloudfront_tls_1_3_2025, - .allow_chacha20_boosting = false, -}; - -/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256, DES-CBC3-SHA, and - * RC4-MD5 added for compatibility. */ -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_ssl_v3[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, - &s2n_rsa_with_rc4_128_md5 -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_ssl_v3 = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_ssl_v3), - .suites = cipher_suites_aws_crt_sdk_ssl_v3, - .allow_chacha20_boosting = false, -}; - -/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256 added for - * compatibility. */ -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_default[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_default = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_default), - .suites = cipher_suites_aws_crt_sdk_default, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_2025[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_2025 = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_2025), - .suites = cipher_suites_aws_crt_sdk_2025, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_tls_13[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 -}; - -const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_tls_13 = { - .count = s2n_array_len(cipher_suites_aws_crt_sdk_tls_13), - .suites = cipher_suites_aws_crt_sdk_tls_13, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2018_10[] = { - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2018_10 = { - .count = s2n_array_len(cipher_suites_kms_tls_1_0_2018_10), - .suites = cipher_suites_kms_tls_1_0_2018_10, - .allow_chacha20_boosting = false, -}; - - -struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2021_08[] = { - S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2021_08 = { - .count = s2n_array_len(cipher_suites_kms_tls_1_0_2021_08), - .suites = cipher_suites_kms_tls_1_0_2021_08, - .allow_chacha20_boosting = false, -}; - - -struct s2n_cipher_suite *cipher_suites_20231213[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20231213 = { - .count = s2n_array_len(cipher_suites_20231213), - .suites = cipher_suites_20231213, -}; - -struct s2n_cipher_suite *cipher_suites_20231214[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20231214 = { - .count = s2n_array_len(cipher_suites_20231214), - .suites = cipher_suites_20231214, -}; - -struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2018_10[] = { - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2018_10 = { - .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2018_10), - .suites = cipher_suites_kms_fips_tls_1_2_2018_10, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2021_08[] = { - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2021_08 = { - .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2021_08), - .suites = cipher_suites_kms_fips_tls_1_2_2021_08, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20210816[] = { - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210816 = { - .count = s2n_array_len(cipher_suites_20210816), - .suites = cipher_suites_20210816, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20210816_gcm[] = { - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20210816_gcm = { - .count = s2n_array_len(cipher_suites_20210816_gcm), - .suites = cipher_suites_20210816_gcm, - .allow_chacha20_boosting = false, -}; - -/* Cipher suite options for backwards compatibility with older clients, - * while prioritizing forward secret key exchange and ECDSA certificates. - */ -struct s2n_cipher_suite *cipher_suites_20240603[] = { - /* TLS1.3 suites */ - &s2n_tls13_aes_128_gcm_sha256, - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_chacha20_poly1305_sha256, - - /* Preferred ECDHE + ECDSA suites */ - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, - - /* Preferred ECDHE + RSA suites */ - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, - - /* Legacy ECDHE suites */ - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - - /* DHE suites */ - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - - /* 3DES suites */ - &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, - &s2n_dhe_rsa_with_3des_ede_cbc_sha, - - /* RSA kex suites */ - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_3des_ede_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20240603 = { - .count = s2n_array_len(cipher_suites_20240603), - .suites = cipher_suites_20240603, - .allow_chacha20_boosting = true, -}; - -struct s2n_cipher_suite *cipher_suites_20250429[] = { - /* TLS1.2 */ - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20250429 = { - .count = s2n_array_len(cipher_suites_20250429), - .suites = cipher_suites_20250429, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251013[] = { - /* TLS1.2 */ - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251013 = { - .count = s2n_array_len(cipher_suites_20251013), - .suites = cipher_suites_20251013, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20250211[] = { - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20250211 = { - .count = s2n_array_len(cipher_suites_20250211), - .suites = cipher_suites_20250211, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251113[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251113 = { - .count = s2n_array_len(cipher_suites_20251113), - .suites = cipher_suites_20251113, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251114[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251114 = { - .count = s2n_array_len(cipher_suites_20251114), - .suites = cipher_suites_20251114, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251115[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251115 = { - .count = s2n_array_len(cipher_suites_20251115), - .suites = cipher_suites_20251115, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251116[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha, - &s2n_rsa_with_aes_256_gcm_sha384, - &s2n_rsa_with_aes_256_cbc_sha256, - &s2n_rsa_with_aes_256_cbc_sha, - &s2n_rsa_with_aes_128_gcm_sha256, - &s2n_rsa_with_aes_128_cbc_sha256, - &s2n_rsa_with_aes_128_cbc_sha, - &s2n_dhe_rsa_with_aes_256_gcm_sha384, - &s2n_dhe_rsa_with_aes_256_cbc_sha256, - &s2n_dhe_rsa_with_aes_256_cbc_sha, - &s2n_dhe_rsa_with_aes_128_gcm_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha256, - &s2n_dhe_rsa_with_aes_128_cbc_sha, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251116 = { - .count = s2n_array_len(cipher_suites_20251116), - .suites = cipher_suites_20251116, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20251117[] = { - &s2n_tls13_aes_256_gcm_sha384, - &s2n_tls13_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, - &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, - &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, - &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, -}; - -const struct s2n_cipher_preferences cipher_preferences_20251117 = { - .count = s2n_array_len(cipher_suites_20251117), - .suites = cipher_suites_20251117, - .allow_chacha20_boosting = false, -}; - -struct s2n_cipher_suite *cipher_suites_20260220[] = { - /* TLS1.3 */ - &s2n_tls13_aes_256_gcm_sha384, - - /* TLS1.2 */ - &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, - &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, -}; - -const struct s2n_cipher_preferences cipher_preferences_20260220 = { - .count = s2n_array_len(cipher_suites_20260220), - .suites = cipher_suites_20260220, - .allow_chacha20_boosting = false, -}; - -/* clang-format on */ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_cipher_preferences.h" + +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "tls/s2n_config.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_kex.h" +#include "utils/s2n_safety.h" + +/* clang-format off */ +/* TLS 1.3 cipher suites, in order of preference. + * Can be added to other ciphers suite lists to enable + * TLS1.3 compatibility. */ +#define S2N_TLS13_CIPHER_SUITES_20190801 \ + &s2n_tls13_aes_256_gcm_sha384, \ + &s2n_tls13_aes_128_gcm_sha256, \ + &s2n_tls13_chacha20_poly1305_sha256 + +#define S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 \ + &s2n_tls13_aes_128_gcm_sha256, \ + &s2n_tls13_aes_256_gcm_sha384, \ + &s2n_tls13_chacha20_poly1305_sha256 + +/* s2n's list of cipher suites, in order of preferences, as of 2019-08-01 */ +struct s2n_cipher_suite *cipher_suites_20190801[] = { + S2N_TLS13_CIPHER_SUITES_20190801, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20190801 = { + .count = s2n_array_len(cipher_suites_20190801), + .suites = cipher_suites_20190801, + .allow_chacha20_boosting = false, +}; + +/* Same as 20190801, but with ECDSA for TLS 1.2 added */ +struct s2n_cipher_suite *cipher_suites_20210831[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20210831 = { + .count = s2n_array_len(cipher_suites_20210831), + .suites = cipher_suites_20210831, + .allow_chacha20_boosting = false, +}; + +/* + * These cipher suites were chosen based on the following specification: + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf + */ +struct s2n_cipher_suite *cipher_suites_default_fips[] = { + /* tls1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* tls1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_default_fips = { + .count = s2n_array_len(cipher_suites_default_fips), + .suites = cipher_suites_default_fips, + .allow_chacha20_boosting = false, +}; + +/* s2n's list of cipher suites, in order of preference, as of 2014-06-01 */ +struct s2n_cipher_suite *cipher_suites_20140601[] = { + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_20140601 = { + .count = s2n_array_len(cipher_suites_20140601), + .suites = cipher_suites_20140601, + .allow_chacha20_boosting = false, +}; + +/* Disable SSLv3 due to POODLE */ +const struct s2n_cipher_preferences cipher_preferences_20141001 = { + .count = s2n_array_len(cipher_suites_20140601), + .suites = cipher_suites_20140601, + .allow_chacha20_boosting = false, +}; + +/* Disable RC4 */ +struct s2n_cipher_suite *cipher_suites_20150202[] = { + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20150202 = { + .count = s2n_array_len(cipher_suites_20150202), + .suites = cipher_suites_20150202, + .allow_chacha20_boosting = false, +}; + +/* Support AES-GCM modes */ +struct s2n_cipher_suite *cipher_suites_20150214[] = { + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20150214 = { + .count = s2n_array_len(cipher_suites_20150214), + .suites = cipher_suites_20150214, + .allow_chacha20_boosting = false, +}; + +/* Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients */ +struct s2n_cipher_suite *cipher_suites_20160411[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20160411 = { + .count = s2n_array_len(cipher_suites_20160411), + .suites = cipher_suites_20160411, + .allow_chacha20_boosting = false, +}; + +/* Use ECDHE instead of plain DHE. Prioritize ECDHE in favour of non ECDHE; GCM in favour of CBC; AES128 in favour of AES256. */ +struct s2n_cipher_suite *cipher_suites_20150306[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20150306 = { + .count = s2n_array_len(cipher_suites_20150306), + .suites = cipher_suites_20150306, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20160804[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20160804 = { + .count = s2n_array_len(cipher_suites_20160804), + .suites = cipher_suites_20160804, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20160824[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20160824 = { + .count = s2n_array_len(cipher_suites_20160824), + .suites = cipher_suites_20160824, + .allow_chacha20_boosting = false, +}; + +/* Add ChaCha20 suite */ +struct s2n_cipher_suite *cipher_suites_20170210[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20170210 = { + .count = s2n_array_len(cipher_suites_20170210), + .suites = cipher_suites_20170210, + .allow_chacha20_boosting = false, +}; + +/* + * TLS1.3 support. + * FIPS compliant. + * No DHE (would require extra setup with s2n_config_add_dhparams) + */ +struct s2n_cipher_suite *cipher_suites_20230317[] = { + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + + /* TLS1.3 */ + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20230317 = { + .count = s2n_array_len(cipher_suites_20230317), + .suites = cipher_suites_20230317, + .allow_chacha20_boosting = false, +}; + +/* + * TLS1.3 support. + */ +struct s2n_cipher_suite *cipher_suites_20251014[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251014 = { + .count = s2n_array_len(cipher_suites_20251014), + .suites = cipher_suites_20251014, + .allow_chacha20_boosting = false, +}; + +/* + * FIPS + * TLS1.3 support. + * Same as 20251014 but without chachapoly + */ +struct s2n_cipher_suite *cipher_suites_20251015[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251015 = { + .count = s2n_array_len(cipher_suites_20251015), + .suites = cipher_suites_20251015, + .allow_chacha20_boosting = false, +}; + +/* + * No TLS1.3 support. + * FIPS compliant. + * No DHE (would require extra setup with s2n_config_add_dhparams) + */ +struct s2n_cipher_suite *cipher_suites_20240331[] = { + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20240331 = { + .count = s2n_array_len(cipher_suites_20240331), + .suites = cipher_suites_20240331, + .allow_chacha20_boosting = false, +}; + +/* Same as 20160411, but with ChaCha20 added as 1st in Preference List */ +struct s2n_cipher_suite *cipher_suites_20190122[] = { + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190122 = { + .count = s2n_array_len(cipher_suites_20190122), + .suites = cipher_suites_20190122, + .allow_chacha20_boosting = false, +}; + +/* Same as 20160804, but with ChaCha20 added as 2nd in Preference List */ +struct s2n_cipher_suite *cipher_suites_20190121[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_20190121 = { + .count = s2n_array_len(cipher_suites_20190121), + .suites = cipher_suites_20190121, + .allow_chacha20_boosting = false, +}; + +/* Same as 20160411, but with ChaCha20 in 3rd Place after CBC and GCM */ +struct s2n_cipher_suite *cipher_suites_20190120[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190120 = { + .count = s2n_array_len(cipher_suites_20190120), + .suites = cipher_suites_20190120, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for interop, includes ECDSA priortitized. DHE and 3DES are added(at the lowest preference). */ +struct s2n_cipher_suite *cipher_suites_20190214[] = { + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190214 = { + .count = s2n_array_len(cipher_suites_20190214), + .suites = cipher_suites_20190214, + .allow_chacha20_boosting = false, +}; + +/* 20190214 with aes-gcm prioritized above aes-cbc */ +struct s2n_cipher_suite *cipher_suites_20190214_gcm[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20190214_gcm = { + .count = s2n_array_len(cipher_suites_20190214_gcm), + .suites = cipher_suites_20190214_gcm, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20190214, but with TLS 1.3 Ciphers */ +struct s2n_cipher_suite *cipher_suites_20210825[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210825 = { + .count = s2n_array_len(cipher_suites_20210825), + .suites = cipher_suites_20210825, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20190214_gcm, but with TLS 1.3 Ciphers */ +struct s2n_cipher_suite *cipher_suites_20210825_gcm[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210825_gcm = { + .count = s2n_array_len(cipher_suites_20210825_gcm), + .suites = cipher_suites_20210825_gcm, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20210825, but with 3DES removed */ +struct s2n_cipher_suite *cipher_suites_20241008[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20241008 = { + .count = s2n_array_len(cipher_suites_20241008), + .suites = cipher_suites_20241008, + .allow_chacha20_boosting = false, +}; + +/* Same as cipher_suites_20210825_gcm and cipher_suites_pq_tls_1_0_2021_05_26, but with 3DES removed */ +struct s2n_cipher_suite *cipher_suites_20241008_gcm[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20241008_gcm = { + .count = s2n_array_len(cipher_suites_20241008_gcm), + .suites = cipher_suites_20241008_gcm, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20241009[] = { + S2N_TLS13_CIPHER_SUITES_20190801, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20241009 = { + .count = s2n_array_len(cipher_suites_20241009), + .suites = cipher_suites_20241009, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_null[] = { + &s2n_null_cipher_suite +}; + +const struct s2n_cipher_preferences cipher_preferences_null = { + .count = s2n_array_len(cipher_suites_null), + .suites = cipher_suites_null, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for interop. DHE and 3DES are added(at the lowest preference). */ +struct s2n_cipher_suite *cipher_suites_20170328[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170328 = { + .count = s2n_array_len(cipher_suites_20170328), + .suites = cipher_suites_20170328, + .allow_chacha20_boosting = false, +}; + +/* Equivalent to cipher_suites_20170328 with aes-gcm prioritized above aes-cbc */ +struct s2n_cipher_suite *cipher_suites_20170328_gcm[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170328_gcm = { + .count = s2n_array_len(cipher_suites_20170328_gcm), + .suites = cipher_suites_20170328_gcm, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for FIPS compatibility. */ +struct s2n_cipher_suite *cipher_suites_20170405[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170405 = { + .count = s2n_array_len(cipher_suites_20170405), + .suites = cipher_suites_20170405, + .allow_chacha20_boosting = false, +}; + +/* Preferences optimized for FIPS compatibility with GCM prioritized */ +struct s2n_cipher_suite *cipher_suites_20170405_gcm[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170405_gcm = { + .count = s2n_array_len(cipher_suites_20170405_gcm), + .suites = cipher_suites_20170405_gcm, + .allow_chacha20_boosting = false, +}; + +/* Equivalent to cipher_suite_20160411 with 3DES removed. + * Make a CBC cipher #1 to avoid negotiating GCM with buggy Java clients. */ +struct s2n_cipher_suite *cipher_suites_20170718[] = { + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170718 = { + .count = s2n_array_len(cipher_suites_20170718), + .suites = cipher_suites_20170718, + .allow_chacha20_boosting = false, +}; + +/* Equivalent to cipher_suites_20170718 with aes-gcm prioritized above aes-cbc */ +struct s2n_cipher_suite *cipher_suites_20170718_gcm[] = { + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20170718_gcm = { + .count = s2n_array_len(cipher_suites_20170718_gcm), + .suites = cipher_suites_20170718_gcm, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_2015_04[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_2015_04 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_2015_04), + .suites = cipher_suites_elb_security_policy_2015_04, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_2016_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_2016_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_2016_08), + .suites = cipher_suites_elb_security_policy_2016_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_2017_01[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls_1_2_2017_01 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_2017_01), + .suites = cipher_suites_elb_security_policy_tls_1_2_2017_01, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_1_2017_01[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls_1_1_2017_01 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_1_2017_01), + .suites = cipher_suites_elb_security_policy_tls_1_1_2017_01, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls_1_2_ext_2018_06[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls_1_2_ext_2018_06 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls_1_2_ext_2018_06), + .suites = cipher_suites_elb_security_policy_tls_1_2_ext_2018_06, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_2018_06[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_2018_06 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_2018_06), + .suites = cipher_suites_elb_security_policy_fs_2018_06, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_2019_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_1_2_2019_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_2019_08), + .suites = cipher_suites_elb_security_policy_fs_1_2_2019_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_1_2019_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_1_1_2019_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_1_2019_08), + .suites = cipher_suites_elb_security_policy_fs_1_1_2019_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_elb_security_policy_fs_1_2_Res_2019_08[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences elb_security_policy_fs_1_2_Res_2019_08 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_fs_1_2_Res_2019_08), + .suites = cipher_suites_elb_security_policy_fs_1_2_Res_2019_08, + .allow_chacha20_boosting = false, +}; + +/* + * S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 on top of cipher_suites_elb_security_policy_tls_1_2_ext_2018_06 +*/ +struct s2n_cipher_suite *cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences elb_security_policy_tls13_1_2_Ext2_2021_06 = { + .count = s2n_array_len(cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06), + .suites = cipher_suites_elb_security_policy_tls13_1_2_Ext2_2021_06, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream), + .suites = cipher_suites_cloudfront_upstream, + .allow_chacha20_boosting = false, +}; + +/* CloudFront viewer facing (with TLS 1.3) */ +struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3 = { + .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3), + .suites = cipher_suites_cloudfront_ssl_v_3, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014), + .suites = cipher_suites_cloudfront_tls_1_0_2014, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_sha256[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_sha256 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_sha256), + .suites = cipher_suites_cloudfront_tls_1_0_2014_sha256, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016), + .suites = cipher_suites_cloudfront_tls_1_0_2016, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016), + .suites = cipher_suites_cloudfront_tls_1_1_2016, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2017[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2017 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2017), + .suites = cipher_suites_cloudfront_tls_1_2_2017, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018), + .suites = cipher_suites_cloudfront_tls_1_2_2018, + .allow_chacha20_boosting = false, +}; + +/* TLS 1.3 beta policy */ +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_beta[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_beta = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_beta), + .suites = cipher_suites_cloudfront_tls_1_2_2018_beta, +}; + +/* CloudFront viewer facing legacy TLS 1.2 policies */ +struct s2n_cipher_suite *cipher_suites_cloudfront_ssl_v_3_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_ssl_v_3_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_ssl_v_3_legacy), + .suites = cipher_suites_cloudfront_ssl_v_3_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2014_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2014_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2014_legacy), + .suites = cipher_suites_cloudfront_tls_1_0_2014_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_0_2016_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_0_2016_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_0_2016_legacy), + .suites = cipher_suites_cloudfront_tls_1_0_2016_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_1_2016_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_1_2016_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_1_2016_legacy), + .suites = cipher_suites_cloudfront_tls_1_1_2016_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2018_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2018_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2018_legacy), + .suites = cipher_suites_cloudfront_tls_1_2_2018_legacy, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019_legacy[] = { + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019_legacy = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019_legacy), + .suites = cipher_suites_cloudfront_tls_1_2_2019_legacy, + .allow_chacha20_boosting = false, +}; + +/* CloudFront upstream */ +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls10[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls10 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls10), + .suites = cipher_suites_cloudfront_upstream_tls10, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls11[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls11 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls11), + .suites = cipher_suites_cloudfront_upstream_tls11, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_tls12[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_tls12 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_tls12), + .suites = cipher_suites_cloudfront_upstream_tls12, + .allow_chacha20_boosting = false, +}; + +/* CloudFront upstream with TLS 1.3 enabled */ +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08), + .suites = cipher_suites_cloudfront_upstream_2025_08_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_upstream_2025_08_08_tls13[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_upstream_2025_08_08_tls13 = { + .count = s2n_array_len(cipher_suites_cloudfront_upstream_2025_08_08_tls13), + .suites = cipher_suites_cloudfront_upstream_2025_08_08_tls13, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2019[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2019 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2019), + .suites = cipher_suites_cloudfront_tls_1_2_2019, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2021[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), + .suites = cipher_suites_cloudfront_tls_1_2_2021, + .allow_chacha20_boosting = false, +}; + +/* Duplicate of cipher_preferences_cloudfront_tls_1_2_2021 but with allow_chacha20_boosting enabled */ +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2021_chacha20_boosted = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2021), + .suites = cipher_suites_cloudfront_tls_1_2_2021, + .allow_chacha20_boosting = true, +}; + +/* FIPS 140-3 compliant version of cipher_preferences_cloudfront_tls_1_2_2021 */ +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_2_2025[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_2_2025 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_2_2025), + .suites = cipher_suites_cloudfront_tls_1_2_2025, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_cloudfront_tls_1_3_2025[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 +}; + +const struct s2n_cipher_preferences cipher_preferences_cloudfront_tls_1_3_2025 = { + .count = s2n_array_len(cipher_suites_cloudfront_tls_1_3_2025), + .suites = cipher_suites_cloudfront_tls_1_3_2025, + .allow_chacha20_boosting = false, +}; + +/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256, DES-CBC3-SHA, and + * RC4-MD5 added for compatibility. */ +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_ssl_v3[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, + &s2n_rsa_with_rc4_128_md5 +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_ssl_v3 = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_ssl_v3), + .suites = cipher_suites_aws_crt_sdk_ssl_v3, + .allow_chacha20_boosting = false, +}; + +/* Based on cipher_preferences_cloudfront_tls_1_0_2016, but with ordering changed and AES256-SHA256 added for + * compatibility. */ +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_default[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_default = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_default), + .suites = cipher_suites_aws_crt_sdk_default, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_2025[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_2025 = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_2025), + .suites = cipher_suites_aws_crt_sdk_2025, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_aws_crt_sdk_tls_13[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716 +}; + +const struct s2n_cipher_preferences cipher_preferences_aws_crt_sdk_tls_13 = { + .count = s2n_array_len(cipher_suites_aws_crt_sdk_tls_13), + .suites = cipher_suites_aws_crt_sdk_tls_13, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2018_10[] = { + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2018_10 = { + .count = s2n_array_len(cipher_suites_kms_tls_1_0_2018_10), + .suites = cipher_suites_kms_tls_1_0_2018_10, + .allow_chacha20_boosting = false, +}; + + +struct s2n_cipher_suite *cipher_suites_kms_tls_1_0_2021_08[] = { + S2N_TLS13_CLOUDFRONT_CIPHER_SUITES_20200716, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_tls_1_0_2021_08 = { + .count = s2n_array_len(cipher_suites_kms_tls_1_0_2021_08), + .suites = cipher_suites_kms_tls_1_0_2021_08, + .allow_chacha20_boosting = false, +}; + + +struct s2n_cipher_suite *cipher_suites_20231213[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20231213 = { + .count = s2n_array_len(cipher_suites_20231213), + .suites = cipher_suites_20231213, +}; + +struct s2n_cipher_suite *cipher_suites_20231214[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20231214 = { + .count = s2n_array_len(cipher_suites_20231214), + .suites = cipher_suites_20231214, +}; + +struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2018_10[] = { + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2018_10 = { + .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2018_10), + .suites = cipher_suites_kms_fips_tls_1_2_2018_10, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_kms_fips_tls_1_2_2021_08[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_kms_fips_tls_1_2_2021_08 = { + .count = s2n_array_len(cipher_suites_kms_fips_tls_1_2_2021_08), + .suites = cipher_suites_kms_fips_tls_1_2_2021_08, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20210816[] = { + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210816 = { + .count = s2n_array_len(cipher_suites_20210816), + .suites = cipher_suites_20210816, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20210816_gcm[] = { + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20210816_gcm = { + .count = s2n_array_len(cipher_suites_20210816_gcm), + .suites = cipher_suites_20210816_gcm, + .allow_chacha20_boosting = false, +}; + +/* Cipher suite options for backwards compatibility with older clients, + * while prioritizing forward secret key exchange and ECDSA certificates. + */ +struct s2n_cipher_suite *cipher_suites_20240603[] = { + /* TLS1.3 suites */ + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_chacha20_poly1305_sha256, + + /* Preferred ECDHE + ECDSA suites */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + + /* Preferred ECDHE + RSA suites */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + + /* Legacy ECDHE suites */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + + /* DHE suites */ + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + + /* 3DES suites */ + &s2n_ecdhe_rsa_with_3des_ede_cbc_sha, + &s2n_dhe_rsa_with_3des_ede_cbc_sha, + + /* RSA kex suites */ + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_3des_ede_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20240603 = { + .count = s2n_array_len(cipher_suites_20240603), + .suites = cipher_suites_20240603, + .allow_chacha20_boosting = true, +}; + +struct s2n_cipher_suite *cipher_suites_20250429[] = { + /* TLS1.2 */ + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20250429 = { + .count = s2n_array_len(cipher_suites_20250429), + .suites = cipher_suites_20250429, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251013[] = { + /* TLS1.2 */ + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251013 = { + .count = s2n_array_len(cipher_suites_20251013), + .suites = cipher_suites_20251013, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20250211[] = { + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20250211 = { + .count = s2n_array_len(cipher_suites_20250211), + .suites = cipher_suites_20250211, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251113[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251113 = { + .count = s2n_array_len(cipher_suites_20251113), + .suites = cipher_suites_20251113, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251114[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251114 = { + .count = s2n_array_len(cipher_suites_20251114), + .suites = cipher_suites_20251114, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251115[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251115 = { + .count = s2n_array_len(cipher_suites_20251115), + .suites = cipher_suites_20251115, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251116[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, + &s2n_rsa_with_aes_256_gcm_sha384, + &s2n_rsa_with_aes_256_cbc_sha256, + &s2n_rsa_with_aes_256_cbc_sha, + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_rsa_with_aes_128_cbc_sha256, + &s2n_rsa_with_aes_128_cbc_sha, + &s2n_dhe_rsa_with_aes_256_gcm_sha384, + &s2n_dhe_rsa_with_aes_256_cbc_sha256, + &s2n_dhe_rsa_with_aes_256_cbc_sha, + &s2n_dhe_rsa_with_aes_128_gcm_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha256, + &s2n_dhe_rsa_with_aes_128_cbc_sha, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251116 = { + .count = s2n_array_len(cipher_suites_20251116), + .suites = cipher_suites_20251116, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20251117[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_tls13_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, +}; + +const struct s2n_cipher_preferences cipher_preferences_20251117 = { + .count = s2n_array_len(cipher_suites_20251117), + .suites = cipher_suites_20251117, + .allow_chacha20_boosting = false, +}; + +struct s2n_cipher_suite *cipher_suites_20260220[] = { + /* TLS1.3 */ + &s2n_tls13_aes_256_gcm_sha384, + + /* TLS1.2 */ + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20260220 = { + .count = s2n_array_len(cipher_suites_20260220), + .suites = cipher_suites_20260220, + .allow_chacha20_boosting = false, +}; + +/* clang-format on */ diff --git a/tls/s2n_client_hello.c b/tls/s2n_client_hello.c index 530f159c4ba..2de59981293 100644 --- a/tls/s2n_client_hello.c +++ b/tls/s2n_client_hello.c @@ -1,1107 +1,1107 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_client_hello.h" - -#include -#include -#include - -#include "api/unstable/fingerprint.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hash.h" -#include "crypto/s2n_pq.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/extensions/s2n_client_server_name.h" -#include "tls/extensions/s2n_client_supported_groups.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/extensions/s2n_server_key_share.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_auth_selection.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake_type.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_signature_algorithms.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn) -{ - if (conn->client_hello.parsed != 1) { - return NULL; - } - - return &conn->client_hello; -} - -static uint32_t min_size(struct s2n_blob *blob, uint32_t max_length) -{ - return blob->size < max_length ? blob->size : max_length; -} - -static S2N_RESULT s2n_generate_client_session_id(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - /* Session id already generated - no-op */ - if (conn->session_id_len) { - return S2N_RESULT_OK; - } - - /* Only generate the session id if using tickets */ - bool generate = conn->config->use_tickets; - - /* TLS1.3 doesn't require session ids. The field is actually renamed to legacy_session_id. - * However, we still set a session id if dealing with troublesome middleboxes - * (middlebox compatibility mode) or if trying to use a TLS1.2 ticket. - */ - if (conn->client_protocol_version >= S2N_TLS13) { - generate = s2n_is_middlebox_compat_enabled(conn) || conn->resume_protocol_version; - } - - /* Session id not needed - no-op */ - if (!generate) { - return S2N_RESULT_OK; - } - - /* QUIC should not allow session ids for any reason. - * - *= https://www.rfc-editor.org/rfc/rfc9001#section-8.4 - *# A server SHOULD treat the receipt of a TLS ClientHello with a non-empty - *# legacy_session_id field as a connection error of type PROTOCOL_VIOLATION. - */ - RESULT_ENSURE(!conn->quic_enabled, S2N_ERR_UNSUPPORTED_WITH_QUIC); - - struct s2n_blob session_id = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); - RESULT_GUARD(s2n_get_public_random_data(&session_id)); - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - return S2N_RESULT_OK; -} - -ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client_hello *ch) -{ - POSIX_ENSURE_REF(ch); - - return ch->raw_message.size; -} - -ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - - uint32_t len = min_size(&ch->raw_message, max_length); - POSIX_CHECKED_MEMCPY(out, ch->raw_message.data, len); - return len; -} - -ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch) -{ - POSIX_ENSURE_REF(ch); - - return ch->cipher_suites.size; -} - -int s2n_client_hello_cb_done(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE(conn->config->client_hello_cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); - POSIX_ENSURE(conn->client_hello.callback_invoked == 1, S2N_ERR_ASYNC_NOT_PERFORMED); - POSIX_ENSURE(conn->client_hello.parsed == 1, S2N_ERR_INVALID_STATE); - - conn->client_hello.callback_async_blocked = 0; - conn->client_hello.callback_async_done = 1; - - return S2N_SUCCESS; -} - -ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(ch->cipher_suites.data); - - uint32_t len = min_size(&ch->cipher_suites, max_length); - - POSIX_CHECKED_MEMCPY(out, ch->cipher_suites.data, len); - - return len; -} - -ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch) -{ - POSIX_ENSURE_REF(ch); - - return ch->extensions.raw.size; -} - -ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(ch->extensions.raw.data); - - uint32_t len = min_size(&ch->extensions.raw, max_length); - - POSIX_CHECKED_MEMCPY(out, ch->extensions.raw.data, len); - - return len; -} - -int s2n_client_hello_get_random(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - - POSIX_ENSURE(max_length >= S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(out, ch->random, S2N_TLS_RANDOM_DATA_LEN); - return S2N_SUCCESS; -} - -int s2n_client_hello_free_raw_message(struct s2n_client_hello *client_hello) -{ - POSIX_ENSURE_REF(client_hello); - - POSIX_GUARD(s2n_free(&client_hello->raw_message)); - - /* These point to data in the raw_message stuffer, - so we don't need to free them */ - client_hello->cipher_suites.data = NULL; - client_hello->extensions.raw.data = NULL; - - return 0; -} - -int s2n_client_hello_free(struct s2n_client_hello **ch) -{ - POSIX_ENSURE_REF(ch); - if (*ch == NULL) { - return S2N_SUCCESS; - } - - POSIX_ENSURE((*ch)->alloced, S2N_ERR_INVALID_ARGUMENT); - POSIX_GUARD(s2n_client_hello_free_raw_message(*ch)); - POSIX_GUARD(s2n_free_object((uint8_t **) ch, sizeof(struct s2n_client_hello))); - *ch = NULL; - return S2N_SUCCESS; -} - -int s2n_collect_client_hello(struct s2n_client_hello *ch, struct s2n_stuffer *source) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(source); - - uint32_t size = s2n_stuffer_data_available(source); - S2N_ERROR_IF(size == 0, S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_realloc(&ch->raw_message, size)); - POSIX_GUARD(s2n_stuffer_read(source, &ch->raw_message)); - - return 0; -} - -static S2N_RESULT s2n_client_hello_verify_for_retry(struct s2n_connection *conn, - struct s2n_client_hello *old_ch, struct s2n_client_hello *new_ch, - uint8_t *previous_client_random) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(old_ch); - RESULT_ENSURE_REF(new_ch); - RESULT_ENSURE_REF(previous_client_random); - - if (!s2n_is_hello_retry_handshake(conn)) { - return S2N_RESULT_OK; - } - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# The client will also send a - *# ClientHello when the server has responded to its ClientHello with a - *# HelloRetryRequest. In that case, the client MUST send the same - *# ClientHello without modification, except as follows: - * - * All of the exceptions that follow are extensions. - */ - RESULT_ENSURE(old_ch->legacy_version == new_ch->legacy_version, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(old_ch->compression_methods.size == new_ch->compression_methods.size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals(old_ch->compression_methods.data, new_ch->compression_methods.data, - new_ch->compression_methods.size), - S2N_ERR_BAD_MESSAGE); - - /* Some clients are not compliant with TLS 1.3 RFC, and send mismatching values in their second - * ClientHello. For increased compatibility, these checks are skipped outside of tests. The - * checks are still included in tests to ensure the s2n-tls client remains compliant. - */ - if (s2n_in_test()) { - /* In the past, the s2n-tls client updated the client random in the second ClientHello - * which is not allowed by RFC8446: https://github.com/aws/s2n-tls/pull/3311. Although the - * issue was addressed, its existence means that old versions of the s2n-tls client will - * fail this validation. - */ - RESULT_ENSURE(s2n_constant_time_equals( - previous_client_random, - conn->client_hello.random, - S2N_TLS_RANDOM_DATA_LEN), - S2N_ERR_BAD_MESSAGE); - - /* Some clients have been found to send a mismatching legacy session ID. */ - RESULT_ENSURE(old_ch->session_id.size == new_ch->session_id.size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals(old_ch->session_id.data, new_ch->session_id.data, - new_ch->session_id.size), - S2N_ERR_BAD_MESSAGE); - - /* Some clients have been found to send a mismatching cipher suite list. */ - RESULT_ENSURE(old_ch->cipher_suites.size == new_ch->cipher_suites.size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals(old_ch->cipher_suites.data, new_ch->cipher_suites.data, - new_ch->cipher_suites.size), - S2N_ERR_BAD_MESSAGE); - } - - /* - * Now enforce that the extensions also exactly match, - * except for the exceptions described in the RFC. - */ - for (size_t i = 0; i < s2n_array_len(s2n_supported_extensions); i++) { - s2n_parsed_extension *old_extension = &old_ch->extensions.parsed_extensions[i]; - uint32_t old_size = old_extension->extension.size; - s2n_parsed_extension *new_extension = &new_ch->extensions.parsed_extensions[i]; - uint32_t new_size = new_extension->extension.size; - - /* The extension type is only set if the extension is present. - * Look for a non-zero-length extension. - */ - uint16_t extension_type = 0; - if (old_size != 0) { - extension_type = old_extension->extension_type; - } else if (new_size != 0) { - extension_type = new_extension->extension_type; - } else { - continue; - } - - switch (extension_type) { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - If a "key_share" extension was supplied in the HelloRetryRequest, - *# replacing the list of shares with a list containing a single - *# KeyShareEntry from the indicated group. - */ - case TLS_EXTENSION_KEY_SHARE: - /* Handled when parsing the key share extension */ - break; - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - Removing the "early_data" extension (Section 4.2.10) if one was - *# present. Early data is not permitted after a HelloRetryRequest. - */ - case TLS_EXTENSION_EARLY_DATA: - RESULT_ENSURE(new_size == 0, S2N_ERR_BAD_MESSAGE); - break; - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - Including a "cookie" extension if one was provided in the - *# HelloRetryRequest. - */ - case TLS_EXTENSION_COOKIE: - /* Handled when parsing the cookie extension */ - break; - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 - *# - Updating the "pre_shared_key" extension if present by recomputing - *# the "obfuscated_ticket_age" and binder values and (optionally) - *# removing any PSKs which are incompatible with the server's - *# indicated cipher suite. - */ - case TLS_EXTENSION_PRE_SHARED_KEY: - /* Handled when parsing the psk extension */ - break; - - /* Some clients have been found to send mismatching supported versions in their second - * ClientHello. The extension isn't compared byte-for-byte for increased compatibility - * with these clients. - */ - case TLS_EXTENSION_SUPPORTED_VERSIONS: - /* Additional HRR validation for the supported versions extension is performed when - * parsing the extension. - */ - break; - - /* - * No more exceptions. - * All other extensions must match. - */ - default: - RESULT_ENSURE(old_size == new_size, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(s2n_constant_time_equals( - new_extension->extension.data, - old_extension->extension.data, - old_size), - S2N_ERR_BAD_MESSAGE); - } - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_client_hello_parse_raw(struct s2n_client_hello *client_hello) -{ - RESULT_ENSURE_REF(client_hello); - - struct s2n_stuffer in_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&in_stuffer, &client_hello->raw_message)); - struct s2n_stuffer *in = &in_stuffer; - - /** - * https://tools.ietf.org/rfc/rfc8446#4.1.2 - * Structure of this message: - * - * uint16 ProtocolVersion; - * opaque Random[32]; - * - * uint8 CipherSuite[2]; - * - * struct { - * ProtocolVersion legacy_version = 0x0303; - * Random random; - * opaque legacy_session_id<0..32>; - * CipherSuite cipher_suites<2..2^16-2>; - * opaque legacy_compression_methods<1..2^8-1>; - * Extension extensions<8..2^16-1>; - * } ClientHello; - **/ - - /* legacy_version */ - uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(in, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - /* Encode the version as a 1 byte representation of the two protocol version bytes, with the - * major version in the tens place and the minor version in the ones place. For example, the - * TLS 1.2 protocol version is 0x0303, which is encoded as S2N_TLS12 (33). - */ - client_hello->legacy_version = (client_protocol_version[0] * 10) + client_protocol_version[1]; - - /* random - read and store it, erasing from raw message */ - RESULT_GUARD_POSIX(s2n_stuffer_erase_and_read_bytes(in, client_hello->random, S2N_TLS_RANDOM_DATA_LEN)); - - /* legacy_session_id */ - uint8_t session_id_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &session_id_len)); - RESULT_ENSURE(session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); - uint8_t *session_id = s2n_stuffer_raw_read(in, session_id_len); - RESULT_ENSURE(session_id != NULL, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->session_id, session_id, session_id_len)); - - /* cipher suites */ - uint16_t cipher_suites_length = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(in, &cipher_suites_length)); - RESULT_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(cipher_suites_length % S2N_TLS_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); - uint8_t *cipher_suites = s2n_stuffer_raw_read(in, cipher_suites_length); - RESULT_ENSURE(cipher_suites != NULL, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->cipher_suites, cipher_suites, cipher_suites_length)); - - /* legacy_compression_methods */ - uint8_t compression_methods_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &compression_methods_len)); - uint8_t *compression_methods = s2n_stuffer_raw_read(in, compression_methods_len); - RESULT_ENSURE(compression_methods != NULL, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->compression_methods, compression_methods, compression_methods_len)); - - /* extensions */ - RESULT_GUARD_POSIX(s2n_extension_list_parse(in, &client_hello->extensions)); - - return S2N_RESULT_OK; -} - -int s2n_parse_client_hello(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* SSLv2 ClientHellos are not allowed during a HelloRetryRequest */ - if (s2n_is_hello_retry_handshake(conn)) { - POSIX_ENSURE(!conn->client_hello.sslv2, S2N_ERR_BAD_MESSAGE); - } - - /* If a retry, move the old version of the client hello - * somewhere safe so we can compare it to the new client hello later. - */ - DEFER_CLEANUP(struct s2n_client_hello previous_hello_retry = conn->client_hello, - s2n_client_hello_free_raw_message); - - /* Save the client random before clearing for retry validation */ - uint8_t previous_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; - POSIX_CHECKED_MEMCPY(previous_client_random, conn->client_hello.random, - S2N_TLS_RANDOM_DATA_LEN); - - if (s2n_is_hello_retry_handshake(conn)) { - POSIX_CHECKED_MEMSET(&conn->client_hello, 0, sizeof(struct s2n_client_hello)); - } - - POSIX_GUARD(s2n_collect_client_hello(&conn->client_hello, &conn->handshake.io)); - - if (conn->client_hello.sslv2) { - POSIX_GUARD(s2n_sslv2_client_hello_parse(conn)); - return S2N_SUCCESS; - } - - /* Parse raw, collected client hello */ - POSIX_GUARD_RESULT(s2n_client_hello_parse_raw(&conn->client_hello)); - - /* Protocol version in the ClientHello is fixed at 0x0303(TLS 1.2) for - * future versions of TLS. Therefore, we will negotiate down if a client sends - * an unexpected value above 0x0303. - */ - conn->client_protocol_version = S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); - - /* Copy the session id to the connection. */ - conn->session_id_len = conn->client_hello.session_id.size; - POSIX_CHECKED_MEMCPY(conn->session_id, conn->client_hello.session_id.data, conn->session_id_len); - - POSIX_GUARD_RESULT(s2n_client_hello_verify_for_retry(conn, - &previous_hello_retry, &conn->client_hello, previous_client_random)); - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_client_hello_parse_message_impl(struct s2n_client_hello **result, - const uint8_t *raw_message, uint32_t raw_message_size) -{ - RESULT_ENSURE_REF(result); - - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_client_hello))); - RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); - - DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); - client_hello = (struct s2n_client_hello *) (void *) mem.data; - client_hello->alloced = true; - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); - RESULT_GUARD_POSIX(s2n_stuffer_alloc(&in, raw_message_size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&in, raw_message, raw_message_size)); - - uint8_t message_type = 0; - uint32_t message_len = 0; - RESULT_GUARD(s2n_handshake_parse_header(&in, &message_type, &message_len)); - RESULT_ENSURE(message_type == TLS_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(message_len == s2n_stuffer_data_available(&in), S2N_ERR_BAD_MESSAGE); - - RESULT_GUARD_POSIX(s2n_collect_client_hello(client_hello, &in)); - RESULT_ENSURE(s2n_stuffer_data_available(&in) == 0, S2N_ERR_BAD_MESSAGE); - - RESULT_GUARD(s2n_client_hello_parse_raw(client_hello)); - - *result = client_hello; - ZERO_TO_DISABLE_DEFER_CLEANUP(client_hello); - return S2N_RESULT_OK; -} - -struct s2n_client_hello *s2n_client_hello_parse_message(const uint8_t *raw_message, uint32_t raw_message_size) -{ - struct s2n_client_hello *result = NULL; - PTR_GUARD_RESULT(s2n_client_hello_parse_message_impl(&result, raw_message, raw_message_size)); - return result; -} - -int s2n_process_client_hello(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - /* Client hello is parsed and config is finalized. - * Negotiate protocol version, cipher suite, ALPN, select a cert, etc. */ - struct s2n_client_hello *client_hello = &conn->client_hello; - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - - if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { - conn->server_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); - conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); - } - - /* Set default key exchange curve. - * This is going to be our fallback if the client has no preference. - * - * P-256 is our preferred fallback option because the TLS1.3 RFC requires - * all implementations to support it: - * - * https://tools.ietf.org/rfc/rfc8446#section-9.1 - * A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256) - * and SHOULD support key exchange with X25519 [RFC7748] - * - *= https://www.rfc-editor.org/rfc/rfc4492#section-4 - *# A client that proposes ECC cipher suites may choose not to include these extensions. - *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. - * - */ - const struct s2n_ecc_preferences *ecc_pref = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - POSIX_ENSURE_REF(ecc_pref); - - if (ecc_pref->count == 0) { - conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; - } else if (s2n_ecc_preferences_includes_curve(ecc_pref, TLS_EC_CURVE_SECP_256_R1)) { - conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; - } else { - /* If P-256 isn't allowed by the current security policy, choose the first / most preferred curve. */ - conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; - } - - POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &conn->client_hello.extensions)); - - /* After parsing extensions, select a curve and corresponding keyshare to use */ - if (conn->actual_protocol_version >= S2N_TLS13) { - POSIX_GUARD(s2n_extensions_server_key_share_select(conn)); - } - - /* for pre TLS 1.3 connections, protocol selection is not done in supported_versions extensions, so do it here */ - if (conn->actual_protocol_version < S2N_TLS13) { - conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); - } - - if (conn->client_protocol_version < security_policy->minimum_protocol_version) { - POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); - POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - if (s2n_connection_is_quic_enabled(conn)) { - POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - /* Find potential certificate matches before we choose the cipher. */ - POSIX_GUARD(s2n_conn_find_name_matching_certs(conn)); - - /* Save the previous cipher suite */ - uint8_t previous_cipher_suite_iana[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - POSIX_CHECKED_MEMCPY(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN); - - /* Now choose the ciphers we have certs for. */ - if (conn->client_hello.sslv2) { - POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, - client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN)); - } else { - POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, - client_hello->cipher_suites.size / 2)); - } - - /* Check if this is the second client hello in a hello retry handshake */ - if (s2n_is_hello_retry_handshake(conn) && conn->handshake.message_number > 0) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Servers MUST ensure that they negotiate the - *# same cipher suite when receiving a conformant updated ClientHello (if - *# the server selects the cipher suite as the first step in the - *# negotiation, then this will happen automatically). - **/ - POSIX_ENSURE(s2n_constant_time_equals(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, - S2N_TLS_CIPHER_SUITE_LEN), - S2N_ERR_BAD_MESSAGE); - } - - /* If we're using a PSK, we don't need to choose a signature algorithm or certificate, - * because no additional auth is required. */ - if (conn->psk_params.chosen_psk != NULL) { - return S2N_SUCCESS; - } - - /* And set the signature and hash algorithm used for key exchange signatures */ - POSIX_GUARD_RESULT(s2n_signature_algorithm_select(conn)); - - /* And finally, set the certs specified by the final auth + sig_alg combo. */ - POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &conn->handshake_params.our_chain_and_key)); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_client_hello_process_cb_response(struct s2n_connection *conn, int rc) -{ - if (rc < 0) { - goto fail; - } - switch (conn->config->client_hello_cb_mode) { - case S2N_CLIENT_HELLO_CB_BLOCKING: { - if (rc) { - conn->server_name_used = 1; - } - return S2N_RESULT_OK; - } - case S2N_CLIENT_HELLO_CB_NONBLOCKING: { - if (conn->client_hello.callback_async_done) { - return S2N_RESULT_OK; - } - conn->client_hello.callback_async_blocked = 1; - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - } -fail: - /* rc < 0 */ - RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); - RESULT_BAIL(S2N_ERR_CANCELLED); -} - -int s2n_client_hello_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE(!conn->client_hello.callback_async_blocked, S2N_ERR_ASYNC_BLOCKED); - - /* Only parse the ClientHello once */ - if (!conn->client_hello.parsed) { - POSIX_GUARD(s2n_parse_client_hello(conn)); - /* Mark the collected client hello as available when parsing is done and before the client hello callback */ - conn->client_hello.parsed = true; - } - - /* Only invoke the ClientHello callback once. - * This means that we do NOT invoke the callback again on the second ClientHello - * in a TLS1.3 retry handshake. We explicitly check for a retry because the - * callback state may have been cleared while parsing the second ClientHello. - */ - if (!conn->client_hello.callback_invoked && !IS_HELLO_RETRY_HANDSHAKE(conn)) { - /* Mark the client hello callback as invoked to avoid calling it again. */ - conn->client_hello.callback_invoked = true; - - /* Do NOT move this null check. A test exists to assert that a server connection can get - * as far as the client hello callback without using its config. To do this we need a - * specific error for a null config just before the client hello callback. The test's - * assertions are weakened if this check is moved. */ - POSIX_ENSURE(conn->config, S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); - - /* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */ - if (conn->config->client_hello_cb) { - int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx); - POSIX_GUARD_RESULT(s2n_client_hello_process_cb_response(conn, rc)); - } - } - - POSIX_GUARD(s2n_process_client_hello(conn)); - - return 0; -} - -S2N_RESULT s2n_cipher_suite_validate_available(struct s2n_connection *conn, struct s2n_cipher_suite *cipher) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(cipher); - RESULT_ENSURE_EQ(cipher->available, true); - RESULT_ENSURE_LTE(cipher->minimum_required_tls_version, conn->client_protocol_version); - if (s2n_connection_is_quic_enabled(conn)) { - RESULT_ENSURE_GTE(cipher->minimum_required_tls_version, S2N_TLS13); - } - return S2N_RESULT_OK; -} - -int s2n_client_hello_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - - const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; - POSIX_ENSURE_REF(cipher_preferences); - - if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { - conn->client_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); - conn->actual_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); - } - - struct s2n_stuffer *out = &conn->handshake.io; - uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - - uint8_t reported_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); - conn->client_hello.legacy_version = reported_protocol_version; - client_protocol_version[0] = reported_protocol_version / 10; - client_protocol_version[1] = reported_protocol_version % 10; - POSIX_GUARD(s2n_stuffer_write_bytes(out, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - struct s2n_blob client_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); - if (!s2n_is_hello_retry_handshake(conn)) { - /* Only generate the random data for our first client hello. - * If we retry, we'll reuse the value. */ - POSIX_GUARD_RESULT(s2n_get_public_random_data(&client_random)); - } - POSIX_GUARD(s2n_stuffer_write(out, &client_random)); - - POSIX_GUARD_RESULT(s2n_generate_client_session_id(conn)); - POSIX_GUARD(s2n_stuffer_write_uint8(out, conn->session_id_len)); - if (conn->session_id_len > 0) { - POSIX_GUARD(s2n_stuffer_write_bytes(out, conn->session_id, conn->session_id_len)); - } - - /* Reserve space for size of the list of available ciphers */ - struct s2n_stuffer_reservation available_cipher_suites_size; - POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &available_cipher_suites_size)); - - /* Now, write the IANA values of every available cipher suite in our list */ - struct s2n_cipher_suite *cipher = NULL; - bool tls12_is_possible = false; - for (size_t i = 0; i < security_policy->cipher_preferences->count; i++) { - cipher = cipher_preferences->suites[i]; - if (s2n_result_is_error(s2n_cipher_suite_validate_available(conn, cipher))) { - continue; - } - if (cipher->minimum_required_tls_version < S2N_TLS13) { - tls12_is_possible = true; - } - POSIX_GUARD(s2n_stuffer_write_bytes(out, cipher->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - } - - /** - * For initial handshakes: - *= https://www.rfc-editor.org/rfc/rfc5746#3.4 - *# o The client MUST include either an empty "renegotiation_info" - *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling - *# cipher suite value in the ClientHello. Including both is NOT - *# RECOMMENDED. - * For maximum backwards compatibility, we choose to use the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite - * rather than the "renegotiation_info" extension. - * - * For renegotiation handshakes: - *= https://www.rfc-editor.org/rfc/rfc5746#3.5 - *# The SCSV MUST NOT be included. - */ - if (tls12_is_possible && !s2n_handshake_is_renegotiation(conn)) { - uint8_t renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; - POSIX_GUARD(s2n_stuffer_write_bytes(out, renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN)); - } - - /* Write size of the list of available ciphers */ - uint32_t ciphers_size = 0; - POSIX_GUARD(s2n_stuffer_get_vector_size(&available_cipher_suites_size, &ciphers_size)); - POSIX_ENSURE(ciphers_size > 0, S2N_ERR_INVALID_CIPHER_PREFERENCES); - POSIX_GUARD(s2n_stuffer_write_reservation(&available_cipher_suites_size, ciphers_size)); - - /* Zero compression methods */ - POSIX_GUARD(s2n_stuffer_write_uint8(out, 1)); - POSIX_GUARD(s2n_stuffer_write_uint8(out, 0)); - - /* Write the extensions */ - POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, out)); - - /* Once the message is complete, finish calculating the PSK binders. - * - * The PSK binders require all the sizes in the ClientHello to be written correctly, - * including the extension size and extension list size, and therefore have - * to be calculated AFTER we finish writing the entire extension list. */ - POSIX_GUARD_RESULT(s2n_finish_psk_extension(conn)); - - /* If early data was not requested as part of the ClientHello, it never will be. */ - if (conn->early_data_state == S2N_UNKNOWN_EARLY_DATA_STATE) { - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_NOT_REQUESTED)); - } - - return S2N_SUCCESS; -} - -/* - * s2n-tls does NOT support SSLv2. However, it does support SSLv2 ClientHellos. - * Clients may send SSLv2 ClientHellos advertising higher protocol versions for - * backwards compatibility reasons. See https://tools.ietf.org/rfc/rfc2246 Appendix E. - * - * In this case, conn->client_hello.legacy_version and conn->client_protocol_version - * will be higher than SSLv2. - * - * See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html Section 2.5 - * for a description of the expected SSLv2 format. - * Alternatively, the TLS1.0 RFC includes a more modern description of the format: - * https://tools.ietf.org/rfc/rfc2246 Appendix E.1 - */ -int s2n_sslv2_client_hello_parse(struct s2n_connection *conn) -{ - struct s2n_client_hello *client_hello = &conn->client_hello; - conn->client_protocol_version = S2N_MIN(client_hello->legacy_version, S2N_TLS12); - - struct s2n_stuffer in_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&in_stuffer, &client_hello->raw_message)); - POSIX_GUARD(s2n_stuffer_skip_write(&in_stuffer, client_hello->raw_message.size)); - struct s2n_stuffer *in = &in_stuffer; - - /* We start 5 bytes into the record */ - uint16_t cipher_suites_length = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); - POSIX_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(cipher_suites_length % S2N_SSLv2_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); - - uint16_t session_id_length = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &session_id_length)); - - uint16_t challenge_length = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &challenge_length)); - - S2N_ERROR_IF(challenge_length > S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_BAD_MESSAGE); - - client_hello->cipher_suites.size = cipher_suites_length; - client_hello->cipher_suites.data = s2n_stuffer_raw_read(in, cipher_suites_length); - POSIX_ENSURE_REF(client_hello->cipher_suites.data); - - S2N_ERROR_IF(session_id_length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_blob_init(&client_hello->session_id, s2n_stuffer_raw_read(in, session_id_length), session_id_length)); - if (session_id_length > 0 && session_id_length <= S2N_TLS_SESSION_ID_MAX_LEN) { - POSIX_CHECKED_MEMCPY(conn->session_id, client_hello->session_id.data, session_id_length); - conn->session_id_len = (uint8_t) session_id_length; - } - - struct s2n_blob b = { 0 }; - POSIX_GUARD(s2n_blob_init(&b, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); - - b.data += S2N_TLS_RANDOM_DATA_LEN - challenge_length; - b.size -= S2N_TLS_RANDOM_DATA_LEN - challenge_length; - - POSIX_GUARD(s2n_stuffer_read(in, &b)); - - return 0; -} - -int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type, - s2n_parsed_extensions_list *parsed_extension_list, s2n_parsed_extension **parsed_extension) -{ - POSIX_ENSURE_REF(parsed_extension_list); - POSIX_ENSURE_REF(parsed_extension); - - s2n_extension_type_id extension_type_id = 0; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type, &extension_type_id)); - - s2n_parsed_extension *found_parsed_extension = &parsed_extension_list->parsed_extensions[extension_type_id]; - POSIX_ENSURE(found_parsed_extension->extension.data, S2N_ERR_EXTENSION_NOT_RECEIVED); - POSIX_ENSURE(found_parsed_extension->extension_type == extension_type, S2N_ERR_INVALID_PARSED_EXTENSIONS); - - *parsed_extension = found_parsed_extension; - return S2N_SUCCESS; -} - -ssize_t s2n_client_hello_get_extension_length(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type) -{ - POSIX_ENSURE_REF(ch); - - s2n_parsed_extension *parsed_extension = NULL; - if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { - return 0; - } - - return parsed_extension->extension.size; -} - -ssize_t s2n_client_hello_get_extension_by_id(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type, uint8_t *out, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - - s2n_parsed_extension *parsed_extension = NULL; - if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { - return 0; - } - - uint32_t len = min_size(&parsed_extension->extension, max_length); - POSIX_CHECKED_MEMCPY(out, parsed_extension->extension.data, len); - return len; -} - -int s2n_client_hello_get_session_id_length(struct s2n_client_hello *ch, uint32_t *out_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out_length); - *out_length = ch->session_id.size; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE_REF(out_length); - - uint32_t len = min_size(&ch->session_id, max_length); - POSIX_CHECKED_MEMCPY(out, ch->session_id.data, len); - *out_length = len; - - return S2N_SUCCESS; -} - -int s2n_client_hello_get_compression_methods_length(struct s2n_client_hello *ch, uint32_t *out_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out_length); - *out_length = ch->compression_methods.size; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_compression_methods(struct s2n_client_hello *ch, uint8_t *list, uint32_t list_length, uint32_t *out_length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(list); - POSIX_ENSURE_REF(out_length); - - POSIX_ENSURE(list_length >= ch->compression_methods.size, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(list, ch->compression_methods.data, ch->compression_methods.size); - *out_length = ch->compression_methods.size; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_legacy_protocol_version(struct s2n_client_hello *ch, uint8_t *out) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - *out = ch->legacy_version; - return S2N_SUCCESS; -} - -int s2n_client_hello_get_legacy_record_version(struct s2n_client_hello *ch, uint8_t *out) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(out); - POSIX_ENSURE(ch->record_version_recorded, S2N_ERR_INVALID_ARGUMENT); - *out = ch->legacy_record_version; - return S2N_SUCCESS; -} - -S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, - struct s2n_blob *raw_extensions, struct s2n_blob *extension) -{ - RESULT_ENSURE_REF(raw_extensions); - RESULT_ENSURE_REF(extension); - - *extension = (struct s2n_blob){ 0 }; - - struct s2n_stuffer raw_extensions_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&raw_extensions_stuffer, raw_extensions)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&raw_extensions_stuffer, raw_extensions->size)); - - while (s2n_stuffer_data_available(&raw_extensions_stuffer) > 0) { - uint16_t extension_type = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_type)); - - uint16_t extension_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_size)); - - uint8_t *extension_data = s2n_stuffer_raw_read(&raw_extensions_stuffer, extension_size); - RESULT_ENSURE_REF(extension_data); - - if (extension_iana == extension_type) { - RESULT_GUARD_POSIX(s2n_blob_init(extension, extension_data, extension_size)); - return S2N_RESULT_OK; - } - } - return S2N_RESULT_OK; -} - -int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extension_iana, bool *exists) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(exists); - - *exists = false; - - s2n_extension_type_id extension_type_id = s2n_unsupported_extension; - if (s2n_extension_supported_iana_value_to_id(extension_iana, &extension_type_id) == S2N_SUCCESS) { - s2n_parsed_extension *parsed_extension = NULL; - if (s2n_client_hello_get_parsed_extension(extension_iana, &ch->extensions, &parsed_extension) == S2N_SUCCESS) { - *exists = true; - } - return S2N_SUCCESS; - } - - struct s2n_blob extension = { 0 }; - POSIX_GUARD_RESULT(s2n_client_hello_get_raw_extension(extension_iana, &ch->extensions.raw, &extension)); - if (extension.data != NULL) { - *exists = true; - } - return S2N_SUCCESS; -} - -int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups, - uint16_t groups_count_max, uint16_t *groups_count_out) -{ - POSIX_ENSURE_REF(groups_count_out); - *groups_count_out = 0; - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(groups); - - s2n_parsed_extension *supported_groups_extension = NULL; - POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS, &ch->extensions, &supported_groups_extension)); - POSIX_ENSURE_REF(supported_groups_extension); - - struct s2n_stuffer extension_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &supported_groups_extension->extension)); - - uint16_t supported_groups_count = 0; - POSIX_GUARD_RESULT(s2n_supported_groups_parse_count(&extension_stuffer, &supported_groups_count)); - POSIX_ENSURE(supported_groups_count <= groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); - - for (size_t i = 0; i < supported_groups_count; i++) { - /* s2n_stuffer_read_uint16 is used to read each of the supported groups in network-order - * endianness. - */ - POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &groups[i])); - } - - *groups_count_out = supported_groups_count; - - return S2N_SUCCESS; -} - -int s2n_client_hello_get_server_name_length(struct s2n_client_hello *ch, uint16_t *length) -{ - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(length); - *length = 0; - - s2n_parsed_extension *server_name_extension = NULL; - POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); - POSIX_ENSURE_REF(server_name_extension); - - struct s2n_stuffer extension_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); - - struct s2n_blob blob = { 0 }; - POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); - *length = blob.size; - - return S2N_SUCCESS; -} - -int s2n_client_hello_get_server_name(struct s2n_client_hello *ch, uint8_t *server_name, uint16_t length, uint16_t *out_length) -{ - POSIX_ENSURE_REF(out_length); - POSIX_ENSURE_REF(ch); - POSIX_ENSURE_REF(server_name); - *out_length = 0; - - s2n_parsed_extension *server_name_extension = NULL; - POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); - POSIX_ENSURE_REF(server_name_extension); - - struct s2n_stuffer extension_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); - - struct s2n_blob blob = { 0 }; - POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); - POSIX_ENSURE_LTE(blob.size, length); - POSIX_CHECKED_MEMCPY(server_name, blob.data, blob.size); - - *out_length = blob.size; - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_client_hello.h" + +#include +#include +#include + +#include "api/unstable/fingerprint.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_pq.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_client_server_name.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_auth_selection.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake_type.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_signature_algorithms.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn) +{ + if (conn->client_hello.parsed != 1) { + return NULL; + } + + return &conn->client_hello; +} + +static uint32_t min_size(struct s2n_blob *blob, uint32_t max_length) +{ + return blob->size < max_length ? blob->size : max_length; +} + +static S2N_RESULT s2n_generate_client_session_id(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + /* Session id already generated - no-op */ + if (conn->session_id_len) { + return S2N_RESULT_OK; + } + + /* Only generate the session id if using tickets */ + bool generate = conn->config->use_tickets; + + /* TLS1.3 doesn't require session ids. The field is actually renamed to legacy_session_id. + * However, we still set a session id if dealing with troublesome middleboxes + * (middlebox compatibility mode) or if trying to use a TLS1.2 ticket. + */ + if (conn->client_protocol_version >= S2N_TLS13) { + generate = s2n_is_middlebox_compat_enabled(conn) || conn->resume_protocol_version; + } + + /* Session id not needed - no-op */ + if (!generate) { + return S2N_RESULT_OK; + } + + /* QUIC should not allow session ids for any reason. + * + *= https://www.rfc-editor.org/rfc/rfc9001#section-8.4 + *# A server SHOULD treat the receipt of a TLS ClientHello with a non-empty + *# legacy_session_id field as a connection error of type PROTOCOL_VIOLATION. + */ + RESULT_ENSURE(!conn->quic_enabled, S2N_ERR_UNSUPPORTED_WITH_QUIC); + + struct s2n_blob session_id = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + RESULT_GUARD(s2n_get_public_random_data(&session_id)); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + return S2N_RESULT_OK; +} + +ssize_t s2n_client_hello_get_raw_message_length(struct s2n_client_hello *ch) +{ + POSIX_ENSURE_REF(ch); + + return ch->raw_message.size; +} + +ssize_t s2n_client_hello_get_raw_message(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + + uint32_t len = min_size(&ch->raw_message, max_length); + POSIX_CHECKED_MEMCPY(out, ch->raw_message.data, len); + return len; +} + +ssize_t s2n_client_hello_get_cipher_suites_length(struct s2n_client_hello *ch) +{ + POSIX_ENSURE_REF(ch); + + return ch->cipher_suites.size; +} + +int s2n_client_hello_cb_done(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE(conn->config->client_hello_cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(conn->client_hello.callback_invoked == 1, S2N_ERR_ASYNC_NOT_PERFORMED); + POSIX_ENSURE(conn->client_hello.parsed == 1, S2N_ERR_INVALID_STATE); + + conn->client_hello.callback_async_blocked = 0; + conn->client_hello.callback_async_done = 1; + + return S2N_SUCCESS; +} + +ssize_t s2n_client_hello_get_cipher_suites(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(ch->cipher_suites.data); + + uint32_t len = min_size(&ch->cipher_suites, max_length); + + POSIX_CHECKED_MEMCPY(out, ch->cipher_suites.data, len); + + return len; +} + +ssize_t s2n_client_hello_get_extensions_length(struct s2n_client_hello *ch) +{ + POSIX_ENSURE_REF(ch); + + return ch->extensions.raw.size; +} + +ssize_t s2n_client_hello_get_extensions(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(ch->extensions.raw.data); + + uint32_t len = min_size(&ch->extensions.raw, max_length); + + POSIX_CHECKED_MEMCPY(out, ch->extensions.raw.data, len); + + return len; +} + +int s2n_client_hello_get_random(struct s2n_client_hello *ch, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + + POSIX_ENSURE(max_length >= S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(out, ch->random, S2N_TLS_RANDOM_DATA_LEN); + return S2N_SUCCESS; +} + +int s2n_client_hello_free_raw_message(struct s2n_client_hello *client_hello) +{ + POSIX_ENSURE_REF(client_hello); + + POSIX_GUARD(s2n_free(&client_hello->raw_message)); + + /* These point to data in the raw_message stuffer, + so we don't need to free them */ + client_hello->cipher_suites.data = NULL; + client_hello->extensions.raw.data = NULL; + + return 0; +} + +int s2n_client_hello_free(struct s2n_client_hello **ch) +{ + POSIX_ENSURE_REF(ch); + if (*ch == NULL) { + return S2N_SUCCESS; + } + + POSIX_ENSURE((*ch)->alloced, S2N_ERR_INVALID_ARGUMENT); + POSIX_GUARD(s2n_client_hello_free_raw_message(*ch)); + POSIX_GUARD(s2n_free_object((uint8_t **) ch, sizeof(struct s2n_client_hello))); + *ch = NULL; + return S2N_SUCCESS; +} + +int s2n_collect_client_hello(struct s2n_client_hello *ch, struct s2n_stuffer *source) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(source); + + uint32_t size = s2n_stuffer_data_available(source); + S2N_ERROR_IF(size == 0, S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_realloc(&ch->raw_message, size)); + POSIX_GUARD(s2n_stuffer_read(source, &ch->raw_message)); + + return 0; +} + +static S2N_RESULT s2n_client_hello_verify_for_retry(struct s2n_connection *conn, + struct s2n_client_hello *old_ch, struct s2n_client_hello *new_ch, + uint8_t *previous_client_random) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(old_ch); + RESULT_ENSURE_REF(new_ch); + RESULT_ENSURE_REF(previous_client_random); + + if (!s2n_is_hello_retry_handshake(conn)) { + return S2N_RESULT_OK; + } + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# The client will also send a + *# ClientHello when the server has responded to its ClientHello with a + *# HelloRetryRequest. In that case, the client MUST send the same + *# ClientHello without modification, except as follows: + * + * All of the exceptions that follow are extensions. + */ + RESULT_ENSURE(old_ch->legacy_version == new_ch->legacy_version, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(old_ch->compression_methods.size == new_ch->compression_methods.size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals(old_ch->compression_methods.data, new_ch->compression_methods.data, + new_ch->compression_methods.size), + S2N_ERR_BAD_MESSAGE); + + /* Some clients are not compliant with TLS 1.3 RFC, and send mismatching values in their second + * ClientHello. For increased compatibility, these checks are skipped outside of tests. The + * checks are still included in tests to ensure the s2n-tls client remains compliant. + */ + if (s2n_in_test()) { + /* In the past, the s2n-tls client updated the client random in the second ClientHello + * which is not allowed by RFC8446: https://github.com/aws/s2n-tls/pull/3311. Although the + * issue was addressed, its existence means that old versions of the s2n-tls client will + * fail this validation. + */ + RESULT_ENSURE(s2n_constant_time_equals( + previous_client_random, + conn->client_hello.random, + S2N_TLS_RANDOM_DATA_LEN), + S2N_ERR_BAD_MESSAGE); + + /* Some clients have been found to send a mismatching legacy session ID. */ + RESULT_ENSURE(old_ch->session_id.size == new_ch->session_id.size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals(old_ch->session_id.data, new_ch->session_id.data, + new_ch->session_id.size), + S2N_ERR_BAD_MESSAGE); + + /* Some clients have been found to send a mismatching cipher suite list. */ + RESULT_ENSURE(old_ch->cipher_suites.size == new_ch->cipher_suites.size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals(old_ch->cipher_suites.data, new_ch->cipher_suites.data, + new_ch->cipher_suites.size), + S2N_ERR_BAD_MESSAGE); + } + + /* + * Now enforce that the extensions also exactly match, + * except for the exceptions described in the RFC. + */ + for (size_t i = 0; i < s2n_array_len(s2n_supported_extensions); i++) { + s2n_parsed_extension *old_extension = &old_ch->extensions.parsed_extensions[i]; + uint32_t old_size = old_extension->extension.size; + s2n_parsed_extension *new_extension = &new_ch->extensions.parsed_extensions[i]; + uint32_t new_size = new_extension->extension.size; + + /* The extension type is only set if the extension is present. + * Look for a non-zero-length extension. + */ + uint16_t extension_type = 0; + if (old_size != 0) { + extension_type = old_extension->extension_type; + } else if (new_size != 0) { + extension_type = new_extension->extension_type; + } else { + continue; + } + + switch (extension_type) { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - If a "key_share" extension was supplied in the HelloRetryRequest, + *# replacing the list of shares with a list containing a single + *# KeyShareEntry from the indicated group. + */ + case TLS_EXTENSION_KEY_SHARE: + /* Handled when parsing the key share extension */ + break; + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - Removing the "early_data" extension (Section 4.2.10) if one was + *# present. Early data is not permitted after a HelloRetryRequest. + */ + case TLS_EXTENSION_EARLY_DATA: + RESULT_ENSURE(new_size == 0, S2N_ERR_BAD_MESSAGE); + break; + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - Including a "cookie" extension if one was provided in the + *# HelloRetryRequest. + */ + case TLS_EXTENSION_COOKIE: + /* Handled when parsing the cookie extension */ + break; + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2 + *# - Updating the "pre_shared_key" extension if present by recomputing + *# the "obfuscated_ticket_age" and binder values and (optionally) + *# removing any PSKs which are incompatible with the server's + *# indicated cipher suite. + */ + case TLS_EXTENSION_PRE_SHARED_KEY: + /* Handled when parsing the psk extension */ + break; + + /* Some clients have been found to send mismatching supported versions in their second + * ClientHello. The extension isn't compared byte-for-byte for increased compatibility + * with these clients. + */ + case TLS_EXTENSION_SUPPORTED_VERSIONS: + /* Additional HRR validation for the supported versions extension is performed when + * parsing the extension. + */ + break; + + /* + * No more exceptions. + * All other extensions must match. + */ + default: + RESULT_ENSURE(old_size == new_size, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(s2n_constant_time_equals( + new_extension->extension.data, + old_extension->extension.data, + old_size), + S2N_ERR_BAD_MESSAGE); + } + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_client_hello_parse_raw(struct s2n_client_hello *client_hello) +{ + RESULT_ENSURE_REF(client_hello); + + struct s2n_stuffer in_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&in_stuffer, &client_hello->raw_message)); + struct s2n_stuffer *in = &in_stuffer; + + /** + * https://tools.ietf.org/rfc/rfc8446#4.1.2 + * Structure of this message: + * + * uint16 ProtocolVersion; + * opaque Random[32]; + * + * uint8 CipherSuite[2]; + * + * struct { + * ProtocolVersion legacy_version = 0x0303; + * Random random; + * opaque legacy_session_id<0..32>; + * CipherSuite cipher_suites<2..2^16-2>; + * opaque legacy_compression_methods<1..2^8-1>; + * Extension extensions<8..2^16-1>; + * } ClientHello; + **/ + + /* legacy_version */ + uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(in, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + /* Encode the version as a 1 byte representation of the two protocol version bytes, with the + * major version in the tens place and the minor version in the ones place. For example, the + * TLS 1.2 protocol version is 0x0303, which is encoded as S2N_TLS12 (33). + */ + client_hello->legacy_version = (client_protocol_version[0] * 10) + client_protocol_version[1]; + + /* random - read and store it, erasing from raw message */ + RESULT_GUARD_POSIX(s2n_stuffer_erase_and_read_bytes(in, client_hello->random, S2N_TLS_RANDOM_DATA_LEN)); + + /* legacy_session_id */ + uint8_t session_id_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &session_id_len)); + RESULT_ENSURE(session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); + uint8_t *session_id = s2n_stuffer_raw_read(in, session_id_len); + RESULT_ENSURE(session_id != NULL, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->session_id, session_id, session_id_len)); + + /* cipher suites */ + uint16_t cipher_suites_length = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(in, &cipher_suites_length)); + RESULT_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(cipher_suites_length % S2N_TLS_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); + uint8_t *cipher_suites = s2n_stuffer_raw_read(in, cipher_suites_length); + RESULT_ENSURE(cipher_suites != NULL, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->cipher_suites, cipher_suites, cipher_suites_length)); + + /* legacy_compression_methods */ + uint8_t compression_methods_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(in, &compression_methods_len)); + uint8_t *compression_methods = s2n_stuffer_raw_read(in, compression_methods_len); + RESULT_ENSURE(compression_methods != NULL, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD_POSIX(s2n_blob_init(&client_hello->compression_methods, compression_methods, compression_methods_len)); + + /* extensions */ + RESULT_GUARD_POSIX(s2n_extension_list_parse(in, &client_hello->extensions)); + + return S2N_RESULT_OK; +} + +int s2n_parse_client_hello(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* SSLv2 ClientHellos are not allowed during a HelloRetryRequest */ + if (s2n_is_hello_retry_handshake(conn)) { + POSIX_ENSURE(!conn->client_hello.sslv2, S2N_ERR_BAD_MESSAGE); + } + + /* If a retry, move the old version of the client hello + * somewhere safe so we can compare it to the new client hello later. + */ + DEFER_CLEANUP(struct s2n_client_hello previous_hello_retry = conn->client_hello, + s2n_client_hello_free_raw_message); + + /* Save the client random before clearing for retry validation */ + uint8_t previous_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + POSIX_CHECKED_MEMCPY(previous_client_random, conn->client_hello.random, + S2N_TLS_RANDOM_DATA_LEN); + + if (s2n_is_hello_retry_handshake(conn)) { + POSIX_CHECKED_MEMSET(&conn->client_hello, 0, sizeof(struct s2n_client_hello)); + } + + POSIX_GUARD(s2n_collect_client_hello(&conn->client_hello, &conn->handshake.io)); + + if (conn->client_hello.sslv2) { + POSIX_GUARD(s2n_sslv2_client_hello_parse(conn)); + return S2N_SUCCESS; + } + + /* Parse raw, collected client hello */ + POSIX_GUARD_RESULT(s2n_client_hello_parse_raw(&conn->client_hello)); + + /* Protocol version in the ClientHello is fixed at 0x0303(TLS 1.2) for + * future versions of TLS. Therefore, we will negotiate down if a client sends + * an unexpected value above 0x0303. + */ + conn->client_protocol_version = S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); + + /* Copy the session id to the connection. */ + conn->session_id_len = conn->client_hello.session_id.size; + POSIX_CHECKED_MEMCPY(conn->session_id, conn->client_hello.session_id.data, conn->session_id_len); + + POSIX_GUARD_RESULT(s2n_client_hello_verify_for_retry(conn, + &previous_hello_retry, &conn->client_hello, previous_client_random)); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_client_hello_parse_message_impl(struct s2n_client_hello **result, + const uint8_t *raw_message, uint32_t raw_message_size) +{ + RESULT_ENSURE_REF(result); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_client_hello))); + RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + client_hello = (struct s2n_client_hello *) (void *) mem.data; + client_hello->alloced = true; + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_alloc(&in, raw_message_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&in, raw_message, raw_message_size)); + + uint8_t message_type = 0; + uint32_t message_len = 0; + RESULT_GUARD(s2n_handshake_parse_header(&in, &message_type, &message_len)); + RESULT_ENSURE(message_type == TLS_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(message_len == s2n_stuffer_data_available(&in), S2N_ERR_BAD_MESSAGE); + + RESULT_GUARD_POSIX(s2n_collect_client_hello(client_hello, &in)); + RESULT_ENSURE(s2n_stuffer_data_available(&in) == 0, S2N_ERR_BAD_MESSAGE); + + RESULT_GUARD(s2n_client_hello_parse_raw(client_hello)); + + *result = client_hello; + ZERO_TO_DISABLE_DEFER_CLEANUP(client_hello); + return S2N_RESULT_OK; +} + +struct s2n_client_hello *s2n_client_hello_parse_message(const uint8_t *raw_message, uint32_t raw_message_size) +{ + struct s2n_client_hello *result = NULL; + PTR_GUARD_RESULT(s2n_client_hello_parse_message_impl(&result, raw_message, raw_message_size)); + return result; +} + +int s2n_process_client_hello(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + /* Client hello is parsed and config is finalized. + * Negotiate protocol version, cipher suite, ALPN, select a cert, etc. */ + struct s2n_client_hello *client_hello = &conn->client_hello; + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + + if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { + conn->server_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); + conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, S2N_TLS12); + } + + /* Set default key exchange curve. + * This is going to be our fallback if the client has no preference. + * + * P-256 is our preferred fallback option because the TLS1.3 RFC requires + * all implementations to support it: + * + * https://tools.ietf.org/rfc/rfc8446#section-9.1 + * A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256) + * and SHOULD support key exchange with X25519 [RFC7748] + * + *= https://www.rfc-editor.org/rfc/rfc4492#section-4 + *# A client that proposes ECC cipher suites may choose not to include these extensions. + *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. + * + */ + const struct s2n_ecc_preferences *ecc_pref = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + POSIX_ENSURE_REF(ecc_pref); + + if (ecc_pref->count == 0) { + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + } else if (s2n_ecc_preferences_includes_curve(ecc_pref, TLS_EC_CURVE_SECP_256_R1)) { + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + } else { + /* If P-256 isn't allowed by the current security policy, choose the first / most preferred curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + } + + POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &conn->client_hello.extensions)); + + /* After parsing extensions, select a curve and corresponding keyshare to use */ + if (conn->actual_protocol_version >= S2N_TLS13) { + POSIX_GUARD(s2n_extensions_server_key_share_select(conn)); + } + + /* for pre TLS 1.3 connections, protocol selection is not done in supported_versions extensions, so do it here */ + if (conn->actual_protocol_version < S2N_TLS13) { + conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); + } + + if (conn->client_protocol_version < security_policy->minimum_protocol_version) { + POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); + POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + if (s2n_connection_is_quic_enabled(conn)) { + POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + /* Find potential certificate matches before we choose the cipher. */ + POSIX_GUARD(s2n_conn_find_name_matching_certs(conn)); + + /* Save the previous cipher suite */ + uint8_t previous_cipher_suite_iana[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + POSIX_CHECKED_MEMCPY(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN); + + /* Now choose the ciphers we have certs for. */ + if (conn->client_hello.sslv2) { + POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, + client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN)); + } else { + POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, + client_hello->cipher_suites.size / 2)); + } + + /* Check if this is the second client hello in a hello retry handshake */ + if (s2n_is_hello_retry_handshake(conn) && conn->handshake.message_number > 0) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + POSIX_ENSURE(s2n_constant_time_equals(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, + S2N_TLS_CIPHER_SUITE_LEN), + S2N_ERR_BAD_MESSAGE); + } + + /* If we're using a PSK, we don't need to choose a signature algorithm or certificate, + * because no additional auth is required. */ + if (conn->psk_params.chosen_psk != NULL) { + return S2N_SUCCESS; + } + + /* And set the signature and hash algorithm used for key exchange signatures */ + POSIX_GUARD_RESULT(s2n_signature_algorithm_select(conn)); + + /* And finally, set the certs specified by the final auth + sig_alg combo. */ + POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &conn->handshake_params.our_chain_and_key)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_client_hello_process_cb_response(struct s2n_connection *conn, int rc) +{ + if (rc < 0) { + goto fail; + } + switch (conn->config->client_hello_cb_mode) { + case S2N_CLIENT_HELLO_CB_BLOCKING: { + if (rc) { + conn->server_name_used = 1; + } + return S2N_RESULT_OK; + } + case S2N_CLIENT_HELLO_CB_NONBLOCKING: { + if (conn->client_hello.callback_async_done) { + return S2N_RESULT_OK; + } + conn->client_hello.callback_async_blocked = 1; + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + } +fail: + /* rc < 0 */ + RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn)); + RESULT_BAIL(S2N_ERR_CANCELLED); +} + +int s2n_client_hello_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE(!conn->client_hello.callback_async_blocked, S2N_ERR_ASYNC_BLOCKED); + + /* Only parse the ClientHello once */ + if (!conn->client_hello.parsed) { + POSIX_GUARD(s2n_parse_client_hello(conn)); + /* Mark the collected client hello as available when parsing is done and before the client hello callback */ + conn->client_hello.parsed = true; + } + + /* Only invoke the ClientHello callback once. + * This means that we do NOT invoke the callback again on the second ClientHello + * in a TLS1.3 retry handshake. We explicitly check for a retry because the + * callback state may have been cleared while parsing the second ClientHello. + */ + if (!conn->client_hello.callback_invoked && !IS_HELLO_RETRY_HANDSHAKE(conn)) { + /* Mark the client hello callback as invoked to avoid calling it again. */ + conn->client_hello.callback_invoked = true; + + /* Do NOT move this null check. A test exists to assert that a server connection can get + * as far as the client hello callback without using its config. To do this we need a + * specific error for a null config just before the client hello callback. The test's + * assertions are weakened if this check is moved. */ + POSIX_ENSURE(conn->config, S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); + + /* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */ + if (conn->config->client_hello_cb) { + int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx); + POSIX_GUARD_RESULT(s2n_client_hello_process_cb_response(conn, rc)); + } + } + + POSIX_GUARD(s2n_process_client_hello(conn)); + + return 0; +} + +S2N_RESULT s2n_cipher_suite_validate_available(struct s2n_connection *conn, struct s2n_cipher_suite *cipher) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(cipher); + RESULT_ENSURE_EQ(cipher->available, true); + RESULT_ENSURE_LTE(cipher->minimum_required_tls_version, conn->client_protocol_version); + if (s2n_connection_is_quic_enabled(conn)) { + RESULT_ENSURE_GTE(cipher->minimum_required_tls_version, S2N_TLS13); + } + return S2N_RESULT_OK; +} + +int s2n_client_hello_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + + const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; + POSIX_ENSURE_REF(cipher_preferences); + + if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) { + conn->client_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); + conn->actual_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); + } + + struct s2n_stuffer *out = &conn->handshake.io; + uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + + uint8_t reported_protocol_version = S2N_MIN(conn->client_protocol_version, S2N_TLS12); + conn->client_hello.legacy_version = reported_protocol_version; + client_protocol_version[0] = reported_protocol_version / 10; + client_protocol_version[1] = reported_protocol_version % 10; + POSIX_GUARD(s2n_stuffer_write_bytes(out, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + struct s2n_blob client_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); + if (!s2n_is_hello_retry_handshake(conn)) { + /* Only generate the random data for our first client hello. + * If we retry, we'll reuse the value. */ + POSIX_GUARD_RESULT(s2n_get_public_random_data(&client_random)); + } + POSIX_GUARD(s2n_stuffer_write(out, &client_random)); + + POSIX_GUARD_RESULT(s2n_generate_client_session_id(conn)); + POSIX_GUARD(s2n_stuffer_write_uint8(out, conn->session_id_len)); + if (conn->session_id_len > 0) { + POSIX_GUARD(s2n_stuffer_write_bytes(out, conn->session_id, conn->session_id_len)); + } + + /* Reserve space for size of the list of available ciphers */ + struct s2n_stuffer_reservation available_cipher_suites_size; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &available_cipher_suites_size)); + + /* Now, write the IANA values of every available cipher suite in our list */ + struct s2n_cipher_suite *cipher = NULL; + bool tls12_is_possible = false; + for (size_t i = 0; i < security_policy->cipher_preferences->count; i++) { + cipher = cipher_preferences->suites[i]; + if (s2n_result_is_error(s2n_cipher_suite_validate_available(conn, cipher))) { + continue; + } + if (cipher->minimum_required_tls_version < S2N_TLS13) { + tls12_is_possible = true; + } + POSIX_GUARD(s2n_stuffer_write_bytes(out, cipher->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + } + + /** + * For initial handshakes: + *= https://www.rfc-editor.org/rfc/rfc5746#3.4 + *# o The client MUST include either an empty "renegotiation_info" + *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling + *# cipher suite value in the ClientHello. Including both is NOT + *# RECOMMENDED. + * For maximum backwards compatibility, we choose to use the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite + * rather than the "renegotiation_info" extension. + * + * For renegotiation handshakes: + *= https://www.rfc-editor.org/rfc/rfc5746#3.5 + *# The SCSV MUST NOT be included. + */ + if (tls12_is_possible && !s2n_handshake_is_renegotiation(conn)) { + uint8_t renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; + POSIX_GUARD(s2n_stuffer_write_bytes(out, renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN)); + } + + /* Write size of the list of available ciphers */ + uint32_t ciphers_size = 0; + POSIX_GUARD(s2n_stuffer_get_vector_size(&available_cipher_suites_size, &ciphers_size)); + POSIX_ENSURE(ciphers_size > 0, S2N_ERR_INVALID_CIPHER_PREFERENCES); + POSIX_GUARD(s2n_stuffer_write_reservation(&available_cipher_suites_size, ciphers_size)); + + /* Zero compression methods */ + POSIX_GUARD(s2n_stuffer_write_uint8(out, 1)); + POSIX_GUARD(s2n_stuffer_write_uint8(out, 0)); + + /* Write the extensions */ + POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, out)); + + /* Once the message is complete, finish calculating the PSK binders. + * + * The PSK binders require all the sizes in the ClientHello to be written correctly, + * including the extension size and extension list size, and therefore have + * to be calculated AFTER we finish writing the entire extension list. */ + POSIX_GUARD_RESULT(s2n_finish_psk_extension(conn)); + + /* If early data was not requested as part of the ClientHello, it never will be. */ + if (conn->early_data_state == S2N_UNKNOWN_EARLY_DATA_STATE) { + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_NOT_REQUESTED)); + } + + return S2N_SUCCESS; +} + +/* + * s2n-tls does NOT support SSLv2. However, it does support SSLv2 ClientHellos. + * Clients may send SSLv2 ClientHellos advertising higher protocol versions for + * backwards compatibility reasons. See https://tools.ietf.org/rfc/rfc2246 Appendix E. + * + * In this case, conn->client_hello.legacy_version and conn->client_protocol_version + * will be higher than SSLv2. + * + * See http://www-archive.mozilla.org/projects/security/pki/nss/ssl/draft02.html Section 2.5 + * for a description of the expected SSLv2 format. + * Alternatively, the TLS1.0 RFC includes a more modern description of the format: + * https://tools.ietf.org/rfc/rfc2246 Appendix E.1 + */ +int s2n_sslv2_client_hello_parse(struct s2n_connection *conn) +{ + struct s2n_client_hello *client_hello = &conn->client_hello; + conn->client_protocol_version = S2N_MIN(client_hello->legacy_version, S2N_TLS12); + + struct s2n_stuffer in_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&in_stuffer, &client_hello->raw_message)); + POSIX_GUARD(s2n_stuffer_skip_write(&in_stuffer, client_hello->raw_message.size)); + struct s2n_stuffer *in = &in_stuffer; + + /* We start 5 bytes into the record */ + uint16_t cipher_suites_length = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); + POSIX_ENSURE(cipher_suites_length > 0, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(cipher_suites_length % S2N_SSLv2_CIPHER_SUITE_LEN == 0, S2N_ERR_BAD_MESSAGE); + + uint16_t session_id_length = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &session_id_length)); + + uint16_t challenge_length = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &challenge_length)); + + S2N_ERROR_IF(challenge_length > S2N_TLS_RANDOM_DATA_LEN, S2N_ERR_BAD_MESSAGE); + + client_hello->cipher_suites.size = cipher_suites_length; + client_hello->cipher_suites.data = s2n_stuffer_raw_read(in, cipher_suites_length); + POSIX_ENSURE_REF(client_hello->cipher_suites.data); + + S2N_ERROR_IF(session_id_length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_blob_init(&client_hello->session_id, s2n_stuffer_raw_read(in, session_id_length), session_id_length)); + if (session_id_length > 0 && session_id_length <= S2N_TLS_SESSION_ID_MAX_LEN) { + POSIX_CHECKED_MEMCPY(conn->session_id, client_hello->session_id.data, session_id_length); + conn->session_id_len = (uint8_t) session_id_length; + } + + struct s2n_blob b = { 0 }; + POSIX_GUARD(s2n_blob_init(&b, conn->client_hello.random, S2N_TLS_RANDOM_DATA_LEN)); + + b.data += S2N_TLS_RANDOM_DATA_LEN - challenge_length; + b.size -= S2N_TLS_RANDOM_DATA_LEN - challenge_length; + + POSIX_GUARD(s2n_stuffer_read(in, &b)); + + return 0; +} + +int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type, + s2n_parsed_extensions_list *parsed_extension_list, s2n_parsed_extension **parsed_extension) +{ + POSIX_ENSURE_REF(parsed_extension_list); + POSIX_ENSURE_REF(parsed_extension); + + s2n_extension_type_id extension_type_id = 0; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type, &extension_type_id)); + + s2n_parsed_extension *found_parsed_extension = &parsed_extension_list->parsed_extensions[extension_type_id]; + POSIX_ENSURE(found_parsed_extension->extension.data, S2N_ERR_EXTENSION_NOT_RECEIVED); + POSIX_ENSURE(found_parsed_extension->extension_type == extension_type, S2N_ERR_INVALID_PARSED_EXTENSIONS); + + *parsed_extension = found_parsed_extension; + return S2N_SUCCESS; +} + +ssize_t s2n_client_hello_get_extension_length(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type) +{ + POSIX_ENSURE_REF(ch); + + s2n_parsed_extension *parsed_extension = NULL; + if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { + return 0; + } + + return parsed_extension->extension.size; +} + +ssize_t s2n_client_hello_get_extension_by_id(struct s2n_client_hello *ch, s2n_tls_extension_type extension_type, uint8_t *out, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + + s2n_parsed_extension *parsed_extension = NULL; + if (s2n_client_hello_get_parsed_extension(extension_type, &ch->extensions, &parsed_extension) != S2N_SUCCESS) { + return 0; + } + + uint32_t len = min_size(&parsed_extension->extension, max_length); + POSIX_CHECKED_MEMCPY(out, parsed_extension->extension.data, len); + return len; +} + +int s2n_client_hello_get_session_id_length(struct s2n_client_hello *ch, uint32_t *out_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out_length); + *out_length = ch->session_id.size; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(out_length); + + uint32_t len = min_size(&ch->session_id, max_length); + POSIX_CHECKED_MEMCPY(out, ch->session_id.data, len); + *out_length = len; + + return S2N_SUCCESS; +} + +int s2n_client_hello_get_compression_methods_length(struct s2n_client_hello *ch, uint32_t *out_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out_length); + *out_length = ch->compression_methods.size; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_compression_methods(struct s2n_client_hello *ch, uint8_t *list, uint32_t list_length, uint32_t *out_length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(list); + POSIX_ENSURE_REF(out_length); + + POSIX_ENSURE(list_length >= ch->compression_methods.size, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(list, ch->compression_methods.data, ch->compression_methods.size); + *out_length = ch->compression_methods.size; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_legacy_protocol_version(struct s2n_client_hello *ch, uint8_t *out) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + *out = ch->legacy_version; + return S2N_SUCCESS; +} + +int s2n_client_hello_get_legacy_record_version(struct s2n_client_hello *ch, uint8_t *out) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(out); + POSIX_ENSURE(ch->record_version_recorded, S2N_ERR_INVALID_ARGUMENT); + *out = ch->legacy_record_version; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_client_hello_get_raw_extension(uint16_t extension_iana, + struct s2n_blob *raw_extensions, struct s2n_blob *extension) +{ + RESULT_ENSURE_REF(raw_extensions); + RESULT_ENSURE_REF(extension); + + *extension = (struct s2n_blob){ 0 }; + + struct s2n_stuffer raw_extensions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&raw_extensions_stuffer, raw_extensions)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&raw_extensions_stuffer, raw_extensions->size)); + + while (s2n_stuffer_data_available(&raw_extensions_stuffer) > 0) { + uint16_t extension_type = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_type)); + + uint16_t extension_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&raw_extensions_stuffer, &extension_size)); + + uint8_t *extension_data = s2n_stuffer_raw_read(&raw_extensions_stuffer, extension_size); + RESULT_ENSURE_REF(extension_data); + + if (extension_iana == extension_type) { + RESULT_GUARD_POSIX(s2n_blob_init(extension, extension_data, extension_size)); + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extension_iana, bool *exists) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(exists); + + *exists = false; + + s2n_extension_type_id extension_type_id = s2n_unsupported_extension; + if (s2n_extension_supported_iana_value_to_id(extension_iana, &extension_type_id) == S2N_SUCCESS) { + s2n_parsed_extension *parsed_extension = NULL; + if (s2n_client_hello_get_parsed_extension(extension_iana, &ch->extensions, &parsed_extension) == S2N_SUCCESS) { + *exists = true; + } + return S2N_SUCCESS; + } + + struct s2n_blob extension = { 0 }; + POSIX_GUARD_RESULT(s2n_client_hello_get_raw_extension(extension_iana, &ch->extensions.raw, &extension)); + if (extension.data != NULL) { + *exists = true; + } + return S2N_SUCCESS; +} + +int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups, + uint16_t groups_count_max, uint16_t *groups_count_out) +{ + POSIX_ENSURE_REF(groups_count_out); + *groups_count_out = 0; + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(groups); + + s2n_parsed_extension *supported_groups_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS, &ch->extensions, &supported_groups_extension)); + POSIX_ENSURE_REF(supported_groups_extension); + + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &supported_groups_extension->extension)); + + uint16_t supported_groups_count = 0; + POSIX_GUARD_RESULT(s2n_supported_groups_parse_count(&extension_stuffer, &supported_groups_count)); + POSIX_ENSURE(supported_groups_count <= groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); + + for (size_t i = 0; i < supported_groups_count; i++) { + /* s2n_stuffer_read_uint16 is used to read each of the supported groups in network-order + * endianness. + */ + POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &groups[i])); + } + + *groups_count_out = supported_groups_count; + + return S2N_SUCCESS; +} + +int s2n_client_hello_get_server_name_length(struct s2n_client_hello *ch, uint16_t *length) +{ + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(length); + *length = 0; + + s2n_parsed_extension *server_name_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); + POSIX_ENSURE_REF(server_name_extension); + + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); + + struct s2n_blob blob = { 0 }; + POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); + *length = blob.size; + + return S2N_SUCCESS; +} + +int s2n_client_hello_get_server_name(struct s2n_client_hello *ch, uint8_t *server_name, uint16_t length, uint16_t *out_length) +{ + POSIX_ENSURE_REF(out_length); + POSIX_ENSURE_REF(ch); + POSIX_ENSURE_REF(server_name); + *out_length = 0; + + s2n_parsed_extension *server_name_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SERVER_NAME, &ch->extensions, &server_name_extension)); + POSIX_ENSURE_REF(server_name_extension); + + struct s2n_stuffer extension_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &server_name_extension->extension)); + + struct s2n_blob blob = { 0 }; + POSIX_GUARD_RESULT(s2n_client_server_name_parse(&extension_stuffer, &blob)); + POSIX_ENSURE_LTE(blob.size, length); + POSIX_CHECKED_MEMCPY(server_name, blob.data, blob.size); + + *out_length = blob.size; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_client_key_exchange.c b/tls/s2n_client_key_exchange.c index c40239a8658..ece19ecb5d0 100644 --- a/tls/s2n_client_key_exchange.c +++ b/tls/s2n_client_key_exchange.c @@ -1,348 +1,348 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_dhe.h" -#include "crypto/s2n_pkey.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_async_pkey.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_kex.h" -#include "tls/s2n_key_log.h" -#include "tls/s2n_resume.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -typedef S2N_RESULT s2n_kex_client_key_method(const struct s2n_kex *kex, struct s2n_connection *conn, struct s2n_blob *shared_key); -typedef void *s2n_stuffer_action(struct s2n_stuffer *stuffer, uint32_t data_len); - -static int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *shared_key); - -/* - *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 - *# client_version - *# The latest (newest) version supported by the client. This is - *# used to detect version rollback attacks. - * - * However, TLS1.2 rsa kex does not account for the existence of TLS1.3. - * Therefore "latest" actually means "latest up to TLS1.2". - */ -static S2N_RESULT s2n_client_key_exchange_get_rsa_client_version(struct s2n_connection *conn, - uint8_t client_version[S2N_TLS_PROTOCOL_VERSION_LEN]) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(client_version); - uint8_t client_version_for_rsa = S2N_MIN(conn->client_protocol_version, S2N_TLS12); - client_version[0] = client_version_for_rsa / 10; - client_version[1] = client_version_for_rsa % 10; - return S2N_RESULT_OK; -} - -static int s2n_hybrid_client_action(struct s2n_connection *conn, struct s2n_blob *combined_shared_key, - s2n_kex_client_key_method kex_method, uint32_t *cursor, s2n_stuffer_action stuffer_action) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(kex_method); - POSIX_ENSURE_REF(stuffer_action); - - struct s2n_stuffer *io = &conn->handshake.io; - const struct s2n_kex *hybrid_kex_0 = conn->secure->cipher_suite->key_exchange_alg->hybrid[0]; - const struct s2n_kex *hybrid_kex_1 = conn->secure->cipher_suite->key_exchange_alg->hybrid[1]; - - /* Keep a copy to the start of the entire hybrid client key exchange message for the hybrid PRF */ - struct s2n_blob *client_key_exchange_message = &conn->kex_params.client_key_exchange_message; - client_key_exchange_message->data = stuffer_action(io, 0); - POSIX_ENSURE_REF(client_key_exchange_message->data); - const uint32_t start_cursor = *cursor; - - DEFER_CLEANUP(struct s2n_blob shared_key_0 = { 0 }, s2n_free); - POSIX_GUARD_RESULT(kex_method(hybrid_kex_0, conn, &shared_key_0)); - - struct s2n_blob *shared_key_1 = &(conn->kex_params.kem_params.shared_secret); - POSIX_GUARD_RESULT(kex_method(hybrid_kex_1, conn, shared_key_1)); - - const uint32_t end_cursor = *cursor; - POSIX_ENSURE_GTE(end_cursor, start_cursor); - client_key_exchange_message->size = end_cursor - start_cursor; - - POSIX_GUARD(s2n_alloc(combined_shared_key, shared_key_0.size + shared_key_1->size)); - struct s2n_stuffer stuffer_combiner = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, combined_shared_key)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &shared_key_0)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, shared_key_1)); - - POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); - - return 0; -} - -static int s2n_calculate_keys(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - /* Turn the pre-master secret into a master secret */ - POSIX_GUARD_RESULT(s2n_kex_tls_prf(conn->secure->cipher_suite->key_exchange_alg, conn, shared_key)); - - /* Expand the keys */ - POSIX_GUARD(s2n_prf_key_expansion(conn)); - /* Save the master secret in the cache. - * Failing to cache the session should not affect the current handshake. - */ - if (s2n_allowed_to_cache_connection(conn)) { - s2n_result_ignore(s2n_store_to_cache(conn)); - } - /* log the secret, if needed */ - s2n_result_ignore(s2n_key_log_tls12_secret(conn)); - return 0; -} - -int s2n_rsa_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - /* Set shared_key before async guard to pass the proper shared_key to the caller upon async completion */ - POSIX_ENSURE_REF(shared_key); - shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; - shared_key->size = S2N_TLS_SECRET_LEN; - - S2N_ASYNC_PKEY_GUARD(conn); - - struct s2n_stuffer *in = &conn->handshake.io; - uint16_t length = 0; - - if (conn->actual_protocol_version == S2N_SSLv3) { - length = s2n_stuffer_data_available(in); - } else { - POSIX_GUARD(s2n_stuffer_read_uint16(in, &length)); - } - - S2N_ERROR_IF(length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); - - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); - - /* Decrypt the pre-master secret */ - struct s2n_blob encrypted = { 0 }; - POSIX_GUARD(s2n_blob_init(&encrypted, s2n_stuffer_raw_read(in, length), length)); - POSIX_ENSURE_REF(encrypted.data); - POSIX_ENSURE_GT(encrypted.size, 0); - - /* First: use a random pre-master secret */ - POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); - conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; - conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; - - S2N_ASYNC_PKEY_DECRYPT(conn, &encrypted, shared_key, s2n_rsa_client_key_recv_complete); -} - -int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) -{ - S2N_ERROR_IF(decrypted->size != S2N_TLS_SECRET_LEN, S2N_ERR_SIZE_MISMATCH); - - /* Avoid copying the same buffer for the case where async pkey is not used */ - if (conn->secrets.version.tls12.rsa_premaster_secret != decrypted->data) { - /* Copy (maybe) decrypted data into shared key */ - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, decrypted->data, S2N_TLS_SECRET_LEN); - } - - /* Get client hello protocol version for comparison with decrypted data */ - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); - - conn->handshake.rsa_failed = rsa_failed; - - /* Set rsa_failed to true, if it isn't already, if the protocol version isn't what we expect */ - conn->handshake.rsa_failed |= !s2n_constant_time_equals(protocol_version, - conn->secrets.version.tls12.rsa_premaster_secret, S2N_TLS_PROTOCOL_VERSION_LEN); - - /* Required to protect against Bleichenbacher attack. - * See https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 - * We choose the first option: always setting the version in the rsa_premaster_secret - * from our local view of the client_hello value. - */ - conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; - conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; - - return 0; -} - -int s2n_dhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *in = &conn->handshake.io; - - /* Get the shared key */ - POSIX_GUARD(s2n_dh_compute_shared_secret_as_server(&conn->kex_params.server_dh_params, in, shared_key)); - /* We don't need the server params any more */ - POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); - return 0; -} - -int s2n_ecdhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *in = &conn->handshake.io; - - /* Get the shared key */ - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_server(&conn->kex_params.server_ecc_evp_params, in, shared_key)); - /* We don't need the server params any more */ - POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); - return 0; -} - -int s2n_kem_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - /* s2n_kem_recv_ciphertext() writes the KEM shared secret directly to - * conn->kex_params.kem_params. However, the calling function - * likely expects *shared_key to point to the shared secret. We - * can't reassign *shared_key to point to kem_params.shared_secret, - * because that would require us to take struct s2n_blob **shared_key - * as the argument, but we can't (easily) change the function signature - * because it has to be consistent with what is defined in s2n_kex. - * - * So, we assert that the caller already has *shared_key pointing - * to kem_params.shared_secret. */ - POSIX_ENSURE_REF(shared_key); - S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); - conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed. */ - - POSIX_GUARD(s2n_kem_recv_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); - - return 0; -} - -int s2n_hybrid_client_key_recv(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) -{ - return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_recv, &conn->handshake.io.read_cursor, - &s2n_stuffer_raw_read); -} - -int s2n_client_key_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; - DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); - POSIX_GUARD_RESULT(s2n_kex_client_key_recv(key_exchange, conn, &shared_key)); - - POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); - return 0; -} - -int s2n_dhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *out = &conn->handshake.io; - POSIX_GUARD(s2n_dh_compute_shared_secret_as_client(&conn->kex_params.server_dh_params, out, shared_key)); - - /* We don't need the server params any more */ - POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); - return 0; -} - -int s2n_ecdhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - struct s2n_stuffer *out = &conn->handshake.io; - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_client(&conn->kex_params.server_ecc_evp_params, out, shared_key)); - - /* We don't need the server params any more */ - POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); - return 0; -} - -int s2n_rsa_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); - - shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; - shared_key->size = S2N_TLS_SECRET_LEN; - - POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); - - /* Over-write the first two bytes with the client hello version, per RFC2246/RFC4346/RFC5246 7.4.7.1. - * The latest version supported by client (as seen from the the client hello version) are <= TLS1.2 - * for all clients, because TLS 1.3 clients freezes the TLS1.2 legacy version in client hello. - */ - POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN); - - uint32_t encrypted_size = 0; - POSIX_GUARD_RESULT(s2n_pkey_size(&conn->handshake_params.server_public_key, &encrypted_size)); - S2N_ERROR_IF(encrypted_size > 0xffff, S2N_ERR_SIZE_MISMATCH); - - if (conn->actual_protocol_version > S2N_SSLv3) { - POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, encrypted_size)); - } - - struct s2n_blob encrypted = { 0 }; - encrypted.data = s2n_stuffer_raw_write(&conn->handshake.io, encrypted_size); - encrypted.size = encrypted_size; - POSIX_ENSURE_REF(encrypted.data); - - /* Encrypt the secret and send it on */ - POSIX_GUARD(s2n_pkey_encrypt(&conn->handshake_params.server_public_key, shared_key, &encrypted)); - - /* We don't need the key any more, so free it */ - POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); - return 0; -} - -int s2n_kem_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) -{ - /* s2n_kem_send_ciphertext() writes the KEM shared secret directly to - * conn->kex_params.kem_params. However, the calling function - * likely expects *shared_key to point to the shared secret. We - * can't reassign *shared_key to point to kem_params.shared_secret, - * because that would require us to take struct s2n_blob **shared_key - * as the argument, but we can't (easily) change the function signature - * because it has to be consistent with what is defined in s2n_kex. - * - * So, we assert that the caller already has *shared_key pointing - * to kem_params.shared_secret. */ - POSIX_ENSURE_REF(shared_key); - S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); - - conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed */ - - POSIX_GUARD(s2n_kem_send_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); - - return 0; -} - -int s2n_hybrid_client_key_send(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) -{ - return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_send, &conn->handshake.io.write_cursor, - s2n_stuffer_raw_write); -} - -int s2n_client_key_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - - const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; - DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); - - POSIX_GUARD_RESULT(s2n_kex_client_key_send(key_exchange, conn, &shared_key)); - - POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_dhe.h" +#include "crypto/s2n_pkey.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_key_log.h" +#include "tls/s2n_resume.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +typedef S2N_RESULT s2n_kex_client_key_method(const struct s2n_kex *kex, struct s2n_connection *conn, struct s2n_blob *shared_key); +typedef void *s2n_stuffer_action(struct s2n_stuffer *stuffer, uint32_t data_len); + +static int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *shared_key); + +/* + *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 + *# client_version + *# The latest (newest) version supported by the client. This is + *# used to detect version rollback attacks. + * + * However, TLS1.2 rsa kex does not account for the existence of TLS1.3. + * Therefore "latest" actually means "latest up to TLS1.2". + */ +static S2N_RESULT s2n_client_key_exchange_get_rsa_client_version(struct s2n_connection *conn, + uint8_t client_version[S2N_TLS_PROTOCOL_VERSION_LEN]) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(client_version); + uint8_t client_version_for_rsa = S2N_MIN(conn->client_protocol_version, S2N_TLS12); + client_version[0] = client_version_for_rsa / 10; + client_version[1] = client_version_for_rsa % 10; + return S2N_RESULT_OK; +} + +static int s2n_hybrid_client_action(struct s2n_connection *conn, struct s2n_blob *combined_shared_key, + s2n_kex_client_key_method kex_method, uint32_t *cursor, s2n_stuffer_action stuffer_action) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(kex_method); + POSIX_ENSURE_REF(stuffer_action); + + struct s2n_stuffer *io = &conn->handshake.io; + const struct s2n_kex *hybrid_kex_0 = conn->secure->cipher_suite->key_exchange_alg->hybrid[0]; + const struct s2n_kex *hybrid_kex_1 = conn->secure->cipher_suite->key_exchange_alg->hybrid[1]; + + /* Keep a copy to the start of the entire hybrid client key exchange message for the hybrid PRF */ + struct s2n_blob *client_key_exchange_message = &conn->kex_params.client_key_exchange_message; + client_key_exchange_message->data = stuffer_action(io, 0); + POSIX_ENSURE_REF(client_key_exchange_message->data); + const uint32_t start_cursor = *cursor; + + DEFER_CLEANUP(struct s2n_blob shared_key_0 = { 0 }, s2n_free); + POSIX_GUARD_RESULT(kex_method(hybrid_kex_0, conn, &shared_key_0)); + + struct s2n_blob *shared_key_1 = &(conn->kex_params.kem_params.shared_secret); + POSIX_GUARD_RESULT(kex_method(hybrid_kex_1, conn, shared_key_1)); + + const uint32_t end_cursor = *cursor; + POSIX_ENSURE_GTE(end_cursor, start_cursor); + client_key_exchange_message->size = end_cursor - start_cursor; + + POSIX_GUARD(s2n_alloc(combined_shared_key, shared_key_0.size + shared_key_1->size)); + struct s2n_stuffer stuffer_combiner = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, combined_shared_key)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &shared_key_0)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, shared_key_1)); + + POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); + + return 0; +} + +static int s2n_calculate_keys(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + /* Turn the pre-master secret into a master secret */ + POSIX_GUARD_RESULT(s2n_kex_tls_prf(conn->secure->cipher_suite->key_exchange_alg, conn, shared_key)); + + /* Expand the keys */ + POSIX_GUARD(s2n_prf_key_expansion(conn)); + /* Save the master secret in the cache. + * Failing to cache the session should not affect the current handshake. + */ + if (s2n_allowed_to_cache_connection(conn)) { + s2n_result_ignore(s2n_store_to_cache(conn)); + } + /* log the secret, if needed */ + s2n_result_ignore(s2n_key_log_tls12_secret(conn)); + return 0; +} + +int s2n_rsa_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + /* Set shared_key before async guard to pass the proper shared_key to the caller upon async completion */ + POSIX_ENSURE_REF(shared_key); + shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; + shared_key->size = S2N_TLS_SECRET_LEN; + + S2N_ASYNC_PKEY_GUARD(conn); + + struct s2n_stuffer *in = &conn->handshake.io; + uint16_t length = 0; + + if (conn->actual_protocol_version == S2N_SSLv3) { + length = s2n_stuffer_data_available(in); + } else { + POSIX_GUARD(s2n_stuffer_read_uint16(in, &length)); + } + + S2N_ERROR_IF(length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); + + /* Decrypt the pre-master secret */ + struct s2n_blob encrypted = { 0 }; + POSIX_GUARD(s2n_blob_init(&encrypted, s2n_stuffer_raw_read(in, length), length)); + POSIX_ENSURE_REF(encrypted.data); + POSIX_ENSURE_GT(encrypted.size, 0); + + /* First: use a random pre-master secret */ + POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); + conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; + conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; + + S2N_ASYNC_PKEY_DECRYPT(conn, &encrypted, shared_key, s2n_rsa_client_key_recv_complete); +} + +int s2n_rsa_client_key_recv_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) +{ + S2N_ERROR_IF(decrypted->size != S2N_TLS_SECRET_LEN, S2N_ERR_SIZE_MISMATCH); + + /* Avoid copying the same buffer for the case where async pkey is not used */ + if (conn->secrets.version.tls12.rsa_premaster_secret != decrypted->data) { + /* Copy (maybe) decrypted data into shared key */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, decrypted->data, S2N_TLS_SECRET_LEN); + } + + /* Get client hello protocol version for comparison with decrypted data */ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); + + conn->handshake.rsa_failed = rsa_failed; + + /* Set rsa_failed to true, if it isn't already, if the protocol version isn't what we expect */ + conn->handshake.rsa_failed |= !s2n_constant_time_equals(protocol_version, + conn->secrets.version.tls12.rsa_premaster_secret, S2N_TLS_PROTOCOL_VERSION_LEN); + + /* Required to protect against Bleichenbacher attack. + * See https://www.rfc-editor.org/rfc/rfc5246#section-7.4.7.1 + * We choose the first option: always setting the version in the rsa_premaster_secret + * from our local view of the client_hello value. + */ + conn->secrets.version.tls12.rsa_premaster_secret[0] = protocol_version[0]; + conn->secrets.version.tls12.rsa_premaster_secret[1] = protocol_version[1]; + + return 0; +} + +int s2n_dhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *in = &conn->handshake.io; + + /* Get the shared key */ + POSIX_GUARD(s2n_dh_compute_shared_secret_as_server(&conn->kex_params.server_dh_params, in, shared_key)); + /* We don't need the server params any more */ + POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); + return 0; +} + +int s2n_ecdhe_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *in = &conn->handshake.io; + + /* Get the shared key */ + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_server(&conn->kex_params.server_ecc_evp_params, in, shared_key)); + /* We don't need the server params any more */ + POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + return 0; +} + +int s2n_kem_client_key_recv(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + /* s2n_kem_recv_ciphertext() writes the KEM shared secret directly to + * conn->kex_params.kem_params. However, the calling function + * likely expects *shared_key to point to the shared secret. We + * can't reassign *shared_key to point to kem_params.shared_secret, + * because that would require us to take struct s2n_blob **shared_key + * as the argument, but we can't (easily) change the function signature + * because it has to be consistent with what is defined in s2n_kex. + * + * So, we assert that the caller already has *shared_key pointing + * to kem_params.shared_secret. */ + POSIX_ENSURE_REF(shared_key); + S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); + conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed. */ + + POSIX_GUARD(s2n_kem_recv_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); + + return 0; +} + +int s2n_hybrid_client_key_recv(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) +{ + return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_recv, &conn->handshake.io.read_cursor, + &s2n_stuffer_raw_read); +} + +int s2n_client_key_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; + DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); + POSIX_GUARD_RESULT(s2n_kex_client_key_recv(key_exchange, conn, &shared_key)); + + POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); + return 0; +} + +int s2n_dhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *out = &conn->handshake.io; + POSIX_GUARD(s2n_dh_compute_shared_secret_as_client(&conn->kex_params.server_dh_params, out, shared_key)); + + /* We don't need the server params any more */ + POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); + return 0; +} + +int s2n_ecdhe_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + struct s2n_stuffer *out = &conn->handshake.io; + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_as_client(&conn->kex_params.server_ecc_evp_params, out, shared_key)); + + /* We don't need the server params any more */ + POSIX_GUARD(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + return 0; +} + +int s2n_rsa_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_client_key_exchange_get_rsa_client_version(conn, protocol_version)); + + shared_key->data = conn->secrets.version.tls12.rsa_premaster_secret; + shared_key->size = S2N_TLS_SECRET_LEN; + + POSIX_GUARD_RESULT(s2n_get_private_random_data(shared_key)); + + /* Over-write the first two bytes with the client hello version, per RFC2246/RFC4346/RFC5246 7.4.7.1. + * The latest version supported by client (as seen from the the client hello version) are <= TLS1.2 + * for all clients, because TLS 1.3 clients freezes the TLS1.2 legacy version in client hello. + */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls12.rsa_premaster_secret, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN); + + uint32_t encrypted_size = 0; + POSIX_GUARD_RESULT(s2n_pkey_size(&conn->handshake_params.server_public_key, &encrypted_size)); + S2N_ERROR_IF(encrypted_size > 0xffff, S2N_ERR_SIZE_MISMATCH); + + if (conn->actual_protocol_version > S2N_SSLv3) { + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, encrypted_size)); + } + + struct s2n_blob encrypted = { 0 }; + encrypted.data = s2n_stuffer_raw_write(&conn->handshake.io, encrypted_size); + encrypted.size = encrypted_size; + POSIX_ENSURE_REF(encrypted.data); + + /* Encrypt the secret and send it on */ + POSIX_GUARD(s2n_pkey_encrypt(&conn->handshake_params.server_public_key, shared_key, &encrypted)); + + /* We don't need the key any more, so free it */ + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); + return 0; +} + +int s2n_kem_client_key_send(struct s2n_connection *conn, struct s2n_blob *shared_key) +{ + /* s2n_kem_send_ciphertext() writes the KEM shared secret directly to + * conn->kex_params.kem_params. However, the calling function + * likely expects *shared_key to point to the shared secret. We + * can't reassign *shared_key to point to kem_params.shared_secret, + * because that would require us to take struct s2n_blob **shared_key + * as the argument, but we can't (easily) change the function signature + * because it has to be consistent with what is defined in s2n_kex. + * + * So, we assert that the caller already has *shared_key pointing + * to kem_params.shared_secret. */ + POSIX_ENSURE_REF(shared_key); + S2N_ERROR_IF(shared_key != &(conn->kex_params.kem_params.shared_secret), S2N_ERR_SAFETY); + + conn->kex_params.kem_params.len_prefixed = true; /* PQ TLS 1.2 is always length prefixed */ + + POSIX_GUARD(s2n_kem_send_ciphertext(&(conn->handshake.io), &(conn->kex_params.kem_params))); + + return 0; +} + +int s2n_hybrid_client_key_send(struct s2n_connection *conn, struct s2n_blob *combined_shared_key) +{ + return s2n_hybrid_client_action(conn, combined_shared_key, &s2n_kex_client_key_send, &conn->handshake.io.write_cursor, + s2n_stuffer_raw_write); +} + +int s2n_client_key_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + + const struct s2n_kex *key_exchange = conn->secure->cipher_suite->key_exchange_alg; + DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free_or_wipe); + + POSIX_GUARD_RESULT(s2n_kex_client_key_send(key_exchange, conn, &shared_key)); + + POSIX_GUARD(s2n_calculate_keys(conn, &shared_key)); + return 0; +} diff --git a/tls/s2n_config.c b/tls/s2n_config.c index b69af707f95..2e69cf777ef 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -1,1345 +1,1345 @@ -/* - * 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. - */ - -#ifndef _S2N_PRELUDE_INCLUDED - #if !defined(_MSC_VER) - /* make sure s2n_prelude.h is includes as part of the compiler flags, if not then fail the build */ - #error "Expected s2n_prelude.h to be included as part of the compiler flags" - #endif -#endif - -#if !defined(_MSC_VER) -#include -#endif -#include - -#include "api/unstable/custom_x509_extensions.h" -#include "api/unstable/npn.h" -#include "crypto/s2n_certificate.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hkdf.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_pq.h" -#include "error/s2n_errno.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_map.h" -#include "utils/s2n_safety.h" - -#if defined(_MSC_VER) - #define S2N_CLOCK_HW 0 - #define S2N_CLOCK_SYS 0 -#elif defined(CLOCK_MONOTONIC_RAW) - #define S2N_CLOCK_HW CLOCK_MONOTONIC_RAW - #define S2N_CLOCK_SYS CLOCK_REALTIME -#else - #define S2N_CLOCK_HW CLOCK_MONOTONIC - #define S2N_CLOCK_SYS CLOCK_REALTIME -#endif - -int s2n_default_monotonic_clock(void *unused_data, uint64_t *nanoseconds) -{ - struct timespec current_time = { 0 }; - - POSIX_GUARD(clock_gettime(S2N_CLOCK_HW, ¤t_time)); - - *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; - *nanoseconds += current_time.tv_nsec; - - return 0; -} - -static int wall_clock(void *data, uint64_t *nanoseconds) -{ - struct timespec current_time = { 0 }; - - POSIX_GUARD(clock_gettime(S2N_CLOCK_SYS, ¤t_time)); - - *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; - *nanoseconds += current_time.tv_nsec; - - return 0; -} - -static struct s2n_config s2n_default_config = { 0 }; -static struct s2n_config s2n_default_fips_config = { 0 }; -static struct s2n_config s2n_default_tls13_config = { 0 }; - -static int s2n_config_setup_default(struct s2n_config *config) -{ - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default")); - return S2N_SUCCESS; -} - -static int s2n_config_setup_tls13(struct s2n_config *config) -{ - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_tls13")); - return S2N_SUCCESS; -} - -static int s2n_config_setup_fips(struct s2n_config *config) -{ - POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_fips")); - return S2N_SUCCESS; -} - -static int s2n_config_init(struct s2n_config *config) -{ - config->wall_clock = wall_clock; - config->monotonic_clock = s2n_default_monotonic_clock; - config->ct_type = S2N_CT_SUPPORT_NONE; - config->mfl_code = S2N_TLS_MAX_FRAG_LEN_EXT_NONE; - config->alert_behavior = S2N_ALERT_FAIL_ON_WARNINGS; - config->session_state_lifetime_in_nanos = S2N_STATE_LIFETIME_IN_NANOS; - config->encrypt_decrypt_key_lifetime_in_nanos = S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS; - config->decrypt_key_lifetime_in_nanos = S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS; - config->async_pkey_validation_mode = S2N_ASYNC_PKEY_VALIDATION_FAST; - config->check_ocsp = 1; - - config->client_hello_cb_mode = S2N_CLIENT_HELLO_CB_BLOCKING; - - POSIX_GUARD(s2n_config_setup_default(config)); - if (s2n_use_default_tls13_config()) { - POSIX_GUARD(s2n_config_setup_tls13(config)); - } else if (s2n_is_in_fips_mode()) { - POSIX_GUARD(s2n_config_setup_fips(config)); - } - - POSIX_GUARD_PTR(config->domain_name_to_cert_map = s2n_map_new_with_initial_capacity(1)); - POSIX_GUARD_RESULT(s2n_map_complete(config->domain_name_to_cert_map)); - - s2n_x509_trust_store_init_empty(&config->trust_store); - - return 0; -} - -static int s2n_config_cleanup(struct s2n_config *config) -{ - s2n_x509_trust_store_wipe(&config->trust_store); - config->check_ocsp = 0; - - if (config->custom_x509_extension_oids) { - sk_ASN1_OBJECT_pop_free(config->custom_x509_extension_oids, ASN1_OBJECT_free); - config->custom_x509_extension_oids = NULL; - } - - POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); - POSIX_GUARD(s2n_config_free_cert_chain_and_key(config)); - POSIX_GUARD(s2n_config_free_dhparams(config)); - POSIX_GUARD(s2n_free(&config->application_protocols)); - POSIX_GUARD(s2n_free(&config->cert_authorities)); - POSIX_GUARD_RESULT(s2n_map_free(config->domain_name_to_cert_map)); - - POSIX_CHECKED_MEMSET(config, 0, sizeof(struct s2n_config)); - - return 0; -} - -static int s2n_config_update_domain_name_to_cert_map(struct s2n_config *config, - struct s2n_blob *name, - struct s2n_cert_chain_and_key *cert_key_pair) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(name); - - struct s2n_map *domain_name_to_cert_map = config->domain_name_to_cert_map; - /* s2n_map does not allow zero-size key */ - if (name->size == 0) { - return 0; - } - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); - struct s2n_blob s2n_map_value = { 0 }; - bool key_found = false; - POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, name, &s2n_map_value, &key_found)); - if (!key_found) { - struct certs_by_type value = { { 0 } }; - value.certs[cert_type] = cert_key_pair; - s2n_map_value.data = (uint8_t *) &value; - s2n_map_value.size = sizeof(struct certs_by_type); - - POSIX_GUARD_RESULT(s2n_map_unlock(domain_name_to_cert_map)); - - int add_result = s2n_result_is_ok(s2n_map_add(domain_name_to_cert_map, name, &s2n_map_value)) ? S2N_SUCCESS : S2N_FAILURE; - - /* The map must always be re-completed (made immutable) after an unlock, - * even if the add failed. Otherwise s2n_map_lookup, called on every - * ClientHello for SNI matching, will fail its RESULT_ENSURE(map->immutable) - * check, silently disabling SNI-based certificate selection for the - * lifetime of this config. - */ - POSIX_GUARD_RESULT(s2n_map_complete(domain_name_to_cert_map)); - POSIX_GUARD(add_result); - } else { - struct certs_by_type *value = (void *) s2n_map_value.data; - if (value->certs[cert_type] == NULL) { - value->certs[cert_type] = cert_key_pair; - } else if (config->cert_tiebreak_cb) { - /* There's an existing certificate for this (domain_name, auth_method). - * Run the application's tiebreaking callback to decide which cert should be used. - * An application may have some context specific logic to resolve ties that are based - * on factors like trust, expiry, etc. - */ - struct s2n_cert_chain_and_key *winner = config->cert_tiebreak_cb( - value->certs[cert_type], - cert_key_pair, - name->data, - name->size); - if (winner) { - value->certs[cert_type] = winner; - } - } - } - - return 0; -} - -int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) -{ - uint32_t cn_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->cn_names, &cn_len)); - uint32_t san_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->san_names, &san_len)); - - if (san_len == 0) { - for (uint32_t i = 0; i < cn_len; i++) { - struct s2n_blob *cn_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->cn_names, i, (void **) &cn_name)); - POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, cn_name, cert_key_pair)); - } - } else { - for (uint32_t i = 0; i < san_len; i++) { - struct s2n_blob *san_name = NULL; - POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->san_names, i, (void **) &san_name)); - POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, san_name, cert_key_pair)); - } - } - - return 0; -} - -struct s2n_config *s2n_fetch_default_config(void) -{ - if (s2n_use_default_tls13_config()) { - return &s2n_default_tls13_config; - } - if (s2n_is_in_fips_mode()) { - return &s2n_default_fips_config; - } - return &s2n_default_config; -} - -int s2n_config_set_unsafe_for_testing(struct s2n_config *config) -{ - POSIX_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); - config->check_ocsp = 0; - config->disable_x509_validation = 1; - - return S2N_SUCCESS; -} - -int s2n_config_defaults_init(void) -{ - /* Set up fips defaults */ - if (s2n_is_in_fips_mode()) { - POSIX_GUARD(s2n_config_init(&s2n_default_fips_config)); - POSIX_GUARD(s2n_config_setup_fips(&s2n_default_fips_config)); - POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_fips_config)); - } else { - /* Set up default */ - POSIX_GUARD(s2n_config_init(&s2n_default_config)); - POSIX_GUARD(s2n_config_setup_default(&s2n_default_config)); - POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_config)); - } - - /* TLS 1.3 default config is only used in tests so avoid initialization costs in applications */ - POSIX_GUARD(s2n_config_init(&s2n_default_tls13_config)); - POSIX_GUARD(s2n_config_setup_tls13(&s2n_default_tls13_config)); - - return S2N_SUCCESS; -} - -void s2n_wipe_static_configs(void) -{ - s2n_config_cleanup(&s2n_default_fips_config); - s2n_config_cleanup(&s2n_default_config); - s2n_config_cleanup(&s2n_default_tls13_config); -} - -int s2n_config_load_system_certs(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - struct s2n_x509_trust_store *store = &config->trust_store; - POSIX_ENSURE(!store->loaded_system_certs, S2N_ERR_X509_TRUST_STORE); - - if (!store->trust_store) { - store->trust_store = X509_STORE_new(); - POSIX_ENSURE_REF(store->trust_store); - } - - int err_code = X509_STORE_set_default_paths(store->trust_store); - if (!err_code) { - s2n_x509_trust_store_wipe(store); - POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); - } - store->loaded_system_certs = true; - - return S2N_SUCCESS; -} - -struct s2n_config *s2n_config_new_minimal(void) -{ - struct s2n_blob allocator = { 0 }; - struct s2n_config *new_config = NULL; - - PTR_GUARD_POSIX(s2n_alloc(&allocator, sizeof(struct s2n_config))); - PTR_GUARD_POSIX(s2n_blob_zero(&allocator)); - - new_config = (struct s2n_config *) (void *) allocator.data; - if (s2n_config_init(new_config) != S2N_SUCCESS) { - s2n_free(&allocator); - return NULL; - } - - return new_config; -} - -struct s2n_config *s2n_config_new(void) -{ - struct s2n_config *new_config = s2n_config_new_minimal(); - PTR_ENSURE_REF(new_config); - - /* For backwards compatibility, s2n_config_new loads system certs by default. */ - PTR_GUARD_POSIX(s2n_config_load_system_certs(new_config)); - - return new_config; -} - -int s2n_config_init_session_ticket_keys(struct s2n_config *config) -{ - if (config->ticket_keys == NULL) { - POSIX_ENSURE_REF(config->ticket_keys = s2n_array_new_with_capacity(sizeof(struct s2n_ticket_key), S2N_MAX_TICKET_KEYS)); - } - - return 0; -} - -int s2n_config_free_session_ticket_keys(struct s2n_config *config) -{ - if (config->ticket_keys != NULL) { - POSIX_GUARD_RESULT(s2n_array_free_p(&config->ticket_keys)); - } - - return 0; -} - -int s2n_config_free_cert_chain_and_key(struct s2n_config *config) -{ - /* We track certificate ownership on the config itself because a config - * CANNOT use a combination of owned and unowned chains. - * If it does, and the unowned chains are freed before the config is, - * then iterating over the chains to determine which are owned and need to be freed - * will mean also reading the invalid, freed memory of any unowned certificates. - * As of now, some tests free chains before the config, so that pattern may also - * appear in application code. - */ - if (config->cert_ownership != S2N_LIB_OWNED) { - return S2N_SUCCESS; - } - - /* Free the cert_chain_and_key since the application has no reference - * to it. This is necessary until s2n_config_add_cert_chain_and_key is deprecated. */ - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - s2n_cert_chain_and_key_free(config->default_certs_by_type.certs[i]); - config->default_certs_by_type.certs[i] = NULL; - } - - config->cert_ownership = S2N_NOT_OWNED; - return S2N_SUCCESS; -} - -int s2n_config_free_dhparams(struct s2n_config *config) -{ - if (config->dhparams) { - POSIX_GUARD(s2n_dh_params_free(config->dhparams)); - } - - POSIX_GUARD(s2n_free_object((uint8_t **) &config->dhparams, sizeof(struct s2n_dh_params))); - return 0; -} - -S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config) -{ - RESULT_ENSURE_REF(config); - RESULT_GUARD_POSIX(s2n_config_free(*config)); - *config = NULL; - return S2N_RESULT_OK; -} - -int s2n_config_free(struct s2n_config *config) -{ - s2n_config_cleanup(config); - - POSIX_GUARD(s2n_free_object((uint8_t **) &config, sizeof(struct s2n_config))); - return 0; -} - -int s2n_config_get_client_auth_type(struct s2n_config *config, s2n_cert_auth_type *client_auth_type) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(client_auth_type); - *client_auth_type = config->client_cert_auth_type; - return 0; -} - -int s2n_config_set_client_auth_type(struct s2n_config *config, s2n_cert_auth_type client_auth_type) -{ - POSIX_ENSURE_REF(config); - config->client_cert_auth_type_overridden = 1; - config->client_cert_auth_type = client_auth_type; - return 0; -} - -int s2n_config_set_ct_support_level(struct s2n_config *config, s2n_ct_support_level type) -{ - POSIX_ENSURE_REF(config); - config->ct_type = type; - - return 0; -} - -int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_behavior alert_behavior) -{ - POSIX_ENSURE_REF(config); - - switch (alert_behavior) { - case S2N_ALERT_FAIL_ON_WARNINGS: - case S2N_ALERT_IGNORE_WARNINGS: - config->alert_behavior = alert_behavior; - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); - } - - return 0; -} - -int s2n_config_set_verify_host_callback(struct s2n_config *config, s2n_verify_host_fn verify_host_fn, void *data) -{ - POSIX_ENSURE_REF(config); - config->verify_host_fn = verify_host_fn; - config->data_for_verify_host = data; - return 0; -} - -int s2n_config_set_check_stapled_ocsp_response(struct s2n_config *config, uint8_t check_ocsp) -{ - POSIX_ENSURE_REF(config); - S2N_ERROR_IF(check_ocsp && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); - config->check_ocsp = check_ocsp; - return 0; -} - -int s2n_config_disable_x509_time_verification(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - config->disable_x509_time_validation = true; - return S2N_SUCCESS; -} - -int s2n_config_disable_x509_intent_verification(struct s2n_config *config) -{ - POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); - config->disable_x509_intent_verification = true; - return S2N_SUCCESS; -} - -int s2n_config_disable_x509_verification(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - s2n_x509_trust_store_wipe(&config->trust_store); - config->disable_x509_validation = 1; - return 0; -} - -int s2n_config_set_max_cert_chain_depth(struct s2n_config *config, uint16_t max_depth) -{ - POSIX_ENSURE_REF(config); - S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); - - config->max_verify_cert_chain_depth = max_depth; - config->max_verify_cert_chain_depth_set = 1; - return 0; -} - -int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type) -{ - S2N_ERROR_IF(type == S2N_STATUS_REQUEST_OCSP && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); - - POSIX_ENSURE_REF(config); - config->ocsp_status_requested_by_user = (type == S2N_STATUS_REQUEST_OCSP); - - /* Reset the ocsp_status_requested_by_s2n flag if OCSP status requests were disabled. */ - if (type == S2N_STATUS_REQUEST_NONE) { - config->ocsp_status_requested_by_s2n = false; - } - - return 0; -} - -int s2n_config_wipe_trust_store(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - s2n_x509_trust_store_wipe(&config->trust_store); - - return S2N_SUCCESS; -} - -int s2n_config_add_pem_to_trust_store(struct s2n_config *config, const char *pem) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(pem); - - POSIX_GUARD(s2n_x509_trust_store_add_pem(&config->trust_store, pem)); - - return 0; -} - -int s2n_config_set_verification_ca_location(struct s2n_config *config, const char *ca_pem_filename, const char *ca_dir) -{ - POSIX_ENSURE_REF(config); - int err_code = s2n_x509_trust_store_from_ca_file(&config->trust_store, ca_pem_filename, ca_dir); - - if (!err_code) { - config->ocsp_status_requested_by_s2n = s2n_x509_ocsp_stapling_supported() ? S2N_STATUS_REQUEST_OCSP : S2N_STATUS_REQUEST_NONE; - } - - return err_code; -} - -static int s2n_config_add_cert_chain_and_key_impl(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) -{ - POSIX_ENSURE_REF(config->domain_name_to_cert_map); - POSIX_ENSURE_REF(cert_key_pair); - - POSIX_GUARD_RESULT(s2n_security_policy_validate_certificate_chain(config->security_policy, cert_key_pair)); - - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); - config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); - - POSIX_GUARD(s2n_config_build_domain_name_to_cert_map(config, cert_key_pair)); - - if (!config->default_certs_are_explicit) { - POSIX_ENSURE(cert_type >= 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); - POSIX_ENSURE(cert_type < S2N_CERT_TYPE_COUNT, S2N_ERR_CERT_TYPE_UNSUPPORTED); - /* Attempt to auto set default based on ordering. ie: first RSA cert is the default, first ECDSA cert is the - * default, etc. */ - if (config->default_certs_by_type.certs[cert_type] == NULL) { - config->default_certs_by_type.certs[cert_type] = cert_key_pair; - } else { - /* Because library-owned certificates are tracked and cleaned up via the - * default_certs_by_type mapping, library-owned chains MUST be set as the default - * to avoid a memory leak. If they're not the default, they're not freed. - */ - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, - S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - } - } - - if (s2n_pkey_check_key_exists(cert_key_pair->private_key) != S2N_SUCCESS) { - config->no_signing_key = true; - } - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_config_validate_loaded_certificates(const struct s2n_config *config, - const struct s2n_security_policy *security_policy) -{ - RESULT_ENSURE_REF(config); - RESULT_ENSURE_REF(security_policy); - - if (security_policy->certificate_key_preferences == NULL - && security_policy->certificate_signature_preferences == NULL) { - return S2N_RESULT_OK; - } - - /* Duplicates a check in s2n_security_policy_validate_certificate_chain. - * If a large number of certificates are configured, even iterating - * over the chains to call s2n_security_policy_validate_certificate_chain - * could be prohibitively expensive. - */ - if (!security_policy->certificate_preferences_apply_locally) { - return S2N_RESULT_OK; - } - - /* validate the default certs */ - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - struct s2n_cert_chain_and_key *cert = config->default_certs_by_type.certs[i]; - if (cert == NULL) { - continue; - } - RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); - } - - /* validate the certs in the domain map */ - if (config->domain_name_to_cert_map == NULL) { - return S2N_RESULT_OK; - } - - struct s2n_map_iterator iter = { 0 }; - RESULT_GUARD(s2n_map_iterator_init(&iter, config->domain_name_to_cert_map)); - - while (s2n_map_iterator_has_next(&iter)) { - struct s2n_blob value = { 0 }; - RESULT_GUARD(s2n_map_iterator_next(&iter, &value)); - - struct certs_by_type *domain_certs = (void *) value.data; - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - struct s2n_cert_chain_and_key *cert = domain_certs->certs[i]; - if (cert == NULL) { - continue; - } - RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); - } - } - return S2N_RESULT_OK; -} - -/* Deprecated. Superseded by s2n_config_add_cert_chain_and_key_to_store */ -int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); - config->cert_ownership = S2N_LIB_OWNED; - - ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); - return S2N_SUCCESS; -} - -/* Only used in the Rust bindings. Superseded by s2n_config_add_cert_chain_and_key_to_store */ -int s2n_config_add_cert_chain(struct s2n_config *config, - uint8_t *cert_chain_pem, uint32_t cert_chain_pem_size) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), - s2n_cert_chain_and_key_ptr_free); - POSIX_ENSURE_REF(chain_and_key); - POSIX_GUARD(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, - cert_chain_pem, cert_chain_pem_size)); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); - config->cert_ownership = S2N_LIB_OWNED; - - ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); - return S2N_SUCCESS; -} - -int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - POSIX_ENSURE_REF(cert_key_pair); - POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, cert_key_pair)); - config->cert_ownership = S2N_APP_OWNED; - - return S2N_SUCCESS; -} - -int s2n_config_set_async_offload_callback(struct s2n_config *config, uint32_t allow_list, s2n_async_offload_cb fn, void *ctx) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(fn); - POSIX_ENSURE(allow_list != 0, S2N_ERR_INVALID_ARGUMENT); - - config->async_offload_allow_list = allow_list; - config->async_offload_cb = fn; - config->async_offload_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn) -{ - POSIX_ENSURE_REF(config); - - config->async_pkey_cb = fn; - - return S2N_SUCCESS; -} - -static int s2n_config_clear_default_certificates(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - /* Clearing library-owned chains would lead to a memory leak. - * See s2n_config_free_cert_chain_and_key. - */ - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - config->default_certs_by_type.certs[i] = NULL; - } - config->cert_ownership = S2N_NOT_OWNED; - return 0; -} - -int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, - struct s2n_cert_chain_and_key **cert_key_pairs, - uint32_t num_cert_key_pairs) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(cert_key_pairs); - S2N_ERROR_IF(num_cert_key_pairs < 1 || num_cert_key_pairs > S2N_CERT_TYPE_COUNT, - S2N_ERR_NUM_DEFAULT_CERTIFICATES); - - /* This method will set application-owned chains, so we must not already be using - * any library owned chains. See s2n_config_free_cert_chain_and_key. - */ - POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - /* Validate certs being set before clearing auto-chosen defaults or previously set defaults */ - struct certs_by_type new_defaults = { { 0 } }; - for (size_t i = 0; i < num_cert_key_pairs; i++) { - POSIX_ENSURE_REF(cert_key_pairs[i]); - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); - S2N_ERROR_IF(new_defaults.certs[cert_type] != NULL, S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); - new_defaults.certs[cert_type] = cert_key_pairs[i]; - } - - POSIX_GUARD(s2n_config_clear_default_certificates(config)); - for (size_t i = 0; i < num_cert_key_pairs; i++) { - s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); - config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); - config->default_certs_by_type.certs[cert_type] = cert_key_pairs[i]; - } - - config->default_certs_are_explicit = 1; - config->cert_ownership = S2N_APP_OWNED; - return 0; -} - -int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem) -{ - DEFER_CLEANUP(struct s2n_stuffer dhparams_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer dhparams_out_stuffer = { 0 }, s2n_stuffer_free); - struct s2n_blob dhparams_blob = { 0 }; - struct s2n_blob mem = { 0 }; - - /* Allocate the memory for the chain and key struct */ - POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_dh_params))); - config->dhparams = (struct s2n_dh_params *) (void *) mem.data; - - if (s2n_stuffer_alloc_ro_from_string(&dhparams_in_stuffer, dhparams_pem) != S2N_SUCCESS) { - s2n_free(&mem); - S2N_ERROR_PRESERVE_ERRNO(); - } - if (s2n_stuffer_growable_alloc(&dhparams_out_stuffer, strlen(dhparams_pem)) != S2N_SUCCESS) { - s2n_free(&mem); - S2N_ERROR_PRESERVE_ERRNO(); - } - - /* Convert pem to asn1 and asn1 to the private key */ - POSIX_GUARD(s2n_stuffer_dhparams_from_pem(&dhparams_in_stuffer, &dhparams_out_stuffer)); - - dhparams_blob.size = s2n_stuffer_data_available(&dhparams_out_stuffer); - dhparams_blob.data = s2n_stuffer_raw_read(&dhparams_out_stuffer, dhparams_blob.size); - POSIX_ENSURE_REF(dhparams_blob.data); - - POSIX_GUARD(s2n_pkcs3_to_dh_params(config->dhparams, &dhparams_blob)); - - return 0; -} - -int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) -{ - POSIX_ENSURE_REF(clock_fn); - - config->wall_clock = clock_fn; - config->sys_clock_ctx = ctx; - - return 0; -} - -int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) -{ - POSIX_ENSURE_REF(clock_fn); - - config->monotonic_clock = clock_fn; - config->monotonic_clock_ctx = ctx; - - return 0; -} - -int s2n_config_set_cache_store_callback(struct s2n_config *config, s2n_cache_store_callback cache_store_callback, void *data) -{ - POSIX_ENSURE_REF(cache_store_callback); - - config->cache_store = cache_store_callback; - config->cache_store_data = data; - - return 0; -} - -int s2n_config_set_cache_retrieve_callback(struct s2n_config *config, s2n_cache_retrieve_callback cache_retrieve_callback, void *data) -{ - POSIX_ENSURE_REF(cache_retrieve_callback); - - config->cache_retrieve = cache_retrieve_callback; - config->cache_retrieve_data = data; - - return 0; -} - -int s2n_config_set_cache_delete_callback(struct s2n_config *config, s2n_cache_delete_callback cache_delete_callback, void *data) -{ - POSIX_ENSURE_REF(cache_delete_callback); - - config->cache_delete = cache_delete_callback; - config->cache_delete_data = data; - - return 0; -} - -int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_type type, const uint8_t *data, uint32_t length) -{ - POSIX_ENSURE_REF(config); - - if (s2n_config_get_num_default_certs(config) == 0) { - POSIX_BAIL(S2N_ERR_UPDATING_EXTENSION); - } - struct s2n_cert_chain_and_key *config_chain_and_key = s2n_config_get_single_default_cert(config); - POSIX_ENSURE_REF(config_chain_and_key); - POSIX_ENSURE(config->cert_ownership == S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); - - switch (type) { - case S2N_EXTENSION_CERTIFICATE_TRANSPARENCY: - POSIX_GUARD(s2n_cert_chain_and_key_set_sct_list(config_chain_and_key, data, length)); - break; - case S2N_EXTENSION_OCSP_STAPLING: - POSIX_GUARD(s2n_cert_chain_and_key_set_ocsp_data(config_chain_and_key, data, length)); - break; - default: - POSIX_BAIL(S2N_ERR_UNRECOGNIZED_EXTENSION); - } - - return 0; -} - -int s2n_config_add_custom_x509_extension(struct s2n_config *config, uint8_t *extension_oid, uint32_t extension_oid_len) -{ - POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(extension_oid, S2N_ERR_INVALID_ARGUMENT); - - POSIX_ENSURE(s2n_libcrypto_supports_custom_oid(), S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO); - - if (config->custom_x509_extension_oids == NULL) { - config->custom_x509_extension_oids = sk_ASN1_OBJECT_new_null(); - } - POSIX_ENSURE_REF(config->custom_x509_extension_oids); - - DEFER_CLEANUP(struct s2n_blob oid_buffer = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&oid_buffer, extension_oid_len + 1)); - - POSIX_GUARD(s2n_blob_zero(&oid_buffer)); - POSIX_CHECKED_MEMCPY(oid_buffer.data, extension_oid, extension_oid_len); - oid_buffer.data[extension_oid_len] = '\0'; - - const char *oid_string = (const char *) oid_buffer.data; - POSIX_ENSURE_REF(oid_string); - - ASN1_OBJECT *critical_oid = OBJ_txt2obj(oid_string, 1); - POSIX_ENSURE_REF(critical_oid); - POSIX_ENSURE(sk_ASN1_OBJECT_push(config->custom_x509_extension_oids, critical_oid) > 0, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - return S2N_SUCCESS; -} - -int s2n_config_set_cert_request_callback(struct s2n_config *config, s2n_cert_request_callback cert_request_cb, void *ctx) -{ - POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); - - config->cert_request_cb = cert_request_cb; - config->cert_request_cb_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_set_client_hello_cb(struct s2n_config *config, s2n_client_hello_fn client_hello_cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - - config->client_hello_cb = client_hello_cb; - config->client_hello_cb_ctx = ctx; - return 0; -} - -int s2n_config_set_client_hello_cb_mode(struct s2n_config *config, s2n_client_hello_cb_mode cb_mode) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(cb_mode == S2N_CLIENT_HELLO_CB_BLOCKING || cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); - - config->client_hello_cb_mode = cb_mode; - return S2N_SUCCESS; -} - -int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code) -{ - POSIX_ENSURE_REF(config); - - S2N_ERROR_IF(mfl_code > S2N_TLS_MAX_FRAG_LEN_4096, S2N_ERR_INVALID_MAX_FRAG_LEN); - - config->mfl_code = mfl_code; - - return 0; -} - -int s2n_config_accept_max_fragment_length(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - config->accept_mfl = 1; - - return 0; -} - -int s2n_config_set_session_state_lifetime(struct s2n_config *config, - uint64_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(config); - - config->session_state_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); - return 0; -} - -int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled) -{ - POSIX_ENSURE_REF(config); - - if (config->use_tickets == enabled) { - return 0; - } - - config->use_tickets = enabled; - - if (config->initial_tickets_to_send == 0) { - /* Normally initial_tickets_to_send is set via s2n_config_set_initial_ticket_count. - * However, s2n_config_set_initial_ticket_count calls this method. - * So we set initial_tickets_to_send directly to avoid infinite recursion. */ - config->initial_tickets_to_send = 1; - } - - /* session ticket || session id is enabled */ - if (enabled) { - POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); - } else if (!config->use_session_cache) { - POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); - } - - return 0; -} - -int s2n_config_set_session_cache_onoff(struct s2n_config *config, uint8_t enabled) -{ - POSIX_ENSURE_REF(config); - if (enabled && config->cache_store && config->cache_retrieve && config->cache_delete) { - POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); - config->use_session_cache = 1; - } else { - if (!config->use_tickets) { - POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); - } - config->use_session_cache = 0; - } - return 0; -} - -int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, - uint64_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(config); - - config->encrypt_decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); - return 0; -} - -int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, - uint64_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(config); - - config->decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); - return 0; -} - -int s2n_config_add_ticket_crypto_key(struct s2n_config *config, - const uint8_t *name, uint32_t name_len, - uint8_t *key, uint32_t key_len, - uint64_t intro_time_in_seconds_from_epoch) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(name); - POSIX_ENSURE_REF(key); - - /* both session ticket and session cache encryption/decryption can use the same key mechanism */ - if (!config->use_tickets && !config->use_session_cache) { - return 0; - } - - POSIX_GUARD(s2n_config_wipe_expired_ticket_crypto_keys(config, -1)); - - POSIX_ENSURE(key_len != 0, S2N_ERR_INVALID_TICKET_KEY_LENGTH); - - uint32_t ticket_keys_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - POSIX_ENSURE(ticket_keys_len < S2N_MAX_TICKET_KEYS, S2N_ERR_TICKET_KEY_LIMIT); - - POSIX_ENSURE(name_len != 0, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - POSIX_ENSURE(name_len <= S2N_TICKET_KEY_NAME_LEN, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - - /* Copy the name into a zero-padded array. */ - /* This ensures that all ticket names are equal in length, as the serialized name is fixed length */ - uint8_t name_data[S2N_TICKET_KEY_NAME_LEN] = { 0 }; - POSIX_CHECKED_MEMCPY(name_data, name, name_len); - - uint8_t output_pad[S2N_AES256_KEY_LEN + S2N_TICKET_AAD_IMPLICIT_LEN] = { 0 }; - struct s2n_blob out_key = { 0 }; - POSIX_GUARD(s2n_blob_init(&out_key, output_pad, s2n_array_len(output_pad))); - struct s2n_blob in_key = { 0 }; - POSIX_GUARD(s2n_blob_init(&in_key, key, key_len)); - struct s2n_blob salt = { 0 }; - POSIX_GUARD(s2n_blob_init(&salt, NULL, 0)); - struct s2n_blob info = { 0 }; - POSIX_GUARD(s2n_blob_init(&info, NULL, 0)); - - struct s2n_ticket_key *session_ticket_key = { 0 }; - DEFER_CLEANUP(struct s2n_blob allocator = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&allocator, sizeof(struct s2n_ticket_key))); - session_ticket_key = (struct s2n_ticket_key *) (void *) allocator.data; - - DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free); - - POSIX_GUARD(s2n_hmac_new(&hmac)); - POSIX_GUARD(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &in_key, &info, &out_key)); - - POSIX_CHECKED_MEMCPY(session_ticket_key->key_name, name_data, s2n_array_len(name_data)); - POSIX_CHECKED_MEMCPY(session_ticket_key->aes_key, out_key.data, S2N_AES256_KEY_LEN); - out_key.data = output_pad + S2N_AES256_KEY_LEN; - POSIX_CHECKED_MEMCPY(session_ticket_key->implicit_aad, out_key.data, S2N_TICKET_AAD_IMPLICIT_LEN); - - if (intro_time_in_seconds_from_epoch == 0) { - uint64_t now = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - session_ticket_key->intro_timestamp = now; - } else { - session_ticket_key->intro_timestamp = (intro_time_in_seconds_from_epoch * ONE_SEC_IN_NANOS); - } - - POSIX_GUARD(s2n_config_store_ticket_key(config, session_ticket_key)); - - return 0; -} - -int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled) -{ - POSIX_ENSURE_REF(config); - - config->ticket_forward_secrecy = enabled; - - return S2N_SUCCESS; -} - -int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb) -{ - config->cert_tiebreak_cb = cert_tiebreak_cb; - return 0; -} - -struct s2n_cert_chain_and_key *s2n_config_get_single_default_cert(struct s2n_config *config) -{ - PTR_ENSURE_REF(config); - struct s2n_cert_chain_and_key *cert = NULL; - - for (int i = S2N_CERT_TYPE_COUNT - 1; i >= 0; i--) { - if (config->default_certs_by_type.certs[i] != NULL) { - cert = config->default_certs_by_type.certs[i]; - } - } - return cert; -} - -int s2n_config_get_num_default_certs(const struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - int num_certs = 0; - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - if (config->default_certs_by_type.certs[i] != NULL) { - num_certs++; - } - } - - return num_certs; -} - -int s2n_config_enable_cert_req_dss_legacy_compat(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - config->cert_req_dss_legacy_compat_enabled = 1; - return S2N_SUCCESS; -} - -int s2n_config_set_psk_selection_callback(struct s2n_config *config, s2n_psk_selection_callback cb, void *context) -{ - POSIX_ENSURE_REF(config); - config->psk_selection_cb = cb; - config->psk_selection_ctx = context; - return S2N_SUCCESS; -} - -int s2n_config_set_key_log_cb(struct s2n_config *config, s2n_key_log_fn callback, void *ctx) -{ - POSIX_ENSURE_MUT(config); - - config->key_log_cb = callback; - config->key_log_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_set_async_pkey_validation_mode(struct s2n_config *config, s2n_async_pkey_validation_mode mode) -{ - POSIX_ENSURE_REF(config); - - switch (mode) { - case S2N_ASYNC_PKEY_VALIDATION_FAST: - case S2N_ASYNC_PKEY_VALIDATION_STRICT: - config->async_pkey_validation_mode = mode; - return S2N_SUCCESS; - } - - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); -} - -int s2n_config_set_ctx(struct s2n_config *config, void *ctx) -{ - POSIX_ENSURE_REF(config); - - config->context = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_get_ctx(struct s2n_config *config, void **ctx) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(ctx); - - *ctx = config->context; - - return S2N_SUCCESS; -} - -int s2n_config_set_send_buffer_size(struct s2n_config *config, uint32_t size) -{ - POSIX_ENSURE_REF(config); - POSIX_ENSURE(size >= S2N_MIN_SEND_BUFFER_SIZE, S2N_ERR_INVALID_ARGUMENT); - config->send_buffer_size_override = size; - return S2N_SUCCESS; -} - -int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after_sign mode) -{ - POSIX_ENSURE_REF(config); - switch (mode) { - case S2N_VERIFY_AFTER_SIGN_DISABLED: - config->verify_after_sign = false; - break; - case S2N_VERIFY_AFTER_SIGN_ENABLED: - config->verify_after_sign = true; - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); - } - return S2N_SUCCESS; -} - -/* - *= https://www.rfc-editor.org/rfc/rfc5746#5 - *# TLS implementations SHOULD provide a mechanism to disable and enable - *# renegotiation. - */ -int s2n_config_set_renegotiate_request_cb(struct s2n_config *config, s2n_renegotiate_request_cb cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - - /* This feature cannot be used with serialization currently */ - POSIX_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE, S2N_ERR_INVALID_STATE); - - config->renegotiate_request_cb = cb; - config->renegotiate_request_ctx = ctx; - return S2N_SUCCESS; -} - -int s2n_config_set_npn(struct s2n_config *config, bool enable) -{ - POSIX_ENSURE_REF(config); - - config->npn_supported = enable; - - return S2N_SUCCESS; -} - -/* - * Wrapper for wall_clock callback. This wrapper will ensure right return of s2n_errno everytime wall_clock - * callback is called. - */ -S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output) -{ - RESULT_ENSURE_REF(config); - RESULT_ENSURE(config->wall_clock(config->sys_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); - return S2N_RESULT_OK; -} - -/* - * Get the indicated time from the monotonic clock. - * - * This callback ensures that the correct errno is set in the case of failure. - */ -S2N_RESULT s2n_config_monotonic_clock(struct s2n_config *config, uint64_t *output) -{ - RESULT_ENSURE_REF(config); - RESULT_ENSURE(config->monotonic_clock(config->monotonic_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); - return S2N_RESULT_OK; -} - -int s2n_config_set_crl_lookup_cb(struct s2n_config *config, s2n_crl_lookup_callback cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - config->crl_lookup_cb = cb; - config->crl_lookup_ctx = ctx; - return S2N_SUCCESS; -} - -int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled) -{ - POSIX_ENSURE_REF(config); - - config->recv_multi_record = enabled; - - return S2N_SUCCESS; -} - -int s2n_config_set_cert_validation_cb(struct s2n_config *config, s2n_cert_validation_callback cb, void *ctx) -{ - POSIX_ENSURE_REF(config); - - config->cert_validation_cb = cb; - config->cert_validation_ctx = ctx; - - return S2N_SUCCESS; -} - -int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, - uint16_t *groups_count_out) -{ - POSIX_ENSURE_REF(groups_count_out); - *groups_count_out = 0; - POSIX_ENSURE_REF(config); - POSIX_ENSURE_REF(groups); - - const struct s2n_security_policy *security_policy = config->security_policy; - POSIX_ENSURE_REF(security_policy); - const struct s2n_kem_preferences *kem_preferences = security_policy->kem_preferences; - POSIX_ENSURE_REF(kem_preferences); - const struct s2n_ecc_preferences *ecc_preferences = security_policy->ecc_preferences; - POSIX_ENSURE_REF(ecc_preferences); - - uint16_t groups_count = 0; - for (uint8_t i = 0; i < kem_preferences->tls13_kem_group_count; i++) { - const struct s2n_kem_group *kem_group = kem_preferences->tls13_kem_groups[i]; - POSIX_ENSURE_REF(kem_group); - if (!s2n_kem_group_is_available(kem_group)) { - continue; - } - - POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); - groups[groups_count] = kem_group->iana_id; - groups_count += 1; - } - - for (uint8_t i = 0; i < ecc_preferences->count; i++) { - const struct s2n_ecc_named_curve *ecc_curve = ecc_preferences->ecc_curves[i]; - POSIX_ENSURE_REF(ecc_curve); - - POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); - groups[groups_count] = ecc_curve->iana_id; - groups_count += 1; - } - - *groups_count_out = groups_count; - - return S2N_SUCCESS; -} - -int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version) -{ - POSIX_ENSURE_REF(config); - - /* This feature cannot be used with renegotiation currently */ - POSIX_ENSURE(config->renegotiate_request_cb == NULL, S2N_ERR_INVALID_STATE); - - /* Currently there is only one format version supported */ - POSIX_ENSURE_EQ(version, S2N_SERIALIZED_CONN_V1); - config->serialized_connection_version = version; - - return S2N_SUCCESS; -} - -int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds) -{ - POSIX_ENSURE_REF(config); - - config->custom_blinding_set = 1; - config->max_blinding = seconds; - - return S2N_SUCCESS; -} - -int s2n_config_set_subscriber(struct s2n_config *config, void *subscriber) -{ - POSIX_ENSURE_REF(config); - config->subscriber = subscriber; - return S2N_SUCCESS; -} - -int s2n_config_set_handshake_event(struct s2n_config *config, s2n_event_on_handshake_cb callback) -{ - POSIX_ENSURE_REF(config); - config->on_handshake_event = callback; - return S2N_SUCCESS; -} +/* + * 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. + */ + +#ifndef _S2N_PRELUDE_INCLUDED + #if !defined(_MSC_VER) + /* make sure s2n_prelude.h is includes as part of the compiler flags, if not then fail the build */ + #error "Expected s2n_prelude.h to be included as part of the compiler flags" + #endif +#endif + +#if !defined(_MSC_VER) +#include +#endif +#include + +#include "api/unstable/custom_x509_extensions.h" +#include "api/unstable/npn.h" +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hkdf.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_pq.h" +#include "error/s2n_errno.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_map.h" +#include "utils/s2n_safety.h" + +#if defined(_MSC_VER) + #define S2N_CLOCK_HW 0 + #define S2N_CLOCK_SYS 0 +#elif defined(CLOCK_MONOTONIC_RAW) + #define S2N_CLOCK_HW CLOCK_MONOTONIC_RAW + #define S2N_CLOCK_SYS CLOCK_REALTIME +#else + #define S2N_CLOCK_HW CLOCK_MONOTONIC + #define S2N_CLOCK_SYS CLOCK_REALTIME +#endif + +int s2n_default_monotonic_clock(void *unused_data, uint64_t *nanoseconds) +{ + struct timespec current_time = { 0 }; + + POSIX_GUARD(clock_gettime(S2N_CLOCK_HW, ¤t_time)); + + *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; + *nanoseconds += current_time.tv_nsec; + + return 0; +} + +static int wall_clock(void *data, uint64_t *nanoseconds) +{ + struct timespec current_time = { 0 }; + + POSIX_GUARD(clock_gettime(S2N_CLOCK_SYS, ¤t_time)); + + *nanoseconds = (uint64_t) current_time.tv_sec * 1000000000ull; + *nanoseconds += current_time.tv_nsec; + + return 0; +} + +static struct s2n_config s2n_default_config = { 0 }; +static struct s2n_config s2n_default_fips_config = { 0 }; +static struct s2n_config s2n_default_tls13_config = { 0 }; + +static int s2n_config_setup_default(struct s2n_config *config) +{ + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default")); + return S2N_SUCCESS; +} + +static int s2n_config_setup_tls13(struct s2n_config *config) +{ + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_tls13")); + return S2N_SUCCESS; +} + +static int s2n_config_setup_fips(struct s2n_config *config) +{ + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "default_fips")); + return S2N_SUCCESS; +} + +static int s2n_config_init(struct s2n_config *config) +{ + config->wall_clock = wall_clock; + config->monotonic_clock = s2n_default_monotonic_clock; + config->ct_type = S2N_CT_SUPPORT_NONE; + config->mfl_code = S2N_TLS_MAX_FRAG_LEN_EXT_NONE; + config->alert_behavior = S2N_ALERT_FAIL_ON_WARNINGS; + config->session_state_lifetime_in_nanos = S2N_STATE_LIFETIME_IN_NANOS; + config->encrypt_decrypt_key_lifetime_in_nanos = S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS; + config->decrypt_key_lifetime_in_nanos = S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS; + config->async_pkey_validation_mode = S2N_ASYNC_PKEY_VALIDATION_FAST; + config->check_ocsp = 1; + + config->client_hello_cb_mode = S2N_CLIENT_HELLO_CB_BLOCKING; + + POSIX_GUARD(s2n_config_setup_default(config)); + if (s2n_use_default_tls13_config()) { + POSIX_GUARD(s2n_config_setup_tls13(config)); + } else if (s2n_is_in_fips_mode()) { + POSIX_GUARD(s2n_config_setup_fips(config)); + } + + POSIX_GUARD_PTR(config->domain_name_to_cert_map = s2n_map_new_with_initial_capacity(1)); + POSIX_GUARD_RESULT(s2n_map_complete(config->domain_name_to_cert_map)); + + s2n_x509_trust_store_init_empty(&config->trust_store); + + return 0; +} + +static int s2n_config_cleanup(struct s2n_config *config) +{ + s2n_x509_trust_store_wipe(&config->trust_store); + config->check_ocsp = 0; + + if (config->custom_x509_extension_oids) { + sk_ASN1_OBJECT_pop_free(config->custom_x509_extension_oids, ASN1_OBJECT_free); + config->custom_x509_extension_oids = NULL; + } + + POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); + POSIX_GUARD(s2n_config_free_cert_chain_and_key(config)); + POSIX_GUARD(s2n_config_free_dhparams(config)); + POSIX_GUARD(s2n_free(&config->application_protocols)); + POSIX_GUARD(s2n_free(&config->cert_authorities)); + POSIX_GUARD_RESULT(s2n_map_free(config->domain_name_to_cert_map)); + + POSIX_CHECKED_MEMSET(config, 0, sizeof(struct s2n_config)); + + return 0; +} + +static int s2n_config_update_domain_name_to_cert_map(struct s2n_config *config, + struct s2n_blob *name, + struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(name); + + struct s2n_map *domain_name_to_cert_map = config->domain_name_to_cert_map; + /* s2n_map does not allow zero-size key */ + if (name->size == 0) { + return 0; + } + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); + struct s2n_blob s2n_map_value = { 0 }; + bool key_found = false; + POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, name, &s2n_map_value, &key_found)); + if (!key_found) { + struct certs_by_type value = { { 0 } }; + value.certs[cert_type] = cert_key_pair; + s2n_map_value.data = (uint8_t *) &value; + s2n_map_value.size = sizeof(struct certs_by_type); + + POSIX_GUARD_RESULT(s2n_map_unlock(domain_name_to_cert_map)); + + int add_result = s2n_result_is_ok(s2n_map_add(domain_name_to_cert_map, name, &s2n_map_value)) ? S2N_SUCCESS : S2N_FAILURE; + + /* The map must always be re-completed (made immutable) after an unlock, + * even if the add failed. Otherwise s2n_map_lookup, called on every + * ClientHello for SNI matching, will fail its RESULT_ENSURE(map->immutable) + * check, silently disabling SNI-based certificate selection for the + * lifetime of this config. + */ + POSIX_GUARD_RESULT(s2n_map_complete(domain_name_to_cert_map)); + POSIX_GUARD(add_result); + } else { + struct certs_by_type *value = (void *) s2n_map_value.data; + if (value->certs[cert_type] == NULL) { + value->certs[cert_type] = cert_key_pair; + } else if (config->cert_tiebreak_cb) { + /* There's an existing certificate for this (domain_name, auth_method). + * Run the application's tiebreaking callback to decide which cert should be used. + * An application may have some context specific logic to resolve ties that are based + * on factors like trust, expiry, etc. + */ + struct s2n_cert_chain_and_key *winner = config->cert_tiebreak_cb( + value->certs[cert_type], + cert_key_pair, + name->data, + name->size); + if (winner) { + value->certs[cert_type] = winner; + } + } + } + + return 0; +} + +int s2n_config_build_domain_name_to_cert_map(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + uint32_t cn_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->cn_names, &cn_len)); + uint32_t san_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(cert_key_pair->san_names, &san_len)); + + if (san_len == 0) { + for (uint32_t i = 0; i < cn_len; i++) { + struct s2n_blob *cn_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->cn_names, i, (void **) &cn_name)); + POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, cn_name, cert_key_pair)); + } + } else { + for (uint32_t i = 0; i < san_len; i++) { + struct s2n_blob *san_name = NULL; + POSIX_GUARD_RESULT(s2n_array_get(cert_key_pair->san_names, i, (void **) &san_name)); + POSIX_GUARD(s2n_config_update_domain_name_to_cert_map(config, san_name, cert_key_pair)); + } + } + + return 0; +} + +struct s2n_config *s2n_fetch_default_config(void) +{ + if (s2n_use_default_tls13_config()) { + return &s2n_default_tls13_config; + } + if (s2n_is_in_fips_mode()) { + return &s2n_default_fips_config; + } + return &s2n_default_config; +} + +int s2n_config_set_unsafe_for_testing(struct s2n_config *config) +{ + POSIX_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); + config->check_ocsp = 0; + config->disable_x509_validation = 1; + + return S2N_SUCCESS; +} + +int s2n_config_defaults_init(void) +{ + /* Set up fips defaults */ + if (s2n_is_in_fips_mode()) { + POSIX_GUARD(s2n_config_init(&s2n_default_fips_config)); + POSIX_GUARD(s2n_config_setup_fips(&s2n_default_fips_config)); + POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_fips_config)); + } else { + /* Set up default */ + POSIX_GUARD(s2n_config_init(&s2n_default_config)); + POSIX_GUARD(s2n_config_setup_default(&s2n_default_config)); + POSIX_GUARD(s2n_config_load_system_certs(&s2n_default_config)); + } + + /* TLS 1.3 default config is only used in tests so avoid initialization costs in applications */ + POSIX_GUARD(s2n_config_init(&s2n_default_tls13_config)); + POSIX_GUARD(s2n_config_setup_tls13(&s2n_default_tls13_config)); + + return S2N_SUCCESS; +} + +void s2n_wipe_static_configs(void) +{ + s2n_config_cleanup(&s2n_default_fips_config); + s2n_config_cleanup(&s2n_default_config); + s2n_config_cleanup(&s2n_default_tls13_config); +} + +int s2n_config_load_system_certs(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + struct s2n_x509_trust_store *store = &config->trust_store; + POSIX_ENSURE(!store->loaded_system_certs, S2N_ERR_X509_TRUST_STORE); + + if (!store->trust_store) { + store->trust_store = X509_STORE_new(); + POSIX_ENSURE_REF(store->trust_store); + } + + int err_code = X509_STORE_set_default_paths(store->trust_store); + if (!err_code) { + s2n_x509_trust_store_wipe(store); + POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); + } + store->loaded_system_certs = true; + + return S2N_SUCCESS; +} + +struct s2n_config *s2n_config_new_minimal(void) +{ + struct s2n_blob allocator = { 0 }; + struct s2n_config *new_config = NULL; + + PTR_GUARD_POSIX(s2n_alloc(&allocator, sizeof(struct s2n_config))); + PTR_GUARD_POSIX(s2n_blob_zero(&allocator)); + + new_config = (struct s2n_config *) (void *) allocator.data; + if (s2n_config_init(new_config) != S2N_SUCCESS) { + s2n_free(&allocator); + return NULL; + } + + return new_config; +} + +struct s2n_config *s2n_config_new(void) +{ + struct s2n_config *new_config = s2n_config_new_minimal(); + PTR_ENSURE_REF(new_config); + + /* For backwards compatibility, s2n_config_new loads system certs by default. */ + PTR_GUARD_POSIX(s2n_config_load_system_certs(new_config)); + + return new_config; +} + +int s2n_config_init_session_ticket_keys(struct s2n_config *config) +{ + if (config->ticket_keys == NULL) { + POSIX_ENSURE_REF(config->ticket_keys = s2n_array_new_with_capacity(sizeof(struct s2n_ticket_key), S2N_MAX_TICKET_KEYS)); + } + + return 0; +} + +int s2n_config_free_session_ticket_keys(struct s2n_config *config) +{ + if (config->ticket_keys != NULL) { + POSIX_GUARD_RESULT(s2n_array_free_p(&config->ticket_keys)); + } + + return 0; +} + +int s2n_config_free_cert_chain_and_key(struct s2n_config *config) +{ + /* We track certificate ownership on the config itself because a config + * CANNOT use a combination of owned and unowned chains. + * If it does, and the unowned chains are freed before the config is, + * then iterating over the chains to determine which are owned and need to be freed + * will mean also reading the invalid, freed memory of any unowned certificates. + * As of now, some tests free chains before the config, so that pattern may also + * appear in application code. + */ + if (config->cert_ownership != S2N_LIB_OWNED) { + return S2N_SUCCESS; + } + + /* Free the cert_chain_and_key since the application has no reference + * to it. This is necessary until s2n_config_add_cert_chain_and_key is deprecated. */ + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + s2n_cert_chain_and_key_free(config->default_certs_by_type.certs[i]); + config->default_certs_by_type.certs[i] = NULL; + } + + config->cert_ownership = S2N_NOT_OWNED; + return S2N_SUCCESS; +} + +int s2n_config_free_dhparams(struct s2n_config *config) +{ + if (config->dhparams) { + POSIX_GUARD(s2n_dh_params_free(config->dhparams)); + } + + POSIX_GUARD(s2n_free_object((uint8_t **) &config->dhparams, sizeof(struct s2n_dh_params))); + return 0; +} + +S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config) +{ + RESULT_ENSURE_REF(config); + RESULT_GUARD_POSIX(s2n_config_free(*config)); + *config = NULL; + return S2N_RESULT_OK; +} + +int s2n_config_free(struct s2n_config *config) +{ + s2n_config_cleanup(config); + + POSIX_GUARD(s2n_free_object((uint8_t **) &config, sizeof(struct s2n_config))); + return 0; +} + +int s2n_config_get_client_auth_type(struct s2n_config *config, s2n_cert_auth_type *client_auth_type) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(client_auth_type); + *client_auth_type = config->client_cert_auth_type; + return 0; +} + +int s2n_config_set_client_auth_type(struct s2n_config *config, s2n_cert_auth_type client_auth_type) +{ + POSIX_ENSURE_REF(config); + config->client_cert_auth_type_overridden = 1; + config->client_cert_auth_type = client_auth_type; + return 0; +} + +int s2n_config_set_ct_support_level(struct s2n_config *config, s2n_ct_support_level type) +{ + POSIX_ENSURE_REF(config); + config->ct_type = type; + + return 0; +} + +int s2n_config_set_alert_behavior(struct s2n_config *config, s2n_alert_behavior alert_behavior) +{ + POSIX_ENSURE_REF(config); + + switch (alert_behavior) { + case S2N_ALERT_FAIL_ON_WARNINGS: + case S2N_ALERT_IGNORE_WARNINGS: + config->alert_behavior = alert_behavior; + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + + return 0; +} + +int s2n_config_set_verify_host_callback(struct s2n_config *config, s2n_verify_host_fn verify_host_fn, void *data) +{ + POSIX_ENSURE_REF(config); + config->verify_host_fn = verify_host_fn; + config->data_for_verify_host = data; + return 0; +} + +int s2n_config_set_check_stapled_ocsp_response(struct s2n_config *config, uint8_t check_ocsp) +{ + POSIX_ENSURE_REF(config); + S2N_ERROR_IF(check_ocsp && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); + config->check_ocsp = check_ocsp; + return 0; +} + +int s2n_config_disable_x509_time_verification(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + config->disable_x509_time_validation = true; + return S2N_SUCCESS; +} + +int s2n_config_disable_x509_intent_verification(struct s2n_config *config) +{ + POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); + config->disable_x509_intent_verification = true; + return S2N_SUCCESS; +} + +int s2n_config_disable_x509_verification(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + s2n_x509_trust_store_wipe(&config->trust_store); + config->disable_x509_validation = 1; + return 0; +} + +int s2n_config_set_max_cert_chain_depth(struct s2n_config *config, uint16_t max_depth) +{ + POSIX_ENSURE_REF(config); + S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); + + config->max_verify_cert_chain_depth = max_depth; + config->max_verify_cert_chain_depth_set = 1; + return 0; +} + +int s2n_config_set_status_request_type(struct s2n_config *config, s2n_status_request_type type) +{ + S2N_ERROR_IF(type == S2N_STATUS_REQUEST_OCSP && !s2n_x509_ocsp_stapling_supported(), S2N_ERR_OCSP_NOT_SUPPORTED); + + POSIX_ENSURE_REF(config); + config->ocsp_status_requested_by_user = (type == S2N_STATUS_REQUEST_OCSP); + + /* Reset the ocsp_status_requested_by_s2n flag if OCSP status requests were disabled. */ + if (type == S2N_STATUS_REQUEST_NONE) { + config->ocsp_status_requested_by_s2n = false; + } + + return 0; +} + +int s2n_config_wipe_trust_store(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + s2n_x509_trust_store_wipe(&config->trust_store); + + return S2N_SUCCESS; +} + +int s2n_config_add_pem_to_trust_store(struct s2n_config *config, const char *pem) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(pem); + + POSIX_GUARD(s2n_x509_trust_store_add_pem(&config->trust_store, pem)); + + return 0; +} + +int s2n_config_set_verification_ca_location(struct s2n_config *config, const char *ca_pem_filename, const char *ca_dir) +{ + POSIX_ENSURE_REF(config); + int err_code = s2n_x509_trust_store_from_ca_file(&config->trust_store, ca_pem_filename, ca_dir); + + if (!err_code) { + config->ocsp_status_requested_by_s2n = s2n_x509_ocsp_stapling_supported() ? S2N_STATUS_REQUEST_OCSP : S2N_STATUS_REQUEST_NONE; + } + + return err_code; +} + +static int s2n_config_add_cert_chain_and_key_impl(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config->domain_name_to_cert_map); + POSIX_ENSURE_REF(cert_key_pair); + + POSIX_GUARD_RESULT(s2n_security_policy_validate_certificate_chain(config->security_policy, cert_key_pair)); + + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pair); + config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); + + POSIX_GUARD(s2n_config_build_domain_name_to_cert_map(config, cert_key_pair)); + + if (!config->default_certs_are_explicit) { + POSIX_ENSURE(cert_type >= 0, S2N_ERR_CERT_TYPE_UNSUPPORTED); + POSIX_ENSURE(cert_type < S2N_CERT_TYPE_COUNT, S2N_ERR_CERT_TYPE_UNSUPPORTED); + /* Attempt to auto set default based on ordering. ie: first RSA cert is the default, first ECDSA cert is the + * default, etc. */ + if (config->default_certs_by_type.certs[cert_type] == NULL) { + config->default_certs_by_type.certs[cert_type] = cert_key_pair; + } else { + /* Because library-owned certificates are tracked and cleaned up via the + * default_certs_by_type mapping, library-owned chains MUST be set as the default + * to avoid a memory leak. If they're not the default, they're not freed. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + } + } + + if (s2n_pkey_check_key_exists(cert_key_pair->private_key) != S2N_SUCCESS) { + config->no_signing_key = true; + } + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_config_validate_loaded_certificates(const struct s2n_config *config, + const struct s2n_security_policy *security_policy) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE_REF(security_policy); + + if (security_policy->certificate_key_preferences == NULL + && security_policy->certificate_signature_preferences == NULL) { + return S2N_RESULT_OK; + } + + /* Duplicates a check in s2n_security_policy_validate_certificate_chain. + * If a large number of certificates are configured, even iterating + * over the chains to call s2n_security_policy_validate_certificate_chain + * could be prohibitively expensive. + */ + if (!security_policy->certificate_preferences_apply_locally) { + return S2N_RESULT_OK; + } + + /* validate the default certs */ + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + struct s2n_cert_chain_and_key *cert = config->default_certs_by_type.certs[i]; + if (cert == NULL) { + continue; + } + RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); + } + + /* validate the certs in the domain map */ + if (config->domain_name_to_cert_map == NULL) { + return S2N_RESULT_OK; + } + + struct s2n_map_iterator iter = { 0 }; + RESULT_GUARD(s2n_map_iterator_init(&iter, config->domain_name_to_cert_map)); + + while (s2n_map_iterator_has_next(&iter)) { + struct s2n_blob value = { 0 }; + RESULT_GUARD(s2n_map_iterator_next(&iter, &value)); + + struct certs_by_type *domain_certs = (void *) value.data; + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + struct s2n_cert_chain_and_key *cert = domain_certs->certs[i]; + if (cert == NULL) { + continue; + } + RESULT_GUARD(s2n_security_policy_validate_certificate_chain(security_policy, cert)); + } + } + return S2N_RESULT_OK; +} + +/* Deprecated. Superseded by s2n_config_add_cert_chain_and_key_to_store */ +int s2n_config_add_cert_chain_and_key(struct s2n_config *config, const char *cert_chain_pem, const char *private_key_pem) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); + config->cert_ownership = S2N_LIB_OWNED; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); + return S2N_SUCCESS; +} + +/* Only used in the Rust bindings. Superseded by s2n_config_add_cert_chain_and_key_to_store */ +int s2n_config_add_cert_chain(struct s2n_config *config, + uint8_t *cert_chain_pem, uint32_t cert_chain_pem_size) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_APP_OWNED, S2N_ERR_CERT_OWNERSHIP); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + POSIX_ENSURE_REF(chain_and_key); + POSIX_GUARD(s2n_cert_chain_and_key_load_public_pem_bytes(chain_and_key, + cert_chain_pem, cert_chain_pem_size)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, chain_and_key)); + config->cert_ownership = S2N_LIB_OWNED; + + ZERO_TO_DISABLE_DEFER_CLEANUP(chain_and_key); + return S2N_SUCCESS; +} + +int s2n_config_add_cert_chain_and_key_to_store(struct s2n_config *config, struct s2n_cert_chain_and_key *cert_key_pair) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + POSIX_ENSURE_REF(cert_key_pair); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_impl(config, cert_key_pair)); + config->cert_ownership = S2N_APP_OWNED; + + return S2N_SUCCESS; +} + +int s2n_config_set_async_offload_callback(struct s2n_config *config, uint32_t allow_list, s2n_async_offload_cb fn, void *ctx) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(fn); + POSIX_ENSURE(allow_list != 0, S2N_ERR_INVALID_ARGUMENT); + + config->async_offload_allow_list = allow_list; + config->async_offload_cb = fn; + config->async_offload_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_set_async_pkey_callback(struct s2n_config *config, s2n_async_pkey_fn fn) +{ + POSIX_ENSURE_REF(config); + + config->async_pkey_cb = fn; + + return S2N_SUCCESS; +} + +static int s2n_config_clear_default_certificates(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + /* Clearing library-owned chains would lead to a memory leak. + * See s2n_config_free_cert_chain_and_key. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + config->default_certs_by_type.certs[i] = NULL; + } + config->cert_ownership = S2N_NOT_OWNED; + return 0; +} + +int s2n_config_set_cert_chain_and_key_defaults(struct s2n_config *config, + struct s2n_cert_chain_and_key **cert_key_pairs, + uint32_t num_cert_key_pairs) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(cert_key_pairs); + S2N_ERROR_IF(num_cert_key_pairs < 1 || num_cert_key_pairs > S2N_CERT_TYPE_COUNT, + S2N_ERR_NUM_DEFAULT_CERTIFICATES); + + /* This method will set application-owned chains, so we must not already be using + * any library owned chains. See s2n_config_free_cert_chain_and_key. + */ + POSIX_ENSURE(config->cert_ownership != S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + /* Validate certs being set before clearing auto-chosen defaults or previously set defaults */ + struct certs_by_type new_defaults = { { 0 } }; + for (size_t i = 0; i < num_cert_key_pairs; i++) { + POSIX_ENSURE_REF(cert_key_pairs[i]); + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); + S2N_ERROR_IF(new_defaults.certs[cert_type] != NULL, S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + new_defaults.certs[cert_type] = cert_key_pairs[i]; + } + + POSIX_GUARD(s2n_config_clear_default_certificates(config)); + for (size_t i = 0; i < num_cert_key_pairs; i++) { + s2n_pkey_type cert_type = s2n_cert_chain_and_key_get_pkey_type(cert_key_pairs[i]); + config->is_rsa_cert_configured |= (cert_type == S2N_PKEY_TYPE_RSA); + config->default_certs_by_type.certs[cert_type] = cert_key_pairs[i]; + } + + config->default_certs_are_explicit = 1; + config->cert_ownership = S2N_APP_OWNED; + return 0; +} + +int s2n_config_add_dhparams(struct s2n_config *config, const char *dhparams_pem) +{ + DEFER_CLEANUP(struct s2n_stuffer dhparams_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer dhparams_out_stuffer = { 0 }, s2n_stuffer_free); + struct s2n_blob dhparams_blob = { 0 }; + struct s2n_blob mem = { 0 }; + + /* Allocate the memory for the chain and key struct */ + POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_dh_params))); + config->dhparams = (struct s2n_dh_params *) (void *) mem.data; + + if (s2n_stuffer_alloc_ro_from_string(&dhparams_in_stuffer, dhparams_pem) != S2N_SUCCESS) { + s2n_free(&mem); + S2N_ERROR_PRESERVE_ERRNO(); + } + if (s2n_stuffer_growable_alloc(&dhparams_out_stuffer, strlen(dhparams_pem)) != S2N_SUCCESS) { + s2n_free(&mem); + S2N_ERROR_PRESERVE_ERRNO(); + } + + /* Convert pem to asn1 and asn1 to the private key */ + POSIX_GUARD(s2n_stuffer_dhparams_from_pem(&dhparams_in_stuffer, &dhparams_out_stuffer)); + + dhparams_blob.size = s2n_stuffer_data_available(&dhparams_out_stuffer); + dhparams_blob.data = s2n_stuffer_raw_read(&dhparams_out_stuffer, dhparams_blob.size); + POSIX_ENSURE_REF(dhparams_blob.data); + + POSIX_GUARD(s2n_pkcs3_to_dh_params(config->dhparams, &dhparams_blob)); + + return 0; +} + +int s2n_config_set_wall_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) +{ + POSIX_ENSURE_REF(clock_fn); + + config->wall_clock = clock_fn; + config->sys_clock_ctx = ctx; + + return 0; +} + +int s2n_config_set_monotonic_clock(struct s2n_config *config, s2n_clock_time_nanoseconds clock_fn, void *ctx) +{ + POSIX_ENSURE_REF(clock_fn); + + config->monotonic_clock = clock_fn; + config->monotonic_clock_ctx = ctx; + + return 0; +} + +int s2n_config_set_cache_store_callback(struct s2n_config *config, s2n_cache_store_callback cache_store_callback, void *data) +{ + POSIX_ENSURE_REF(cache_store_callback); + + config->cache_store = cache_store_callback; + config->cache_store_data = data; + + return 0; +} + +int s2n_config_set_cache_retrieve_callback(struct s2n_config *config, s2n_cache_retrieve_callback cache_retrieve_callback, void *data) +{ + POSIX_ENSURE_REF(cache_retrieve_callback); + + config->cache_retrieve = cache_retrieve_callback; + config->cache_retrieve_data = data; + + return 0; +} + +int s2n_config_set_cache_delete_callback(struct s2n_config *config, s2n_cache_delete_callback cache_delete_callback, void *data) +{ + POSIX_ENSURE_REF(cache_delete_callback); + + config->cache_delete = cache_delete_callback; + config->cache_delete_data = data; + + return 0; +} + +int s2n_config_set_extension_data(struct s2n_config *config, s2n_tls_extension_type type, const uint8_t *data, uint32_t length) +{ + POSIX_ENSURE_REF(config); + + if (s2n_config_get_num_default_certs(config) == 0) { + POSIX_BAIL(S2N_ERR_UPDATING_EXTENSION); + } + struct s2n_cert_chain_and_key *config_chain_and_key = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(config_chain_and_key); + POSIX_ENSURE(config->cert_ownership == S2N_LIB_OWNED, S2N_ERR_CERT_OWNERSHIP); + + switch (type) { + case S2N_EXTENSION_CERTIFICATE_TRANSPARENCY: + POSIX_GUARD(s2n_cert_chain_and_key_set_sct_list(config_chain_and_key, data, length)); + break; + case S2N_EXTENSION_OCSP_STAPLING: + POSIX_GUARD(s2n_cert_chain_and_key_set_ocsp_data(config_chain_and_key, data, length)); + break; + default: + POSIX_BAIL(S2N_ERR_UNRECOGNIZED_EXTENSION); + } + + return 0; +} + +int s2n_config_add_custom_x509_extension(struct s2n_config *config, uint8_t *extension_oid, uint32_t extension_oid_len) +{ + POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(extension_oid, S2N_ERR_INVALID_ARGUMENT); + + POSIX_ENSURE(s2n_libcrypto_supports_custom_oid(), S2N_ERR_API_UNSUPPORTED_BY_LIBCRYPTO); + + if (config->custom_x509_extension_oids == NULL) { + config->custom_x509_extension_oids = sk_ASN1_OBJECT_new_null(); + } + POSIX_ENSURE_REF(config->custom_x509_extension_oids); + + DEFER_CLEANUP(struct s2n_blob oid_buffer = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&oid_buffer, extension_oid_len + 1)); + + POSIX_GUARD(s2n_blob_zero(&oid_buffer)); + POSIX_CHECKED_MEMCPY(oid_buffer.data, extension_oid, extension_oid_len); + oid_buffer.data[extension_oid_len] = '\0'; + + const char *oid_string = (const char *) oid_buffer.data; + POSIX_ENSURE_REF(oid_string); + + ASN1_OBJECT *critical_oid = OBJ_txt2obj(oid_string, 1); + POSIX_ENSURE_REF(critical_oid); + POSIX_ENSURE(sk_ASN1_OBJECT_push(config->custom_x509_extension_oids, critical_oid) > 0, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + return S2N_SUCCESS; +} + +int s2n_config_set_cert_request_callback(struct s2n_config *config, s2n_cert_request_callback cert_request_cb, void *ctx) +{ + POSIX_ENSURE(config, S2N_ERR_INVALID_ARGUMENT); + + config->cert_request_cb = cert_request_cb; + config->cert_request_cb_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_set_client_hello_cb(struct s2n_config *config, s2n_client_hello_fn client_hello_cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + + config->client_hello_cb = client_hello_cb; + config->client_hello_cb_ctx = ctx; + return 0; +} + +int s2n_config_set_client_hello_cb_mode(struct s2n_config *config, s2n_client_hello_cb_mode cb_mode) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(cb_mode == S2N_CLIENT_HELLO_CB_BLOCKING || cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING, S2N_ERR_INVALID_STATE); + + config->client_hello_cb_mode = cb_mode; + return S2N_SUCCESS; +} + +int s2n_config_send_max_fragment_length(struct s2n_config *config, s2n_max_frag_len mfl_code) +{ + POSIX_ENSURE_REF(config); + + S2N_ERROR_IF(mfl_code > S2N_TLS_MAX_FRAG_LEN_4096, S2N_ERR_INVALID_MAX_FRAG_LEN); + + config->mfl_code = mfl_code; + + return 0; +} + +int s2n_config_accept_max_fragment_length(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + config->accept_mfl = 1; + + return 0; +} + +int s2n_config_set_session_state_lifetime(struct s2n_config *config, + uint64_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(config); + + config->session_state_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); + return 0; +} + +int s2n_config_set_session_tickets_onoff(struct s2n_config *config, uint8_t enabled) +{ + POSIX_ENSURE_REF(config); + + if (config->use_tickets == enabled) { + return 0; + } + + config->use_tickets = enabled; + + if (config->initial_tickets_to_send == 0) { + /* Normally initial_tickets_to_send is set via s2n_config_set_initial_ticket_count. + * However, s2n_config_set_initial_ticket_count calls this method. + * So we set initial_tickets_to_send directly to avoid infinite recursion. */ + config->initial_tickets_to_send = 1; + } + + /* session ticket || session id is enabled */ + if (enabled) { + POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); + } else if (!config->use_session_cache) { + POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); + } + + return 0; +} + +int s2n_config_set_session_cache_onoff(struct s2n_config *config, uint8_t enabled) +{ + POSIX_ENSURE_REF(config); + if (enabled && config->cache_store && config->cache_retrieve && config->cache_delete) { + POSIX_GUARD(s2n_config_init_session_ticket_keys(config)); + config->use_session_cache = 1; + } else { + if (!config->use_tickets) { + POSIX_GUARD(s2n_config_free_session_ticket_keys(config)); + } + config->use_session_cache = 0; + } + return 0; +} + +int s2n_config_set_ticket_encrypt_decrypt_key_lifetime(struct s2n_config *config, + uint64_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(config); + + config->encrypt_decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); + return 0; +} + +int s2n_config_set_ticket_decrypt_key_lifetime(struct s2n_config *config, + uint64_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(config); + + config->decrypt_key_lifetime_in_nanos = (lifetime_in_secs * ONE_SEC_IN_NANOS); + return 0; +} + +int s2n_config_add_ticket_crypto_key(struct s2n_config *config, + const uint8_t *name, uint32_t name_len, + uint8_t *key, uint32_t key_len, + uint64_t intro_time_in_seconds_from_epoch) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(name); + POSIX_ENSURE_REF(key); + + /* both session ticket and session cache encryption/decryption can use the same key mechanism */ + if (!config->use_tickets && !config->use_session_cache) { + return 0; + } + + POSIX_GUARD(s2n_config_wipe_expired_ticket_crypto_keys(config, -1)); + + POSIX_ENSURE(key_len != 0, S2N_ERR_INVALID_TICKET_KEY_LENGTH); + + uint32_t ticket_keys_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + POSIX_ENSURE(ticket_keys_len < S2N_MAX_TICKET_KEYS, S2N_ERR_TICKET_KEY_LIMIT); + + POSIX_ENSURE(name_len != 0, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + POSIX_ENSURE(name_len <= S2N_TICKET_KEY_NAME_LEN, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Copy the name into a zero-padded array. */ + /* This ensures that all ticket names are equal in length, as the serialized name is fixed length */ + uint8_t name_data[S2N_TICKET_KEY_NAME_LEN] = { 0 }; + POSIX_CHECKED_MEMCPY(name_data, name, name_len); + + uint8_t output_pad[S2N_AES256_KEY_LEN + S2N_TICKET_AAD_IMPLICIT_LEN] = { 0 }; + struct s2n_blob out_key = { 0 }; + POSIX_GUARD(s2n_blob_init(&out_key, output_pad, s2n_array_len(output_pad))); + struct s2n_blob in_key = { 0 }; + POSIX_GUARD(s2n_blob_init(&in_key, key, key_len)); + struct s2n_blob salt = { 0 }; + POSIX_GUARD(s2n_blob_init(&salt, NULL, 0)); + struct s2n_blob info = { 0 }; + POSIX_GUARD(s2n_blob_init(&info, NULL, 0)); + + struct s2n_ticket_key *session_ticket_key = { 0 }; + DEFER_CLEANUP(struct s2n_blob allocator = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&allocator, sizeof(struct s2n_ticket_key))); + session_ticket_key = (struct s2n_ticket_key *) (void *) allocator.data; + + DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free); + + POSIX_GUARD(s2n_hmac_new(&hmac)); + POSIX_GUARD(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &in_key, &info, &out_key)); + + POSIX_CHECKED_MEMCPY(session_ticket_key->key_name, name_data, s2n_array_len(name_data)); + POSIX_CHECKED_MEMCPY(session_ticket_key->aes_key, out_key.data, S2N_AES256_KEY_LEN); + out_key.data = output_pad + S2N_AES256_KEY_LEN; + POSIX_CHECKED_MEMCPY(session_ticket_key->implicit_aad, out_key.data, S2N_TICKET_AAD_IMPLICIT_LEN); + + if (intro_time_in_seconds_from_epoch == 0) { + uint64_t now = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + session_ticket_key->intro_timestamp = now; + } else { + session_ticket_key->intro_timestamp = (intro_time_in_seconds_from_epoch * ONE_SEC_IN_NANOS); + } + + POSIX_GUARD(s2n_config_store_ticket_key(config, session_ticket_key)); + + return 0; +} + +int s2n_config_require_ticket_forward_secrecy(struct s2n_config *config, bool enabled) +{ + POSIX_ENSURE_REF(config); + + config->ticket_forward_secrecy = enabled; + + return S2N_SUCCESS; +} + +int s2n_config_set_cert_tiebreak_callback(struct s2n_config *config, s2n_cert_tiebreak_callback cert_tiebreak_cb) +{ + config->cert_tiebreak_cb = cert_tiebreak_cb; + return 0; +} + +struct s2n_cert_chain_and_key *s2n_config_get_single_default_cert(struct s2n_config *config) +{ + PTR_ENSURE_REF(config); + struct s2n_cert_chain_and_key *cert = NULL; + + for (int i = S2N_CERT_TYPE_COUNT - 1; i >= 0; i--) { + if (config->default_certs_by_type.certs[i] != NULL) { + cert = config->default_certs_by_type.certs[i]; + } + } + return cert; +} + +int s2n_config_get_num_default_certs(const struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + int num_certs = 0; + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + if (config->default_certs_by_type.certs[i] != NULL) { + num_certs++; + } + } + + return num_certs; +} + +int s2n_config_enable_cert_req_dss_legacy_compat(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + config->cert_req_dss_legacy_compat_enabled = 1; + return S2N_SUCCESS; +} + +int s2n_config_set_psk_selection_callback(struct s2n_config *config, s2n_psk_selection_callback cb, void *context) +{ + POSIX_ENSURE_REF(config); + config->psk_selection_cb = cb; + config->psk_selection_ctx = context; + return S2N_SUCCESS; +} + +int s2n_config_set_key_log_cb(struct s2n_config *config, s2n_key_log_fn callback, void *ctx) +{ + POSIX_ENSURE_MUT(config); + + config->key_log_cb = callback; + config->key_log_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_set_async_pkey_validation_mode(struct s2n_config *config, s2n_async_pkey_validation_mode mode) +{ + POSIX_ENSURE_REF(config); + + switch (mode) { + case S2N_ASYNC_PKEY_VALIDATION_FAST: + case S2N_ASYNC_PKEY_VALIDATION_STRICT: + config->async_pkey_validation_mode = mode; + return S2N_SUCCESS; + } + + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); +} + +int s2n_config_set_ctx(struct s2n_config *config, void *ctx) +{ + POSIX_ENSURE_REF(config); + + config->context = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_get_ctx(struct s2n_config *config, void **ctx) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(ctx); + + *ctx = config->context; + + return S2N_SUCCESS; +} + +int s2n_config_set_send_buffer_size(struct s2n_config *config, uint32_t size) +{ + POSIX_ENSURE_REF(config); + POSIX_ENSURE(size >= S2N_MIN_SEND_BUFFER_SIZE, S2N_ERR_INVALID_ARGUMENT); + config->send_buffer_size_override = size; + return S2N_SUCCESS; +} + +int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after_sign mode) +{ + POSIX_ENSURE_REF(config); + switch (mode) { + case S2N_VERIFY_AFTER_SIGN_DISABLED: + config->verify_after_sign = false; + break; + case S2N_VERIFY_AFTER_SIGN_ENABLED: + config->verify_after_sign = true; + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); + } + return S2N_SUCCESS; +} + +/* + *= https://www.rfc-editor.org/rfc/rfc5746#5 + *# TLS implementations SHOULD provide a mechanism to disable and enable + *# renegotiation. + */ +int s2n_config_set_renegotiate_request_cb(struct s2n_config *config, s2n_renegotiate_request_cb cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + + /* This feature cannot be used with serialization currently */ + POSIX_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE, S2N_ERR_INVALID_STATE); + + config->renegotiate_request_cb = cb; + config->renegotiate_request_ctx = ctx; + return S2N_SUCCESS; +} + +int s2n_config_set_npn(struct s2n_config *config, bool enable) +{ + POSIX_ENSURE_REF(config); + + config->npn_supported = enable; + + return S2N_SUCCESS; +} + +/* + * Wrapper for wall_clock callback. This wrapper will ensure right return of s2n_errno everytime wall_clock + * callback is called. + */ +S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE(config->wall_clock(config->sys_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); + return S2N_RESULT_OK; +} + +/* + * Get the indicated time from the monotonic clock. + * + * This callback ensures that the correct errno is set in the case of failure. + */ +S2N_RESULT s2n_config_monotonic_clock(struct s2n_config *config, uint64_t *output) +{ + RESULT_ENSURE_REF(config); + RESULT_ENSURE(config->monotonic_clock(config->monotonic_clock_ctx, output) >= S2N_SUCCESS, S2N_ERR_CANCELLED); + return S2N_RESULT_OK; +} + +int s2n_config_set_crl_lookup_cb(struct s2n_config *config, s2n_crl_lookup_callback cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + config->crl_lookup_cb = cb; + config->crl_lookup_ctx = ctx; + return S2N_SUCCESS; +} + +int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled) +{ + POSIX_ENSURE_REF(config); + + config->recv_multi_record = enabled; + + return S2N_SUCCESS; +} + +int s2n_config_set_cert_validation_cb(struct s2n_config *config, s2n_cert_validation_callback cb, void *ctx) +{ + POSIX_ENSURE_REF(config); + + config->cert_validation_cb = cb; + config->cert_validation_ctx = ctx; + + return S2N_SUCCESS; +} + +int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, + uint16_t *groups_count_out) +{ + POSIX_ENSURE_REF(groups_count_out); + *groups_count_out = 0; + POSIX_ENSURE_REF(config); + POSIX_ENSURE_REF(groups); + + const struct s2n_security_policy *security_policy = config->security_policy; + POSIX_ENSURE_REF(security_policy); + const struct s2n_kem_preferences *kem_preferences = security_policy->kem_preferences; + POSIX_ENSURE_REF(kem_preferences); + const struct s2n_ecc_preferences *ecc_preferences = security_policy->ecc_preferences; + POSIX_ENSURE_REF(ecc_preferences); + + uint16_t groups_count = 0; + for (uint8_t i = 0; i < kem_preferences->tls13_kem_group_count; i++) { + const struct s2n_kem_group *kem_group = kem_preferences->tls13_kem_groups[i]; + POSIX_ENSURE_REF(kem_group); + if (!s2n_kem_group_is_available(kem_group)) { + continue; + } + + POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); + groups[groups_count] = kem_group->iana_id; + groups_count += 1; + } + + for (uint8_t i = 0; i < ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *ecc_curve = ecc_preferences->ecc_curves[i]; + POSIX_ENSURE_REF(ecc_curve); + + POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE); + groups[groups_count] = ecc_curve->iana_id; + groups_count += 1; + } + + *groups_count_out = groups_count; + + return S2N_SUCCESS; +} + +int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version) +{ + POSIX_ENSURE_REF(config); + + /* This feature cannot be used with renegotiation currently */ + POSIX_ENSURE(config->renegotiate_request_cb == NULL, S2N_ERR_INVALID_STATE); + + /* Currently there is only one format version supported */ + POSIX_ENSURE_EQ(version, S2N_SERIALIZED_CONN_V1); + config->serialized_connection_version = version; + + return S2N_SUCCESS; +} + +int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds) +{ + POSIX_ENSURE_REF(config); + + config->custom_blinding_set = 1; + config->max_blinding = seconds; + + return S2N_SUCCESS; +} + +int s2n_config_set_subscriber(struct s2n_config *config, void *subscriber) +{ + POSIX_ENSURE_REF(config); + config->subscriber = subscriber; + return S2N_SUCCESS; +} + +int s2n_config_set_handshake_event(struct s2n_config *config, s2n_event_on_handshake_cb callback) +{ + POSIX_ENSURE_REF(config); + config->on_handshake_event = callback; + return S2N_SUCCESS; +} diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index 01333216ca2..22a64b52db4 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -1,1895 +1,1895 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_connection.h" - -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -/* Required for s2n_connection_get_key_update_counts */ -#include "api/unstable/ktls.h" -#include "crypto/s2n_certificate.h" -#include "crypto/s2n_cipher.h" -#include "crypto/s2n_crypto.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_openssl_x509.h" -#include "error/s2n_errno.h" -#include "tls/extensions/s2n_client_server_name.h" -#include "tls/extensions/s2n_client_supported_versions.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_kem.h" -#include "tls/s2n_prf.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls_parameters.h" -#include "utils/s2n_atomic.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_compiler.h" -#include "utils/s2n_io.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" -#include "utils/s2n_timer.h" - -#define S2N_SET_KEY_SHARE_LIST_EMPTY(keyshares) (keyshares |= 1) -#define S2N_SET_KEY_SHARE_REQUEST(keyshares, i) (keyshares |= (1 << (i + 1))) - -static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, - const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type); - -/* Allocates and initializes memory for a new connection. - * - * Since customers can reuse a connection, ensure that values on the connection are - * initialized in `s2n_connection_wipe` where possible. */ -struct s2n_connection *s2n_connection_new(s2n_mode mode) -{ - struct s2n_blob blob = { 0 }; - PTR_GUARD_POSIX(s2n_alloc(&blob, sizeof(struct s2n_connection))); - PTR_GUARD_POSIX(s2n_blob_zero(&blob)); - - /* Cast 'through' void to acknowledge that we are changing alignment, - * which is ok, as blob.data is always aligned. - */ - struct s2n_connection *conn = (struct s2n_connection *) (void *) blob.data; - - PTR_GUARD_POSIX(s2n_connection_set_config(conn, s2n_fetch_default_config())); - - /* `mode` is initialized here since it's passed in as a parameter. */ - conn->mode = mode; - - /* Allocate the fixed-size stuffers */ - blob = (struct s2n_blob){ 0 }; - PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->alert_in_data, S2N_ALERT_LENGTH)); - PTR_GUARD_POSIX(s2n_stuffer_init(&conn->alert_in, &blob)); - - blob = (struct s2n_blob){ 0 }; - PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->ticket_ext_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); - PTR_GUARD_POSIX(s2n_stuffer_init(&conn->client_ticket_to_decrypt, &blob)); - - /* Allocate long term hash and HMAC memory */ - PTR_GUARD_RESULT(s2n_prf_new(conn)); - PTR_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); - - /* Initialize the growable stuffers. Zero length at first, but the resize - * in _wipe will fix that - */ - blob = (struct s2n_blob){ 0 }; - PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->header_in_data, S2N_TLS_RECORD_HEADER_LENGTH)); - PTR_GUARD_POSIX(s2n_stuffer_init(&conn->header_in, &blob)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->handshake.io, 0)); - PTR_GUARD_RESULT(s2n_timer_start(conn->config, &conn->write_timer)); - - /* NOTE: s2n_connection_wipe MUST be called last in this function. - * - * s2n_connection_wipe is used for initializing values but also used by customers to - * reset/reuse the connection. Calling it last ensures that s2n_connection_wipe is - * implemented correctly and safe. - */ - PTR_GUARD_POSIX(s2n_connection_wipe(conn)); - return conn; -} - -static int s2n_connection_zero(struct s2n_connection *conn, int mode, struct s2n_config *config) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(config); - - /* Zero the whole connection structure */ - POSIX_CHECKED_MEMSET(conn, 0, sizeof(struct s2n_connection)); - - conn->mode = mode; - conn->max_outgoing_fragment_length = S2N_DEFAULT_FRAGMENT_LENGTH; - conn->handshake.end_of_messages = APPLICATION_DATA; - s2n_connection_set_config(conn, config); - - return 0; -} - -S2N_RESULT s2n_connection_wipe_all_keyshares(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); - RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.client_ecc_evp_params)); - - RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.server_kem_group_params)); - RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.client_kem_group_params)); - - return S2N_RESULT_OK; -} - -static int s2n_connection_wipe_keys(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* Free any server key received (we may not have completed a - * handshake, so this may not have been free'd yet) */ - POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); - POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.server_public_key)); - POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.client_public_key)); - POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.client_public_key)); - s2n_x509_validator_wipe(&conn->x509_validator); - POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); - POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); - POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); - POSIX_GUARD(s2n_free(&conn->handshake_params.client_cert_chain)); - POSIX_GUARD(s2n_free(&conn->ct_response)); - - return 0; -} - -static int s2n_connection_free_managed_recv_io(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->managed_recv_io) { - POSIX_GUARD(s2n_free_object((uint8_t **) &conn->recv_io_context, sizeof(struct s2n_socket_read_io_context))); - conn->managed_recv_io = false; - conn->recv = NULL; - } - return S2N_SUCCESS; -} - -static int s2n_connection_free_managed_send_io(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->managed_send_io) { - POSIX_GUARD(s2n_free_object((uint8_t **) &conn->send_io_context, sizeof(struct s2n_socket_write_io_context))); - conn->managed_send_io = false; - conn->send = NULL; - } - return S2N_SUCCESS; -} - -static int s2n_connection_free_managed_io(struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); - POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); - return S2N_SUCCESS; -} - -static int s2n_connection_wipe_io(struct s2n_connection *conn) -{ - if (s2n_connection_is_managed_corked(conn) && conn->recv) { - POSIX_GUARD(s2n_socket_read_restore(conn)); - } - if (s2n_connection_is_managed_corked(conn) && conn->send) { - POSIX_GUARD(s2n_socket_write_restore(conn)); - } - - /* Remove all I/O-related members */ - POSIX_GUARD(s2n_connection_free_managed_io(conn)); - - return 0; -} - -static uint8_t s2n_default_verify_host(const char *host_name, size_t len, void *data) -{ - /* if present, match server_name of the connection using rules - * outlined in RFC6125 6.4. */ - - struct s2n_connection *conn = data; - - if (conn->server_name[0] == '\0') { - return 0; - } - - /* complete match */ - if (strlen(conn->server_name) == len && strncasecmp(conn->server_name, host_name, len) == 0) { - return 1; - } - - /* match 1 level of wildcard */ - if (len > 2 && host_name[0] == '*' && host_name[1] == '.') { - const char *suffix = strchr(conn->server_name, '.'); - - if (suffix == NULL) { - return 0; - } - - if (strlen(suffix) == len - 1 && strncasecmp(suffix, host_name + 1, len - 1) == 0) { - return 1; - } - } - - return 0; -} - -S2N_CLEANUP_RESULT s2n_connection_ptr_free(struct s2n_connection **conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_GUARD_POSIX(s2n_connection_free(*conn)); - *conn = NULL; - return S2N_RESULT_OK; -} - -int s2n_connection_free(struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_connection_wipe_keys(conn)); - POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); - - POSIX_GUARD_RESULT(s2n_prf_free(conn)); - POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); - - POSIX_GUARD(s2n_connection_free_managed_io(conn)); - - POSIX_GUARD(s2n_free(&conn->client_ticket)); - POSIX_GUARD(s2n_free(&conn->status_response)); - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->server_early_data_context)); - POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); - POSIX_GUARD(s2n_stuffer_free(&conn->buffer_in)); - POSIX_GUARD(s2n_stuffer_free(&conn->in)); - POSIX_GUARD(s2n_stuffer_free(&conn->out)); - POSIX_GUARD(s2n_stuffer_free(&conn->handshake.io)); - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - s2n_x509_validator_wipe(&conn->x509_validator); - POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); - POSIX_GUARD(s2n_client_hello_free_raw_message(&conn->client_hello)); - POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); - POSIX_GUARD(s2n_free(&conn->cookie)); - POSIX_GUARD(s2n_free(&conn->cert_authorities)); - POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); - POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->secure)); - POSIX_GUARD(s2n_free_object((uint8_t **) &conn, sizeof(struct s2n_connection))); - - return 0; -} - -int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *config) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(config); - - if (conn->config == config) { - return 0; - } - - /* s2n_config invariant: any s2n_config is always in a state that respects the - * config->security_policy certificate preferences. Therefore we only need to - * validate certificates here if the connection is using a security policy override. - */ - const struct s2n_security_policy *security_policy_override = conn->security_policy_override; - if (security_policy_override) { - POSIX_GUARD_RESULT(s2n_config_validate_loaded_certificates(config, security_policy_override)); - } - - /* We only support one client certificate */ - if (s2n_config_get_num_default_certs(config) > 1 && conn->mode == S2N_CLIENT) { - POSIX_BAIL(S2N_ERR_TOO_MANY_CERTIFICATES); - } - - s2n_x509_validator_wipe(&conn->x509_validator); - - 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)); - if (!conn->verify_host_fn_overridden) { - if (config->verify_host_fn != NULL) { - conn->verify_host_fn = config->verify_host_fn; - conn->data_for_verify_host = config->data_for_verify_host; - } else { - conn->verify_host_fn = s2n_default_verify_host; - conn->data_for_verify_host = conn; - } - } - - if (config->max_verify_cert_chain_depth_set) { - POSIX_GUARD(s2n_x509_validator_set_max_chain_depth(&conn->x509_validator, config->max_verify_cert_chain_depth)); - } - } - conn->tickets_to_send = config->initial_tickets_to_send; - - if (conn->psk_params.psk_list.len == 0 && !conn->psk_mode_overridden) { - POSIX_GUARD(s2n_connection_set_psk_mode(conn, config->psk_mode)); - conn->psk_mode_overridden = false; - } - - /* If at least one certificate does not have a private key configured, - * the config must provide an async pkey callback. - * The handshake could still fail if the callback doesn't offload the - * signature, but this at least catches configuration mistakes. - */ - if (config->no_signing_key) { - POSIX_ENSURE(config->async_pkey_cb, S2N_ERR_NO_PRIVATE_KEY); - } - - if (config->quic_enabled) { - /* If QUIC is ever enabled for a connection via the config, - * we should enforce that it can never be disabled by - * changing the config. - * - * Enabling QUIC indicates that the connection is being used by - * a QUIC implementation, which never changes. Disabling QUIC - * partially through a connection could also potentially be - * dangerous, as QUIC handles encryption. - */ - POSIX_GUARD(s2n_connection_enable_quic(conn)); - } - - if (config->send_buffer_size_override) { - conn->multirecord_send = true; - } - - /* Historically, calling s2n_config_set_verification_ca_location enabled OCSP stapling - * regardless of the value set by an application calling s2n_config_set_status_request_type. - * We maintain this behavior for backwards compatibility. - * - * However, the s2n_config_set_verification_ca_location behavior predates client authentication - * support for OCSP stapling, so could only affect whether clients requested OCSP stapling. We - * therefore only have to maintain the legacy behavior for clients, not servers. - * - * Note: The Rust bindings do not maintain the legacy behavior. - */ - conn->request_ocsp_status = config->ocsp_status_requested_by_user; - if (config->ocsp_status_requested_by_s2n && conn->mode == S2N_CLIENT) { - conn->request_ocsp_status = true; - } - - conn->config = config; - return S2N_SUCCESS; -} - -int s2n_connection_server_name_extension_used(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_INVALID_STATE); - POSIX_ENSURE(!(conn->handshake.client_hello_received), S2N_ERR_INVALID_STATE); - - conn->server_name_used = 1; - return S2N_SUCCESS; -} - -int s2n_connection_set_ctx(struct s2n_connection *conn, void *ctx) -{ - POSIX_ENSURE_REF(conn); - - conn->context = ctx; - return S2N_SUCCESS; -} - -void *s2n_connection_get_ctx(struct s2n_connection *conn) -{ - return conn->context; -} - -int s2n_connection_release_buffers(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_PRECONDITION(s2n_stuffer_validate(&conn->out)); - POSIX_PRECONDITION(s2n_stuffer_validate(&conn->in)); - - POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->out), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); - - POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - if (s2n_stuffer_is_consumed(&conn->buffer_in)) { - POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); - } - - POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->post_handshake.in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - - POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->out)); - POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->in)); - return S2N_SUCCESS; -} - -int s2n_connection_free_handshake(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* We are done with the handshake */ - POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); - POSIX_GUARD_RESULT(s2n_prf_free(conn)); - - /* All IO should use conn->secure after the handshake. - * However, if this method is called before the handshake completes, - * the connection may still be using conn->initial. - */ - if (conn->client != conn->initial && conn->server != conn->initial) { - POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); - } - - /* Wipe the buffers we are going to free */ - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); - - /* Truncate buffers to save memory, we are done with the handshake */ - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); - POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); - - /* We can free extension data we no longer need */ - POSIX_GUARD(s2n_free(&conn->client_ticket)); - POSIX_GUARD(s2n_free(&conn->status_response)); - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); - POSIX_GUARD(s2n_free(&conn->cookie)); - POSIX_GUARD(s2n_free(&conn->cert_authorities)); - - return 0; -} - -/* An idempotent operation which initializes values on the connection. - * - * Called in order to reuse a connection structure for a new connection. Should wipe - * any persistent memory, free any temporary memory, and set all fields back to their - * defaults. - */ -int s2n_connection_wipe(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* First make a copy of everything we'd like to save, which isn't very much. */ - int mode = conn->mode; - struct s2n_config *config = conn->config; - struct s2n_stuffer alert_in = { 0 }; - struct s2n_stuffer client_ticket_to_decrypt = { 0 }; - struct s2n_stuffer handshake_io = { 0 }; - struct s2n_stuffer header_in = { 0 }; - struct s2n_stuffer buffer_in = { 0 }; - struct s2n_stuffer out = { 0 }; - - /* Some required structures might have been freed to conserve memory between handshakes. - * Restore them. - */ - if (!conn->handshake.hashes) { - POSIX_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); - } - POSIX_GUARD_RESULT(s2n_handshake_hashes_wipe(conn->handshake.hashes)); - struct s2n_handshake_hashes *handshake_hashes = conn->handshake.hashes; - if (!conn->prf_space) { - POSIX_GUARD_RESULT(s2n_prf_new(conn)); - } - POSIX_GUARD_RESULT(s2n_prf_wipe(conn)); - struct s2n_prf_working_space *prf_workspace = conn->prf_space; - if (!conn->initial) { - POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->initial)); - } else { - POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->initial)); - } - struct s2n_crypto_parameters *initial = conn->initial; - if (!conn->secure) { - POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->secure)); - } else { - POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->secure)); - } - struct s2n_crypto_parameters *secure = conn->secure; - - /* Wipe all of the sensitive stuff */ - POSIX_GUARD(s2n_connection_wipe_keys(conn)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->client_ticket_to_decrypt)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->post_handshake.in)); - POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->header_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->buffer_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); - - /* Free stuffers we plan to just recreate */ - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - POSIX_GUARD(s2n_stuffer_free(&conn->in)); - - POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); - POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); - - /* Wipe the I/O-related info and restore the original socket if necessary */ - POSIX_GUARD(s2n_connection_wipe_io(conn)); - - POSIX_GUARD(s2n_free(&conn->client_ticket)); - POSIX_GUARD(s2n_free(&conn->status_response)); - POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); - POSIX_GUARD(s2n_free(&conn->server_early_data_context)); - POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); - POSIX_GUARD(s2n_free(&conn->cookie)); - POSIX_GUARD(s2n_free(&conn->cert_authorities)); - - /* Allocate memory for handling handshakes */ - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, S2N_LARGE_RECORD_LENGTH)); - - /* Truncate the message buffers to save memory, we will dynamically resize it as needed */ - POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); - POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); - POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); - - /* Remove context associated with connection */ - conn->context = NULL; - conn->verify_host_fn_overridden = 0; - conn->verify_host_fn = NULL; - conn->data_for_verify_host = NULL; - - /* Clone the stuffers */ - /* ignore address warnings because dest is allocated on the stack */ -#ifdef S2N_DIAGNOSTICS_PUSH_SUPPORTED - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Waddress" -#endif - POSIX_CHECKED_MEMCPY(&alert_in, &conn->alert_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&client_ticket_to_decrypt, &conn->client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&buffer_in, &conn->buffer_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&out, &conn->out, sizeof(struct s2n_stuffer)); -#ifdef S2N_DIAGNOSTICS_POP_SUPPORTED - #pragma GCC diagnostic pop -#endif - - POSIX_GUARD(s2n_connection_zero(conn, mode, config)); - - POSIX_CHECKED_MEMCPY(&conn->alert_in, &alert_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->client_ticket_to_decrypt, &client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->buffer_in, &buffer_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->out, &out, sizeof(struct s2n_stuffer)); - - /* conn->in will eventually point to part of conn->buffer_in, but we initialize - * it as growable and allocated to support legacy tests. - */ - POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->in, 0)); - - conn->handshake.hashes = handshake_hashes; - conn->prf_space = prf_workspace; - conn->initial = initial; - conn->secure = secure; - conn->client = conn->initial; - conn->server = conn->initial; - conn->handshake_params.client_cert_sig_scheme = &s2n_null_sig_scheme; - conn->handshake_params.server_cert_sig_scheme = &s2n_null_sig_scheme; - - POSIX_GUARD_RESULT(s2n_psk_parameters_init(&conn->psk_params)); - conn->server_keying_material_lifetime = ONE_WEEK_IN_SEC; - - /* Require all handshakes hashes. This set can be reduced as the handshake progresses. */ - POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); - - if (conn->mode == S2N_SERVER) { - /* Start with the highest protocol version so that the highest common protocol version can be selected */ - /* during handshake. */ - conn->server_protocol_version = s2n_highest_protocol_version; - conn->client_protocol_version = s2n_unknown_protocol_version; - conn->actual_protocol_version = s2n_unknown_protocol_version; - } else { - /* For clients, also set actual_protocol_version. Record generation uses that value for the initial */ - /* ClientHello record version. Not all servers ignore the record version in ClientHello. */ - conn->server_protocol_version = s2n_unknown_protocol_version; - conn->client_protocol_version = s2n_highest_protocol_version; - conn->actual_protocol_version = s2n_highest_protocol_version; - } - - /* Initialize remaining values */ - conn->blinding = S2N_BUILT_IN_BLINDING; - conn->session_ticket_status = S2N_NO_TICKET; - - return 0; -} - -int s2n_connection_set_recv_ctx(struct s2n_connection *conn, void *ctx) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); - conn->recv_io_context = ctx; - return S2N_SUCCESS; -} - -int s2n_connection_set_send_ctx(struct s2n_connection *conn, void *ctx) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); - conn->send_io_context = ctx; - return S2N_SUCCESS; -} - -int s2n_connection_set_recv_cb(struct s2n_connection *conn, s2n_recv_fn recv) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); - conn->recv = recv; - return S2N_SUCCESS; -} - -int s2n_connection_set_send_cb(struct s2n_connection *conn, s2n_send_fn send) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); - conn->send = send; - return S2N_SUCCESS; -} - -int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t **cert_chain_out, uint32_t *cert_chain_len) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(cert_chain_out); - POSIX_ENSURE_REF(cert_chain_len); - POSIX_ENSURE_REF(conn->handshake_params.client_cert_chain.data); - - *cert_chain_out = conn->handshake_params.client_cert_chain.data; - *cert_chain_len = conn->handshake_params.client_cert_chain.size; - - return S2N_SUCCESS; -} - -int s2n_connection_get_cipher_preferences(struct s2n_connection *conn, const struct s2n_cipher_preferences **cipher_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(cipher_preferences); - - if (conn->security_policy_override != NULL) { - *cipher_preferences = conn->security_policy_override->cipher_preferences; - } else if (conn->config->security_policy != NULL) { - *cipher_preferences = conn->config->security_policy->cipher_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_CIPHER_PREFERENCES); - } - - POSIX_ENSURE_REF(*cipher_preferences); - return 0; -} - -int s2n_connection_get_certificate_match(struct s2n_connection *conn, s2n_cert_sni_match *match_status) -{ - POSIX_ENSURE(conn, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(match_status, S2N_ERR_INVALID_ARGUMENT); - POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); - - /* Server must have gotten past certificate selection */ - POSIX_ENSURE(conn->handshake_params.our_chain_and_key, S2N_ERR_NO_CERT_FOUND); - - if (!s2n_server_received_server_name(conn)) { - *match_status = S2N_SNI_NONE; - } else if (conn->handshake_params.exact_sni_match_exists) { - *match_status = S2N_SNI_EXACT_MATCH; - } else if (conn->handshake_params.wc_sni_match_exists) { - *match_status = S2N_SNI_WILDCARD_MATCH; - } else { - *match_status = S2N_SNI_NO_MATCH; - } - - return S2N_SUCCESS; -} - -int s2n_connection_get_security_policy(struct s2n_connection *conn, const struct s2n_security_policy **security_policy) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(security_policy); - - if (conn->security_policy_override != NULL) { - *security_policy = conn->security_policy_override; - } else if (conn->config->security_policy != NULL) { - *security_policy = conn->config->security_policy; - } else { - POSIX_BAIL(S2N_ERR_INVALID_SECURITY_POLICY); - } - - POSIX_ENSURE_REF(*security_policy); - return 0; -} - -int s2n_connection_get_kem_preferences(struct s2n_connection *conn, const struct s2n_kem_preferences **kem_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(kem_preferences); - - if (conn->security_policy_override != NULL) { - *kem_preferences = conn->security_policy_override->kem_preferences; - } else if (conn->config->security_policy != NULL) { - *kem_preferences = conn->config->security_policy->kem_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_KEM_PREFERENCES); - } - - POSIX_ENSURE_REF(*kem_preferences); - return 0; -} - -int s2n_connection_get_signature_preferences(struct s2n_connection *conn, const struct s2n_signature_preferences **signature_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(signature_preferences); - - if (conn->security_policy_override != NULL) { - *signature_preferences = conn->security_policy_override->signature_preferences; - } else if (conn->config->security_policy != NULL) { - *signature_preferences = conn->config->security_policy->signature_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES); - } - - POSIX_ENSURE_REF(*signature_preferences); - return 0; -} - -int s2n_connection_get_ecc_preferences(struct s2n_connection *conn, const struct s2n_ecc_preferences **ecc_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->config); - POSIX_ENSURE_REF(ecc_preferences); - - if (conn->security_policy_override != NULL) { - *ecc_preferences = conn->security_policy_override->ecc_preferences; - } else if (conn->config->security_policy != NULL) { - *ecc_preferences = conn->config->security_policy->ecc_preferences; - } else { - POSIX_BAIL(S2N_ERR_INVALID_ECC_PREFERENCES); - } - - POSIX_ENSURE_REF(*ecc_preferences); - return 0; -} - -int s2n_connection_get_protocol_preferences(struct s2n_connection *conn, struct s2n_blob **protocol_preferences) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(protocol_preferences); - - *protocol_preferences = NULL; - if (conn->application_protocols_overridden.size > 0) { - *protocol_preferences = &conn->application_protocols_overridden; - } else { - POSIX_ENSURE_REF(conn->config); - *protocol_preferences = &conn->config->application_protocols; - } - - POSIX_ENSURE_REF(*protocol_preferences); - return 0; -} - -static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, - const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(config); - RESULT_ENSURE_REF(client_cert_auth_type); - - if (conn->client_cert_auth_type_overridden) { - *client_cert_auth_type = conn->client_cert_auth_type; - } else if (config->client_cert_auth_type_overridden) { - *client_cert_auth_type = config->client_cert_auth_type; - } else if (conn->mode == S2N_CLIENT) { - /* Clients should default to "Optional" so that they handle any - * CertificateRequests sent by the server. - */ - *client_cert_auth_type = S2N_CERT_AUTH_OPTIONAL; - } else { - /* Servers should default to "None" so that they send no CertificateRequests. */ - *client_cert_auth_type = S2N_CERT_AUTH_NONE; - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_client_auth_type(struct s2n_connection *conn, - s2n_cert_auth_type *client_cert_auth_type) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_and_config_get_client_auth_type( - conn, conn->config, client_cert_auth_type)); - return S2N_SUCCESS; -} - -int s2n_connection_set_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type client_cert_auth_type) -{ - POSIX_ENSURE_REF(conn); - - conn->client_cert_auth_type_overridden = 1; - conn->client_cert_auth_type = client_cert_auth_type; - return 0; -} - -int s2n_connection_set_read_fd(struct s2n_connection *conn, int rfd) -{ - struct s2n_blob ctx_mem = { 0 }; - struct s2n_socket_read_io_context *peer_socket_ctx = NULL; - - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_read_io_context))); - POSIX_GUARD(s2n_blob_zero(&ctx_mem)); - - peer_socket_ctx = (struct s2n_socket_read_io_context *) (void *) ctx_mem.data; - peer_socket_ctx->fd = rfd; - - POSIX_GUARD(s2n_connection_set_recv_cb(conn, s2n_socket_read)); - POSIX_GUARD(s2n_connection_set_recv_ctx(conn, peer_socket_ctx)); - conn->managed_recv_io = true; - - /* This is only needed if the user is using corked io. - * Take the snapshot in case optimized io is enabled after setting the fd. - */ - POSIX_GUARD(s2n_socket_read_snapshot(conn)); - - return 0; -} - -int s2n_connection_get_read_fd(struct s2n_connection *conn, int *readfd) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(readfd); - POSIX_ENSURE((conn->managed_recv_io && conn->recv_io_context), S2N_ERR_INVALID_STATE); - - const struct s2n_socket_read_io_context *peer_socket_ctx = conn->recv_io_context; - *readfd = peer_socket_ctx->fd; - return S2N_SUCCESS; -} - -int s2n_connection_set_write_fd(struct s2n_connection *conn, int wfd) -{ - struct s2n_blob ctx_mem = { 0 }; - struct s2n_socket_write_io_context *peer_socket_ctx = NULL; - - POSIX_ENSURE_REF(conn); - POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_write_io_context))); - - peer_socket_ctx = (struct s2n_socket_write_io_context *) (void *) ctx_mem.data; - peer_socket_ctx->fd = wfd; - - POSIX_GUARD(s2n_connection_set_send_cb(conn, s2n_socket_write)); - POSIX_GUARD(s2n_connection_set_send_ctx(conn, peer_socket_ctx)); - conn->managed_send_io = true; - - /* This is only needed if the user is using corked io. - * Take the snapshot in case optimized io is enabled after setting the fd. - */ - POSIX_GUARD(s2n_socket_write_snapshot(conn)); - - uint8_t ipv6 = 0; - if (0 == s2n_socket_is_ipv6(wfd, &ipv6)) { - conn->ipv6 = (ipv6 ? 1 : 0); - } - - conn->write_fd_broken = 0; - - return 0; -} - -int s2n_connection_get_write_fd(struct s2n_connection *conn, int *writefd) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(writefd); - POSIX_ENSURE((conn->managed_send_io && conn->send_io_context), S2N_ERR_INVALID_STATE); - - const struct s2n_socket_write_io_context *peer_socket_ctx = conn->send_io_context; - *writefd = peer_socket_ctx->fd; - return S2N_SUCCESS; -} -int s2n_connection_set_fd(struct s2n_connection *conn, int fd) -{ - POSIX_GUARD(s2n_connection_set_read_fd(conn, fd)); - POSIX_GUARD(s2n_connection_set_write_fd(conn, fd)); - return 0; -} - -int s2n_connection_use_corked_io(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* Caller shouldn't be trying to set s2n IO corked on non-s2n-managed IO */ - POSIX_ENSURE(conn->managed_send_io, S2N_ERR_CORK_SET_ON_UNMANAGED); - conn->corked_io = 1; - - return 0; -} - -uint64_t s2n_connection_get_wire_bytes_in(struct s2n_connection *conn) -{ - if (conn->ktls_recv_enabled) { - return 0; - } - return conn->wire_bytes_in; -} - -uint64_t s2n_connection_get_wire_bytes_out(struct s2n_connection *conn) -{ - if (conn->ktls_send_enabled) { - return 0; - } - return conn->wire_bytes_out; -} - -const char *s2n_connection_get_cipher(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_ENSURE_REF(conn->secure); - PTR_ENSURE_REF(conn->secure->cipher_suite); - - return conn->secure->cipher_suite->name; -} - -int s2n_connection_get_cipher_iana_value(struct s2n_connection *conn, uint8_t *first, uint8_t *second) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_MUT(first); - POSIX_ENSURE_MUT(second); - - /* ensure we've negotiated a cipher suite */ - POSIX_ENSURE(!s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, - s2n_null_cipher_suite.iana_value, sizeof(s2n_null_cipher_suite.iana_value)), - S2N_ERR_INVALID_STATE); - - const uint8_t *iana_value = conn->secure->cipher_suite->iana_value; - *first = iana_value[0]; - *second = iana_value[1]; - - return S2N_SUCCESS; -} - -const char *s2n_connection_get_curve(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_ENSURE_REF(conn->secure); - PTR_ENSURE_REF(conn->secure->cipher_suite); - - if (conn->kex_params.server_ecc_evp_params.negotiated_curve) { - /* TLS1.3 currently only uses ECC groups. */ - bool tls13 = conn->actual_protocol_version >= S2N_TLS13; - /* we check for a full handshake, because TLS 1.2 resumption does not perform - * an additional diffie-hellman exchange */ - bool ecdhe_cipher_negotiated = s2n_kex_includes(conn->secure->cipher_suite->key_exchange_alg, &s2n_ecdhe) - && IS_FULL_HANDSHAKE(conn); - if (tls13 || ecdhe_cipher_negotiated) { - return conn->kex_params.server_ecc_evp_params.negotiated_curve->name; - } - } - - return "NONE"; -} - -const char *s2n_connection_get_kem_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (!conn->kex_params.kem_params.kem) { - return "NONE"; - } - - return conn->kex_params.kem_params.kem->name; -} - -const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (conn->actual_protocol_version < S2N_TLS13 || !conn->kex_params.server_kem_group_params.kem_group) { - return "NONE"; - } - - return conn->kex_params.server_kem_group_params.kem_group->name; -} - -int s2n_connection_get_key_exchange_group(struct s2n_connection *conn, const char **group_name) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(group_name); - - /* s2n_connection_get_curve returns only the ECDH curve portion of a named group, even if - the negotiated group was a hybrid PQ key exchange also containing a KEM. Therefore, - we use the result of s2n_connection_get_kem_group_name if the connection supports PQ. */ - if (s2n_tls13_pq_hybrid_supported(conn)) { - *group_name = s2n_connection_get_kem_group_name(conn); - } else { - *group_name = s2n_connection_get_curve(conn); - } - - POSIX_ENSURE(*group_name != NULL && strcmp(*group_name, "NONE"), S2N_ERR_INVALID_STATE); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_connection_get_client_supported_version(struct s2n_connection *conn, - uint8_t *client_supported_version) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_EQ(conn->mode, S2N_SERVER); - - struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); - RESULT_ENSURE_REF(client_hello); - - s2n_parsed_extension *supported_versions_extension = NULL; - RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_VERSIONS, &client_hello->extensions, - &supported_versions_extension)); - RESULT_ENSURE_REF(supported_versions_extension); - - struct s2n_stuffer supported_versions_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions_stuffer, &supported_versions_extension->extension)); - - uint8_t client_protocol_version = s2n_unknown_protocol_version; - uint8_t actual_protocol_version = s2n_unknown_protocol_version; - RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, &supported_versions_stuffer, - &client_protocol_version, &actual_protocol_version)); - - RESULT_ENSURE_NE(client_protocol_version, s2n_unknown_protocol_version); - - *client_supported_version = client_protocol_version; - - return S2N_RESULT_OK; -} - -int s2n_connection_get_client_protocol_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* For backwards compatibility, the client_protocol_version field isn't updated via the - * supported versions extension on TLS 1.2 servers. See - * https://github.com/aws/s2n-tls/issues/4240. - * - * The extension is processed here to ensure that TLS 1.2 servers report the same client - * protocol version to applications as TLS 1.3 servers. - */ - if (conn->mode == S2N_SERVER && conn->server_protocol_version <= S2N_TLS12) { - uint8_t client_supported_version = s2n_unknown_protocol_version; - s2n_result result = s2n_connection_get_client_supported_version(conn, &client_supported_version); - - /* If the extension wasn't received, or if a client protocol version couldn't be determined - * after processing the extension, the extension is ignored. - */ - if (s2n_result_is_ok(result)) { - return client_supported_version; - } - } - - return conn->client_protocol_version; -} - -int s2n_connection_get_server_protocol_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - return conn->server_protocol_version; -} - -int s2n_connection_get_actual_protocol_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - return conn->actual_protocol_version; -} - -int s2n_connection_get_client_hello_version(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->client_hello.sslv2) { - return S2N_SSLv2; - } else { - return S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); - } -} - -int s2n_connection_client_cert_used(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (IS_CLIENT_AUTH_HANDSHAKE(conn) && is_handshake_complete(conn)) { - if (IS_CLIENT_AUTH_NO_CERT(conn)) { - return 0; - } - return 1; - } - return 0; -} - -int s2n_connection_get_alert(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) != 2, S2N_ERR_NO_ALERT); - - /* Shallow copy the stuffer. We assume that multiple threads might call this - * function concurrently, so we must not mutate anything outside of the function scope */ - struct s2n_stuffer alert_stuffer = conn->alert_in; - uint8_t alert_code = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); - POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); - - return alert_code; -} - -int s2n_set_server_name(struct s2n_connection *conn, const char *server_name) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(server_name); - - S2N_ERROR_IF(conn->mode != S2N_CLIENT, S2N_ERR_CLIENT_MODE); - - int len = strlen(server_name); - S2N_ERROR_IF(len > S2N_MAX_SERVER_NAME, S2N_ERR_SERVER_NAME_TOO_LONG); - - POSIX_CHECKED_MEMCPY(conn->server_name, server_name, len); - - return 0; -} - -const char *s2n_get_server_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (conn->server_name[0]) { - return conn->server_name; - } - - PTR_GUARD_POSIX(s2n_extension_process(&s2n_client_server_name_extension, conn, &conn->client_hello.extensions)); - - if (!conn->server_name[0]) { - return NULL; - } - - return conn->server_name; -} - -const char *s2n_get_application_protocol(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - - if (strlen(conn->application_protocol) == 0) { - return NULL; - } - - return conn->application_protocol; -} - -int s2n_connection_get_session_id_length(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - /* Stateful session resumption in TLS1.3 using session id is not yet supported. */ - if (conn->actual_protocol_version >= S2N_TLS13) { - return 0; - } - return conn->session_id_len; -} - -int s2n_connection_get_session_id(struct s2n_connection *conn, uint8_t *session_id, size_t max_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(session_id); - - const int session_id_len = s2n_connection_get_session_id_length(conn); - POSIX_GUARD(session_id_len); - - POSIX_ENSURE((size_t) session_id_len <= max_length, S2N_ERR_SESSION_ID_TOO_LONG); - - POSIX_CHECKED_MEMCPY(session_id, conn->session_id, session_id_len); - - return session_id_len; -} - -int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blinding) -{ - POSIX_ENSURE_REF(conn); - conn->blinding = blinding; - - return 0; -} - -#define ONE_S INT64_C(1000000000) - -static S2N_RESULT s2n_connection_get_delay_impl(struct s2n_connection *conn, uint64_t *delay) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(delay); - - if (!conn->delay) { - *delay = 0; - return S2N_RESULT_OK; - } - - uint64_t elapsed = 0; - RESULT_GUARD(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); - - if (elapsed > conn->delay) { - *delay = 0; - return S2N_RESULT_OK; - } - - *delay = conn->delay - elapsed; - - return S2N_RESULT_OK; -} - -uint64_t s2n_connection_get_delay(struct s2n_connection *conn) -{ - uint64_t delay = 0; - if (s2n_result_is_ok(s2n_connection_get_delay_impl(conn, &delay))) { - return delay; - } else { - return UINT64_MAX; - } -} - -/* s2n-tls has a random delay that will trigger for sensitive errors. This is a mitigation - * for possible timing sidechannels. - * - * The historical sidechannel that inspired s2n-tls blinding was the Lucky 13 attack, which takes - * advantage of potential timing differences when removing padding from a record encrypted in CBC mode. - * The attack is only theoretical in TLS; the attack criteria is unlikely to ever occur - * (See: Fardan, N. J. A., & Paterson, K. G. (2013, May 1). Lucky Thirteen: Breaking the TLS and - * DTLS Record Protocols.) However, we still include blinding to provide a defense in depth mitigation. - */ -S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(min); - RESULT_ENSURE_REF(max); - RESULT_ENSURE_REF(conn->config); - - /* - * The default delay is a random value between 10-30s. The rationale behind the range is that the - * floor is the fixed cost that an attacker must pay per attempt, in this case, 10s. The length of - * the range then affects the number of attempts that an attacker must perform in order to recover a - * byte of plaintext with a certain degree of confidence. - * - * A uniform distribution of the range [a, b] has a variance of ((b - a)^2)/12. Therefore, given a - * hypothetical timing difference of 1us, the number of attempts necessary to distinguish the correct - * byte from an incorrect byte in a Lucky13-style attack is (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion - * (note that we first have to convert from seconds to microseconds to match the unit of the timing difference.) - */ - *min = S2N_DEFAULT_BLINDING_MIN * ONE_S; - *max = S2N_DEFAULT_BLINDING_MAX * ONE_S; - - /* Setting the min to 1/3 of the max is an arbitrary ratio of fixed to variable delay. - * It is based on the ratio of our original default values. - */ - if (conn->config->custom_blinding_set) { - *max = conn->config->max_blinding * ONE_S; - *min = *max / 3; - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_GUARD(s2n_connection_set_closed(conn)); - - 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; - RESULT_GUARD(s2n_public_random(max - min, &rand_delay)); - - conn->delay = min + rand_delay; - - /* Restart the write timer */ - RESULT_GUARD(s2n_timer_start(conn->config, &conn->write_timer)); - - if (conn->blinding == S2N_BUILT_IN_BLINDING) { - struct timespec sleep_time = { .tv_sec = conn->delay / ONE_S, .tv_nsec = conn->delay % ONE_S }; - - int r = 0; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - } - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection **conn) -{ - RESULT_ENSURE_REF(conn); - if (*conn == NULL) { - return S2N_RESULT_OK; - } - - int error_code = s2n_errno; - int error_type = s2n_error_get_type(error_code); - - switch (error_type) { - case S2N_ERR_T_OK: - /* Ignore no error */ - return S2N_RESULT_OK; - case S2N_ERR_T_BLOCKED: - /* All blocking errors are retriable and should trigger no further action. */ - return S2N_RESULT_OK; - default: - break; - } - - /* Ensure that conn->in doesn't contain any leftover invalid or unauthenticated data. */ - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&(*conn)->in)); - - switch (error_code) { - /* Don't invoke blinding on some of the common errors. - * - * Be careful adding new errors here. Disabling blinding for an - * error that can be triggered by secret / encrypted values can - * potentially lead to a side channel attack. - * - * We may want to someday add an explicit error type for these errors. - */ - case S2N_ERR_CLOSED: - case S2N_ERR_CANCELLED: - case S2N_ERR_CIPHER_NOT_SUPPORTED: - case S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED: - case S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK: - RESULT_GUARD(s2n_connection_set_closed(*conn)); - break; - default: - /* Apply blinding to all other errors */ - RESULT_GUARD(s2n_connection_kill(*conn)); - break; - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_set_closed(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - s2n_atomic_flag_set(&conn->read_closed); - s2n_atomic_flag_set(&conn->write_closed); - return S2N_RESULT_OK; -} - -const uint8_t *s2n_connection_get_ocsp_response(struct s2n_connection *conn, uint32_t *length) -{ - PTR_ENSURE_REF(conn); - PTR_ENSURE_REF(length); - - *length = conn->status_response.size; - return conn->status_response.data; -} - -S2N_RESULT s2n_connection_set_max_fragment_length(struct s2n_connection *conn, uint16_t max_frag_length) -{ - RESULT_ENSURE_REF(conn); - - if (conn->negotiated_mfl_code) { - /* Respect the upper limit agreed on with the peer */ - RESULT_ENSURE_LT(conn->negotiated_mfl_code, s2n_array_len(mfl_code_to_length)); - conn->max_outgoing_fragment_length = S2N_MIN(mfl_code_to_length[conn->negotiated_mfl_code], max_frag_length); - } else { - conn->max_outgoing_fragment_length = max_frag_length; - } - - /* If no buffer has been initialized yet, no need to resize. - * The standard I/O logic will handle initializing the buffer. - */ - if (s2n_stuffer_is_freed(&conn->out)) { - return S2N_RESULT_OK; - } - - uint16_t max_wire_record_size = 0; - RESULT_GUARD(s2n_record_max_write_size(conn, conn->max_outgoing_fragment_length, &max_wire_record_size)); - if ((conn->out.blob.size < max_wire_record_size)) { - RESULT_GUARD_POSIX(s2n_realloc(&conn->out.blob, max_wire_record_size)); - } - - return S2N_RESULT_OK; -} - -int s2n_connection_prefer_throughput(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_LARGE_FRAGMENT_LENGTH)); - return S2N_SUCCESS; -} - -int s2n_connection_prefer_low_latency(struct s2n_connection *conn) -{ - POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_SMALL_FRAGMENT_LENGTH)); - return S2N_SUCCESS; -} - -int s2n_connection_set_dynamic_buffers(struct s2n_connection *conn, bool enabled) -{ - POSIX_ENSURE_REF(conn); - conn->dynamic_buffers = enabled; - return S2N_SUCCESS; -} - -int s2n_connection_set_dynamic_record_threshold(struct s2n_connection *conn, uint32_t resize_threshold, uint16_t timeout_threshold) -{ - POSIX_ENSURE_REF(conn); - S2N_ERROR_IF(resize_threshold > S2N_TLS_MAX_RESIZE_THRESHOLD, S2N_ERR_INVALID_DYNAMIC_THRESHOLD); - - conn->dynamic_record_resize_threshold = resize_threshold; - conn->dynamic_record_timeout_threshold = timeout_threshold; - return 0; -} - -int s2n_connection_set_verify_host_callback(struct s2n_connection *conn, s2n_verify_host_fn verify_host_fn, void *data) -{ - POSIX_ENSURE_REF(conn); - - conn->verify_host_fn = verify_host_fn; - conn->data_for_verify_host = data; - conn->verify_host_fn_overridden = 1; - - return 0; -} - -int s2n_connection_recv_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) -{ - POSIX_ENSURE_REF(conn->recv); - /* Make sure we have enough space to write */ - POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, len)); - - int r = 0; - S2N_IO_RETRY_EINTR(r, - conn->recv(conn->recv_io_context, stuffer->blob.data + stuffer->write_cursor, len)); - POSIX_ENSURE(r >= 0, S2N_ERR_RECV_STUFFER_FROM_CONN); - - /* Record just how many bytes we have written */ - POSIX_GUARD(s2n_stuffer_skip_write(stuffer, r)); - return r; -} - -int s2n_connection_send_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->send); - if (conn->write_fd_broken) { - POSIX_BAIL(S2N_ERR_SEND_STUFFER_TO_CONN); - } - /* Make sure we even have the data */ - S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) < len, S2N_ERR_STUFFER_OUT_OF_DATA); - - int w = 0; - S2N_IO_RETRY_EINTR(w, - conn->send(conn->send_io_context, stuffer->blob.data + stuffer->read_cursor, len)); - if (w < 0 && errno == EPIPE) { - conn->write_fd_broken = 1; - } - POSIX_ENSURE(w >= 0, S2N_ERR_SEND_STUFFER_TO_CONN); - - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, w)); - return w; -} - -int s2n_connection_is_managed_corked(const struct s2n_connection *s2n_connection) -{ - POSIX_ENSURE_REF(s2n_connection); - - return (s2n_connection->managed_send_io && s2n_connection->corked_io); -} - -const uint8_t *s2n_connection_get_sct_list(struct s2n_connection *conn, uint32_t *length) -{ - if (!length) { - return NULL; - } - - *length = conn->ct_response.size; - return conn->ct_response.data; -} - -int s2n_connection_is_client_auth_enabled(struct s2n_connection *s2n_connection) -{ - s2n_cert_auth_type auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(s2n_connection, &auth_type)); - - return (auth_type != S2N_CERT_AUTH_NONE); -} - -struct s2n_cert_chain_and_key *s2n_connection_get_selected_cert(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - return conn->handshake_params.our_chain_and_key; -} - -uint8_t s2n_connection_get_protocol_version(const struct s2n_connection *conn) -{ - if (conn == NULL) { - return S2N_UNKNOWN_PROTOCOL_VERSION; - } - - if (conn->actual_protocol_version != S2N_UNKNOWN_PROTOCOL_VERSION) { - return conn->actual_protocol_version; - } - - if (conn->mode == S2N_CLIENT) { - return conn->client_protocol_version; - } - return conn->server_protocol_version; -} - -DEFINE_POINTER_CLEANUP_FUNC(struct s2n_cert_chain *, s2n_cert_chain_free); - -int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain_and_key) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(cert_chain_and_key); - POSIX_ENSURE_REF(cert_chain_and_key->cert_chain); - - /* Ensure that cert_chain_and_key is empty BEFORE we modify it in any way. - * That includes before tying its cert_chain to DEFER_CLEANUP. - */ - POSIX_ENSURE(cert_chain_and_key->cert_chain->head == NULL, S2N_ERR_INVALID_ARGUMENT); - - DEFER_CLEANUP(struct s2n_cert_chain *cert_chain = cert_chain_and_key->cert_chain, s2n_cert_chain_free_pointer); - struct s2n_cert **insert = &cert_chain->head; - - const struct s2n_x509_validator *validator = &conn->x509_validator; - POSIX_ENSURE_REF(validator); - POSIX_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_CERT_NOT_VALIDATED); - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - POSIX_GUARD_RESULT(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - STACK_OF(X509) *cert_chain_validated = validated_cert_chain.stack; - POSIX_ENSURE_REF(cert_chain_validated); - - int cert_count = sk_X509_num(cert_chain_validated); - POSIX_ENSURE_GTE(cert_count, 0); - - for (size_t cert_idx = 0; cert_idx < (size_t) cert_count; cert_idx++) { - X509 *cert = sk_X509_value(cert_chain_validated, cert_idx); - POSIX_ENSURE_REF(cert); - DEFER_CLEANUP(uint8_t *cert_data = NULL, s2n_crypto_free); - int cert_size = i2d_X509(cert, &cert_data); - POSIX_ENSURE_GT(cert_size, 0); - - struct s2n_blob mem = { 0 }; - POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); - - struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; - POSIX_ENSURE_REF(new_node); - - new_node->next = NULL; - *insert = new_node; - insert = &new_node->next; - - POSIX_GUARD(s2n_alloc(&new_node->raw, cert_size)); - POSIX_CHECKED_MEMCPY(new_node->raw.data, cert_data, cert_size); - } - - ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_signature_scheme_to_tls_iana(const struct s2n_signature_scheme *sig_scheme, - s2n_tls_hash_algorithm *converted_scheme) -{ - RESULT_ENSURE_REF(sig_scheme); - RESULT_ENSURE_REF(converted_scheme); - *converted_scheme = S2N_TLS_HASH_NONE; - - switch (sig_scheme->hash_alg) { - case S2N_HASH_MD5: - *converted_scheme = S2N_TLS_HASH_MD5; - break; - case S2N_HASH_SHA1: - *converted_scheme = S2N_TLS_HASH_SHA1; - break; - case S2N_HASH_SHA224: - *converted_scheme = S2N_TLS_HASH_SHA224; - break; - case S2N_HASH_SHA256: - *converted_scheme = S2N_TLS_HASH_SHA256; - break; - case S2N_HASH_SHA384: - *converted_scheme = S2N_TLS_HASH_SHA384; - break; - case S2N_HASH_SHA512: - *converted_scheme = S2N_TLS_HASH_SHA512; - break; - case S2N_HASH_MD5_SHA1: - *converted_scheme = S2N_TLS_HASH_MD5_SHA1; - break; - case S2N_HASH_NONE: - case S2N_HASH_SHAKE256_64: - case S2N_HASH_ALGS_COUNT: - *converted_scheme = S2N_TLS_HASH_NONE; - break; - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_selected_digest_algorithm(struct s2n_connection *conn, - s2n_tls_hash_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( - conn->handshake_params.server_cert_sig_scheme, converted_scheme)); - - return S2N_SUCCESS; -} - -int s2n_connection_get_selected_client_cert_digest_algorithm(struct s2n_connection *conn, - s2n_tls_hash_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( - conn->handshake_params.client_cert_sig_scheme, converted_scheme)); - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_signature_scheme_to_signature_algorithm(const struct s2n_signature_scheme *sig_scheme, - s2n_tls_signature_algorithm *converted_scheme) -{ - RESULT_ENSURE_REF(sig_scheme); - RESULT_ENSURE_REF(converted_scheme); - *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; - - switch (sig_scheme->sig_alg) { - case S2N_SIGNATURE_RSA: - *converted_scheme = S2N_TLS_SIGNATURE_RSA; - break; - case S2N_SIGNATURE_ECDSA: - *converted_scheme = S2N_TLS_SIGNATURE_ECDSA; - break; - case S2N_SIGNATURE_RSA_PSS_RSAE: - *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_RSAE; - break; - case S2N_SIGNATURE_RSA_PSS_PSS: - *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_PSS; - break; - case S2N_SIGNATURE_MLDSA: - *converted_scheme = S2N_TLS_SIGNATURE_MLDSA; - break; - case S2N_SIGNATURE_ANONYMOUS: - *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; - break; - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_selected_signature_algorithm(struct s2n_connection *conn, - s2n_tls_signature_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( - conn->handshake_params.server_cert_sig_scheme, converted_scheme)); - - return S2N_SUCCESS; -} - -int s2n_connection_get_selected_client_cert_signature_algorithm(struct s2n_connection *conn, - s2n_tls_signature_algorithm *converted_scheme) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(converted_scheme); - - POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( - conn->handshake_params.client_cert_sig_scheme, converted_scheme)); - - return S2N_SUCCESS; -} - -int s2n_connection_get_signature_scheme(struct s2n_connection *conn, const char **scheme_name) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(scheme_name); - POSIX_ENSURE(IS_NEGOTIATED(conn), S2N_ERR_INVALID_STATE); - - const struct s2n_signature_scheme *scheme = conn->handshake_params.server_cert_sig_scheme; - /* The scheme should never be NULL. A "none" placeholder is used if no - * scheme has been negotiated. - */ - POSIX_ENSURE_REF(scheme); - - *scheme_name = scheme->name; - if (scheme->signature_curve) { - /* Some TLS1.2 and TLS1.3 signature schemes share an IANA value, - * but are NOT the same. The TLS1.3 version implies a specific curve. - */ - if (conn->actual_protocol_version >= S2N_TLS13) { - *scheme_name = scheme->tls13_name; - } else { - *scheme_name = scheme->legacy_name; - } - } - - POSIX_ENSURE_REF(*scheme_name); - return S2N_SUCCESS; -} - -/* - * Gets the config set on the connection. - */ -int s2n_connection_get_config(struct s2n_connection *conn, struct s2n_config **config) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(config); - - if (s2n_fetch_default_config() == conn->config) { - POSIX_BAIL(S2N_ERR_NULL); - } - - *config = conn->config; - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_connection_dynamic_free_out_buffer(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* free the out buffer if we're in dynamic mode and it's completely flushed */ - if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->out)) { - /* since outgoing buffers are already encrypted, the buffers don't need to be zeroed, which saves some overhead */ - RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->out)); - - /* reset the stuffer to its initial state */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_dynamic_free_in_buffer(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* free `buffer_in` if we're in dynamic mode and it's completely flushed */ - if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->buffer_in)) { - /* when copying the buffer into the application, we use `s2n_stuffer_erase_and_read`, which already zeroes the memory */ - RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->buffer_in)); - - /* reset the stuffer to its initial state */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); - } - - return S2N_RESULT_OK; -} - -bool s2n_connection_check_io_status(struct s2n_connection *conn, s2n_io_status status) -{ - if (!conn) { - return false; - } - - bool read_closed = s2n_atomic_flag_test(&conn->read_closed); - bool write_closed = s2n_atomic_flag_test(&conn->write_closed); - bool full_duplex = !read_closed && !write_closed; - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-6.1 - *# Note that this is a change from versions of TLS prior to TLS 1.3 in - *# which implementations were required to react to a "close_notify" by - *# discarding pending writes and sending an immediate "close_notify" - *# alert of their own. - */ - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - switch (status) { - case S2N_IO_WRITABLE: - case S2N_IO_READABLE: - case S2N_IO_FULL_DUPLEX: - return full_duplex; - case S2N_IO_CLOSED: - return !full_duplex; - } - } - - switch (status) { - case S2N_IO_WRITABLE: - return !write_closed; - case S2N_IO_READABLE: - return !read_closed; - case S2N_IO_FULL_DUPLEX: - return full_duplex; - case S2N_IO_CLOSED: - return read_closed && write_closed; - } - - return false; -} - -S2N_RESULT s2n_connection_get_secure_cipher(struct s2n_connection *conn, const struct s2n_cipher **cipher) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(cipher); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); - *cipher = conn->secure->cipher_suite->record_alg->cipher; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_connection_get_sequence_number(struct s2n_connection *conn, - s2n_mode mode, struct s2n_blob *seq_num) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(seq_num); - RESULT_ENSURE_REF(conn->secure); - - switch (mode) { - case S2N_CLIENT: - RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->client_sequence_number, - sizeof(conn->secure->client_sequence_number))); - break; - case S2N_SERVER: - RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->server_sequence_number, - sizeof(conn->secure->server_sequence_number))); - break; - default: - RESULT_BAIL(S2N_ERR_SAFETY); - } - - return S2N_RESULT_OK; -} - -int s2n_connection_get_key_update_counts(struct s2n_connection *conn, - uint8_t *send_key_updates, uint8_t *recv_key_updates) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(send_key_updates); - POSIX_ENSURE_REF(recv_key_updates); - *send_key_updates = conn->send_key_updated; - *recv_key_updates = conn->recv_key_updated; - return S2N_SUCCESS; -} - -int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled) -{ - POSIX_ENSURE_REF(conn); - /* QUIC support is not currently compatible with recv_buffering */ - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_INVALID_STATE); - conn->recv_buffering = enabled; - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_connection.h" + +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +/* Required for s2n_connection_get_key_update_counts */ +#include "api/unstable/ktls.h" +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_crypto.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_openssl_x509.h" +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_client_server_name.h" +#include "tls/extensions/s2n_client_supported_versions.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_atomic.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_compiler.h" +#include "utils/s2n_io.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" +#include "utils/s2n_timer.h" + +#define S2N_SET_KEY_SHARE_LIST_EMPTY(keyshares) (keyshares |= 1) +#define S2N_SET_KEY_SHARE_REQUEST(keyshares, i) (keyshares |= (1 << (i + 1))) + +static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, + const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type); + +/* Allocates and initializes memory for a new connection. + * + * Since customers can reuse a connection, ensure that values on the connection are + * initialized in `s2n_connection_wipe` where possible. */ +struct s2n_connection *s2n_connection_new(s2n_mode mode) +{ + struct s2n_blob blob = { 0 }; + PTR_GUARD_POSIX(s2n_alloc(&blob, sizeof(struct s2n_connection))); + PTR_GUARD_POSIX(s2n_blob_zero(&blob)); + + /* Cast 'through' void to acknowledge that we are changing alignment, + * which is ok, as blob.data is always aligned. + */ + struct s2n_connection *conn = (struct s2n_connection *) (void *) blob.data; + + PTR_GUARD_POSIX(s2n_connection_set_config(conn, s2n_fetch_default_config())); + + /* `mode` is initialized here since it's passed in as a parameter. */ + conn->mode = mode; + + /* Allocate the fixed-size stuffers */ + blob = (struct s2n_blob){ 0 }; + PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->alert_in_data, S2N_ALERT_LENGTH)); + PTR_GUARD_POSIX(s2n_stuffer_init(&conn->alert_in, &blob)); + + blob = (struct s2n_blob){ 0 }; + PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->ticket_ext_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + PTR_GUARD_POSIX(s2n_stuffer_init(&conn->client_ticket_to_decrypt, &blob)); + + /* Allocate long term hash and HMAC memory */ + PTR_GUARD_RESULT(s2n_prf_new(conn)); + PTR_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); + + /* Initialize the growable stuffers. Zero length at first, but the resize + * in _wipe will fix that + */ + blob = (struct s2n_blob){ 0 }; + PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->header_in_data, S2N_TLS_RECORD_HEADER_LENGTH)); + PTR_GUARD_POSIX(s2n_stuffer_init(&conn->header_in, &blob)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->handshake.io, 0)); + PTR_GUARD_RESULT(s2n_timer_start(conn->config, &conn->write_timer)); + + /* NOTE: s2n_connection_wipe MUST be called last in this function. + * + * s2n_connection_wipe is used for initializing values but also used by customers to + * reset/reuse the connection. Calling it last ensures that s2n_connection_wipe is + * implemented correctly and safe. + */ + PTR_GUARD_POSIX(s2n_connection_wipe(conn)); + return conn; +} + +static int s2n_connection_zero(struct s2n_connection *conn, int mode, struct s2n_config *config) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(config); + + /* Zero the whole connection structure */ + POSIX_CHECKED_MEMSET(conn, 0, sizeof(struct s2n_connection)); + + conn->mode = mode; + conn->max_outgoing_fragment_length = S2N_DEFAULT_FRAGMENT_LENGTH; + conn->handshake.end_of_messages = APPLICATION_DATA; + s2n_connection_set_config(conn, config); + + return 0; +} + +S2N_RESULT s2n_connection_wipe_all_keyshares(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + RESULT_GUARD_POSIX(s2n_ecc_evp_params_free(&conn->kex_params.client_ecc_evp_params)); + + RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.server_kem_group_params)); + RESULT_GUARD_POSIX(s2n_kem_group_free(&conn->kex_params.client_kem_group_params)); + + return S2N_RESULT_OK; +} + +static int s2n_connection_wipe_keys(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* Free any server key received (we may not have completed a + * handshake, so this may not have been free'd yet) */ + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.server_public_key)); + POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.server_public_key)); + POSIX_GUARD(s2n_pkey_free(&conn->handshake_params.client_public_key)); + POSIX_GUARD(s2n_pkey_zero_init(&conn->handshake_params.client_public_key)); + s2n_x509_validator_wipe(&conn->x509_validator); + POSIX_GUARD(s2n_dh_params_free(&conn->kex_params.server_dh_params)); + POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); + POSIX_GUARD(s2n_kem_free(&conn->kex_params.kem_params)); + POSIX_GUARD(s2n_free(&conn->handshake_params.client_cert_chain)); + POSIX_GUARD(s2n_free(&conn->ct_response)); + + return 0; +} + +static int s2n_connection_free_managed_recv_io(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->managed_recv_io) { + POSIX_GUARD(s2n_free_object((uint8_t **) &conn->recv_io_context, sizeof(struct s2n_socket_read_io_context))); + conn->managed_recv_io = false; + conn->recv = NULL; + } + return S2N_SUCCESS; +} + +static int s2n_connection_free_managed_send_io(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->managed_send_io) { + POSIX_GUARD(s2n_free_object((uint8_t **) &conn->send_io_context, sizeof(struct s2n_socket_write_io_context))); + conn->managed_send_io = false; + conn->send = NULL; + } + return S2N_SUCCESS; +} + +static int s2n_connection_free_managed_io(struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); + POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); + return S2N_SUCCESS; +} + +static int s2n_connection_wipe_io(struct s2n_connection *conn) +{ + if (s2n_connection_is_managed_corked(conn) && conn->recv) { + POSIX_GUARD(s2n_socket_read_restore(conn)); + } + if (s2n_connection_is_managed_corked(conn) && conn->send) { + POSIX_GUARD(s2n_socket_write_restore(conn)); + } + + /* Remove all I/O-related members */ + POSIX_GUARD(s2n_connection_free_managed_io(conn)); + + return 0; +} + +static uint8_t s2n_default_verify_host(const char *host_name, size_t len, void *data) +{ + /* if present, match server_name of the connection using rules + * outlined in RFC6125 6.4. */ + + struct s2n_connection *conn = data; + + if (conn->server_name[0] == '\0') { + return 0; + } + + /* complete match */ + if (strlen(conn->server_name) == len && strncasecmp(conn->server_name, host_name, len) == 0) { + return 1; + } + + /* match 1 level of wildcard */ + if (len > 2 && host_name[0] == '*' && host_name[1] == '.') { + const char *suffix = strchr(conn->server_name, '.'); + + if (suffix == NULL) { + return 0; + } + + if (strlen(suffix) == len - 1 && strncasecmp(suffix, host_name + 1, len - 1) == 0) { + return 1; + } + } + + return 0; +} + +S2N_CLEANUP_RESULT s2n_connection_ptr_free(struct s2n_connection **conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_GUARD_POSIX(s2n_connection_free(*conn)); + *conn = NULL; + return S2N_RESULT_OK; +} + +int s2n_connection_free(struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_connection_wipe_keys(conn)); + POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); + + POSIX_GUARD_RESULT(s2n_prf_free(conn)); + POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); + + POSIX_GUARD(s2n_connection_free_managed_io(conn)); + + POSIX_GUARD(s2n_free(&conn->client_ticket)); + POSIX_GUARD(s2n_free(&conn->status_response)); + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->server_early_data_context)); + POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); + POSIX_GUARD(s2n_stuffer_free(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_free(&conn->in)); + POSIX_GUARD(s2n_stuffer_free(&conn->out)); + POSIX_GUARD(s2n_stuffer_free(&conn->handshake.io)); + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + s2n_x509_validator_wipe(&conn->x509_validator); + POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); + POSIX_GUARD(s2n_client_hello_free_raw_message(&conn->client_hello)); + POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); + POSIX_GUARD(s2n_free(&conn->cookie)); + POSIX_GUARD(s2n_free(&conn->cert_authorities)); + POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); + POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->secure)); + POSIX_GUARD(s2n_free_object((uint8_t **) &conn, sizeof(struct s2n_connection))); + + return 0; +} + +int s2n_connection_set_config(struct s2n_connection *conn, struct s2n_config *config) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(config); + + if (conn->config == config) { + return 0; + } + + /* s2n_config invariant: any s2n_config is always in a state that respects the + * config->security_policy certificate preferences. Therefore we only need to + * validate certificates here if the connection is using a security policy override. + */ + const struct s2n_security_policy *security_policy_override = conn->security_policy_override; + if (security_policy_override) { + POSIX_GUARD_RESULT(s2n_config_validate_loaded_certificates(config, security_policy_override)); + } + + /* We only support one client certificate */ + if (s2n_config_get_num_default_certs(config) > 1 && conn->mode == S2N_CLIENT) { + POSIX_BAIL(S2N_ERR_TOO_MANY_CERTIFICATES); + } + + s2n_x509_validator_wipe(&conn->x509_validator); + + 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)); + if (!conn->verify_host_fn_overridden) { + if (config->verify_host_fn != NULL) { + conn->verify_host_fn = config->verify_host_fn; + conn->data_for_verify_host = config->data_for_verify_host; + } else { + conn->verify_host_fn = s2n_default_verify_host; + conn->data_for_verify_host = conn; + } + } + + if (config->max_verify_cert_chain_depth_set) { + POSIX_GUARD(s2n_x509_validator_set_max_chain_depth(&conn->x509_validator, config->max_verify_cert_chain_depth)); + } + } + conn->tickets_to_send = config->initial_tickets_to_send; + + if (conn->psk_params.psk_list.len == 0 && !conn->psk_mode_overridden) { + POSIX_GUARD(s2n_connection_set_psk_mode(conn, config->psk_mode)); + conn->psk_mode_overridden = false; + } + + /* If at least one certificate does not have a private key configured, + * the config must provide an async pkey callback. + * The handshake could still fail if the callback doesn't offload the + * signature, but this at least catches configuration mistakes. + */ + if (config->no_signing_key) { + POSIX_ENSURE(config->async_pkey_cb, S2N_ERR_NO_PRIVATE_KEY); + } + + if (config->quic_enabled) { + /* If QUIC is ever enabled for a connection via the config, + * we should enforce that it can never be disabled by + * changing the config. + * + * Enabling QUIC indicates that the connection is being used by + * a QUIC implementation, which never changes. Disabling QUIC + * partially through a connection could also potentially be + * dangerous, as QUIC handles encryption. + */ + POSIX_GUARD(s2n_connection_enable_quic(conn)); + } + + if (config->send_buffer_size_override) { + conn->multirecord_send = true; + } + + /* Historically, calling s2n_config_set_verification_ca_location enabled OCSP stapling + * regardless of the value set by an application calling s2n_config_set_status_request_type. + * We maintain this behavior for backwards compatibility. + * + * However, the s2n_config_set_verification_ca_location behavior predates client authentication + * support for OCSP stapling, so could only affect whether clients requested OCSP stapling. We + * therefore only have to maintain the legacy behavior for clients, not servers. + * + * Note: The Rust bindings do not maintain the legacy behavior. + */ + conn->request_ocsp_status = config->ocsp_status_requested_by_user; + if (config->ocsp_status_requested_by_s2n && conn->mode == S2N_CLIENT) { + conn->request_ocsp_status = true; + } + + conn->config = config; + return S2N_SUCCESS; +} + +int s2n_connection_server_name_extension_used(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(!(conn->handshake.client_hello_received), S2N_ERR_INVALID_STATE); + + conn->server_name_used = 1; + return S2N_SUCCESS; +} + +int s2n_connection_set_ctx(struct s2n_connection *conn, void *ctx) +{ + POSIX_ENSURE_REF(conn); + + conn->context = ctx; + return S2N_SUCCESS; +} + +void *s2n_connection_get_ctx(struct s2n_connection *conn) +{ + return conn->context; +} + +int s2n_connection_release_buffers(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_PRECONDITION(s2n_stuffer_validate(&conn->out)); + POSIX_PRECONDITION(s2n_stuffer_validate(&conn->in)); + + POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->out), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); + + POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + if (s2n_stuffer_is_consumed(&conn->buffer_in)) { + POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); + } + + POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->post_handshake.in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + + POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->out)); + POSIX_POSTCONDITION(s2n_stuffer_validate(&conn->in)); + return S2N_SUCCESS; +} + +int s2n_connection_free_handshake(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* We are done with the handshake */ + POSIX_GUARD_RESULT(s2n_handshake_hashes_free(&conn->handshake.hashes)); + POSIX_GUARD_RESULT(s2n_prf_free(conn)); + + /* All IO should use conn->secure after the handshake. + * However, if this method is called before the handshake completes, + * the connection may still be using conn->initial. + */ + if (conn->client != conn->initial && conn->server != conn->initial) { + POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial)); + } + + /* Wipe the buffers we are going to free */ + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); + + /* Truncate buffers to save memory, we are done with the handshake */ + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); + POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); + + /* We can free extension data we no longer need */ + POSIX_GUARD(s2n_free(&conn->client_ticket)); + POSIX_GUARD(s2n_free(&conn->status_response)); + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); + POSIX_GUARD(s2n_free(&conn->cookie)); + POSIX_GUARD(s2n_free(&conn->cert_authorities)); + + return 0; +} + +/* An idempotent operation which initializes values on the connection. + * + * Called in order to reuse a connection structure for a new connection. Should wipe + * any persistent memory, free any temporary memory, and set all fields back to their + * defaults. + */ +int s2n_connection_wipe(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* First make a copy of everything we'd like to save, which isn't very much. */ + int mode = conn->mode; + struct s2n_config *config = conn->config; + struct s2n_stuffer alert_in = { 0 }; + struct s2n_stuffer client_ticket_to_decrypt = { 0 }; + struct s2n_stuffer handshake_io = { 0 }; + struct s2n_stuffer header_in = { 0 }; + struct s2n_stuffer buffer_in = { 0 }; + struct s2n_stuffer out = { 0 }; + + /* Some required structures might have been freed to conserve memory between handshakes. + * Restore them. + */ + if (!conn->handshake.hashes) { + POSIX_GUARD_RESULT(s2n_handshake_hashes_new(&conn->handshake.hashes)); + } + POSIX_GUARD_RESULT(s2n_handshake_hashes_wipe(conn->handshake.hashes)); + struct s2n_handshake_hashes *handshake_hashes = conn->handshake.hashes; + if (!conn->prf_space) { + POSIX_GUARD_RESULT(s2n_prf_new(conn)); + } + POSIX_GUARD_RESULT(s2n_prf_wipe(conn)); + struct s2n_prf_working_space *prf_workspace = conn->prf_space; + if (!conn->initial) { + POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->initial)); + } else { + POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->initial)); + } + struct s2n_crypto_parameters *initial = conn->initial; + if (!conn->secure) { + POSIX_GUARD_RESULT(s2n_crypto_parameters_new(&conn->secure)); + } else { + POSIX_GUARD_RESULT(s2n_crypto_parameters_wipe(conn->secure)); + } + struct s2n_crypto_parameters *secure = conn->secure; + + /* Wipe all of the sensitive stuff */ + POSIX_GUARD(s2n_connection_wipe_keys(conn)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->client_ticket_to_decrypt)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->post_handshake.in)); + POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->header_in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); + + /* Free stuffers we plan to just recreate */ + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + POSIX_GUARD(s2n_stuffer_free(&conn->in)); + + POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); + POSIX_GUARD_RESULT(s2n_async_offload_op_wipe(&conn->async_offload_op)); + + /* Wipe the I/O-related info and restore the original socket if necessary */ + POSIX_GUARD(s2n_connection_wipe_io(conn)); + + POSIX_GUARD(s2n_free(&conn->client_ticket)); + POSIX_GUARD(s2n_free(&conn->status_response)); + POSIX_GUARD(s2n_free(&conn->application_protocols_overridden)); + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); + POSIX_GUARD(s2n_free(&conn->server_early_data_context)); + POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); + POSIX_GUARD(s2n_free(&conn->cookie)); + POSIX_GUARD(s2n_free(&conn->cert_authorities)); + + /* Allocate memory for handling handshakes */ + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, S2N_LARGE_RECORD_LENGTH)); + + /* Truncate the message buffers to save memory, we will dynamically resize it as needed */ + POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); + POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); + POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); + + /* Remove context associated with connection */ + conn->context = NULL; + conn->verify_host_fn_overridden = 0; + conn->verify_host_fn = NULL; + conn->data_for_verify_host = NULL; + + /* Clone the stuffers */ + /* ignore address warnings because dest is allocated on the stack */ +#ifdef S2N_DIAGNOSTICS_PUSH_SUPPORTED + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Waddress" +#endif + POSIX_CHECKED_MEMCPY(&alert_in, &conn->alert_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&client_ticket_to_decrypt, &conn->client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&buffer_in, &conn->buffer_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&out, &conn->out, sizeof(struct s2n_stuffer)); +#ifdef S2N_DIAGNOSTICS_POP_SUPPORTED + #pragma GCC diagnostic pop +#endif + + POSIX_GUARD(s2n_connection_zero(conn, mode, config)); + + POSIX_CHECKED_MEMCPY(&conn->alert_in, &alert_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->client_ticket_to_decrypt, &client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->buffer_in, &buffer_in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->out, &out, sizeof(struct s2n_stuffer)); + + /* conn->in will eventually point to part of conn->buffer_in, but we initialize + * it as growable and allocated to support legacy tests. + */ + POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->in, 0)); + + conn->handshake.hashes = handshake_hashes; + conn->prf_space = prf_workspace; + conn->initial = initial; + conn->secure = secure; + conn->client = conn->initial; + conn->server = conn->initial; + conn->handshake_params.client_cert_sig_scheme = &s2n_null_sig_scheme; + conn->handshake_params.server_cert_sig_scheme = &s2n_null_sig_scheme; + + POSIX_GUARD_RESULT(s2n_psk_parameters_init(&conn->psk_params)); + conn->server_keying_material_lifetime = ONE_WEEK_IN_SEC; + + /* Require all handshakes hashes. This set can be reduced as the handshake progresses. */ + POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); + + if (conn->mode == S2N_SERVER) { + /* Start with the highest protocol version so that the highest common protocol version can be selected */ + /* during handshake. */ + conn->server_protocol_version = s2n_highest_protocol_version; + conn->client_protocol_version = s2n_unknown_protocol_version; + conn->actual_protocol_version = s2n_unknown_protocol_version; + } else { + /* For clients, also set actual_protocol_version. Record generation uses that value for the initial */ + /* ClientHello record version. Not all servers ignore the record version in ClientHello. */ + conn->server_protocol_version = s2n_unknown_protocol_version; + conn->client_protocol_version = s2n_highest_protocol_version; + conn->actual_protocol_version = s2n_highest_protocol_version; + } + + /* Initialize remaining values */ + conn->blinding = S2N_BUILT_IN_BLINDING; + conn->session_ticket_status = S2N_NO_TICKET; + + return 0; +} + +int s2n_connection_set_recv_ctx(struct s2n_connection *conn, void *ctx) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); + conn->recv_io_context = ctx; + return S2N_SUCCESS; +} + +int s2n_connection_set_send_ctx(struct s2n_connection *conn, void *ctx) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); + conn->send_io_context = ctx; + return S2N_SUCCESS; +} + +int s2n_connection_set_recv_cb(struct s2n_connection *conn, s2n_recv_fn recv) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_recv_io(conn)); + conn->recv = recv; + return S2N_SUCCESS; +} + +int s2n_connection_set_send_cb(struct s2n_connection *conn, s2n_send_fn send) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_connection_free_managed_send_io(conn)); + conn->send = send; + return S2N_SUCCESS; +} + +int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t **cert_chain_out, uint32_t *cert_chain_len) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(cert_chain_out); + POSIX_ENSURE_REF(cert_chain_len); + POSIX_ENSURE_REF(conn->handshake_params.client_cert_chain.data); + + *cert_chain_out = conn->handshake_params.client_cert_chain.data; + *cert_chain_len = conn->handshake_params.client_cert_chain.size; + + return S2N_SUCCESS; +} + +int s2n_connection_get_cipher_preferences(struct s2n_connection *conn, const struct s2n_cipher_preferences **cipher_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(cipher_preferences); + + if (conn->security_policy_override != NULL) { + *cipher_preferences = conn->security_policy_override->cipher_preferences; + } else if (conn->config->security_policy != NULL) { + *cipher_preferences = conn->config->security_policy->cipher_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_CIPHER_PREFERENCES); + } + + POSIX_ENSURE_REF(*cipher_preferences); + return 0; +} + +int s2n_connection_get_certificate_match(struct s2n_connection *conn, s2n_cert_sni_match *match_status) +{ + POSIX_ENSURE(conn, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(match_status, S2N_ERR_INVALID_ARGUMENT); + POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); + + /* Server must have gotten past certificate selection */ + POSIX_ENSURE(conn->handshake_params.our_chain_and_key, S2N_ERR_NO_CERT_FOUND); + + if (!s2n_server_received_server_name(conn)) { + *match_status = S2N_SNI_NONE; + } else if (conn->handshake_params.exact_sni_match_exists) { + *match_status = S2N_SNI_EXACT_MATCH; + } else if (conn->handshake_params.wc_sni_match_exists) { + *match_status = S2N_SNI_WILDCARD_MATCH; + } else { + *match_status = S2N_SNI_NO_MATCH; + } + + return S2N_SUCCESS; +} + +int s2n_connection_get_security_policy(struct s2n_connection *conn, const struct s2n_security_policy **security_policy) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(security_policy); + + if (conn->security_policy_override != NULL) { + *security_policy = conn->security_policy_override; + } else if (conn->config->security_policy != NULL) { + *security_policy = conn->config->security_policy; + } else { + POSIX_BAIL(S2N_ERR_INVALID_SECURITY_POLICY); + } + + POSIX_ENSURE_REF(*security_policy); + return 0; +} + +int s2n_connection_get_kem_preferences(struct s2n_connection *conn, const struct s2n_kem_preferences **kem_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(kem_preferences); + + if (conn->security_policy_override != NULL) { + *kem_preferences = conn->security_policy_override->kem_preferences; + } else if (conn->config->security_policy != NULL) { + *kem_preferences = conn->config->security_policy->kem_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_KEM_PREFERENCES); + } + + POSIX_ENSURE_REF(*kem_preferences); + return 0; +} + +int s2n_connection_get_signature_preferences(struct s2n_connection *conn, const struct s2n_signature_preferences **signature_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(signature_preferences); + + if (conn->security_policy_override != NULL) { + *signature_preferences = conn->security_policy_override->signature_preferences; + } else if (conn->config->security_policy != NULL) { + *signature_preferences = conn->config->security_policy->signature_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES); + } + + POSIX_ENSURE_REF(*signature_preferences); + return 0; +} + +int s2n_connection_get_ecc_preferences(struct s2n_connection *conn, const struct s2n_ecc_preferences **ecc_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(ecc_preferences); + + if (conn->security_policy_override != NULL) { + *ecc_preferences = conn->security_policy_override->ecc_preferences; + } else if (conn->config->security_policy != NULL) { + *ecc_preferences = conn->config->security_policy->ecc_preferences; + } else { + POSIX_BAIL(S2N_ERR_INVALID_ECC_PREFERENCES); + } + + POSIX_ENSURE_REF(*ecc_preferences); + return 0; +} + +int s2n_connection_get_protocol_preferences(struct s2n_connection *conn, struct s2n_blob **protocol_preferences) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(protocol_preferences); + + *protocol_preferences = NULL; + if (conn->application_protocols_overridden.size > 0) { + *protocol_preferences = &conn->application_protocols_overridden; + } else { + POSIX_ENSURE_REF(conn->config); + *protocol_preferences = &conn->config->application_protocols; + } + + POSIX_ENSURE_REF(*protocol_preferences); + return 0; +} + +static S2N_RESULT s2n_connection_and_config_get_client_auth_type(const struct s2n_connection *conn, + const struct s2n_config *config, s2n_cert_auth_type *client_cert_auth_type) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(config); + RESULT_ENSURE_REF(client_cert_auth_type); + + if (conn->client_cert_auth_type_overridden) { + *client_cert_auth_type = conn->client_cert_auth_type; + } else if (config->client_cert_auth_type_overridden) { + *client_cert_auth_type = config->client_cert_auth_type; + } else if (conn->mode == S2N_CLIENT) { + /* Clients should default to "Optional" so that they handle any + * CertificateRequests sent by the server. + */ + *client_cert_auth_type = S2N_CERT_AUTH_OPTIONAL; + } else { + /* Servers should default to "None" so that they send no CertificateRequests. */ + *client_cert_auth_type = S2N_CERT_AUTH_NONE; + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_client_auth_type(struct s2n_connection *conn, + s2n_cert_auth_type *client_cert_auth_type) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_and_config_get_client_auth_type( + conn, conn->config, client_cert_auth_type)); + return S2N_SUCCESS; +} + +int s2n_connection_set_client_auth_type(struct s2n_connection *conn, s2n_cert_auth_type client_cert_auth_type) +{ + POSIX_ENSURE_REF(conn); + + conn->client_cert_auth_type_overridden = 1; + conn->client_cert_auth_type = client_cert_auth_type; + return 0; +} + +int s2n_connection_set_read_fd(struct s2n_connection *conn, int rfd) +{ + struct s2n_blob ctx_mem = { 0 }; + struct s2n_socket_read_io_context *peer_socket_ctx = NULL; + + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_read_io_context))); + POSIX_GUARD(s2n_blob_zero(&ctx_mem)); + + peer_socket_ctx = (struct s2n_socket_read_io_context *) (void *) ctx_mem.data; + peer_socket_ctx->fd = rfd; + + POSIX_GUARD(s2n_connection_set_recv_cb(conn, s2n_socket_read)); + POSIX_GUARD(s2n_connection_set_recv_ctx(conn, peer_socket_ctx)); + conn->managed_recv_io = true; + + /* This is only needed if the user is using corked io. + * Take the snapshot in case optimized io is enabled after setting the fd. + */ + POSIX_GUARD(s2n_socket_read_snapshot(conn)); + + return 0; +} + +int s2n_connection_get_read_fd(struct s2n_connection *conn, int *readfd) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(readfd); + POSIX_ENSURE((conn->managed_recv_io && conn->recv_io_context), S2N_ERR_INVALID_STATE); + + const struct s2n_socket_read_io_context *peer_socket_ctx = conn->recv_io_context; + *readfd = peer_socket_ctx->fd; + return S2N_SUCCESS; +} + +int s2n_connection_set_write_fd(struct s2n_connection *conn, int wfd) +{ + struct s2n_blob ctx_mem = { 0 }; + struct s2n_socket_write_io_context *peer_socket_ctx = NULL; + + POSIX_ENSURE_REF(conn); + POSIX_GUARD(s2n_alloc(&ctx_mem, sizeof(struct s2n_socket_write_io_context))); + + peer_socket_ctx = (struct s2n_socket_write_io_context *) (void *) ctx_mem.data; + peer_socket_ctx->fd = wfd; + + POSIX_GUARD(s2n_connection_set_send_cb(conn, s2n_socket_write)); + POSIX_GUARD(s2n_connection_set_send_ctx(conn, peer_socket_ctx)); + conn->managed_send_io = true; + + /* This is only needed if the user is using corked io. + * Take the snapshot in case optimized io is enabled after setting the fd. + */ + POSIX_GUARD(s2n_socket_write_snapshot(conn)); + + uint8_t ipv6 = 0; + if (0 == s2n_socket_is_ipv6(wfd, &ipv6)) { + conn->ipv6 = (ipv6 ? 1 : 0); + } + + conn->write_fd_broken = 0; + + return 0; +} + +int s2n_connection_get_write_fd(struct s2n_connection *conn, int *writefd) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(writefd); + POSIX_ENSURE((conn->managed_send_io && conn->send_io_context), S2N_ERR_INVALID_STATE); + + const struct s2n_socket_write_io_context *peer_socket_ctx = conn->send_io_context; + *writefd = peer_socket_ctx->fd; + return S2N_SUCCESS; +} +int s2n_connection_set_fd(struct s2n_connection *conn, int fd) +{ + POSIX_GUARD(s2n_connection_set_read_fd(conn, fd)); + POSIX_GUARD(s2n_connection_set_write_fd(conn, fd)); + return 0; +} + +int s2n_connection_use_corked_io(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* Caller shouldn't be trying to set s2n IO corked on non-s2n-managed IO */ + POSIX_ENSURE(conn->managed_send_io, S2N_ERR_CORK_SET_ON_UNMANAGED); + conn->corked_io = 1; + + return 0; +} + +uint64_t s2n_connection_get_wire_bytes_in(struct s2n_connection *conn) +{ + if (conn->ktls_recv_enabled) { + return 0; + } + return conn->wire_bytes_in; +} + +uint64_t s2n_connection_get_wire_bytes_out(struct s2n_connection *conn) +{ + if (conn->ktls_send_enabled) { + return 0; + } + return conn->wire_bytes_out; +} + +const char *s2n_connection_get_cipher(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_ENSURE_REF(conn->secure); + PTR_ENSURE_REF(conn->secure->cipher_suite); + + return conn->secure->cipher_suite->name; +} + +int s2n_connection_get_cipher_iana_value(struct s2n_connection *conn, uint8_t *first, uint8_t *second) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_MUT(first); + POSIX_ENSURE_MUT(second); + + /* ensure we've negotiated a cipher suite */ + POSIX_ENSURE(!s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, + s2n_null_cipher_suite.iana_value, sizeof(s2n_null_cipher_suite.iana_value)), + S2N_ERR_INVALID_STATE); + + const uint8_t *iana_value = conn->secure->cipher_suite->iana_value; + *first = iana_value[0]; + *second = iana_value[1]; + + return S2N_SUCCESS; +} + +const char *s2n_connection_get_curve(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_ENSURE_REF(conn->secure); + PTR_ENSURE_REF(conn->secure->cipher_suite); + + if (conn->kex_params.server_ecc_evp_params.negotiated_curve) { + /* TLS1.3 currently only uses ECC groups. */ + bool tls13 = conn->actual_protocol_version >= S2N_TLS13; + /* we check for a full handshake, because TLS 1.2 resumption does not perform + * an additional diffie-hellman exchange */ + bool ecdhe_cipher_negotiated = s2n_kex_includes(conn->secure->cipher_suite->key_exchange_alg, &s2n_ecdhe) + && IS_FULL_HANDSHAKE(conn); + if (tls13 || ecdhe_cipher_negotiated) { + return conn->kex_params.server_ecc_evp_params.negotiated_curve->name; + } + } + + return "NONE"; +} + +const char *s2n_connection_get_kem_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (!conn->kex_params.kem_params.kem) { + return "NONE"; + } + + return conn->kex_params.kem_params.kem->name; +} + +const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (conn->actual_protocol_version < S2N_TLS13 || !conn->kex_params.server_kem_group_params.kem_group) { + return "NONE"; + } + + return conn->kex_params.server_kem_group_params.kem_group->name; +} + +int s2n_connection_get_key_exchange_group(struct s2n_connection *conn, const char **group_name) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(group_name); + + /* s2n_connection_get_curve returns only the ECDH curve portion of a named group, even if + the negotiated group was a hybrid PQ key exchange also containing a KEM. Therefore, + we use the result of s2n_connection_get_kem_group_name if the connection supports PQ. */ + if (s2n_tls13_pq_hybrid_supported(conn)) { + *group_name = s2n_connection_get_kem_group_name(conn); + } else { + *group_name = s2n_connection_get_curve(conn); + } + + POSIX_ENSURE(*group_name != NULL && strcmp(*group_name, "NONE"), S2N_ERR_INVALID_STATE); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_connection_get_client_supported_version(struct s2n_connection *conn, + uint8_t *client_supported_version) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_EQ(conn->mode, S2N_SERVER); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + RESULT_ENSURE_REF(client_hello); + + s2n_parsed_extension *supported_versions_extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_VERSIONS, &client_hello->extensions, + &supported_versions_extension)); + RESULT_ENSURE_REF(supported_versions_extension); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions_stuffer, &supported_versions_extension->extension)); + + uint8_t client_protocol_version = s2n_unknown_protocol_version; + uint8_t actual_protocol_version = s2n_unknown_protocol_version; + RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, &supported_versions_stuffer, + &client_protocol_version, &actual_protocol_version)); + + RESULT_ENSURE_NE(client_protocol_version, s2n_unknown_protocol_version); + + *client_supported_version = client_protocol_version; + + return S2N_RESULT_OK; +} + +int s2n_connection_get_client_protocol_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* For backwards compatibility, the client_protocol_version field isn't updated via the + * supported versions extension on TLS 1.2 servers. See + * https://github.com/aws/s2n-tls/issues/4240. + * + * The extension is processed here to ensure that TLS 1.2 servers report the same client + * protocol version to applications as TLS 1.3 servers. + */ + if (conn->mode == S2N_SERVER && conn->server_protocol_version <= S2N_TLS12) { + uint8_t client_supported_version = s2n_unknown_protocol_version; + s2n_result result = s2n_connection_get_client_supported_version(conn, &client_supported_version); + + /* If the extension wasn't received, or if a client protocol version couldn't be determined + * after processing the extension, the extension is ignored. + */ + if (s2n_result_is_ok(result)) { + return client_supported_version; + } + } + + return conn->client_protocol_version; +} + +int s2n_connection_get_server_protocol_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + return conn->server_protocol_version; +} + +int s2n_connection_get_actual_protocol_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + return conn->actual_protocol_version; +} + +int s2n_connection_get_client_hello_version(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->client_hello.sslv2) { + return S2N_SSLv2; + } else { + return S2N_MIN(conn->client_hello.legacy_version, S2N_TLS12); + } +} + +int s2n_connection_client_cert_used(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (IS_CLIENT_AUTH_HANDSHAKE(conn) && is_handshake_complete(conn)) { + if (IS_CLIENT_AUTH_NO_CERT(conn)) { + return 0; + } + return 1; + } + return 0; +} + +int s2n_connection_get_alert(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) != 2, S2N_ERR_NO_ALERT); + + /* Shallow copy the stuffer. We assume that multiple threads might call this + * function concurrently, so we must not mutate anything outside of the function scope */ + struct s2n_stuffer alert_stuffer = conn->alert_in; + uint8_t alert_code = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); + POSIX_GUARD(s2n_stuffer_read_uint8(&alert_stuffer, &alert_code)); + + return alert_code; +} + +int s2n_set_server_name(struct s2n_connection *conn, const char *server_name) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(server_name); + + S2N_ERROR_IF(conn->mode != S2N_CLIENT, S2N_ERR_CLIENT_MODE); + + int len = strlen(server_name); + S2N_ERROR_IF(len > S2N_MAX_SERVER_NAME, S2N_ERR_SERVER_NAME_TOO_LONG); + + POSIX_CHECKED_MEMCPY(conn->server_name, server_name, len); + + return 0; +} + +const char *s2n_get_server_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (conn->server_name[0]) { + return conn->server_name; + } + + PTR_GUARD_POSIX(s2n_extension_process(&s2n_client_server_name_extension, conn, &conn->client_hello.extensions)); + + if (!conn->server_name[0]) { + return NULL; + } + + return conn->server_name; +} + +const char *s2n_get_application_protocol(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + + if (strlen(conn->application_protocol) == 0) { + return NULL; + } + + return conn->application_protocol; +} + +int s2n_connection_get_session_id_length(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + /* Stateful session resumption in TLS1.3 using session id is not yet supported. */ + if (conn->actual_protocol_version >= S2N_TLS13) { + return 0; + } + return conn->session_id_len; +} + +int s2n_connection_get_session_id(struct s2n_connection *conn, uint8_t *session_id, size_t max_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(session_id); + + const int session_id_len = s2n_connection_get_session_id_length(conn); + POSIX_GUARD(session_id_len); + + POSIX_ENSURE((size_t) session_id_len <= max_length, S2N_ERR_SESSION_ID_TOO_LONG); + + POSIX_CHECKED_MEMCPY(session_id, conn->session_id, session_id_len); + + return session_id_len; +} + +int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blinding) +{ + POSIX_ENSURE_REF(conn); + conn->blinding = blinding; + + return 0; +} + +#define ONE_S INT64_C(1000000000) + +static S2N_RESULT s2n_connection_get_delay_impl(struct s2n_connection *conn, uint64_t *delay) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(delay); + + if (!conn->delay) { + *delay = 0; + return S2N_RESULT_OK; + } + + uint64_t elapsed = 0; + RESULT_GUARD(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); + + if (elapsed > conn->delay) { + *delay = 0; + return S2N_RESULT_OK; + } + + *delay = conn->delay - elapsed; + + return S2N_RESULT_OK; +} + +uint64_t s2n_connection_get_delay(struct s2n_connection *conn) +{ + uint64_t delay = 0; + if (s2n_result_is_ok(s2n_connection_get_delay_impl(conn, &delay))) { + return delay; + } else { + return UINT64_MAX; + } +} + +/* s2n-tls has a random delay that will trigger for sensitive errors. This is a mitigation + * for possible timing sidechannels. + * + * The historical sidechannel that inspired s2n-tls blinding was the Lucky 13 attack, which takes + * advantage of potential timing differences when removing padding from a record encrypted in CBC mode. + * The attack is only theoretical in TLS; the attack criteria is unlikely to ever occur + * (See: Fardan, N. J. A., & Paterson, K. G. (2013, May 1). Lucky Thirteen: Breaking the TLS and + * DTLS Record Protocols.) However, we still include blinding to provide a defense in depth mitigation. + */ +S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(min); + RESULT_ENSURE_REF(max); + RESULT_ENSURE_REF(conn->config); + + /* + * The default delay is a random value between 10-30s. The rationale behind the range is that the + * floor is the fixed cost that an attacker must pay per attempt, in this case, 10s. The length of + * the range then affects the number of attempts that an attacker must perform in order to recover a + * byte of plaintext with a certain degree of confidence. + * + * A uniform distribution of the range [a, b] has a variance of ((b - a)^2)/12. Therefore, given a + * hypothetical timing difference of 1us, the number of attempts necessary to distinguish the correct + * byte from an incorrect byte in a Lucky13-style attack is (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion + * (note that we first have to convert from seconds to microseconds to match the unit of the timing difference.) + */ + *min = S2N_DEFAULT_BLINDING_MIN * ONE_S; + *max = S2N_DEFAULT_BLINDING_MAX * ONE_S; + + /* Setting the min to 1/3 of the max is an arbitrary ratio of fixed to variable delay. + * It is based on the ratio of our original default values. + */ + if (conn->config->custom_blinding_set) { + *max = conn->config->max_blinding * ONE_S; + *min = *max / 3; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_GUARD(s2n_connection_set_closed(conn)); + + 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; + RESULT_GUARD(s2n_public_random(max - min, &rand_delay)); + + conn->delay = min + rand_delay; + + /* Restart the write timer */ + RESULT_GUARD(s2n_timer_start(conn->config, &conn->write_timer)); + + if (conn->blinding == S2N_BUILT_IN_BLINDING) { + struct timespec sleep_time = { .tv_sec = conn->delay / ONE_S, .tv_nsec = conn->delay % ONE_S }; + + int r = 0; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + } + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection **conn) +{ + RESULT_ENSURE_REF(conn); + if (*conn == NULL) { + return S2N_RESULT_OK; + } + + int error_code = s2n_errno; + int error_type = s2n_error_get_type(error_code); + + switch (error_type) { + case S2N_ERR_T_OK: + /* Ignore no error */ + return S2N_RESULT_OK; + case S2N_ERR_T_BLOCKED: + /* All blocking errors are retriable and should trigger no further action. */ + return S2N_RESULT_OK; + default: + break; + } + + /* Ensure that conn->in doesn't contain any leftover invalid or unauthenticated data. */ + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&(*conn)->in)); + + switch (error_code) { + /* Don't invoke blinding on some of the common errors. + * + * Be careful adding new errors here. Disabling blinding for an + * error that can be triggered by secret / encrypted values can + * potentially lead to a side channel attack. + * + * We may want to someday add an explicit error type for these errors. + */ + case S2N_ERR_CLOSED: + case S2N_ERR_CANCELLED: + case S2N_ERR_CIPHER_NOT_SUPPORTED: + case S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED: + case S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK: + RESULT_GUARD(s2n_connection_set_closed(*conn)); + break; + default: + /* Apply blinding to all other errors */ + RESULT_GUARD(s2n_connection_kill(*conn)); + break; + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_set_closed(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + s2n_atomic_flag_set(&conn->read_closed); + s2n_atomic_flag_set(&conn->write_closed); + return S2N_RESULT_OK; +} + +const uint8_t *s2n_connection_get_ocsp_response(struct s2n_connection *conn, uint32_t *length) +{ + PTR_ENSURE_REF(conn); + PTR_ENSURE_REF(length); + + *length = conn->status_response.size; + return conn->status_response.data; +} + +S2N_RESULT s2n_connection_set_max_fragment_length(struct s2n_connection *conn, uint16_t max_frag_length) +{ + RESULT_ENSURE_REF(conn); + + if (conn->negotiated_mfl_code) { + /* Respect the upper limit agreed on with the peer */ + RESULT_ENSURE_LT(conn->negotiated_mfl_code, s2n_array_len(mfl_code_to_length)); + conn->max_outgoing_fragment_length = S2N_MIN(mfl_code_to_length[conn->negotiated_mfl_code], max_frag_length); + } else { + conn->max_outgoing_fragment_length = max_frag_length; + } + + /* If no buffer has been initialized yet, no need to resize. + * The standard I/O logic will handle initializing the buffer. + */ + if (s2n_stuffer_is_freed(&conn->out)) { + return S2N_RESULT_OK; + } + + uint16_t max_wire_record_size = 0; + RESULT_GUARD(s2n_record_max_write_size(conn, conn->max_outgoing_fragment_length, &max_wire_record_size)); + if ((conn->out.blob.size < max_wire_record_size)) { + RESULT_GUARD_POSIX(s2n_realloc(&conn->out.blob, max_wire_record_size)); + } + + return S2N_RESULT_OK; +} + +int s2n_connection_prefer_throughput(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_LARGE_FRAGMENT_LENGTH)); + return S2N_SUCCESS; +} + +int s2n_connection_prefer_low_latency(struct s2n_connection *conn) +{ + POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, S2N_SMALL_FRAGMENT_LENGTH)); + return S2N_SUCCESS; +} + +int s2n_connection_set_dynamic_buffers(struct s2n_connection *conn, bool enabled) +{ + POSIX_ENSURE_REF(conn); + conn->dynamic_buffers = enabled; + return S2N_SUCCESS; +} + +int s2n_connection_set_dynamic_record_threshold(struct s2n_connection *conn, uint32_t resize_threshold, uint16_t timeout_threshold) +{ + POSIX_ENSURE_REF(conn); + S2N_ERROR_IF(resize_threshold > S2N_TLS_MAX_RESIZE_THRESHOLD, S2N_ERR_INVALID_DYNAMIC_THRESHOLD); + + conn->dynamic_record_resize_threshold = resize_threshold; + conn->dynamic_record_timeout_threshold = timeout_threshold; + return 0; +} + +int s2n_connection_set_verify_host_callback(struct s2n_connection *conn, s2n_verify_host_fn verify_host_fn, void *data) +{ + POSIX_ENSURE_REF(conn); + + conn->verify_host_fn = verify_host_fn; + conn->data_for_verify_host = data; + conn->verify_host_fn_overridden = 1; + + return 0; +} + +int s2n_connection_recv_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) +{ + POSIX_ENSURE_REF(conn->recv); + /* Make sure we have enough space to write */ + POSIX_GUARD(s2n_stuffer_reserve_space(stuffer, len)); + + int r = 0; + S2N_IO_RETRY_EINTR(r, + conn->recv(conn->recv_io_context, stuffer->blob.data + stuffer->write_cursor, len)); + POSIX_ENSURE(r >= 0, S2N_ERR_RECV_STUFFER_FROM_CONN); + + /* Record just how many bytes we have written */ + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, r)); + return r; +} + +int s2n_connection_send_stuffer(struct s2n_stuffer *stuffer, struct s2n_connection *conn, uint32_t len) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->send); + if (conn->write_fd_broken) { + POSIX_BAIL(S2N_ERR_SEND_STUFFER_TO_CONN); + } + /* Make sure we even have the data */ + S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) < len, S2N_ERR_STUFFER_OUT_OF_DATA); + + int w = 0; + S2N_IO_RETRY_EINTR(w, + conn->send(conn->send_io_context, stuffer->blob.data + stuffer->read_cursor, len)); + if (w < 0 && errno == EPIPE) { + conn->write_fd_broken = 1; + } + POSIX_ENSURE(w >= 0, S2N_ERR_SEND_STUFFER_TO_CONN); + + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, w)); + return w; +} + +int s2n_connection_is_managed_corked(const struct s2n_connection *s2n_connection) +{ + POSIX_ENSURE_REF(s2n_connection); + + return (s2n_connection->managed_send_io && s2n_connection->corked_io); +} + +const uint8_t *s2n_connection_get_sct_list(struct s2n_connection *conn, uint32_t *length) +{ + if (!length) { + return NULL; + } + + *length = conn->ct_response.size; + return conn->ct_response.data; +} + +int s2n_connection_is_client_auth_enabled(struct s2n_connection *s2n_connection) +{ + s2n_cert_auth_type auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(s2n_connection, &auth_type)); + + return (auth_type != S2N_CERT_AUTH_NONE); +} + +struct s2n_cert_chain_and_key *s2n_connection_get_selected_cert(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + return conn->handshake_params.our_chain_and_key; +} + +uint8_t s2n_connection_get_protocol_version(const struct s2n_connection *conn) +{ + if (conn == NULL) { + return S2N_UNKNOWN_PROTOCOL_VERSION; + } + + if (conn->actual_protocol_version != S2N_UNKNOWN_PROTOCOL_VERSION) { + return conn->actual_protocol_version; + } + + if (conn->mode == S2N_CLIENT) { + return conn->client_protocol_version; + } + return conn->server_protocol_version; +} + +DEFINE_POINTER_CLEANUP_FUNC(struct s2n_cert_chain *, s2n_cert_chain_free); + +int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain_and_key) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(cert_chain_and_key); + POSIX_ENSURE_REF(cert_chain_and_key->cert_chain); + + /* Ensure that cert_chain_and_key is empty BEFORE we modify it in any way. + * That includes before tying its cert_chain to DEFER_CLEANUP. + */ + POSIX_ENSURE(cert_chain_and_key->cert_chain->head == NULL, S2N_ERR_INVALID_ARGUMENT); + + DEFER_CLEANUP(struct s2n_cert_chain *cert_chain = cert_chain_and_key->cert_chain, s2n_cert_chain_free_pointer); + struct s2n_cert **insert = &cert_chain->head; + + const struct s2n_x509_validator *validator = &conn->x509_validator; + POSIX_ENSURE_REF(validator); + POSIX_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_CERT_NOT_VALIDATED); + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + POSIX_GUARD_RESULT(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + STACK_OF(X509) *cert_chain_validated = validated_cert_chain.stack; + POSIX_ENSURE_REF(cert_chain_validated); + + int cert_count = sk_X509_num(cert_chain_validated); + POSIX_ENSURE_GTE(cert_count, 0); + + for (size_t cert_idx = 0; cert_idx < (size_t) cert_count; cert_idx++) { + X509 *cert = sk_X509_value(cert_chain_validated, cert_idx); + POSIX_ENSURE_REF(cert); + DEFER_CLEANUP(uint8_t *cert_data = NULL, s2n_crypto_free); + int cert_size = i2d_X509(cert, &cert_data); + POSIX_ENSURE_GT(cert_size, 0); + + struct s2n_blob mem = { 0 }; + POSIX_GUARD(s2n_alloc(&mem, sizeof(struct s2n_cert))); + + struct s2n_cert *new_node = (struct s2n_cert *) (void *) mem.data; + POSIX_ENSURE_REF(new_node); + + new_node->next = NULL; + *insert = new_node; + insert = &new_node->next; + + POSIX_GUARD(s2n_alloc(&new_node->raw, cert_size)); + POSIX_CHECKED_MEMCPY(new_node->raw.data, cert_data, cert_size); + } + + ZERO_TO_DISABLE_DEFER_CLEANUP(cert_chain); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_signature_scheme_to_tls_iana(const struct s2n_signature_scheme *sig_scheme, + s2n_tls_hash_algorithm *converted_scheme) +{ + RESULT_ENSURE_REF(sig_scheme); + RESULT_ENSURE_REF(converted_scheme); + *converted_scheme = S2N_TLS_HASH_NONE; + + switch (sig_scheme->hash_alg) { + case S2N_HASH_MD5: + *converted_scheme = S2N_TLS_HASH_MD5; + break; + case S2N_HASH_SHA1: + *converted_scheme = S2N_TLS_HASH_SHA1; + break; + case S2N_HASH_SHA224: + *converted_scheme = S2N_TLS_HASH_SHA224; + break; + case S2N_HASH_SHA256: + *converted_scheme = S2N_TLS_HASH_SHA256; + break; + case S2N_HASH_SHA384: + *converted_scheme = S2N_TLS_HASH_SHA384; + break; + case S2N_HASH_SHA512: + *converted_scheme = S2N_TLS_HASH_SHA512; + break; + case S2N_HASH_MD5_SHA1: + *converted_scheme = S2N_TLS_HASH_MD5_SHA1; + break; + case S2N_HASH_NONE: + case S2N_HASH_SHAKE256_64: + case S2N_HASH_ALGS_COUNT: + *converted_scheme = S2N_TLS_HASH_NONE; + break; + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_selected_digest_algorithm(struct s2n_connection *conn, + s2n_tls_hash_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( + conn->handshake_params.server_cert_sig_scheme, converted_scheme)); + + return S2N_SUCCESS; +} + +int s2n_connection_get_selected_client_cert_digest_algorithm(struct s2n_connection *conn, + s2n_tls_hash_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_tls_iana( + conn->handshake_params.client_cert_sig_scheme, converted_scheme)); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_signature_scheme_to_signature_algorithm(const struct s2n_signature_scheme *sig_scheme, + s2n_tls_signature_algorithm *converted_scheme) +{ + RESULT_ENSURE_REF(sig_scheme); + RESULT_ENSURE_REF(converted_scheme); + *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; + + switch (sig_scheme->sig_alg) { + case S2N_SIGNATURE_RSA: + *converted_scheme = S2N_TLS_SIGNATURE_RSA; + break; + case S2N_SIGNATURE_ECDSA: + *converted_scheme = S2N_TLS_SIGNATURE_ECDSA; + break; + case S2N_SIGNATURE_RSA_PSS_RSAE: + *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_RSAE; + break; + case S2N_SIGNATURE_RSA_PSS_PSS: + *converted_scheme = S2N_TLS_SIGNATURE_RSA_PSS_PSS; + break; + case S2N_SIGNATURE_MLDSA: + *converted_scheme = S2N_TLS_SIGNATURE_MLDSA; + break; + case S2N_SIGNATURE_ANONYMOUS: + *converted_scheme = S2N_TLS_SIGNATURE_ANONYMOUS; + break; + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_selected_signature_algorithm(struct s2n_connection *conn, + s2n_tls_signature_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( + conn->handshake_params.server_cert_sig_scheme, converted_scheme)); + + return S2N_SUCCESS; +} + +int s2n_connection_get_selected_client_cert_signature_algorithm(struct s2n_connection *conn, + s2n_tls_signature_algorithm *converted_scheme) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(converted_scheme); + + POSIX_GUARD_RESULT(s2n_signature_scheme_to_signature_algorithm( + conn->handshake_params.client_cert_sig_scheme, converted_scheme)); + + return S2N_SUCCESS; +} + +int s2n_connection_get_signature_scheme(struct s2n_connection *conn, const char **scheme_name) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(scheme_name); + POSIX_ENSURE(IS_NEGOTIATED(conn), S2N_ERR_INVALID_STATE); + + const struct s2n_signature_scheme *scheme = conn->handshake_params.server_cert_sig_scheme; + /* The scheme should never be NULL. A "none" placeholder is used if no + * scheme has been negotiated. + */ + POSIX_ENSURE_REF(scheme); + + *scheme_name = scheme->name; + if (scheme->signature_curve) { + /* Some TLS1.2 and TLS1.3 signature schemes share an IANA value, + * but are NOT the same. The TLS1.3 version implies a specific curve. + */ + if (conn->actual_protocol_version >= S2N_TLS13) { + *scheme_name = scheme->tls13_name; + } else { + *scheme_name = scheme->legacy_name; + } + } + + POSIX_ENSURE_REF(*scheme_name); + return S2N_SUCCESS; +} + +/* + * Gets the config set on the connection. + */ +int s2n_connection_get_config(struct s2n_connection *conn, struct s2n_config **config) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(config); + + if (s2n_fetch_default_config() == conn->config) { + POSIX_BAIL(S2N_ERR_NULL); + } + + *config = conn->config; + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_connection_dynamic_free_out_buffer(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* free the out buffer if we're in dynamic mode and it's completely flushed */ + if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->out)) { + /* since outgoing buffers are already encrypted, the buffers don't need to be zeroed, which saves some overhead */ + RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->out)); + + /* reset the stuffer to its initial state */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_dynamic_free_in_buffer(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* free `buffer_in` if we're in dynamic mode and it's completely flushed */ + if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->buffer_in)) { + /* when copying the buffer into the application, we use `s2n_stuffer_erase_and_read`, which already zeroes the memory */ + RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->buffer_in)); + + /* reset the stuffer to its initial state */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); + } + + return S2N_RESULT_OK; +} + +bool s2n_connection_check_io_status(struct s2n_connection *conn, s2n_io_status status) +{ + if (!conn) { + return false; + } + + bool read_closed = s2n_atomic_flag_test(&conn->read_closed); + bool write_closed = s2n_atomic_flag_test(&conn->write_closed); + bool full_duplex = !read_closed && !write_closed; + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-6.1 + *# Note that this is a change from versions of TLS prior to TLS 1.3 in + *# which implementations were required to react to a "close_notify" by + *# discarding pending writes and sending an immediate "close_notify" + *# alert of their own. + */ + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + switch (status) { + case S2N_IO_WRITABLE: + case S2N_IO_READABLE: + case S2N_IO_FULL_DUPLEX: + return full_duplex; + case S2N_IO_CLOSED: + return !full_duplex; + } + } + + switch (status) { + case S2N_IO_WRITABLE: + return !write_closed; + case S2N_IO_READABLE: + return !read_closed; + case S2N_IO_FULL_DUPLEX: + return full_duplex; + case S2N_IO_CLOSED: + return read_closed && write_closed; + } + + return false; +} + +S2N_RESULT s2n_connection_get_secure_cipher(struct s2n_connection *conn, const struct s2n_cipher **cipher) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(cipher); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + *cipher = conn->secure->cipher_suite->record_alg->cipher; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_connection_get_sequence_number(struct s2n_connection *conn, + s2n_mode mode, struct s2n_blob *seq_num) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(seq_num); + RESULT_ENSURE_REF(conn->secure); + + switch (mode) { + case S2N_CLIENT: + RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->client_sequence_number, + sizeof(conn->secure->client_sequence_number))); + break; + case S2N_SERVER: + RESULT_GUARD_POSIX(s2n_blob_init(seq_num, conn->secure->server_sequence_number, + sizeof(conn->secure->server_sequence_number))); + break; + default: + RESULT_BAIL(S2N_ERR_SAFETY); + } + + return S2N_RESULT_OK; +} + +int s2n_connection_get_key_update_counts(struct s2n_connection *conn, + uint8_t *send_key_updates, uint8_t *recv_key_updates) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(send_key_updates); + POSIX_ENSURE_REF(recv_key_updates); + *send_key_updates = conn->send_key_updated; + *recv_key_updates = conn->recv_key_updated; + return S2N_SUCCESS; +} + +int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled) +{ + POSIX_ENSURE_REF(conn); + /* QUIC support is not currently compatible with recv_buffering */ + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_INVALID_STATE); + conn->recv_buffering = enabled; + return S2N_SUCCESS; +} diff --git a/tls/s2n_early_data.c b/tls/s2n_early_data.c index 1d97943075f..934e993e333 100644 --- a/tls/s2n_early_data.c +++ b/tls/s2n_early_data.c @@ -1,436 +1,436 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_early_data.h" - -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_psk.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -const s2n_early_data_state valid_previous_states[] = { - [S2N_EARLY_DATA_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, - [S2N_EARLY_DATA_NOT_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, - [S2N_EARLY_DATA_REJECTED] = S2N_EARLY_DATA_REQUESTED, - [S2N_EARLY_DATA_ACCEPTED] = S2N_EARLY_DATA_REQUESTED, - [S2N_END_OF_EARLY_DATA] = S2N_EARLY_DATA_ACCEPTED, -}; - -S2N_RESULT s2n_connection_set_early_data_state(struct s2n_connection *conn, s2n_early_data_state next_state) -{ - RESULT_ENSURE_REF(conn); - if (conn->early_data_state == next_state) { - return S2N_RESULT_OK; - } - RESULT_ENSURE(next_state < S2N_EARLY_DATA_STATES_COUNT, S2N_ERR_INVALID_EARLY_DATA_STATE); - RESULT_ENSURE(next_state != S2N_UNKNOWN_EARLY_DATA_STATE, S2N_ERR_INVALID_EARLY_DATA_STATE); - RESULT_ENSURE(conn->early_data_state == valid_previous_states[next_state], S2N_ERR_INVALID_EARLY_DATA_STATE); - conn->early_data_state = next_state; - return S2N_RESULT_OK; -} - -int s2n_connection_set_early_data_expected(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - conn->early_data_expected = true; - return S2N_SUCCESS; -} - -int s2n_connection_set_end_of_early_data(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - conn->early_data_expected = false; - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_early_data_validate(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# In order to accept early data, the server MUST have accepted a PSK - *# cipher suite and selected the first key offered in the client's - *# "pre_shared_key" extension. - **/ - RESULT_ENSURE_REF(conn->psk_params.chosen_psk); - RESULT_ENSURE_EQ(conn->psk_params.chosen_psk_wire_index, 0); - - struct s2n_early_data_config *config = &conn->psk_params.chosen_psk->early_data_config; - RESULT_ENSURE_GT(config->max_early_data_size, 0); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# In addition, it MUST verify that the - *# following values are the same as those associated with the - *# selected PSK: - *# - *# - The TLS version number - **/ - RESULT_ENSURE_EQ(config->protocol_version, s2n_connection_get_protocol_version(conn)); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# - The selected cipher suite - **/ - RESULT_ENSURE_EQ(config->cipher_suite, conn->secure->cipher_suite); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# - The selected ALPN [RFC7301] protocol, if any - **/ - const size_t app_protocol_size = strlen(conn->application_protocol); - if (app_protocol_size > 0 || config->application_protocol.size > 0) { - RESULT_ENSURE_EQ(config->application_protocol.size, app_protocol_size + 1 /* null-terminating char */); - RESULT_ENSURE(s2n_constant_time_equals(config->application_protocol.data, (uint8_t *) conn->application_protocol, app_protocol_size), S2N_ERR_SAFETY); - } - - return S2N_RESULT_OK; -} - -bool s2n_early_data_is_valid_for_connection(struct s2n_connection *conn) -{ - return s2n_result_is_ok(s2n_early_data_validate(conn)); -} - -S2N_RESULT s2n_early_data_accept_or_reject(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (conn->early_data_state != S2N_EARLY_DATA_REQUESTED) { - return S2N_RESULT_OK; - } - - if (conn->handshake.early_data_async_state.conn) { - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# If any of these checks fail, the server MUST NOT respond with the - *# extension - **/ - if (!s2n_early_data_is_valid_for_connection(conn)) { - RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - return S2N_RESULT_OK; - } - - /* Even if the connection is valid for early data, the client can't consider - * early data accepted until the server sends the early data indication. */ - if (conn->mode == S2N_CLIENT) { - return S2N_RESULT_OK; - } - - /* The server should reject early data if the application is not prepared to handle it. */ - if (!conn->early_data_expected) { - RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - return S2N_RESULT_OK; - } - - /* If early data would otherwise be accepted, let the application apply any additional restrictions. - * For example, an application could use this callback to implement anti-replay protections. - * - * This callback can be either synchronous or asynchronous. The handshake will not proceed until - * the application either accepts or rejects early data. - */ - RESULT_ENSURE_REF(conn->config); - if (conn->config->early_data_cb) { - conn->handshake.early_data_async_state.conn = conn; - RESULT_ENSURE(conn->config->early_data_cb(conn, &conn->handshake.early_data_async_state) >= S2N_SUCCESS, - S2N_ERR_CANCELLED); - if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - } else { - RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); - } - return S2N_RESULT_OK; -} - -int s2n_config_set_server_max_early_data_size(struct s2n_config *config, uint32_t max_early_data_size) -{ - POSIX_ENSURE_REF(config); - config->server_max_early_data_size = max_early_data_size; - return S2N_SUCCESS; -} - -int s2n_connection_set_server_max_early_data_size(struct s2n_connection *conn, uint32_t max_early_data_size) -{ - POSIX_ENSURE_REF(conn); - conn->server_max_early_data_size = max_early_data_size; - conn->server_max_early_data_size_overridden = true; - return S2N_SUCCESS; -} - -S2N_RESULT s2n_early_data_get_server_max_size(struct s2n_connection *conn, uint32_t *max_early_data_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(max_early_data_size); - if (conn->server_max_early_data_size_overridden) { - *max_early_data_size = conn->server_max_early_data_size; - } else { - RESULT_ENSURE_REF(conn->config); - *max_early_data_size = conn->config->server_max_early_data_size; - } - return S2N_RESULT_OK; -} - -int s2n_connection_set_server_early_data_context(struct s2n_connection *conn, const uint8_t *context, uint16_t context_size) -{ - POSIX_ENSURE_REF(conn); - if (context_size > 0) { - POSIX_ENSURE_REF(context); - } - - POSIX_GUARD(s2n_realloc(&conn->server_early_data_context, context_size)); - POSIX_CHECKED_MEMCPY(conn->server_early_data_context.data, context, context_size); - return S2N_SUCCESS; -} - -S2N_CLEANUP_RESULT s2n_early_data_config_free(struct s2n_early_data_config *config) -{ - if (config == NULL) { - return S2N_RESULT_OK; - } - RESULT_GUARD_POSIX(s2n_free(&config->application_protocol)); - RESULT_GUARD_POSIX(s2n_free(&config->context)); - return S2N_RESULT_OK; -} - -int s2n_psk_configure_early_data(struct s2n_psk *psk, uint32_t max_early_data_size, - uint8_t cipher_suite_first_byte, uint8_t cipher_suite_second_byte) -{ - POSIX_ENSURE_REF(psk); - - const uint8_t cipher_suite_iana[] = { cipher_suite_first_byte, cipher_suite_second_byte }; - struct s2n_cipher_suite *cipher_suite = NULL; - POSIX_GUARD_RESULT(s2n_cipher_suite_from_iana(cipher_suite_iana, sizeof(cipher_suite_iana), &cipher_suite)); - POSIX_ENSURE_REF(cipher_suite); - POSIX_ENSURE(cipher_suite->prf_alg == psk->hmac_alg, S2N_ERR_INVALID_ARGUMENT); - - psk->early_data_config.max_early_data_size = max_early_data_size; - psk->early_data_config.protocol_version = S2N_TLS13; - psk->early_data_config.cipher_suite = cipher_suite; - return S2N_SUCCESS; -} - -int s2n_psk_set_application_protocol(struct s2n_psk *psk, const uint8_t *application_protocol, uint8_t size) -{ - POSIX_ENSURE_REF(psk); - if (size > 0) { - POSIX_ENSURE_REF(application_protocol); - } - struct s2n_blob *protocol_blob = &psk->early_data_config.application_protocol; - POSIX_GUARD(s2n_realloc(protocol_blob, size)); - POSIX_CHECKED_MEMCPY(protocol_blob->data, application_protocol, size); - return S2N_SUCCESS; -} - -int s2n_psk_set_early_data_context(struct s2n_psk *psk, const uint8_t *context, uint16_t size) -{ - POSIX_ENSURE_REF(psk); - if (size > 0) { - POSIX_ENSURE_REF(context); - } - struct s2n_blob *context_blob = &psk->early_data_config.context; - POSIX_GUARD(s2n_realloc(context_blob, size)); - POSIX_CHECKED_MEMCPY(context_blob->data, context, size); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_early_data_config_clone(struct s2n_psk *new_psk, struct s2n_early_data_config *old_config) -{ - RESULT_ENSURE_REF(old_config); - RESULT_ENSURE_REF(new_psk); - - struct s2n_early_data_config config_copy = new_psk->early_data_config; - - /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ - new_psk->early_data_config = *old_config; - new_psk->early_data_config.application_protocol = config_copy.application_protocol; - new_psk->early_data_config.context = config_copy.context; - - /* Clone / realloc blobs */ - RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(new_psk, old_config->application_protocol.data, - old_config->application_protocol.size)); - RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(new_psk, old_config->context.data, - old_config->context.size)); - - return S2N_RESULT_OK; -} - -int s2n_connection_get_early_data_status(struct s2n_connection *conn, s2n_early_data_status_t *status) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(status); - - switch (conn->early_data_state) { - case S2N_EARLY_DATA_STATES_COUNT: - break; - case S2N_EARLY_DATA_NOT_REQUESTED: - *status = S2N_EARLY_DATA_STATUS_NOT_REQUESTED; - return S2N_SUCCESS; - case S2N_EARLY_DATA_REJECTED: - *status = S2N_EARLY_DATA_STATUS_REJECTED; - return S2N_SUCCESS; - case S2N_END_OF_EARLY_DATA: - *status = S2N_EARLY_DATA_STATUS_END; - return S2N_SUCCESS; - case S2N_UNKNOWN_EARLY_DATA_STATE: - case S2N_EARLY_DATA_REQUESTED: - case S2N_EARLY_DATA_ACCEPTED: - *status = S2N_EARLY_DATA_STATUS_OK; - return S2N_SUCCESS; - } - POSIX_BAIL(S2N_ERR_INVALID_EARLY_DATA_STATE); -} - -static S2N_RESULT s2n_get_remaining_early_data_bytes(struct s2n_connection *conn, uint32_t *early_data_allowed) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(early_data_allowed); - *early_data_allowed = 0; - - uint32_t max_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); - - RESULT_ENSURE(max_early_data_size >= conn->early_data_bytes, S2N_ERR_MAX_EARLY_DATA_SIZE); - *early_data_allowed = (max_early_data_size - conn->early_data_bytes); - - return S2N_RESULT_OK; -} - -int s2n_connection_get_remaining_early_data_size(struct s2n_connection *conn, uint32_t *allowed_early_data_size) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(allowed_early_data_size); - *allowed_early_data_size = 0; - - switch (conn->early_data_state) { - case S2N_EARLY_DATA_STATES_COUNT: - case S2N_EARLY_DATA_NOT_REQUESTED: - case S2N_EARLY_DATA_REJECTED: - case S2N_END_OF_EARLY_DATA: - *allowed_early_data_size = 0; - break; - case S2N_UNKNOWN_EARLY_DATA_STATE: - case S2N_EARLY_DATA_REQUESTED: - case S2N_EARLY_DATA_ACCEPTED: - POSIX_GUARD_RESULT(s2n_get_remaining_early_data_bytes(conn, allowed_early_data_size)); - break; - } - return S2N_SUCCESS; -} - -int s2n_connection_get_max_early_data_size(struct s2n_connection *conn, uint32_t *max_early_data_size) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(max_early_data_size); - *max_early_data_size = 0; - - uint32_t server_max_early_data_size = 0; - POSIX_GUARD_RESULT(s2n_early_data_get_server_max_size(conn, &server_max_early_data_size)); - - if (conn->psk_params.psk_list.len == 0) { - /* This method may be called by the server before loading its PSKs. - * The server can load its PSKs during the handshake, either via the PSK selection callback - * or by receiving a stateless session ticket. - * - * Before that happens, we should make an optimistic assumption of the early data size. - * That way, the max early data size always decreases (for example, it won't go from 0 -> UINT32_MAX - * after receiving a PSK in the ClientHello). - */ - if (conn->mode == S2N_SERVER && !IS_NEGOTIATED(conn)) { - *max_early_data_size = server_max_early_data_size; - } - return S2N_SUCCESS; - } - - struct s2n_psk *first_psk = NULL; - POSIX_GUARD_RESULT(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &first_psk)); - POSIX_ENSURE_REF(first_psk); - *max_early_data_size = first_psk->early_data_config.max_early_data_size; - - /* For the server, we should use the minimum of the limit retrieved from the ticket - * and the current limit being set for new tickets. - * - * This is defensive: even if more early data was previously allowed, the server may not be - * willing or able to handle that much early data now. - * - * We don't do this for external PSKs because the server has intentionally set the limit - * while setting up this connection, not during a previous connection. - */ - if (conn->mode == S2N_SERVER && first_psk->type == S2N_PSK_TYPE_RESUMPTION) { - *max_early_data_size = S2N_MIN(*max_early_data_size, server_max_early_data_size); - } - - return S2N_SUCCESS; -} - -int s2n_config_set_early_data_cb(struct s2n_config *config, s2n_early_data_cb cb) -{ - POSIX_ENSURE_REF(config); - config->early_data_cb = cb; - return S2N_SUCCESS; -} - -int s2n_offered_early_data_get_context_length(struct s2n_offered_early_data *early_data, uint16_t *context_len) -{ - POSIX_ENSURE_REF(context_len); - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->psk_params.chosen_psk); - struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; - - *context_len = early_data_config->context.size; - - return S2N_SUCCESS; -} - -int s2n_offered_early_data_get_context(struct s2n_offered_early_data *early_data, uint8_t *context, uint16_t max_len) -{ - POSIX_ENSURE_REF(context); - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->psk_params.chosen_psk); - struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; - - POSIX_ENSURE(early_data_config->context.size <= max_len, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(context, early_data_config->context.data, early_data_config->context.size); - - return S2N_SUCCESS; -} - -int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_data) -{ - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - return S2N_SUCCESS; -} - -int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data) -{ - POSIX_ENSURE_REF(early_data); - struct s2n_connection *conn = early_data->conn; - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_early_data.h" + +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_psk.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +const s2n_early_data_state valid_previous_states[] = { + [S2N_EARLY_DATA_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, + [S2N_EARLY_DATA_NOT_REQUESTED] = S2N_UNKNOWN_EARLY_DATA_STATE, + [S2N_EARLY_DATA_REJECTED] = S2N_EARLY_DATA_REQUESTED, + [S2N_EARLY_DATA_ACCEPTED] = S2N_EARLY_DATA_REQUESTED, + [S2N_END_OF_EARLY_DATA] = S2N_EARLY_DATA_ACCEPTED, +}; + +S2N_RESULT s2n_connection_set_early_data_state(struct s2n_connection *conn, s2n_early_data_state next_state) +{ + RESULT_ENSURE_REF(conn); + if (conn->early_data_state == next_state) { + return S2N_RESULT_OK; + } + RESULT_ENSURE(next_state < S2N_EARLY_DATA_STATES_COUNT, S2N_ERR_INVALID_EARLY_DATA_STATE); + RESULT_ENSURE(next_state != S2N_UNKNOWN_EARLY_DATA_STATE, S2N_ERR_INVALID_EARLY_DATA_STATE); + RESULT_ENSURE(conn->early_data_state == valid_previous_states[next_state], S2N_ERR_INVALID_EARLY_DATA_STATE); + conn->early_data_state = next_state; + return S2N_RESULT_OK; +} + +int s2n_connection_set_early_data_expected(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + conn->early_data_expected = true; + return S2N_SUCCESS; +} + +int s2n_connection_set_end_of_early_data(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + conn->early_data_expected = false; + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_early_data_validate(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# In order to accept early data, the server MUST have accepted a PSK + *# cipher suite and selected the first key offered in the client's + *# "pre_shared_key" extension. + **/ + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + RESULT_ENSURE_EQ(conn->psk_params.chosen_psk_wire_index, 0); + + struct s2n_early_data_config *config = &conn->psk_params.chosen_psk->early_data_config; + RESULT_ENSURE_GT(config->max_early_data_size, 0); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# In addition, it MUST verify that the + *# following values are the same as those associated with the + *# selected PSK: + *# + *# - The TLS version number + **/ + RESULT_ENSURE_EQ(config->protocol_version, s2n_connection_get_protocol_version(conn)); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# - The selected cipher suite + **/ + RESULT_ENSURE_EQ(config->cipher_suite, conn->secure->cipher_suite); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# - The selected ALPN [RFC7301] protocol, if any + **/ + const size_t app_protocol_size = strlen(conn->application_protocol); + if (app_protocol_size > 0 || config->application_protocol.size > 0) { + RESULT_ENSURE_EQ(config->application_protocol.size, app_protocol_size + 1 /* null-terminating char */); + RESULT_ENSURE(s2n_constant_time_equals(config->application_protocol.data, (uint8_t *) conn->application_protocol, app_protocol_size), S2N_ERR_SAFETY); + } + + return S2N_RESULT_OK; +} + +bool s2n_early_data_is_valid_for_connection(struct s2n_connection *conn) +{ + return s2n_result_is_ok(s2n_early_data_validate(conn)); +} + +S2N_RESULT s2n_early_data_accept_or_reject(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (conn->early_data_state != S2N_EARLY_DATA_REQUESTED) { + return S2N_RESULT_OK; + } + + if (conn->handshake.early_data_async_state.conn) { + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# If any of these checks fail, the server MUST NOT respond with the + *# extension + **/ + if (!s2n_early_data_is_valid_for_connection(conn)) { + RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + return S2N_RESULT_OK; + } + + /* Even if the connection is valid for early data, the client can't consider + * early data accepted until the server sends the early data indication. */ + if (conn->mode == S2N_CLIENT) { + return S2N_RESULT_OK; + } + + /* The server should reject early data if the application is not prepared to handle it. */ + if (!conn->early_data_expected) { + RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + return S2N_RESULT_OK; + } + + /* If early data would otherwise be accepted, let the application apply any additional restrictions. + * For example, an application could use this callback to implement anti-replay protections. + * + * This callback can be either synchronous or asynchronous. The handshake will not proceed until + * the application either accepts or rejects early data. + */ + RESULT_ENSURE_REF(conn->config); + if (conn->config->early_data_cb) { + conn->handshake.early_data_async_state.conn = conn; + RESULT_ENSURE(conn->config->early_data_cb(conn, &conn->handshake.early_data_async_state) >= S2N_SUCCESS, + S2N_ERR_CANCELLED); + if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + } else { + RESULT_GUARD(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); + } + return S2N_RESULT_OK; +} + +int s2n_config_set_server_max_early_data_size(struct s2n_config *config, uint32_t max_early_data_size) +{ + POSIX_ENSURE_REF(config); + config->server_max_early_data_size = max_early_data_size; + return S2N_SUCCESS; +} + +int s2n_connection_set_server_max_early_data_size(struct s2n_connection *conn, uint32_t max_early_data_size) +{ + POSIX_ENSURE_REF(conn); + conn->server_max_early_data_size = max_early_data_size; + conn->server_max_early_data_size_overridden = true; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_early_data_get_server_max_size(struct s2n_connection *conn, uint32_t *max_early_data_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(max_early_data_size); + if (conn->server_max_early_data_size_overridden) { + *max_early_data_size = conn->server_max_early_data_size; + } else { + RESULT_ENSURE_REF(conn->config); + *max_early_data_size = conn->config->server_max_early_data_size; + } + return S2N_RESULT_OK; +} + +int s2n_connection_set_server_early_data_context(struct s2n_connection *conn, const uint8_t *context, uint16_t context_size) +{ + POSIX_ENSURE_REF(conn); + if (context_size > 0) { + POSIX_ENSURE_REF(context); + } + + POSIX_GUARD(s2n_realloc(&conn->server_early_data_context, context_size)); + POSIX_CHECKED_MEMCPY(conn->server_early_data_context.data, context, context_size); + return S2N_SUCCESS; +} + +S2N_CLEANUP_RESULT s2n_early_data_config_free(struct s2n_early_data_config *config) +{ + if (config == NULL) { + return S2N_RESULT_OK; + } + RESULT_GUARD_POSIX(s2n_free(&config->application_protocol)); + RESULT_GUARD_POSIX(s2n_free(&config->context)); + return S2N_RESULT_OK; +} + +int s2n_psk_configure_early_data(struct s2n_psk *psk, uint32_t max_early_data_size, + uint8_t cipher_suite_first_byte, uint8_t cipher_suite_second_byte) +{ + POSIX_ENSURE_REF(psk); + + const uint8_t cipher_suite_iana[] = { cipher_suite_first_byte, cipher_suite_second_byte }; + struct s2n_cipher_suite *cipher_suite = NULL; + POSIX_GUARD_RESULT(s2n_cipher_suite_from_iana(cipher_suite_iana, sizeof(cipher_suite_iana), &cipher_suite)); + POSIX_ENSURE_REF(cipher_suite); + POSIX_ENSURE(cipher_suite->prf_alg == psk->hmac_alg, S2N_ERR_INVALID_ARGUMENT); + + psk->early_data_config.max_early_data_size = max_early_data_size; + psk->early_data_config.protocol_version = S2N_TLS13; + psk->early_data_config.cipher_suite = cipher_suite; + return S2N_SUCCESS; +} + +int s2n_psk_set_application_protocol(struct s2n_psk *psk, const uint8_t *application_protocol, uint8_t size) +{ + POSIX_ENSURE_REF(psk); + if (size > 0) { + POSIX_ENSURE_REF(application_protocol); + } + struct s2n_blob *protocol_blob = &psk->early_data_config.application_protocol; + POSIX_GUARD(s2n_realloc(protocol_blob, size)); + POSIX_CHECKED_MEMCPY(protocol_blob->data, application_protocol, size); + return S2N_SUCCESS; +} + +int s2n_psk_set_early_data_context(struct s2n_psk *psk, const uint8_t *context, uint16_t size) +{ + POSIX_ENSURE_REF(psk); + if (size > 0) { + POSIX_ENSURE_REF(context); + } + struct s2n_blob *context_blob = &psk->early_data_config.context; + POSIX_GUARD(s2n_realloc(context_blob, size)); + POSIX_CHECKED_MEMCPY(context_blob->data, context, size); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_early_data_config_clone(struct s2n_psk *new_psk, struct s2n_early_data_config *old_config) +{ + RESULT_ENSURE_REF(old_config); + RESULT_ENSURE_REF(new_psk); + + struct s2n_early_data_config config_copy = new_psk->early_data_config; + + /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ + new_psk->early_data_config = *old_config; + new_psk->early_data_config.application_protocol = config_copy.application_protocol; + new_psk->early_data_config.context = config_copy.context; + + /* Clone / realloc blobs */ + RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(new_psk, old_config->application_protocol.data, + old_config->application_protocol.size)); + RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(new_psk, old_config->context.data, + old_config->context.size)); + + return S2N_RESULT_OK; +} + +int s2n_connection_get_early_data_status(struct s2n_connection *conn, s2n_early_data_status_t *status) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(status); + + switch (conn->early_data_state) { + case S2N_EARLY_DATA_STATES_COUNT: + break; + case S2N_EARLY_DATA_NOT_REQUESTED: + *status = S2N_EARLY_DATA_STATUS_NOT_REQUESTED; + return S2N_SUCCESS; + case S2N_EARLY_DATA_REJECTED: + *status = S2N_EARLY_DATA_STATUS_REJECTED; + return S2N_SUCCESS; + case S2N_END_OF_EARLY_DATA: + *status = S2N_EARLY_DATA_STATUS_END; + return S2N_SUCCESS; + case S2N_UNKNOWN_EARLY_DATA_STATE: + case S2N_EARLY_DATA_REQUESTED: + case S2N_EARLY_DATA_ACCEPTED: + *status = S2N_EARLY_DATA_STATUS_OK; + return S2N_SUCCESS; + } + POSIX_BAIL(S2N_ERR_INVALID_EARLY_DATA_STATE); +} + +static S2N_RESULT s2n_get_remaining_early_data_bytes(struct s2n_connection *conn, uint32_t *early_data_allowed) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(early_data_allowed); + *early_data_allowed = 0; + + uint32_t max_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); + + RESULT_ENSURE(max_early_data_size >= conn->early_data_bytes, S2N_ERR_MAX_EARLY_DATA_SIZE); + *early_data_allowed = (max_early_data_size - conn->early_data_bytes); + + return S2N_RESULT_OK; +} + +int s2n_connection_get_remaining_early_data_size(struct s2n_connection *conn, uint32_t *allowed_early_data_size) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(allowed_early_data_size); + *allowed_early_data_size = 0; + + switch (conn->early_data_state) { + case S2N_EARLY_DATA_STATES_COUNT: + case S2N_EARLY_DATA_NOT_REQUESTED: + case S2N_EARLY_DATA_REJECTED: + case S2N_END_OF_EARLY_DATA: + *allowed_early_data_size = 0; + break; + case S2N_UNKNOWN_EARLY_DATA_STATE: + case S2N_EARLY_DATA_REQUESTED: + case S2N_EARLY_DATA_ACCEPTED: + POSIX_GUARD_RESULT(s2n_get_remaining_early_data_bytes(conn, allowed_early_data_size)); + break; + } + return S2N_SUCCESS; +} + +int s2n_connection_get_max_early_data_size(struct s2n_connection *conn, uint32_t *max_early_data_size) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(max_early_data_size); + *max_early_data_size = 0; + + uint32_t server_max_early_data_size = 0; + POSIX_GUARD_RESULT(s2n_early_data_get_server_max_size(conn, &server_max_early_data_size)); + + if (conn->psk_params.psk_list.len == 0) { + /* This method may be called by the server before loading its PSKs. + * The server can load its PSKs during the handshake, either via the PSK selection callback + * or by receiving a stateless session ticket. + * + * Before that happens, we should make an optimistic assumption of the early data size. + * That way, the max early data size always decreases (for example, it won't go from 0 -> UINT32_MAX + * after receiving a PSK in the ClientHello). + */ + if (conn->mode == S2N_SERVER && !IS_NEGOTIATED(conn)) { + *max_early_data_size = server_max_early_data_size; + } + return S2N_SUCCESS; + } + + struct s2n_psk *first_psk = NULL; + POSIX_GUARD_RESULT(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &first_psk)); + POSIX_ENSURE_REF(first_psk); + *max_early_data_size = first_psk->early_data_config.max_early_data_size; + + /* For the server, we should use the minimum of the limit retrieved from the ticket + * and the current limit being set for new tickets. + * + * This is defensive: even if more early data was previously allowed, the server may not be + * willing or able to handle that much early data now. + * + * We don't do this for external PSKs because the server has intentionally set the limit + * while setting up this connection, not during a previous connection. + */ + if (conn->mode == S2N_SERVER && first_psk->type == S2N_PSK_TYPE_RESUMPTION) { + *max_early_data_size = S2N_MIN(*max_early_data_size, server_max_early_data_size); + } + + return S2N_SUCCESS; +} + +int s2n_config_set_early_data_cb(struct s2n_config *config, s2n_early_data_cb cb) +{ + POSIX_ENSURE_REF(config); + config->early_data_cb = cb; + return S2N_SUCCESS; +} + +int s2n_offered_early_data_get_context_length(struct s2n_offered_early_data *early_data, uint16_t *context_len) +{ + POSIX_ENSURE_REF(context_len); + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->psk_params.chosen_psk); + struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; + + *context_len = early_data_config->context.size; + + return S2N_SUCCESS; +} + +int s2n_offered_early_data_get_context(struct s2n_offered_early_data *early_data, uint8_t *context, uint16_t max_len) +{ + POSIX_ENSURE_REF(context); + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->psk_params.chosen_psk); + struct s2n_early_data_config *early_data_config = &conn->psk_params.chosen_psk->early_data_config; + + POSIX_ENSURE(early_data_config->context.size <= max_len, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(context, early_data_config->context.data, early_data_config->context.size); + + return S2N_SUCCESS; +} + +int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + return S2N_SUCCESS; +} + +int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(early_data); + struct s2n_connection *conn = early_data->conn; + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_ACCEPTED)); + return S2N_SUCCESS; +} diff --git a/tls/s2n_early_data_io.c b/tls/s2n_early_data_io.c index 232266c4bb4..8953a96fb25 100644 --- a/tls/s2n_early_data_io.c +++ b/tls/s2n_early_data_io.c @@ -1,277 +1,277 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_connection.h" -#include "tls/s2n_early_data.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -int s2n_end_of_early_data_send(struct s2n_connection *conn) -{ - if (conn->early_data_expected) { - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); - } - - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); - return S2N_SUCCESS; -} - -int s2n_end_of_early_data_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_BAD_MESSAGE); - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); - return S2N_SUCCESS; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# If the client attempts a 0-RTT handshake but the server - *# rejects it, the server will generally not have the 0-RTT record - *# protection keys and must instead use trial decryption (either with - *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in - *# the case of a HelloRetryRequest) to find the first non-0-RTT message. - */ -bool s2n_early_data_is_trial_decryption_allowed(struct s2n_connection *conn, uint8_t record_type) -{ - return conn && (conn->early_data_state == S2N_EARLY_DATA_REJECTED) - && record_type == TLS_APPLICATION_DATA - /* Only servers receive early data. */ - && (conn->mode == S2N_SERVER) - /* Early data is only expected during the handshake. */ - && (s2n_conn_get_current_message_type(conn) != APPLICATION_DATA); -} - -static bool s2n_is_early_data_io(struct s2n_connection *conn) -{ - if (s2n_conn_get_current_message_type(conn) == APPLICATION_DATA) { - return false; - } - - /* It would be more accurate to not include this check. - * However, before the early data feature was added, s2n_send and s2n_recv - * did not verify that they were being called after a complete handshake. - * Enforcing that broke several S2N tests, and might have broken customers too. - * - * Therefore, only consider this early data if the customer has indicated that - * they are aware of early data, either because early data is currently expected - * or early data is in a state that indicates that early data was previously expected. - */ - if (conn->early_data_expected - || (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) - || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED - || conn->early_data_state == S2N_END_OF_EARLY_DATA) { - return true; - } - return false; -} - -S2N_RESULT s2n_early_data_record_bytes(struct s2n_connection *conn, ssize_t data_len) -{ - RESULT_ENSURE_REF(conn); - if (data_len < 0 || !s2n_is_early_data_io(conn)) { - return S2N_RESULT_OK; - } - - /* Ensure the bytes read are within the bounds of what we can actually record. */ - if ((size_t) data_len > (UINT64_MAX - conn->early_data_bytes)) { - conn->early_data_bytes = UINT64_MAX; - RESULT_BAIL(S2N_ERR_INTEGER_OVERFLOW); - } - - /* Record the early data bytes read, even if they exceed the max_early_data_size. - * This will ensure that if this method is called again, it will fail again: - * Once we receive too many bytes, we can't proceed with the connection. */ - conn->early_data_bytes += data_len; - - uint32_t max_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); - RESULT_ENSURE(conn->early_data_bytes <= max_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_early_data_validate_send(struct s2n_connection *conn, uint32_t bytes_to_send) -{ - RESULT_ENSURE_REF(conn); - if (!s2n_is_early_data_io(conn)) { - return S2N_RESULT_OK; - } - - RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_REQUESTED - || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, - S2N_ERR_EARLY_DATA_NOT_ALLOWED); - - uint32_t allowed_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &allowed_early_data_size)); - RESULT_ENSURE(bytes_to_send <= allowed_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_early_data_validate_recv(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (!s2n_is_early_data_io(conn)) { - return S2N_RESULT_OK; - } - - RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == END_OF_EARLY_DATA, S2N_ERR_EARLY_DATA_NOT_ALLOWED); - return S2N_RESULT_OK; -} - -static bool s2n_early_data_can_continue(struct s2n_connection *conn) -{ - uint32_t remaining_early_data_size = 0; - return s2n_connection_get_remaining_early_data_size(conn, &remaining_early_data_size) >= S2N_SUCCESS - && remaining_early_data_size > 0; -} - -S2N_RESULT s2n_send_early_data_impl(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len_signed, - ssize_t *data_sent, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_GTE(data_len_signed, 0); - size_t data_len = data_len_signed; - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(blocked); - *blocked = S2N_NOT_BLOCKED; - RESULT_ENSURE_REF(data_sent); - *data_sent = 0; - - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_SERVER_MODE); - RESULT_ENSURE(s2n_connection_supports_tls13(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - if (!s2n_early_data_can_continue(conn)) { - return S2N_RESULT_OK; - } - - /* Attempt to make progress in the handshake even if s2n_send eventually fails. - * We only care about the result of this call if it would prevent us from calling s2n_send. */ - int negotiate_result = s2n_negotiate(conn, blocked); - if (negotiate_result < S2N_SUCCESS) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - RESULT_GUARD_POSIX(negotiate_result); - } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA && *blocked != S2N_BLOCKED_ON_READ) { - RESULT_GUARD_POSIX(negotiate_result); - } - } - /* Save the error status for later */ - int negotiate_error = s2n_errno; - s2n_blocked_status negotiate_blocked = *blocked; - - /* Attempt to send the early data. - * We only care about the result of this call if it fails. */ - uint32_t early_data_to_send = 0; - RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &early_data_to_send)); - early_data_to_send = S2N_MIN(data_len, early_data_to_send); - if (early_data_to_send) { - ssize_t send_result = s2n_send(conn, data, early_data_to_send, blocked); - RESULT_GUARD_POSIX(send_result); - *data_sent = send_result; - } - *blocked = S2N_NOT_BLOCKED; - - /* Since the send was successful, report the result of the original negotiate call. - * If we got this far, the result must have been success or a blocking error. */ - if (negotiate_result < S2N_SUCCESS) { - RESULT_ENSURE_EQ(s2n_error_get_type(negotiate_error), S2N_ERR_T_BLOCKED); - if (negotiate_blocked == S2N_BLOCKED_ON_EARLY_DATA) { - return S2N_RESULT_OK; - } else if (s2n_early_data_can_continue(conn)) { - *blocked = negotiate_blocked; - RESULT_BAIL(negotiate_error); - } else { - return S2N_RESULT_OK; - } - } - return S2N_RESULT_OK; -} - -int s2n_send_early_data(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len, - ssize_t *data_sent, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - - /* Calling this method indicates that we expect early data. */ - POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); - - s2n_result result = s2n_send_early_data_impl(conn, data, data_len, data_sent, blocked); - - /* Unless s2n_send_early_data is called again (undoing this), we are done sending early data. - * If s2n_negotiate is called next, we could send the EndOfEarlyData message. */ - POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); - - POSIX_GUARD_RESULT(result); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_recv_early_data_impl(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, - ssize_t *data_received, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(blocked); - *blocked = S2N_NOT_BLOCKED; - RESULT_ENSURE_REF(data_received); - *data_received = 0; - - RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); - - if (!s2n_early_data_can_continue(conn)) { - return S2N_RESULT_OK; - } - - int negotiate_result = S2N_SUCCESS; - while ((negotiate_result = s2n_negotiate(conn, blocked)) != S2N_SUCCESS) { - if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { - RESULT_GUARD_POSIX(negotiate_result); - } else if (max_data_len <= *data_received) { - RESULT_GUARD_POSIX(negotiate_result); - } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA) { - if (s2n_early_data_can_continue(conn)) { - RESULT_GUARD_POSIX(negotiate_result); - } else { - *blocked = S2N_NOT_BLOCKED; - return S2N_RESULT_OK; - } - } - - ssize_t recv_result = s2n_recv(conn, data + *data_received, - max_data_len - *data_received, blocked); - RESULT_GUARD_POSIX(recv_result); - *data_received += recv_result; - } - return S2N_RESULT_OK; -} - -int s2n_recv_early_data(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, - ssize_t *data_received, s2n_blocked_status *blocked) -{ - /* Calling this method indicates that we expect early data. */ - POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); - - s2n_result result = s2n_recv_early_data_impl(conn, data, max_data_len, data_received, blocked); - - /* Unless s2n_recv_early_data is called again (undoing this), we are done accepting early data. */ - POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); - - POSIX_GUARD_RESULT(result); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_connection.h" +#include "tls/s2n_early_data.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +int s2n_end_of_early_data_send(struct s2n_connection *conn) +{ + if (conn->early_data_expected) { + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); + } + + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); + return S2N_SUCCESS; +} + +int s2n_end_of_early_data_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_BAD_MESSAGE); + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA)); + return S2N_SUCCESS; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# If the client attempts a 0-RTT handshake but the server + *# rejects it, the server will generally not have the 0-RTT record + *# protection keys and must instead use trial decryption (either with + *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in + *# the case of a HelloRetryRequest) to find the first non-0-RTT message. + */ +bool s2n_early_data_is_trial_decryption_allowed(struct s2n_connection *conn, uint8_t record_type) +{ + return conn && (conn->early_data_state == S2N_EARLY_DATA_REJECTED) + && record_type == TLS_APPLICATION_DATA + /* Only servers receive early data. */ + && (conn->mode == S2N_SERVER) + /* Early data is only expected during the handshake. */ + && (s2n_conn_get_current_message_type(conn) != APPLICATION_DATA); +} + +static bool s2n_is_early_data_io(struct s2n_connection *conn) +{ + if (s2n_conn_get_current_message_type(conn) == APPLICATION_DATA) { + return false; + } + + /* It would be more accurate to not include this check. + * However, before the early data feature was added, s2n_send and s2n_recv + * did not verify that they were being called after a complete handshake. + * Enforcing that broke several S2N tests, and might have broken customers too. + * + * Therefore, only consider this early data if the customer has indicated that + * they are aware of early data, either because early data is currently expected + * or early data is in a state that indicates that early data was previously expected. + */ + if (conn->early_data_expected + || (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) + || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED + || conn->early_data_state == S2N_END_OF_EARLY_DATA) { + return true; + } + return false; +} + +S2N_RESULT s2n_early_data_record_bytes(struct s2n_connection *conn, ssize_t data_len) +{ + RESULT_ENSURE_REF(conn); + if (data_len < 0 || !s2n_is_early_data_io(conn)) { + return S2N_RESULT_OK; + } + + /* Ensure the bytes read are within the bounds of what we can actually record. */ + if ((size_t) data_len > (UINT64_MAX - conn->early_data_bytes)) { + conn->early_data_bytes = UINT64_MAX; + RESULT_BAIL(S2N_ERR_INTEGER_OVERFLOW); + } + + /* Record the early data bytes read, even if they exceed the max_early_data_size. + * This will ensure that if this method is called again, it will fail again: + * Once we receive too many bytes, we can't proceed with the connection. */ + conn->early_data_bytes += data_len; + + uint32_t max_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_connection_get_max_early_data_size(conn, &max_early_data_size)); + RESULT_ENSURE(conn->early_data_bytes <= max_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_early_data_validate_send(struct s2n_connection *conn, uint32_t bytes_to_send) +{ + RESULT_ENSURE_REF(conn); + if (!s2n_is_early_data_io(conn)) { + return S2N_RESULT_OK; + } + + RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_REQUESTED + || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + uint32_t allowed_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &allowed_early_data_size)); + RESULT_ENSURE(bytes_to_send <= allowed_early_data_size, S2N_ERR_MAX_EARLY_DATA_SIZE); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_early_data_validate_recv(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (!s2n_is_early_data_io(conn)) { + return S2N_RESULT_OK; + } + + RESULT_ENSURE(conn->early_data_expected, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(conn->early_data_state == S2N_EARLY_DATA_ACCEPTED, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == END_OF_EARLY_DATA, S2N_ERR_EARLY_DATA_NOT_ALLOWED); + return S2N_RESULT_OK; +} + +static bool s2n_early_data_can_continue(struct s2n_connection *conn) +{ + uint32_t remaining_early_data_size = 0; + return s2n_connection_get_remaining_early_data_size(conn, &remaining_early_data_size) >= S2N_SUCCESS + && remaining_early_data_size > 0; +} + +S2N_RESULT s2n_send_early_data_impl(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len_signed, + ssize_t *data_sent, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_GTE(data_len_signed, 0); + size_t data_len = data_len_signed; + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(blocked); + *blocked = S2N_NOT_BLOCKED; + RESULT_ENSURE_REF(data_sent); + *data_sent = 0; + + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_SERVER_MODE); + RESULT_ENSURE(s2n_connection_supports_tls13(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + if (!s2n_early_data_can_continue(conn)) { + return S2N_RESULT_OK; + } + + /* Attempt to make progress in the handshake even if s2n_send eventually fails. + * We only care about the result of this call if it would prevent us from calling s2n_send. */ + int negotiate_result = s2n_negotiate(conn, blocked); + if (negotiate_result < S2N_SUCCESS) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + RESULT_GUARD_POSIX(negotiate_result); + } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA && *blocked != S2N_BLOCKED_ON_READ) { + RESULT_GUARD_POSIX(negotiate_result); + } + } + /* Save the error status for later */ + int negotiate_error = s2n_errno; + s2n_blocked_status negotiate_blocked = *blocked; + + /* Attempt to send the early data. + * We only care about the result of this call if it fails. */ + uint32_t early_data_to_send = 0; + RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &early_data_to_send)); + early_data_to_send = S2N_MIN(data_len, early_data_to_send); + if (early_data_to_send) { + ssize_t send_result = s2n_send(conn, data, early_data_to_send, blocked); + RESULT_GUARD_POSIX(send_result); + *data_sent = send_result; + } + *blocked = S2N_NOT_BLOCKED; + + /* Since the send was successful, report the result of the original negotiate call. + * If we got this far, the result must have been success or a blocking error. */ + if (negotiate_result < S2N_SUCCESS) { + RESULT_ENSURE_EQ(s2n_error_get_type(negotiate_error), S2N_ERR_T_BLOCKED); + if (negotiate_blocked == S2N_BLOCKED_ON_EARLY_DATA) { + return S2N_RESULT_OK; + } else if (s2n_early_data_can_continue(conn)) { + *blocked = negotiate_blocked; + RESULT_BAIL(negotiate_error); + } else { + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +int s2n_send_early_data(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len, + ssize_t *data_sent, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + + /* Calling this method indicates that we expect early data. */ + POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); + + s2n_result result = s2n_send_early_data_impl(conn, data, data_len, data_sent, blocked); + + /* Unless s2n_send_early_data is called again (undoing this), we are done sending early data. + * If s2n_negotiate is called next, we could send the EndOfEarlyData message. */ + POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); + + POSIX_GUARD_RESULT(result); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_recv_early_data_impl(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, + ssize_t *data_received, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(blocked); + *blocked = S2N_NOT_BLOCKED; + RESULT_ENSURE_REF(data_received); + *data_received = 0; + + RESULT_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); + + if (!s2n_early_data_can_continue(conn)) { + return S2N_RESULT_OK; + } + + int negotiate_result = S2N_SUCCESS; + while ((negotiate_result = s2n_negotiate(conn, blocked)) != S2N_SUCCESS) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + RESULT_GUARD_POSIX(negotiate_result); + } else if (max_data_len <= *data_received) { + RESULT_GUARD_POSIX(negotiate_result); + } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA) { + if (s2n_early_data_can_continue(conn)) { + RESULT_GUARD_POSIX(negotiate_result); + } else { + *blocked = S2N_NOT_BLOCKED; + return S2N_RESULT_OK; + } + } + + ssize_t recv_result = s2n_recv(conn, data + *data_received, + max_data_len - *data_received, blocked); + RESULT_GUARD_POSIX(recv_result); + *data_received += recv_result; + } + return S2N_RESULT_OK; +} + +int s2n_recv_early_data(struct s2n_connection *conn, uint8_t *data, ssize_t max_data_len, + ssize_t *data_received, s2n_blocked_status *blocked) +{ + /* Calling this method indicates that we expect early data. */ + POSIX_GUARD(s2n_connection_set_early_data_expected(conn)); + + s2n_result result = s2n_recv_early_data_impl(conn, data, max_data_len, data_received, blocked); + + /* Unless s2n_recv_early_data is called again (undoing this), we are done accepting early data. */ + POSIX_GUARD(s2n_connection_set_end_of_early_data(conn)); + + POSIX_GUARD_RESULT(result); + return S2N_SUCCESS; +} diff --git a/tls/s2n_ecc_preferences.h b/tls/s2n_ecc_preferences.h index 704199bcd12..19fb757a02a 100644 --- a/tls/s2n_ecc_preferences.h +++ b/tls/s2n_ecc_preferences.h @@ -1,44 +1,44 @@ -/* - * 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 - -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "crypto/s2n_ecc_evp.h" - -struct s2n_ecc_preferences { - uint8_t count; - const struct s2n_ecc_named_curve *const *ecc_curves; -}; - -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240501; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20140601; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20200310; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20230623; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_default_fips; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20201021; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20210816; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240603; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_20251113; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_test_all; -extern const struct s2n_ecc_preferences s2n_ecc_preferences_null; - -int s2n_check_ecc_preferences_curves_list(const struct s2n_ecc_preferences *ecc_preferences); -bool s2n_ecc_preferences_includes_curve(const struct s2n_ecc_preferences *ecc_preferences, uint16_t query_iana_id); +/* + * 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 + +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_ecc_evp.h" + +struct s2n_ecc_preferences { + uint8_t count; + const struct s2n_ecc_named_curve *const *ecc_curves; +}; + +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240501; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20140601; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20200310; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20230623; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_default_fips; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20201021; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20210816; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20240603; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_20251113; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_test_all; +extern const struct s2n_ecc_preferences s2n_ecc_preferences_null; + +int s2n_check_ecc_preferences_curves_list(const struct s2n_ecc_preferences *ecc_preferences); +bool s2n_ecc_preferences_includes_curve(const struct s2n_ecc_preferences *ecc_preferences, uint16_t query_iana_id); diff --git a/tls/s2n_fingerprint_ja4.c b/tls/s2n_fingerprint_ja4.c index 8454b625d3e..c7b5c8f01f2 100644 --- a/tls/s2n_fingerprint_ja4.c +++ b/tls/s2n_fingerprint_ja4.c @@ -1,602 +1,602 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_hash.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/extensions/s2n_client_supported_versions.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_client_hello.h" -#include "tls/s2n_fingerprint.h" -#include "tls/s2n_protocol_preferences.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -#define S2N_JA4_LIST_DIV ',' -#define S2N_JA4_PART_DIV '_' - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# 2 character number of cipher suites - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Same as counting ciphers. - */ -#define S2N_JA4_COUNT_SIZE 2 - -#define S2N_HEX_PER_BYTE 2 -#define S2N_JA4_DIGEST_HEX_CHAR_LIMIT 12 -#define S2N_JA4_DIGEST_BYTE_LIMIT (S2N_JA4_DIGEST_HEX_CHAR_LIMIT / S2N_HEX_PER_BYTE) - -#define S2N_JA4_A_SIZE 10 -#define S2N_JA4_B_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT -#define S2N_JA4_C_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT -#define S2N_JA4_SIZE (S2N_JA4_A_SIZE + 1 + S2N_JA4_B_SIZE + 1 + S2N_JA4_C_SIZE) - -#define S2N_JA4_LIST_LIMIT 99 -#define S2N_JA4_IANA_HEX_SIZE (S2N_HEX_PER_BYTE * sizeof(uint16_t)) -#define S2N_JA4_IANA_ENTRY_SIZE (S2N_JA4_IANA_HEX_SIZE + 1) -#define S2N_JA4_WORKSPACE_SIZE ((S2N_JA4_LIST_LIMIT * (S2N_JA4_IANA_ENTRY_SIZE))) - -const char *s2n_ja4_version_strings[] = { - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# 0x0304 = TLS 1.3 = “13” - *# 0x0303 = TLS 1.2 = “12” - *# 0x0302 = TLS 1.1 = “11” - *# 0x0301 = TLS 1.0 = “10” - */ - [0x0304] = "13", - [0x0303] = "12", - [0x0302] = "11", - [0x0301] = "10", - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# 0x0300 = SSL 3.0 = “s3” - *# 0x0002 = SSL 2.0 = “s2” - */ - [0x0300] = "s3", - [0x0002] = "s2", -}; - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# Unknown = “00” - */ -#define S2N_JA4_UNKNOWN_STR "00" - -DEFINE_POINTER_CLEANUP_FUNC(struct s2n_stuffer *, s2n_stuffer_wipe); - -static int s2n_fingerprint_ja4_iana_compare(const void *a, const void *b) -{ - const uint8_t *iana_a = (const uint8_t *) a; - const uint8_t *iana_b = (const uint8_t *) b; - for (size_t i = 0; i < S2N_JA4_IANA_HEX_SIZE; i++) { - if (iana_a[i] != iana_b[i]) { - return iana_a[i] - iana_b[i]; - } - } - return 0; -} - -static S2N_RESULT s2n_fingerprint_ja4_digest(struct s2n_fingerprint_hash *hash, - struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(hash); - if (!s2n_fingerprint_hash_do_digest(hash)) { - return S2N_RESULT_OK; - } - - /* Instead of hashing empty inputs, JA4 sets the output to a string of all zeroes. - * (Actually hashing an empty input doesn't produce a digest of all zeroes) - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash - *# If there are no ciphers in the sorted cipher list, then the value of - *# JA4_b is set to `000000000000` - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# If there are no extensions in the sorted extensions list, then the value of - *# JA4_c is set to `000000000000` - */ - uint64_t bytes = 0; - RESULT_GUARD_POSIX(s2n_hash_get_currently_in_hash_total(hash->hash, &bytes)); - if (bytes == 0) { - RESULT_GUARD_POSIX(s2n_stuffer_write_str(out, "000000000000")); - return S2N_RESULT_OK; - } - - uint8_t digest_bytes[SHA256_DIGEST_LENGTH] = { 0 }; - struct s2n_blob digest = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&digest, digest_bytes, sizeof(digest_bytes))); - RESULT_GUARD(s2n_fingerprint_hash_digest(hash, &digest)); - - /* JA4 digests are truncated */ - RESULT_ENSURE_LTE(S2N_JA4_DIGEST_BYTE_LIMIT, digest.size); - digest.size = S2N_JA4_DIGEST_BYTE_LIMIT; - RESULT_GUARD(s2n_stuffer_write_hex(out, &digest)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# 2 character number of cipher suites, so if there’s 6 cipher suites - *# in the hello packet, then the value should be “06”. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Same as counting ciphers. - */ -static S2N_RESULT s2n_fingerprint_ja4_count(struct s2n_blob *output, uint16_t count) -{ - RESULT_ENSURE_REF(output); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# If there’s > 99, which there should never be, then output “99”. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Same as counting ciphers. - */ - count = S2N_MIN(count, 99); - - RESULT_ENSURE_EQ(output->size, 2); - output->data[0] = (count / 10) + '0'; - output->data[1] = (count % 10) + '0'; - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_fingerprint_get_extension_version(struct s2n_client_hello *ch, - uint16_t *client_version) -{ - RESULT_ENSURE_REF(ch); - RESULT_ENSURE_REF(client_version); - - s2n_parsed_extension *extension = NULL; - RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension( - S2N_EXTENSION_SUPPORTED_VERSIONS, &ch->extensions, &extension)); - RESULT_ENSURE_REF(extension); - - struct s2n_stuffer supported_versions = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions, &extension->extension)); - - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&supported_versions, sizeof(uint8_t))); - while (s2n_stuffer_data_available(&supported_versions)) { - uint16_t version = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&supported_versions, &version)); - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# Remember to ignore GREASE values. - */ - if (s2n_fingerprint_is_grease_value(version)) { - continue; - } - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# If extension 0x002b exists (supported_versions), then the version is - *# the highest value in the extension. - */ - *client_version = S2N_MAX(*client_version, version); - } - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_fingerprint_ja4_version(struct s2n_stuffer *output, - struct s2n_client_hello *ch) -{ - uint16_t client_version = 0; - if (s2n_result_is_error(s2n_fingerprint_get_extension_version(ch, &client_version))) { - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# If the extension doesn’t exist, then the TLS version is the value of - *# the Protocol Version. - */ - RESULT_GUARD(s2n_fingerprint_get_legacy_version(ch, &client_version)); - } - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version - *# Handshake version (located at the top of the packet) should be ignored. - */ - - const char *version_str = NULL; - if (client_version < s2n_array_len(s2n_ja4_version_strings)) { - version_str = s2n_ja4_version_strings[client_version]; - } - if (version_str == NULL) { - version_str = S2N_JA4_UNKNOWN_STR; - } - RESULT_GUARD_POSIX(s2n_stuffer_write_str(output, version_str)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_client_hello_get_first_alpn(struct s2n_client_hello *ch, struct s2n_blob *first) -{ - RESULT_ENSURE_REF(ch); - - s2n_parsed_extension *extension = NULL; - RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_ALPN, - &ch->extensions, &extension)); - RESULT_ENSURE_REF(extension); - - struct s2n_stuffer protocols = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&protocols, &extension->extension)); - - uint16_t list_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&protocols, &list_size)); - - RESULT_GUARD(s2n_protocol_preferences_read(&protocols, first)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# The first and last alphanumeric characters of the ALPN (Application-Layer - *# Protocol Negotiation) first value. - */ -static S2N_RESULT s2n_fingerprint_ja4_alpn(struct s2n_stuffer *output, - struct s2n_client_hello *ch) -{ - struct s2n_blob protocol = { 0 }; - if (s2n_result_is_error(s2n_client_hello_get_first_alpn(ch, &protocol))) { - protocol.size = 0; - } - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# If there is no ALPN extension, no ALPN values, or the first ALPN value - *# is empty, then we print "00" as the value in the fingerprint. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# If the first ALPN value is only a single character, then that character - *# is treated as both the first and last character. - */ - uint8_t first_char = '0', last_char = '0'; - if (protocol.size > 0) { - first_char = protocol.data[0]; - last_char = protocol.data[protocol.size - 1]; - } - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value - *# If the first or last byte of the first ALPN is non-alphanumeric (meaning - *# not `0x30-0x39`, `0x41-0x5A`, or `0x61-0x7A`), then we print the first and - *# last characters of the hex representation of the first ALPN instead. - */ - if (!isalnum(first_char) || !isalnum(last_char)) { - RESULT_GUARD(s2n_hex_digit((first_char >> 4), &first_char)); - RESULT_GUARD(s2n_hex_digit((last_char & 0x0F), &last_char)); - } - - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, first_char)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, last_char)); - return S2N_RESULT_OK; -} - -/* Part "a" of the fingerprint is a descriptive prefix. - * - * https://github.com/FoxIO-LLC/ja4/main/technical_details/JA4.md - *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) - *# (2 character TLS version) - *# (SNI=”d” or no SNI=”i”) - *# (2 character count of ciphers) - *# (2 character count of extensions) - *# (first and last characters of first ALPN extension value) - */ -static S2N_RESULT s2n_fingerprint_ja4_a(struct s2n_fingerprint *fingerprint, - struct s2n_stuffer *output, struct s2n_blob *ciphers_count, struct s2n_blob *extensions_count) -{ - RESULT_ENSURE_REF(fingerprint); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#quic-and-dtls - *# If the protocol is QUIC then the first character of the fingerprint is “q”, - *# if DTLS it is "d", else it is “t”. - * - * s2n-tls only supports TLS and QUIC. DTLS is not supported. - */ - bool is_quic = false; - RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, - TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS, &is_quic)); - char protocol_char = (is_quic) ? 'q' : 't'; - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, protocol_char)); - - RESULT_GUARD(s2n_fingerprint_ja4_version(output, fingerprint->client_hello)); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#sni - *# If the SNI extension (0x0000) exists, then the destination of the connection - *# is a domain, or “d” in the fingerprint. - *# If the SNI does not exist, then the destination is an IP address, or “i”. - */ - bool has_sni = false; - RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, - TLS_EXTENSION_SERVER_NAME, &has_sni)); - char sni_char = (has_sni) ? 'd' : 'i'; - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, sni_char)); - - /* Reserve two characters for the "count of ciphers". - * We'll calculate it later when we handle the cipher suite list for JA4_b. - */ - uint8_t *ciphers_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); - RESULT_GUARD_PTR(ciphers_count_mem); - RESULT_GUARD_POSIX(s2n_blob_init(ciphers_count, ciphers_count_mem, S2N_JA4_COUNT_SIZE)); - - /* Reserve two characters for the "count of extensions". - * We'll calculate it later when we handle the extensions list for JA4_c. - */ - uint8_t *extensions_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); - RESULT_GUARD_PTR(extensions_count_mem); - RESULT_GUARD_POSIX(s2n_blob_init(extensions_count, extensions_count_mem, S2N_JA4_COUNT_SIZE)); - - RESULT_GUARD(s2n_fingerprint_ja4_alpn(output, fingerprint->client_hello)); - - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash - *# The list is created using the 4 character hex values of the ciphers, - *# lower case, comma delimited, ignoring GREASE. - */ -static S2N_RESULT s2n_fingerprint_ja4_ciphers(struct s2n_fingerprint_hash *hash, - struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *ciphers_count) -{ - RESULT_ENSURE_REF(ch); - RESULT_ENSURE_REF(sort_space); - RESULT_ENSURE_REF(ciphers_count); - - struct s2n_stuffer cipher_suites = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&cipher_suites, &ch->cipher_suites)); - - DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); - while (s2n_stuffer_data_available(&cipher_suites)) { - uint16_t iana = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&cipher_suites, &iana)); - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers - *# Remember, ignore GREASE values. They don’t count. - */ - if (s2n_fingerprint_is_grease_value(iana)) { - continue; - } - RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); - } - - size_t iana_list_size = s2n_stuffer_data_available(iana_list); - size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; - *ciphers_count = iana_count; - if (iana_count == 0) { - return S2N_RESULT_OK; - } - - uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); - RESULT_ENSURE_REF(ianas); - qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); - RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash - *# A 12 character truncated sha256 hash of the list of ciphers sorted in hex order, - *# first 12 characters. - */ -static S2N_RESULT s2n_fingerprint_ja4_b(struct s2n_fingerprint *fingerprint, - struct s2n_fingerprint_hash *hash, struct s2n_blob *ciphers_count, - struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(fingerprint); - - uint16_t ciphers_count_value = 0; - RESULT_GUARD(s2n_fingerprint_ja4_ciphers(hash, fingerprint->client_hello, - &fingerprint->workspace, &ciphers_count_value)); - - RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); - RESULT_GUARD(s2n_fingerprint_ja4_count(ciphers_count, ciphers_count_value)); - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# The extension list is created using the 4 character hex values of the extensions, - *# lower case, comma delimited, sorted (not in the order they appear). - */ -static S2N_RESULT s2n_fingerprint_ja4_extensions(struct s2n_fingerprint_hash *hash, - struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *extensions_count) -{ - RESULT_ENSURE_REF(ch); - RESULT_ENSURE_REF(sort_space); - RESULT_ENSURE_REF(extensions_count); - - struct s2n_stuffer extensions = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&extensions, &ch->extensions.raw)); - - DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); - while (s2n_stuffer_data_available(&extensions)) { - uint16_t iana = 0; - RESULT_GUARD(s2n_fingerprint_parse_extension(&extensions, &iana)); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Ignore GREASE. - */ - if (s2n_fingerprint_is_grease_value(iana)) { - continue; - } - - /* SNI and ALPN are included in the extension count, but not in the extension list. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# Ignore the SNI extension (0000) and the ALPN extension (0010) - *# as we’ve already captured them in the _a_ section of the fingerprint. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions - *# Include SNI and ALPN. - */ - (*extensions_count)++; - if (iana == TLS_EXTENSION_SERVER_NAME || iana == S2N_EXTENSION_ALPN) { - continue; - } - RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); - } - - size_t iana_list_size = s2n_stuffer_data_available(iana_list); - size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; - if (iana_count == 0) { - return S2N_RESULT_OK; - } - - uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); - RESULT_ENSURE_REF(ianas); - qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); - RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_fingerprint_ja4_sig_algs(struct s2n_fingerprint_hash *hash, - struct s2n_client_hello *ch) -{ - RESULT_ENSURE_REF(ch); - - s2n_parsed_extension *extension = NULL; - int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SIGNATURE_ALGORITHMS, - &ch->extensions, &extension); - if (result != S2N_SUCCESS) { - return S2N_RESULT_OK; - } - RESULT_ENSURE_REF(extension); - - struct s2n_stuffer sig_algs = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&sig_algs, &extension->extension)); - - uint8_t entry_bytes[S2N_JA4_IANA_ENTRY_SIZE] = { 0 }; - struct s2n_stuffer entry = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&entry.blob, entry_bytes, sizeof(entry_bytes))); - - bool is_first = true; - if (s2n_stuffer_skip_read(&sig_algs, sizeof(uint16_t)) != S2N_SUCCESS) { - return S2N_RESULT_OK; - } - while (s2n_stuffer_data_available(&sig_algs)) { - uint16_t iana = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&sig_algs, &iana)); - if (s2n_fingerprint_is_grease_value(iana)) { - continue; - } - if (is_first) { - RESULT_GUARD(s2n_fingerprint_hash_add_char(hash, S2N_JA4_PART_DIV)); - } else { - RESULT_GUARD_POSIX(s2n_stuffer_write_char(&entry, S2N_JA4_LIST_DIV)); - } - RESULT_GUARD(s2n_stuffer_write_uint16_hex(&entry, iana)); - RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, entry_bytes, - s2n_stuffer_data_available(&entry))); - RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&entry)); - is_first = false; - } - return S2N_RESULT_OK; -} - -/** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# A 12 character truncated sha256 hash of the list of extensions, sorted by - *# hex value, followed by the list of signature algorithms, in the order that - *# they appear (not sorted). - */ -static S2N_RESULT s2n_fingerprint_ja4_c(struct s2n_fingerprint *fingerprint, - struct s2n_fingerprint_hash *hash, struct s2n_blob *extensions_count, - struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(fingerprint); - - uint16_t extensions_count_value = 0; - RESULT_GUARD(s2n_fingerprint_ja4_extensions(hash, fingerprint->client_hello, - &fingerprint->workspace, &extensions_count_value)); - - /** - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# The signature algorithm hex values are then added to the end of the list - *# in the order that they appear (not sorted) with an underscore delimiting - *# the two lists. - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash - *# If there are no signature algorithms in the hello packet, - *# then the string ends without an underscore and is hashed. - * - * s2n_fingerprint_ja4_sig_algs handles writing the underscore because we - * need to skip writing it if there are no signature algorithms. - */ - RESULT_GUARD(s2n_fingerprint_ja4_sig_algs(hash, fingerprint->client_hello)); - - RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); - RESULT_GUARD(s2n_fingerprint_ja4_count(extensions_count, extensions_count_value)); - return S2N_RESULT_OK; -} - -/* JA4 fingerprints are basically of the form a_b_c: - * - *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#ja4-algorithm - *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) - *# (2 character TLS version) - *# (SNI=”d” or no SNI=”i”) - *# (2 character count of ciphers) - *# (2 character count of extensions) - *# (first and last characters of first ALPN extension value) - *# _ - *# (sha256 hash of the list of cipher hex codes sorted in hex order, truncated to 12 characters) - *# _ - *# (sha256 hash of (the list of extension hex codes sorted in hex order)_(the list of signature algorithms), truncated to 12 characters) - *# - *# The end result is a fingerprint that looks like: - *# t13d1516h2_8daaf6152771_b186095e22b6 - */ -static S2N_RESULT s2n_fingerprint_ja4(struct s2n_fingerprint *fingerprint, - struct s2n_fingerprint_hash *hash, struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(fingerprint); - RESULT_ENSURE_REF(hash); - RESULT_ENSURE_REF(output); - - if (s2n_stuffer_is_freed(&fingerprint->workspace)) { - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&fingerprint->workspace, S2N_JA4_WORKSPACE_SIZE)); - } - - struct s2n_blob ciphers_count = { 0 }; - struct s2n_blob extensions_count = { 0 }; - RESULT_GUARD(s2n_fingerprint_ja4_a(fingerprint, output, &ciphers_count, &extensions_count)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); - RESULT_GUARD(s2n_fingerprint_ja4_b(fingerprint, hash, &ciphers_count, output)); - RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); - RESULT_GUARD(s2n_fingerprint_ja4_c(fingerprint, hash, &extensions_count, output)); - - if (s2n_fingerprint_hash_do_digest(hash)) { - /* The extra two bytes are for the characters separating the parts */ - fingerprint->raw_size = hash->bytes_digested + S2N_JA4_A_SIZE + 2; - } else { - fingerprint->raw_size = s2n_stuffer_data_available(output); - } - - return S2N_RESULT_OK; -} - -struct s2n_fingerprint_method ja4_fingerprint = { - .hash = S2N_HASH_SHA256, - .hash_str_size = S2N_JA4_SIZE, - .fingerprint = s2n_fingerprint_ja4, -}; +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_hash.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_client_supported_versions.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_fingerprint.h" +#include "tls/s2n_protocol_preferences.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +#define S2N_JA4_LIST_DIV ',' +#define S2N_JA4_PART_DIV '_' + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# 2 character number of cipher suites + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Same as counting ciphers. + */ +#define S2N_JA4_COUNT_SIZE 2 + +#define S2N_HEX_PER_BYTE 2 +#define S2N_JA4_DIGEST_HEX_CHAR_LIMIT 12 +#define S2N_JA4_DIGEST_BYTE_LIMIT (S2N_JA4_DIGEST_HEX_CHAR_LIMIT / S2N_HEX_PER_BYTE) + +#define S2N_JA4_A_SIZE 10 +#define S2N_JA4_B_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT +#define S2N_JA4_C_SIZE S2N_JA4_DIGEST_HEX_CHAR_LIMIT +#define S2N_JA4_SIZE (S2N_JA4_A_SIZE + 1 + S2N_JA4_B_SIZE + 1 + S2N_JA4_C_SIZE) + +#define S2N_JA4_LIST_LIMIT 99 +#define S2N_JA4_IANA_HEX_SIZE (S2N_HEX_PER_BYTE * sizeof(uint16_t)) +#define S2N_JA4_IANA_ENTRY_SIZE (S2N_JA4_IANA_HEX_SIZE + 1) +#define S2N_JA4_WORKSPACE_SIZE ((S2N_JA4_LIST_LIMIT * (S2N_JA4_IANA_ENTRY_SIZE))) + +const char *s2n_ja4_version_strings[] = { + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# 0x0304 = TLS 1.3 = “13” + *# 0x0303 = TLS 1.2 = “12” + *# 0x0302 = TLS 1.1 = “11” + *# 0x0301 = TLS 1.0 = “10” + */ + [0x0304] = "13", + [0x0303] = "12", + [0x0302] = "11", + [0x0301] = "10", + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# 0x0300 = SSL 3.0 = “s3” + *# 0x0002 = SSL 2.0 = “s2” + */ + [0x0300] = "s3", + [0x0002] = "s2", +}; + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# Unknown = “00” + */ +#define S2N_JA4_UNKNOWN_STR "00" + +DEFINE_POINTER_CLEANUP_FUNC(struct s2n_stuffer *, s2n_stuffer_wipe); + +static int s2n_fingerprint_ja4_iana_compare(const void *a, const void *b) +{ + const uint8_t *iana_a = (const uint8_t *) a; + const uint8_t *iana_b = (const uint8_t *) b; + for (size_t i = 0; i < S2N_JA4_IANA_HEX_SIZE; i++) { + if (iana_a[i] != iana_b[i]) { + return iana_a[i] - iana_b[i]; + } + } + return 0; +} + +static S2N_RESULT s2n_fingerprint_ja4_digest(struct s2n_fingerprint_hash *hash, + struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(hash); + if (!s2n_fingerprint_hash_do_digest(hash)) { + return S2N_RESULT_OK; + } + + /* Instead of hashing empty inputs, JA4 sets the output to a string of all zeroes. + * (Actually hashing an empty input doesn't produce a digest of all zeroes) + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash + *# If there are no ciphers in the sorted cipher list, then the value of + *# JA4_b is set to `000000000000` + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# If there are no extensions in the sorted extensions list, then the value of + *# JA4_c is set to `000000000000` + */ + uint64_t bytes = 0; + RESULT_GUARD_POSIX(s2n_hash_get_currently_in_hash_total(hash->hash, &bytes)); + if (bytes == 0) { + RESULT_GUARD_POSIX(s2n_stuffer_write_str(out, "000000000000")); + return S2N_RESULT_OK; + } + + uint8_t digest_bytes[SHA256_DIGEST_LENGTH] = { 0 }; + struct s2n_blob digest = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&digest, digest_bytes, sizeof(digest_bytes))); + RESULT_GUARD(s2n_fingerprint_hash_digest(hash, &digest)); + + /* JA4 digests are truncated */ + RESULT_ENSURE_LTE(S2N_JA4_DIGEST_BYTE_LIMIT, digest.size); + digest.size = S2N_JA4_DIGEST_BYTE_LIMIT; + RESULT_GUARD(s2n_stuffer_write_hex(out, &digest)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# 2 character number of cipher suites, so if there’s 6 cipher suites + *# in the hello packet, then the value should be “06”. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Same as counting ciphers. + */ +static S2N_RESULT s2n_fingerprint_ja4_count(struct s2n_blob *output, uint16_t count) +{ + RESULT_ENSURE_REF(output); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# If there’s > 99, which there should never be, then output “99”. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Same as counting ciphers. + */ + count = S2N_MIN(count, 99); + + RESULT_ENSURE_EQ(output->size, 2); + output->data[0] = (count / 10) + '0'; + output->data[1] = (count % 10) + '0'; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_get_extension_version(struct s2n_client_hello *ch, + uint16_t *client_version) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE_REF(client_version); + + s2n_parsed_extension *extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension( + S2N_EXTENSION_SUPPORTED_VERSIONS, &ch->extensions, &extension)); + RESULT_ENSURE_REF(extension); + + struct s2n_stuffer supported_versions = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions, &extension->extension)); + + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&supported_versions, sizeof(uint8_t))); + while (s2n_stuffer_data_available(&supported_versions)) { + uint16_t version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&supported_versions, &version)); + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# Remember to ignore GREASE values. + */ + if (s2n_fingerprint_is_grease_value(version)) { + continue; + } + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# If extension 0x002b exists (supported_versions), then the version is + *# the highest value in the extension. + */ + *client_version = S2N_MAX(*client_version, version); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_ja4_version(struct s2n_stuffer *output, + struct s2n_client_hello *ch) +{ + uint16_t client_version = 0; + if (s2n_result_is_error(s2n_fingerprint_get_extension_version(ch, &client_version))) { + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# If the extension doesn’t exist, then the TLS version is the value of + *# the Protocol Version. + */ + RESULT_GUARD(s2n_fingerprint_get_legacy_version(ch, &client_version)); + } + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#tls-and-dtls-version + *# Handshake version (located at the top of the packet) should be ignored. + */ + + const char *version_str = NULL; + if (client_version < s2n_array_len(s2n_ja4_version_strings)) { + version_str = s2n_ja4_version_strings[client_version]; + } + if (version_str == NULL) { + version_str = S2N_JA4_UNKNOWN_STR; + } + RESULT_GUARD_POSIX(s2n_stuffer_write_str(output, version_str)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_client_hello_get_first_alpn(struct s2n_client_hello *ch, struct s2n_blob *first) +{ + RESULT_ENSURE_REF(ch); + + s2n_parsed_extension *extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_ALPN, + &ch->extensions, &extension)); + RESULT_ENSURE_REF(extension); + + struct s2n_stuffer protocols = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&protocols, &extension->extension)); + + uint16_t list_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&protocols, &list_size)); + + RESULT_GUARD(s2n_protocol_preferences_read(&protocols, first)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# The first and last alphanumeric characters of the ALPN (Application-Layer + *# Protocol Negotiation) first value. + */ +static S2N_RESULT s2n_fingerprint_ja4_alpn(struct s2n_stuffer *output, + struct s2n_client_hello *ch) +{ + struct s2n_blob protocol = { 0 }; + if (s2n_result_is_error(s2n_client_hello_get_first_alpn(ch, &protocol))) { + protocol.size = 0; + } + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# If there is no ALPN extension, no ALPN values, or the first ALPN value + *# is empty, then we print "00" as the value in the fingerprint. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# If the first ALPN value is only a single character, then that character + *# is treated as both the first and last character. + */ + uint8_t first_char = '0', last_char = '0'; + if (protocol.size > 0) { + first_char = protocol.data[0]; + last_char = protocol.data[protocol.size - 1]; + } + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#alpn-extension-value + *# If the first or last byte of the first ALPN is non-alphanumeric (meaning + *# not `0x30-0x39`, `0x41-0x5A`, or `0x61-0x7A`), then we print the first and + *# last characters of the hex representation of the first ALPN instead. + */ + if (!isalnum(first_char) || !isalnum(last_char)) { + RESULT_GUARD(s2n_hex_digit((first_char >> 4), &first_char)); + RESULT_GUARD(s2n_hex_digit((last_char & 0x0F), &last_char)); + } + + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, first_char)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, last_char)); + return S2N_RESULT_OK; +} + +/* Part "a" of the fingerprint is a descriptive prefix. + * + * https://github.com/FoxIO-LLC/ja4/main/technical_details/JA4.md + *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) + *# (2 character TLS version) + *# (SNI=”d” or no SNI=”i”) + *# (2 character count of ciphers) + *# (2 character count of extensions) + *# (first and last characters of first ALPN extension value) + */ +static S2N_RESULT s2n_fingerprint_ja4_a(struct s2n_fingerprint *fingerprint, + struct s2n_stuffer *output, struct s2n_blob *ciphers_count, struct s2n_blob *extensions_count) +{ + RESULT_ENSURE_REF(fingerprint); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#quic-and-dtls + *# If the protocol is QUIC then the first character of the fingerprint is “q”, + *# if DTLS it is "d", else it is “t”. + * + * s2n-tls only supports TLS and QUIC. DTLS is not supported. + */ + bool is_quic = false; + RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, + TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS, &is_quic)); + char protocol_char = (is_quic) ? 'q' : 't'; + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, protocol_char)); + + RESULT_GUARD(s2n_fingerprint_ja4_version(output, fingerprint->client_hello)); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#sni + *# If the SNI extension (0x0000) exists, then the destination of the connection + *# is a domain, or “d” in the fingerprint. + *# If the SNI does not exist, then the destination is an IP address, or “i”. + */ + bool has_sni = false; + RESULT_GUARD_POSIX(s2n_client_hello_has_extension(fingerprint->client_hello, + TLS_EXTENSION_SERVER_NAME, &has_sni)); + char sni_char = (has_sni) ? 'd' : 'i'; + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, sni_char)); + + /* Reserve two characters for the "count of ciphers". + * We'll calculate it later when we handle the cipher suite list for JA4_b. + */ + uint8_t *ciphers_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); + RESULT_GUARD_PTR(ciphers_count_mem); + RESULT_GUARD_POSIX(s2n_blob_init(ciphers_count, ciphers_count_mem, S2N_JA4_COUNT_SIZE)); + + /* Reserve two characters for the "count of extensions". + * We'll calculate it later when we handle the extensions list for JA4_c. + */ + uint8_t *extensions_count_mem = s2n_stuffer_raw_write(output, S2N_JA4_COUNT_SIZE); + RESULT_GUARD_PTR(extensions_count_mem); + RESULT_GUARD_POSIX(s2n_blob_init(extensions_count, extensions_count_mem, S2N_JA4_COUNT_SIZE)); + + RESULT_GUARD(s2n_fingerprint_ja4_alpn(output, fingerprint->client_hello)); + + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash + *# The list is created using the 4 character hex values of the ciphers, + *# lower case, comma delimited, ignoring GREASE. + */ +static S2N_RESULT s2n_fingerprint_ja4_ciphers(struct s2n_fingerprint_hash *hash, + struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *ciphers_count) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE_REF(sort_space); + RESULT_ENSURE_REF(ciphers_count); + + struct s2n_stuffer cipher_suites = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&cipher_suites, &ch->cipher_suites)); + + DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); + while (s2n_stuffer_data_available(&cipher_suites)) { + uint16_t iana = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&cipher_suites, &iana)); + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-ciphers + *# Remember, ignore GREASE values. They don’t count. + */ + if (s2n_fingerprint_is_grease_value(iana)) { + continue; + } + RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); + } + + size_t iana_list_size = s2n_stuffer_data_available(iana_list); + size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; + *ciphers_count = iana_count; + if (iana_count == 0) { + return S2N_RESULT_OK; + } + + uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); + RESULT_ENSURE_REF(ianas); + qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); + RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#cipher-hash + *# A 12 character truncated sha256 hash of the list of ciphers sorted in hex order, + *# first 12 characters. + */ +static S2N_RESULT s2n_fingerprint_ja4_b(struct s2n_fingerprint *fingerprint, + struct s2n_fingerprint_hash *hash, struct s2n_blob *ciphers_count, + struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(fingerprint); + + uint16_t ciphers_count_value = 0; + RESULT_GUARD(s2n_fingerprint_ja4_ciphers(hash, fingerprint->client_hello, + &fingerprint->workspace, &ciphers_count_value)); + + RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); + RESULT_GUARD(s2n_fingerprint_ja4_count(ciphers_count, ciphers_count_value)); + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# The extension list is created using the 4 character hex values of the extensions, + *# lower case, comma delimited, sorted (not in the order they appear). + */ +static S2N_RESULT s2n_fingerprint_ja4_extensions(struct s2n_fingerprint_hash *hash, + struct s2n_client_hello *ch, struct s2n_stuffer *sort_space, uint16_t *extensions_count) +{ + RESULT_ENSURE_REF(ch); + RESULT_ENSURE_REF(sort_space); + RESULT_ENSURE_REF(extensions_count); + + struct s2n_stuffer extensions = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&extensions, &ch->extensions.raw)); + + DEFER_CLEANUP(struct s2n_stuffer *iana_list = sort_space, s2n_stuffer_wipe_pointer); + while (s2n_stuffer_data_available(&extensions)) { + uint16_t iana = 0; + RESULT_GUARD(s2n_fingerprint_parse_extension(&extensions, &iana)); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Ignore GREASE. + */ + if (s2n_fingerprint_is_grease_value(iana)) { + continue; + } + + /* SNI and ALPN are included in the extension count, but not in the extension list. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# Ignore the SNI extension (0000) and the ALPN extension (0010) + *# as we’ve already captured them in the _a_ section of the fingerprint. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#number-of-extensions + *# Include SNI and ALPN. + */ + (*extensions_count)++; + if (iana == TLS_EXTENSION_SERVER_NAME || iana == S2N_EXTENSION_ALPN) { + continue; + } + RESULT_GUARD(s2n_stuffer_write_uint16_hex(iana_list, iana)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(iana_list, S2N_JA4_LIST_DIV)); + } + + size_t iana_list_size = s2n_stuffer_data_available(iana_list); + size_t iana_count = iana_list_size / S2N_JA4_IANA_ENTRY_SIZE; + if (iana_count == 0) { + return S2N_RESULT_OK; + } + + uint8_t *ianas = s2n_stuffer_raw_read(iana_list, iana_list_size); + RESULT_ENSURE_REF(ianas); + qsort(ianas, iana_count, S2N_JA4_IANA_ENTRY_SIZE, s2n_fingerprint_ja4_iana_compare); + RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, ianas, iana_list_size - 1)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_fingerprint_ja4_sig_algs(struct s2n_fingerprint_hash *hash, + struct s2n_client_hello *ch) +{ + RESULT_ENSURE_REF(ch); + + s2n_parsed_extension *extension = NULL; + int result = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SIGNATURE_ALGORITHMS, + &ch->extensions, &extension); + if (result != S2N_SUCCESS) { + return S2N_RESULT_OK; + } + RESULT_ENSURE_REF(extension); + + struct s2n_stuffer sig_algs = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&sig_algs, &extension->extension)); + + uint8_t entry_bytes[S2N_JA4_IANA_ENTRY_SIZE] = { 0 }; + struct s2n_stuffer entry = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&entry.blob, entry_bytes, sizeof(entry_bytes))); + + bool is_first = true; + if (s2n_stuffer_skip_read(&sig_algs, sizeof(uint16_t)) != S2N_SUCCESS) { + return S2N_RESULT_OK; + } + while (s2n_stuffer_data_available(&sig_algs)) { + uint16_t iana = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&sig_algs, &iana)); + if (s2n_fingerprint_is_grease_value(iana)) { + continue; + } + if (is_first) { + RESULT_GUARD(s2n_fingerprint_hash_add_char(hash, S2N_JA4_PART_DIV)); + } else { + RESULT_GUARD_POSIX(s2n_stuffer_write_char(&entry, S2N_JA4_LIST_DIV)); + } + RESULT_GUARD(s2n_stuffer_write_uint16_hex(&entry, iana)); + RESULT_GUARD(s2n_fingerprint_hash_add_bytes(hash, entry_bytes, + s2n_stuffer_data_available(&entry))); + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&entry)); + is_first = false; + } + return S2N_RESULT_OK; +} + +/** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# A 12 character truncated sha256 hash of the list of extensions, sorted by + *# hex value, followed by the list of signature algorithms, in the order that + *# they appear (not sorted). + */ +static S2N_RESULT s2n_fingerprint_ja4_c(struct s2n_fingerprint *fingerprint, + struct s2n_fingerprint_hash *hash, struct s2n_blob *extensions_count, + struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(fingerprint); + + uint16_t extensions_count_value = 0; + RESULT_GUARD(s2n_fingerprint_ja4_extensions(hash, fingerprint->client_hello, + &fingerprint->workspace, &extensions_count_value)); + + /** + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# The signature algorithm hex values are then added to the end of the list + *# in the order that they appear (not sorted) with an underscore delimiting + *# the two lists. + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#extension-hash + *# If there are no signature algorithms in the hello packet, + *# then the string ends without an underscore and is hashed. + * + * s2n_fingerprint_ja4_sig_algs handles writing the underscore because we + * need to skip writing it if there are no signature algorithms. + */ + RESULT_GUARD(s2n_fingerprint_ja4_sig_algs(hash, fingerprint->client_hello)); + + RESULT_GUARD(s2n_fingerprint_ja4_digest(hash, output)); + RESULT_GUARD(s2n_fingerprint_ja4_count(extensions_count, extensions_count_value)); + return S2N_RESULT_OK; +} + +/* JA4 fingerprints are basically of the form a_b_c: + * + *= https://raw.githubusercontent.com/FoxIO-LLC/ja4/df3c067/technical_details/JA4.md#ja4-algorithm + *# (QUIC=”q”, DTLS="d", or Normal TLS=”t”) + *# (2 character TLS version) + *# (SNI=”d” or no SNI=”i”) + *# (2 character count of ciphers) + *# (2 character count of extensions) + *# (first and last characters of first ALPN extension value) + *# _ + *# (sha256 hash of the list of cipher hex codes sorted in hex order, truncated to 12 characters) + *# _ + *# (sha256 hash of (the list of extension hex codes sorted in hex order)_(the list of signature algorithms), truncated to 12 characters) + *# + *# The end result is a fingerprint that looks like: + *# t13d1516h2_8daaf6152771_b186095e22b6 + */ +static S2N_RESULT s2n_fingerprint_ja4(struct s2n_fingerprint *fingerprint, + struct s2n_fingerprint_hash *hash, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(fingerprint); + RESULT_ENSURE_REF(hash); + RESULT_ENSURE_REF(output); + + if (s2n_stuffer_is_freed(&fingerprint->workspace)) { + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&fingerprint->workspace, S2N_JA4_WORKSPACE_SIZE)); + } + + struct s2n_blob ciphers_count = { 0 }; + struct s2n_blob extensions_count = { 0 }; + RESULT_GUARD(s2n_fingerprint_ja4_a(fingerprint, output, &ciphers_count, &extensions_count)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); + RESULT_GUARD(s2n_fingerprint_ja4_b(fingerprint, hash, &ciphers_count, output)); + RESULT_GUARD_POSIX(s2n_stuffer_write_char(output, S2N_JA4_PART_DIV)); + RESULT_GUARD(s2n_fingerprint_ja4_c(fingerprint, hash, &extensions_count, output)); + + if (s2n_fingerprint_hash_do_digest(hash)) { + /* The extra two bytes are for the characters separating the parts */ + fingerprint->raw_size = hash->bytes_digested + S2N_JA4_A_SIZE + 2; + } else { + fingerprint->raw_size = s2n_stuffer_data_available(output); + } + + return S2N_RESULT_OK; +} + +struct s2n_fingerprint_method ja4_fingerprint = { + .hash = S2N_HASH_SHA256, + .hash_str_size = S2N_JA4_SIZE, + .fingerprint = s2n_fingerprint_ja4, +}; diff --git a/tls/s2n_handshake.c b/tls/s2n_handshake.c index f8d471d5e7f..139e65bab88 100644 --- a/tls/s2n_handshake.c +++ b/tls/s2n_handshake.c @@ -1,383 +1,383 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_map.h" -#include "utils/s2n_safety.h" - -int s2n_handshake_write_header(struct s2n_stuffer *out, uint8_t message_type) -{ - S2N_ERROR_IF(s2n_stuffer_data_available(out), S2N_ERR_HANDSHAKE_STATE); - - /* Write the message header */ - POSIX_GUARD(s2n_stuffer_write_uint8(out, message_type)); - - /* Leave the length blank for now */ - uint16_t length = 0; - POSIX_GUARD(s2n_stuffer_write_uint24(out, length)); - - return S2N_SUCCESS; -} - -int s2n_handshake_finish_header(struct s2n_stuffer *out) -{ - uint32_t length = s2n_stuffer_data_available(out); - S2N_ERROR_IF(length < TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); - - uint32_t payload = length - TLS_HANDSHAKE_HEADER_LENGTH; - - /* Write the message header */ - POSIX_GUARD(s2n_stuffer_rewrite(out)); - POSIX_GUARD(s2n_stuffer_skip_write(out, 1)); - POSIX_GUARD(s2n_stuffer_write_uint24(out, payload)); - POSIX_GUARD(s2n_stuffer_skip_write(out, payload)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_handshake_parse_header(struct s2n_stuffer *io, uint8_t *message_type, uint32_t *length) -{ - RESULT_ENSURE(s2n_stuffer_data_available(io) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); - - /* read the message header */ - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(io, message_type)); - RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(io, length)); - - return S2N_RESULT_OK; -} - -static int s2n_handshake_get_hash_state_ptr(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state **hash_state) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - switch (hash_alg) { - case S2N_HASH_MD5: - *hash_state = &conn->handshake.hashes->md5; - break; - case S2N_HASH_SHA1: - *hash_state = &conn->handshake.hashes->sha1; - break; - case S2N_HASH_SHA224: - *hash_state = &conn->handshake.hashes->sha224; - break; - case S2N_HASH_SHA256: - *hash_state = &conn->handshake.hashes->sha256; - break; - case S2N_HASH_SHA384: - *hash_state = &conn->handshake.hashes->sha384; - break; - case S2N_HASH_SHA512: - *hash_state = &conn->handshake.hashes->sha512; - break; - case S2N_HASH_MD5_SHA1: - *hash_state = &conn->handshake.hashes->md5_sha1; - break; - default: - POSIX_BAIL(S2N_ERR_HASH_INVALID_ALGORITHM); - break; - } - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg) -{ - struct s2n_hash_state *hash_state = NULL; - RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); - RESULT_GUARD_POSIX(s2n_hash_reset(hash_state)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_handshake_copy_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *copy) -{ - struct s2n_hash_state *hash_state = NULL; - RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); - RESULT_GUARD_POSIX(s2n_hash_copy(copy, hash_state)); - return S2N_RESULT_OK; -} - -int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake) -{ - memset(handshake->required_hash_algs, 1, sizeof(handshake->required_hash_algs)); - return S2N_SUCCESS; -} - -static int s2n_handshake_require_hash(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) -{ - handshake->required_hash_algs[hash_alg] = 1; - return S2N_SUCCESS; -} - -uint8_t s2n_handshake_is_hash_required(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) -{ - return handshake->required_hash_algs[hash_alg]; -} - -/* Update the required handshake hash algs depending on current handshake session state. - * This function must called at the end of a handshake message handler. Additionally it must be called after the - * ClientHello or ServerHello is processed in client and server mode respectively. The relevant handshake parameters - * are not available until those messages are processed. - */ -int s2n_conn_update_required_handshake_hashes(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - /* Clear all of the required hashes */ - memset(conn->handshake.required_hash_algs, 0, sizeof(conn->handshake.required_hash_algs)); - - if (conn->actual_protocol_version < S2N_TLS13) { - message_type_t handshake_message = s2n_conn_get_current_message_type(conn); - const uint8_t client_cert_verify_done = (handshake_message >= CLIENT_CERT_VERIFY) ? 1 : 0; - s2n_cert_auth_type client_cert_auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - - /* In TLS1.2 the transcript hash used in the client's certificate verify message - * is determined by the signature algorithm used to sign the certificate verify message. - * Therefore all hashes are needed until we're past CLIENT_CERT_VERIFY if client auth is possible. */ - if ((client_cert_auth_type != S2N_CERT_AUTH_NONE) && !client_cert_verify_done) { - POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); - return S2N_SUCCESS; - } - } - - /* We don't need all of the hashes. Set the hash alg(s) required for the PRF */ - switch (conn->actual_protocol_version) { - case S2N_SSLv3: - case S2N_TLS10: - case S2N_TLS11: - POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_MD5)); - POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_SHA1)); - break; - case S2N_TLS12: - /* fall through */ - case S2N_TLS13: { - /* For TLS 1.2 and TLS 1.3, the cipher suite defines the PRF hash alg */ - s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; - s2n_hash_algorithm hash_alg; - POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); - POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, hash_alg)); - break; - } - } - - return S2N_SUCCESS; -} - -/* - * Take a hostname and return a single "simple" wildcard domain name that matches it. - * The output wildcard representation is meant to be compared directly against a wildcard domain in a certificate. - * We take a restrictive definition of wildcard here to achieve a single unique wildcard representation - * given any input hostname. - * No embedded or trailing wildcards are supported. Additionally, we only support one level of wildcard matching. - * Thus the output should be a single wildcard character in the first(left-most) DNS label. - * - * Example: - * - my.domain.name -> *.domain.name - * - * Not supported: - * - my.domain.name -> m*.domain.name - * - my.domain.name -> my.*.name - * etc. - * - * The motivation for using a constrained definition of wildcard: - * - Support for issuing non-simple wildcard certificates is insignificant. - * - Certificate selection can be implemented with a constant number of lookups(two). - */ -int s2n_create_wildcard_hostname(struct s2n_stuffer *hostname_stuffer, struct s2n_stuffer *output) -{ - /* Find the end of the first label */ - POSIX_GUARD(s2n_stuffer_skip_to_char(hostname_stuffer, '.')); - - /* No first label found */ - if (s2n_stuffer_data_available(hostname_stuffer) == 0) { - return S2N_SUCCESS; - } - - /* Slap a single wildcard character to be the first label in output */ - POSIX_GUARD(s2n_stuffer_write_uint8(output, '*')); - - /* Simply copy the rest of the input to the output. */ - POSIX_GUARD(s2n_stuffer_copy(hostname_stuffer, output, s2n_stuffer_data_available(hostname_stuffer))); - - return S2N_SUCCESS; -} - -static int s2n_find_cert_matches(struct s2n_map *domain_name_to_cert_map, - struct s2n_blob *dns_name, - struct s2n_cert_chain_and_key *matches[S2N_CERT_TYPE_COUNT], - uint8_t *match_exists) -{ - struct s2n_blob map_value = { 0 }; - bool key_found = false; - POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, dns_name, &map_value, &key_found)); - if (key_found) { - struct certs_by_type *value = (void *) map_value.data; - for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { - matches[i] = value->certs[i]; - } - *match_exists = 1; - } - - return S2N_SUCCESS; -} - -/* Find certificates that match the ServerName TLS extension sent by the client. - * For a given ServerName there can be multiple matching certificates based on the - * type of key in the certificate. - * - * A match is determined using s2n_map lookup by DNS name. - * Wildcards that have a single * in the left most label are supported. - */ -int s2n_conn_find_name_matching_certs(struct s2n_connection *conn) -{ - if (!s2n_server_received_server_name(conn)) { - return S2N_SUCCESS; - } - const char *name = conn->server_name; - struct s2n_blob hostname_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) name, strlen(name))); - POSIX_ENSURE_LTE(hostname_blob.size, S2N_MAX_SERVER_NAME); - char normalized_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; - POSIX_CHECKED_MEMCPY(normalized_hostname, hostname_blob.data, hostname_blob.size); - struct s2n_blob normalized_name = { 0 }; - POSIX_GUARD(s2n_blob_init(&normalized_name, (uint8_t *) normalized_hostname, hostname_blob.size)); - - POSIX_GUARD(s2n_blob_char_to_lower(&normalized_name)); - struct s2n_stuffer normalized_hostname_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&normalized_hostname_stuffer, &normalized_name)); - POSIX_GUARD(s2n_stuffer_skip_write(&normalized_hostname_stuffer, normalized_name.size)); - - /* Find the exact matches for the ServerName */ - POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, - &normalized_name, - conn->handshake_params.exact_sni_matches, - &(conn->handshake_params.exact_sni_match_exists))); - - if (!conn->handshake_params.exact_sni_match_exists) { - /* We have not yet found an exact domain match. Try to find wildcard matches. */ - char wildcard_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; - struct s2n_blob wildcard_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&wildcard_blob, (uint8_t *) wildcard_hostname, sizeof(wildcard_hostname))); - struct s2n_stuffer wildcard_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&wildcard_stuffer, &wildcard_blob)); - POSIX_GUARD(s2n_create_wildcard_hostname(&normalized_hostname_stuffer, &wildcard_stuffer)); - const uint32_t wildcard_len = s2n_stuffer_data_available(&wildcard_stuffer); - - /* Couldn't create a valid wildcard from the input */ - if (wildcard_len == 0) { - return S2N_SUCCESS; - } - - /* The client's SNI is wildcardified, do an exact match against the set of server certs. */ - wildcard_blob.size = wildcard_len; - POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, - &wildcard_blob, - conn->handshake_params.wc_sni_matches, - &(conn->handshake_params.wc_sni_match_exists))); - } - - /* If we found a suitable cert, we should send back the ServerName extension. - * Note that this may have already been set by the client hello callback, so we won't override its value - */ - conn->server_name_used = conn->server_name_used - || conn->handshake_params.exact_sni_match_exists - || conn->handshake_params.wc_sni_match_exists; - - return S2N_SUCCESS; -} - -/* Find the optimal certificate of a specific type. - * The priority of set of certificates to choose from: - * 1. Certificates that match the client's ServerName extension. - * 2. Default certificates - */ -struct s2n_cert_chain_and_key *s2n_get_compatible_cert_chain_and_key(struct s2n_connection *conn, const s2n_pkey_type cert_type) -{ - if (conn->handshake_params.exact_sni_match_exists) { - /* This may return NULL if there was an SNI match, but not a match the cipher_suite's authentication type. */ - return conn->handshake_params.exact_sni_matches[cert_type]; - } - if (conn->handshake_params.wc_sni_match_exists) { - return conn->handshake_params.wc_sni_matches[cert_type]; - } else { - /* We don't have any name matches. Use the default certificate that works with the key type. */ - return conn->config->default_certs_by_type.certs[cert_type]; - } -} - -/* This method will work when testing S2N, and for the EndOfEarlyData message. - * - * However, it will NOT work for arbitrary message types when potentially receiving records - * that contain multiple messages, like when talking to a non-S2N TLS implementation. If the "end_message" - * is not the first message in a multi-message record, negotiation will not stop. - * (This is not an issue for EndOfEarlyData because encryption and message order requirements force - * EndOfEarlyData to always be the first and only handshake message in its handshake record) - */ -S2N_RESULT s2n_negotiate_until_message(struct s2n_connection *conn, s2n_blocked_status *blocked, message_type_t end_message) -{ - RESULT_ENSURE_REF(conn); - conn->handshake.end_of_messages = end_message; - int r = s2n_negotiate(conn, blocked); - conn->handshake.end_of_messages = APPLICATION_DATA; - RESULT_GUARD_POSIX(r); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_handshake_validate(const struct s2n_handshake *s2n_handshake) -{ - RESULT_ENSURE_REF(s2n_handshake); - RESULT_DEBUG_ENSURE(s2n_handshake->handshake_type < 256, S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(s2n_handshake->message_number >= 0 && s2n_handshake->message_number < 32, S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_handshake_set_finished_len(struct s2n_connection *conn, uint8_t len) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_GT(len, 0); - RESULT_ENSURE_LTE(len, sizeof(conn->handshake.server_finished)); - RESULT_ENSURE_LTE(len, sizeof(conn->handshake.client_finished)); - - /* - * We maintain a version of the "finished" / "verify_data" field - * for both the client and server, so this method will be called - * once for the client version and once for the server version. - * - * The lengths of both versions must match, or something has - * gone wrong in our implementation. - */ - uint8_t *finished_length = &conn->handshake.finished_len; - if (*finished_length == 0) { - *finished_length = len; - } - RESULT_ENSURE_EQ(*finished_length, len); - - return S2N_RESULT_OK; -} - -bool s2n_handshake_is_renegotiation(struct s2n_connection *conn) -{ - return conn && conn->handshake.renegotiation; -} +#include "utils/s2n_prelude.h" +/* + * 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 "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_map.h" +#include "utils/s2n_safety.h" + +int s2n_handshake_write_header(struct s2n_stuffer *out, uint8_t message_type) +{ + S2N_ERROR_IF(s2n_stuffer_data_available(out), S2N_ERR_HANDSHAKE_STATE); + + /* Write the message header */ + POSIX_GUARD(s2n_stuffer_write_uint8(out, message_type)); + + /* Leave the length blank for now */ + uint16_t length = 0; + POSIX_GUARD(s2n_stuffer_write_uint24(out, length)); + + return S2N_SUCCESS; +} + +int s2n_handshake_finish_header(struct s2n_stuffer *out) +{ + uint32_t length = s2n_stuffer_data_available(out); + S2N_ERROR_IF(length < TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); + + uint32_t payload = length - TLS_HANDSHAKE_HEADER_LENGTH; + + /* Write the message header */ + POSIX_GUARD(s2n_stuffer_rewrite(out)); + POSIX_GUARD(s2n_stuffer_skip_write(out, 1)); + POSIX_GUARD(s2n_stuffer_write_uint24(out, payload)); + POSIX_GUARD(s2n_stuffer_skip_write(out, payload)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_handshake_parse_header(struct s2n_stuffer *io, uint8_t *message_type, uint32_t *length) +{ + RESULT_ENSURE(s2n_stuffer_data_available(io) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_SIZE_MISMATCH); + + /* read the message header */ + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(io, message_type)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(io, length)); + + return S2N_RESULT_OK; +} + +static int s2n_handshake_get_hash_state_ptr(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state **hash_state) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + switch (hash_alg) { + case S2N_HASH_MD5: + *hash_state = &conn->handshake.hashes->md5; + break; + case S2N_HASH_SHA1: + *hash_state = &conn->handshake.hashes->sha1; + break; + case S2N_HASH_SHA224: + *hash_state = &conn->handshake.hashes->sha224; + break; + case S2N_HASH_SHA256: + *hash_state = &conn->handshake.hashes->sha256; + break; + case S2N_HASH_SHA384: + *hash_state = &conn->handshake.hashes->sha384; + break; + case S2N_HASH_SHA512: + *hash_state = &conn->handshake.hashes->sha512; + break; + case S2N_HASH_MD5_SHA1: + *hash_state = &conn->handshake.hashes->md5_sha1; + break; + default: + POSIX_BAIL(S2N_ERR_HASH_INVALID_ALGORITHM); + break; + } + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_handshake_reset_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg) +{ + struct s2n_hash_state *hash_state = NULL; + RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); + RESULT_GUARD_POSIX(s2n_hash_reset(hash_state)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_handshake_copy_hash_state(struct s2n_connection *conn, s2n_hash_algorithm hash_alg, struct s2n_hash_state *copy) +{ + struct s2n_hash_state *hash_state = NULL; + RESULT_GUARD_POSIX(s2n_handshake_get_hash_state_ptr(conn, hash_alg, &hash_state)); + RESULT_GUARD_POSIX(s2n_hash_copy(copy, hash_state)); + return S2N_RESULT_OK; +} + +int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake) +{ + memset(handshake->required_hash_algs, 1, sizeof(handshake->required_hash_algs)); + return S2N_SUCCESS; +} + +static int s2n_handshake_require_hash(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) +{ + handshake->required_hash_algs[hash_alg] = 1; + return S2N_SUCCESS; +} + +uint8_t s2n_handshake_is_hash_required(struct s2n_handshake *handshake, s2n_hash_algorithm hash_alg) +{ + return handshake->required_hash_algs[hash_alg]; +} + +/* Update the required handshake hash algs depending on current handshake session state. + * This function must called at the end of a handshake message handler. Additionally it must be called after the + * ClientHello or ServerHello is processed in client and server mode respectively. The relevant handshake parameters + * are not available until those messages are processed. + */ +int s2n_conn_update_required_handshake_hashes(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + /* Clear all of the required hashes */ + memset(conn->handshake.required_hash_algs, 0, sizeof(conn->handshake.required_hash_algs)); + + if (conn->actual_protocol_version < S2N_TLS13) { + message_type_t handshake_message = s2n_conn_get_current_message_type(conn); + const uint8_t client_cert_verify_done = (handshake_message >= CLIENT_CERT_VERIFY) ? 1 : 0; + s2n_cert_auth_type client_cert_auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + /* In TLS1.2 the transcript hash used in the client's certificate verify message + * is determined by the signature algorithm used to sign the certificate verify message. + * Therefore all hashes are needed until we're past CLIENT_CERT_VERIFY if client auth is possible. */ + if ((client_cert_auth_type != S2N_CERT_AUTH_NONE) && !client_cert_verify_done) { + POSIX_GUARD(s2n_handshake_require_all_hashes(&conn->handshake)); + return S2N_SUCCESS; + } + } + + /* We don't need all of the hashes. Set the hash alg(s) required for the PRF */ + switch (conn->actual_protocol_version) { + case S2N_SSLv3: + case S2N_TLS10: + case S2N_TLS11: + POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_MD5)); + POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, S2N_HASH_SHA1)); + break; + case S2N_TLS12: + /* fall through */ + case S2N_TLS13: { + /* For TLS 1.2 and TLS 1.3, the cipher suite defines the PRF hash alg */ + s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + POSIX_GUARD(s2n_handshake_require_hash(&conn->handshake, hash_alg)); + break; + } + } + + return S2N_SUCCESS; +} + +/* + * Take a hostname and return a single "simple" wildcard domain name that matches it. + * The output wildcard representation is meant to be compared directly against a wildcard domain in a certificate. + * We take a restrictive definition of wildcard here to achieve a single unique wildcard representation + * given any input hostname. + * No embedded or trailing wildcards are supported. Additionally, we only support one level of wildcard matching. + * Thus the output should be a single wildcard character in the first(left-most) DNS label. + * + * Example: + * - my.domain.name -> *.domain.name + * + * Not supported: + * - my.domain.name -> m*.domain.name + * - my.domain.name -> my.*.name + * etc. + * + * The motivation for using a constrained definition of wildcard: + * - Support for issuing non-simple wildcard certificates is insignificant. + * - Certificate selection can be implemented with a constant number of lookups(two). + */ +int s2n_create_wildcard_hostname(struct s2n_stuffer *hostname_stuffer, struct s2n_stuffer *output) +{ + /* Find the end of the first label */ + POSIX_GUARD(s2n_stuffer_skip_to_char(hostname_stuffer, '.')); + + /* No first label found */ + if (s2n_stuffer_data_available(hostname_stuffer) == 0) { + return S2N_SUCCESS; + } + + /* Slap a single wildcard character to be the first label in output */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, '*')); + + /* Simply copy the rest of the input to the output. */ + POSIX_GUARD(s2n_stuffer_copy(hostname_stuffer, output, s2n_stuffer_data_available(hostname_stuffer))); + + return S2N_SUCCESS; +} + +static int s2n_find_cert_matches(struct s2n_map *domain_name_to_cert_map, + struct s2n_blob *dns_name, + struct s2n_cert_chain_and_key *matches[S2N_CERT_TYPE_COUNT], + uint8_t *match_exists) +{ + struct s2n_blob map_value = { 0 }; + bool key_found = false; + POSIX_GUARD_RESULT(s2n_map_lookup(domain_name_to_cert_map, dns_name, &map_value, &key_found)); + if (key_found) { + struct certs_by_type *value = (void *) map_value.data; + for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) { + matches[i] = value->certs[i]; + } + *match_exists = 1; + } + + return S2N_SUCCESS; +} + +/* Find certificates that match the ServerName TLS extension sent by the client. + * For a given ServerName there can be multiple matching certificates based on the + * type of key in the certificate. + * + * A match is determined using s2n_map lookup by DNS name. + * Wildcards that have a single * in the left most label are supported. + */ +int s2n_conn_find_name_matching_certs(struct s2n_connection *conn) +{ + if (!s2n_server_received_server_name(conn)) { + return S2N_SUCCESS; + } + const char *name = conn->server_name; + struct s2n_blob hostname_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) name, strlen(name))); + POSIX_ENSURE_LTE(hostname_blob.size, S2N_MAX_SERVER_NAME); + char normalized_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; + POSIX_CHECKED_MEMCPY(normalized_hostname, hostname_blob.data, hostname_blob.size); + struct s2n_blob normalized_name = { 0 }; + POSIX_GUARD(s2n_blob_init(&normalized_name, (uint8_t *) normalized_hostname, hostname_blob.size)); + + POSIX_GUARD(s2n_blob_char_to_lower(&normalized_name)); + struct s2n_stuffer normalized_hostname_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&normalized_hostname_stuffer, &normalized_name)); + POSIX_GUARD(s2n_stuffer_skip_write(&normalized_hostname_stuffer, normalized_name.size)); + + /* Find the exact matches for the ServerName */ + POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, + &normalized_name, + conn->handshake_params.exact_sni_matches, + &(conn->handshake_params.exact_sni_match_exists))); + + if (!conn->handshake_params.exact_sni_match_exists) { + /* We have not yet found an exact domain match. Try to find wildcard matches. */ + char wildcard_hostname[S2N_MAX_SERVER_NAME + 1] = { 0 }; + struct s2n_blob wildcard_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&wildcard_blob, (uint8_t *) wildcard_hostname, sizeof(wildcard_hostname))); + struct s2n_stuffer wildcard_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&wildcard_stuffer, &wildcard_blob)); + POSIX_GUARD(s2n_create_wildcard_hostname(&normalized_hostname_stuffer, &wildcard_stuffer)); + const uint32_t wildcard_len = s2n_stuffer_data_available(&wildcard_stuffer); + + /* Couldn't create a valid wildcard from the input */ + if (wildcard_len == 0) { + return S2N_SUCCESS; + } + + /* The client's SNI is wildcardified, do an exact match against the set of server certs. */ + wildcard_blob.size = wildcard_len; + POSIX_GUARD(s2n_find_cert_matches(conn->config->domain_name_to_cert_map, + &wildcard_blob, + conn->handshake_params.wc_sni_matches, + &(conn->handshake_params.wc_sni_match_exists))); + } + + /* If we found a suitable cert, we should send back the ServerName extension. + * Note that this may have already been set by the client hello callback, so we won't override its value + */ + conn->server_name_used = conn->server_name_used + || conn->handshake_params.exact_sni_match_exists + || conn->handshake_params.wc_sni_match_exists; + + return S2N_SUCCESS; +} + +/* Find the optimal certificate of a specific type. + * The priority of set of certificates to choose from: + * 1. Certificates that match the client's ServerName extension. + * 2. Default certificates + */ +struct s2n_cert_chain_and_key *s2n_get_compatible_cert_chain_and_key(struct s2n_connection *conn, const s2n_pkey_type cert_type) +{ + if (conn->handshake_params.exact_sni_match_exists) { + /* This may return NULL if there was an SNI match, but not a match the cipher_suite's authentication type. */ + return conn->handshake_params.exact_sni_matches[cert_type]; + } + if (conn->handshake_params.wc_sni_match_exists) { + return conn->handshake_params.wc_sni_matches[cert_type]; + } else { + /* We don't have any name matches. Use the default certificate that works with the key type. */ + return conn->config->default_certs_by_type.certs[cert_type]; + } +} + +/* This method will work when testing S2N, and for the EndOfEarlyData message. + * + * However, it will NOT work for arbitrary message types when potentially receiving records + * that contain multiple messages, like when talking to a non-S2N TLS implementation. If the "end_message" + * is not the first message in a multi-message record, negotiation will not stop. + * (This is not an issue for EndOfEarlyData because encryption and message order requirements force + * EndOfEarlyData to always be the first and only handshake message in its handshake record) + */ +S2N_RESULT s2n_negotiate_until_message(struct s2n_connection *conn, s2n_blocked_status *blocked, message_type_t end_message) +{ + RESULT_ENSURE_REF(conn); + conn->handshake.end_of_messages = end_message; + int r = s2n_negotiate(conn, blocked); + conn->handshake.end_of_messages = APPLICATION_DATA; + RESULT_GUARD_POSIX(r); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_handshake_validate(const struct s2n_handshake *s2n_handshake) +{ + RESULT_ENSURE_REF(s2n_handshake); + RESULT_DEBUG_ENSURE(s2n_handshake->handshake_type < 256, S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(s2n_handshake->message_number >= 0 && s2n_handshake->message_number < 32, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_handshake_set_finished_len(struct s2n_connection *conn, uint8_t len) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_GT(len, 0); + RESULT_ENSURE_LTE(len, sizeof(conn->handshake.server_finished)); + RESULT_ENSURE_LTE(len, sizeof(conn->handshake.client_finished)); + + /* + * We maintain a version of the "finished" / "verify_data" field + * for both the client and server, so this method will be called + * once for the client version and once for the server version. + * + * The lengths of both versions must match, or something has + * gone wrong in our implementation. + */ + uint8_t *finished_length = &conn->handshake.finished_len; + if (*finished_length == 0) { + *finished_length = len; + } + RESULT_ENSURE_EQ(*finished_length, len); + + return S2N_RESULT_OK; +} + +bool s2n_handshake_is_renegotiation(struct s2n_connection *conn) +{ + return conn && conn->handshake.renegotiation; +} diff --git a/tls/s2n_handshake_io.c b/tls/s2n_handshake_io.c index 024f9d72229..c29d7d32620 100644 --- a/tls/s2n_handshake_io.c +++ b/tls/s2n_handshake_io.c @@ -1,1752 +1,1752 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_async_pkey.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_kex.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls13_key_schedule.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_events.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" - -/* clang-format off */ -struct s2n_handshake_action { - uint8_t record_type; - uint8_t message_type; - char writer; /* 'S' or 'C' for server or client, 'B' for both */ - int (*handler[2]) (struct s2n_connection * conn); -}; - -static int s2n_always_fail_send(struct s2n_connection *conn) -{ - /* This state should never be sending a handshake message. */ - POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); -} - -static int s2n_always_fail_recv(struct s2n_connection *conn) -{ - /* This state should never have an incoming handshake message. */ - POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); -} - -/* Client and Server handlers for each message type we support. - * See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7 for the list of handshake message types - */ -static struct s2n_handshake_action state_machine[] = { - /* message_type_t = {Record type Message type Writer S2N_SERVER S2N_CLIENT } */ - [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, - [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, - [SERVER_NEW_SESSION_TICKET] = {TLS_HANDSHAKE, TLS_SERVER_NEW_SESSION_TICKET,'S', {s2n_server_nst_send, s2n_server_nst_recv}}, - [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, - [SERVER_CERT_STATUS] = {TLS_HANDSHAKE, TLS_SERVER_CERT_STATUS, 'S', {s2n_server_status_send, s2n_server_status_recv}}, - [SERVER_KEY] = {TLS_HANDSHAKE, TLS_SERVER_KEY, 'S', {s2n_server_key_send, s2n_server_key_recv}}, - [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_cert_req_send, s2n_cert_req_recv}}, - [SERVER_HELLO_DONE] = {TLS_HANDSHAKE, TLS_SERVER_HELLO_DONE, 'S', {s2n_server_done_send, s2n_server_done_recv}}, - [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, - [CLIENT_KEY] = {TLS_HANDSHAKE, TLS_CLIENT_KEY, 'C', {s2n_client_key_recv, s2n_client_key_send}}, - [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_client_cert_verify_recv, s2n_client_cert_verify_send}}, - [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_client_ccs_recv, s2n_ccs_send}}, - [CLIENT_NPN] = {TLS_HANDSHAKE, TLS_NPN, 'C', {s2n_next_protocol_recv, s2n_next_protocol_send}}, - [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_client_finished_recv, s2n_client_finished_send}}, - [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_server_ccs_recv}}, - [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_server_finished_send, s2n_server_finished_recv}}, - [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, -}; - -/* - * Client and Server handlers for TLS1.3. - */ -static struct s2n_handshake_action tls13_state_machine[] = { - /* message_type_t = {Record type, Message type, Writer, {Server handler, client handler} } */ - [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, - [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, - [HELLO_RETRY_MSG] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_retry_send, s2n_server_hello_retry_recv}}, - [ENCRYPTED_EXTENSIONS] = {TLS_HANDSHAKE, TLS_ENCRYPTED_EXTENSIONS, 'S', {s2n_encrypted_extensions_send, s2n_encrypted_extensions_recv}}, - [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_tls13_cert_req_send, s2n_tls13_cert_req_recv}}, - [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, - [SERVER_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'S', {s2n_tls13_cert_verify_send, s2n_tls13_cert_verify_recv}}, - [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_tls13_server_finished_send, s2n_tls13_server_finished_recv}}, - - [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, - [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_tls13_cert_verify_recv, s2n_tls13_cert_verify_send}}, - [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_tls13_client_finished_recv, s2n_tls13_client_finished_send}}, - [END_OF_EARLY_DATA] = {TLS_HANDSHAKE, TLS_END_OF_EARLY_DATA, 'C', {s2n_end_of_early_data_recv, s2n_end_of_early_data_send}}, - - /* Not used by TLS1.3, except to maintain middlebox compatibility */ - [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_basic_ccs_recv, s2n_ccs_send}}, - [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_basic_ccs_recv}}, - - [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, -}; - -#define MESSAGE_NAME_ENTRY(msg) [msg] = #msg - -static const char *message_names[] = { - MESSAGE_NAME_ENTRY(CLIENT_HELLO), - MESSAGE_NAME_ENTRY(SERVER_HELLO), - MESSAGE_NAME_ENTRY(ENCRYPTED_EXTENSIONS), - MESSAGE_NAME_ENTRY(SERVER_NEW_SESSION_TICKET), - MESSAGE_NAME_ENTRY(SERVER_CERT), - MESSAGE_NAME_ENTRY(SERVER_CERT_STATUS), - MESSAGE_NAME_ENTRY(SERVER_CERT_VERIFY), - MESSAGE_NAME_ENTRY(SERVER_KEY), - MESSAGE_NAME_ENTRY(SERVER_CERT_REQ), - MESSAGE_NAME_ENTRY(SERVER_HELLO_DONE), - MESSAGE_NAME_ENTRY(CLIENT_CERT), - MESSAGE_NAME_ENTRY(CLIENT_KEY), - MESSAGE_NAME_ENTRY(CLIENT_CERT_VERIFY), - MESSAGE_NAME_ENTRY(CLIENT_CHANGE_CIPHER_SPEC), - MESSAGE_NAME_ENTRY(CLIENT_FINISHED), - MESSAGE_NAME_ENTRY(SERVER_CHANGE_CIPHER_SPEC), - MESSAGE_NAME_ENTRY(SERVER_FINISHED), - MESSAGE_NAME_ENTRY(HELLO_RETRY_MSG), - MESSAGE_NAME_ENTRY(END_OF_EARLY_DATA), - MESSAGE_NAME_ENTRY(APPLICATION_DATA), - MESSAGE_NAME_ENTRY(CLIENT_NPN), -}; - -/* We support different ordering of TLS Handshake messages, depending on what is being negotiated. There's also a dummy "INITIAL" handshake - * that everything starts out as until we know better. - */ - -static message_type_t handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { - [INITIAL] = { - CLIENT_HELLO, - SERVER_HELLO - }, - - [NEGOTIATED] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA}, - - [NEGOTIATED | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - APPLICATION_DATA}, - - [NEGOTIATED | FULL_HANDSHAKE ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_NPN ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] ={ - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, - CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH| WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, - CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, - SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, - APPLICATION_DATA - }, -}; - -/* - * This selection of handshakes resembles the standard set, but with changes made to support tls1.3. - * - * The CHANGE_CIPHER_SPEC messages are included only for middlebox compatibility. - * See https://tools.ietf.org/html/rfc8446#appendix-D.4 - */ -static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { - [INITIAL] = { - CLIENT_HELLO, - SERVER_HELLO - }, - - [INITIAL | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO - }, - - [INITIAL | HELLO_RETRY_REQUEST] = { - CLIENT_HELLO, - HELLO_RETRY_MSG - }, - - [INITIAL | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG - }, - - [NEGOTIATED] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | WITH_EARLY_DATA] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - END_OF_EARLY_DATA, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT | WITH_EARLY_DATA] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, END_OF_EARLY_DATA, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS | WITH_EARLY_DATA] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - END_OF_EARLY_DATA, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | HELLO_RETRY_REQUEST] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT] = { - CLIENT_HELLO, - SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { - CLIENT_HELLO, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, - - [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { - CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, - SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, - CLIENT_CERT, CLIENT_FINISHED, - APPLICATION_DATA - }, -}; -/* clang-format on */ - -#define MAX_HANDSHAKE_TYPE_LEN 142 -/* The handshake type labels used for TLS 1.0 - TLS 1.2, e.g. `NEGOTIATED|WITH_SESSION_TICKET` */ -static char handshake_type_str_tls12ish[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; - -/* The handshake type labels used for TLS 1.3, e.g. `NEGOTIATED|HELLO_RETRY_REQUEST` */ -static char handshake_type_str_tls13[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; - -static const char *tls12_handshake_type_names[] = { - "NEGOTIATED|", - "FULL_HANDSHAKE|", - "CLIENT_AUTH|", - "NO_CLIENT_CERT|", - "TLS12_PERFECT_FORWARD_SECRECY|", - "OCSP_STATUS|", - "WITH_SESSION_TICKET|", - "WITH_NPN|", -}; - -static const char *tls13_handshake_type_names[] = { - "NEGOTIATED|", - "FULL_HANDSHAKE|", - "CLIENT_AUTH|", - "NO_CLIENT_CERT|", - "HELLO_RETRY_REQUEST|", - "MIDDLEBOX_COMPAT|", - "WITH_EARLY_DATA|", - "EARLY_CLIENT_CCS|", -}; - -#define IS_TLS13_HANDSHAKE(conn) ((conn)->handshake.state_machine == S2N_STATE_MACHINE_TLS13) - -#define ACTIVE_STATE_MACHINE(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_state_machine : state_machine) -#define ACTIVE_HANDSHAKES(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_handshakes : handshakes) - -#define ACTIVE_MESSAGE(conn) ACTIVE_HANDSHAKES(conn)[(conn)->handshake.handshake_type][(conn)->handshake.message_number] - -#define ACTIVE_STATE(conn) ACTIVE_STATE_MACHINE(conn)[ACTIVE_MESSAGE((conn))] - -#define CCS_STATE(conn) (((conn)->mode == S2N_CLIENT) ? \ - ACTIVE_STATE_MACHINE(conn)[SERVER_CHANGE_CIPHER_SPEC] : \ - ACTIVE_STATE_MACHINE(conn)[CLIENT_CHANGE_CIPHER_SPEC]) - -#define EXPECTED_RECORD_TYPE(conn) ACTIVE_STATE(conn).record_type -#define EXPECTED_MESSAGE_TYPE(conn) ACTIVE_STATE(conn).message_type - -#define CONNECTION_WRITER(conn) (conn->mode == S2N_CLIENT ? 'C' : 'S') -#define CONNECTION_IS_WRITER(conn) (ACTIVE_STATE(conn).writer == CONNECTION_WRITER(conn)) - -/* Only used in our test cases. */ -message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn) -{ - return ACTIVE_MESSAGE(conn); -} - -static int s2n_advance_message(struct s2n_connection *conn) -{ - /* Get the mode: 'C'lient or 'S'erver */ - char previous_writer = ACTIVE_STATE(conn).writer; - char this_mode = CONNECTION_WRITER(conn); - - /* Actually advance the message number */ - conn->handshake.message_number++; - - /* When reading and using TLS1.3, skip optional change_cipher_spec states. */ - if (ACTIVE_STATE(conn).writer != this_mode && EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && IS_TLS13_HANDSHAKE(conn)) { - conn->handshake.message_number++; - } - - /* Set TCP_QUICKACK to avoid artificial delay during the handshake */ - POSIX_GUARD(s2n_socket_quickack(conn)); - - /* If optimized io hasn't been enabled or if the caller started out with a corked socket, - * we don't mess with it - */ - if (!conn->corked_io || s2n_socket_was_corked(conn)) { - return S2N_SUCCESS; - } - - /* Are we changing I/O directions */ - if (ACTIVE_STATE(conn).writer == previous_writer || ACTIVE_STATE(conn).writer == 'A') { - return S2N_SUCCESS; - } - - /* We're the new writer */ - if (ACTIVE_STATE(conn).writer == this_mode) { - if (s2n_connection_is_managed_corked(conn)) { - /* Set TCP_CORK/NOPUSH */ - POSIX_GUARD(s2n_socket_write_cork(conn)); - } - - return S2N_SUCCESS; - } - - /* We're the new reader, or we reached the "B" writer stage indicating that - we're at the application data stage - uncork the data */ - if (s2n_connection_is_managed_corked(conn)) { - POSIX_GUARD(s2n_socket_write_uncork(conn)); - } - - return S2N_SUCCESS; -} - -int s2n_generate_new_client_session_id(struct s2n_connection *conn) -{ - if (conn->mode == S2N_SERVER) { - struct s2n_blob session_id = { 0 }; - POSIX_GUARD(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); - - /* Generate a new session id */ - POSIX_GUARD_RESULT(s2n_get_public_random_data(&session_id)); - conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; - } - - return S2N_SUCCESS; -} - -/* Lets the server flag whether a HelloRetryRequest is needed while processing extensions */ -int s2n_set_hello_retry_required(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_INVALID_HELLO_RETRY); - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls13_flag(conn, HELLO_RETRY_REQUEST)); - - /* HelloRetryRequests also indicate rejection of early data. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# A server which receives an "early_data" extension MUST behave in one - *# of three ways: - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# - Request that the client send another ClientHello by responding - *# with a HelloRetryRequest. - **/ - if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { - POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); - } - - return S2N_SUCCESS; -} - -bool s2n_is_hello_retry_message(struct s2n_connection *conn) -{ - return (conn != NULL && s2n_result_is_ok(s2n_handshake_validate(&(conn->handshake))) && ACTIVE_MESSAGE(conn) == HELLO_RETRY_MSG); -} - -bool s2n_is_hello_retry_handshake(struct s2n_connection *conn) -{ - return IS_HELLO_RETRY_HANDSHAKE(conn); -} - -static S2N_RESULT s2n_conn_set_tls13_handshake_type(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* Most handshake type flags should be reset before we calculate the handshake type, - * in order to handle changes during retries. - * However, flags that have already affected the message order must be kept to avoid - * rewriting the past. - */ - conn->handshake.handshake_type &= (HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS); - - /* A handshake type has been negotiated */ - RESULT_GUARD(s2n_handshake_type_set_flag(conn, NEGOTIATED)); - - if (conn->psk_params.chosen_psk == NULL) { - RESULT_GUARD(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); - } - - if (conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { - conn->handshake.handshake_type |= WITH_EARLY_DATA; - } - - s2n_cert_auth_type client_cert_auth_type; - RESULT_GUARD_POSIX(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - - if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED - && IS_FULL_HANDSHAKE(conn)) { - /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ - RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE - && IS_FULL_HANDSHAKE(conn)) { - /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ - RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } - - if (s2n_is_middlebox_compat_enabled(conn)) { - RESULT_GUARD(s2n_handshake_type_set_tls13_flag(conn, MIDDLEBOX_COMPAT)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_validate_ems_status(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - s2n_extension_type_id ems_ext_id = 0; - RESULT_GUARD_POSIX(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); - bool ems_extension_recv = S2N_CBIT_TEST(conn->extension_requests_received, ems_ext_id); - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *# If the original session used the "extended_master_secret" - *# extension but the new ClientHello does not contain it, the server - *# MUST abort the abbreviated handshake. - **/ - if (conn->ems_negotiated) { - RESULT_ENSURE(ems_extension_recv, S2N_ERR_MISSING_EXTENSION); - } - - /* Since we're discarding the resumption ticket, ignore EMS value from the ticket */ - conn->ems_negotiated = ems_extension_recv; - - return S2N_RESULT_OK; -} - -int s2n_conn_set_handshake_type(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - POSIX_GUARD_RESULT(s2n_conn_choose_state_machine(conn, conn->actual_protocol_version)); - - if (IS_TLS13_HANDSHAKE(conn)) { - POSIX_GUARD_RESULT(s2n_conn_set_tls13_handshake_type(conn)); - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_handshake_type_reset(conn)); - - /* A handshake type has been negotiated */ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NEGOTIATED)); - - s2n_cert_auth_type client_cert_auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - - if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED) { - /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE) { - /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } - - if (conn->npn_negotiated) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_NPN)); - } - - if (conn->config->use_tickets) { - if (conn->session_ticket_status == S2N_DECRYPT_TICKET) { - /* We reuse the session if a valid TLS12 ticket is provided. - * Otherwise, we will perform a full handshake and then generate - * a new session ticket. */ - if (s2n_result_is_ok(s2n_resume_decrypt_session(conn, &conn->client_ticket_to_decrypt))) { - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); - - /* Set up the handshake to send a session ticket since a valid ticket was not provided */ - if (s2n_result_is_ok(s2n_config_is_encrypt_key_available(conn->config))) { - conn->session_ticket_status = S2N_NEW_TICKET; - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); - } - - /* If a session ticket is presented by the client, then skip lookup in Session ID server cache */ - goto skip_cache_lookup; - } - - if (conn->session_ticket_status == S2N_NEW_TICKET) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); - } - } - - /* If a TLS session is resumed, the Server should respond in its ServerHello with the same SessionId the - * Client sent in the ClientHello. */ - if (conn->actual_protocol_version <= S2N_TLS12 && conn->mode == S2N_SERVER && s2n_allowed_to_cache_connection(conn)) { - int r = s2n_resume_from_cache(conn); - if (r == S2N_SUCCESS || (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno))) { - return r; - } - POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); - } - -skip_cache_lookup: - if (conn->mode == S2N_CLIENT && conn->client_session_resumed == 1) { - return S2N_SUCCESS; - } - - /* If we're doing full handshake, generate a new session id. */ - POSIX_GUARD(s2n_generate_new_client_session_id(conn)); - - /* If we get this far, it's a full handshake */ - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); - - bool is_ephemeral = false; - POSIX_GUARD_RESULT(s2n_kex_is_ephemeral(conn->secure->cipher_suite->key_exchange_alg, &is_ephemeral)); - if (is_ephemeral) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, TLS12_PERFECT_FORWARD_SECRECY)); - } - - if (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)) { - POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, OCSP_STATUS)); - } - - return S2N_SUCCESS; -} - -int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn) -{ - s2n_cert_auth_type client_cert_auth_type; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_MISSING_CLIENT_CERT); - - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NO_CLIENT_CERT)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_conn_choose_state_machine(struct s2n_connection *conn, uint8_t protocol_version) -{ - RESULT_ENSURE_REF(conn); - - /* This should never be called before we know what version we're on */ - RESULT_ENSURE_NE(protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); - - if (protocol_version == S2N_TLS13) { - /* State machine should not change once set */ - RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS12); - conn->handshake.state_machine = S2N_STATE_MACHINE_TLS13; - } else { - /* State machine should not change once set */ - RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS13); - conn->handshake.state_machine = S2N_STATE_MACHINE_TLS12; - } - - return S2N_RESULT_OK; -} - -const char *s2n_connection_get_last_message_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_GUARD_RESULT(s2n_handshake_validate(&(conn->handshake))); - return message_names[ACTIVE_MESSAGE(conn)]; -} - -const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn) -{ - PTR_ENSURE_REF(conn); - PTR_PRECONDITION(s2n_handshake_validate(&(conn->handshake))); - - uint32_t handshake_type = conn->handshake.handshake_type; - - if (handshake_type == INITIAL) { - return "INITIAL"; - } - - const char **handshake_labels = tls13_handshake_type_names; - size_t handshake_labels_len = s2n_array_len(tls13_handshake_type_names); - char(*names)[MAX_HANDSHAKE_TYPE_LEN] = handshake_type_str_tls13; - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - handshake_labels = tls12_handshake_type_names; - handshake_labels_len = s2n_array_len(tls12_handshake_type_names); - names = handshake_type_str_tls12ish; - } - - /* Not all handshake strings will be created already. If the handshake string - * is not null, we can just return the handshake. Otherwise we have to compute - * it down below. */ - if (names[handshake_type][0] != '\0') { - return names[handshake_type]; - } - - /* Compute the cached string by concatenating each applicable handshake_type. - * - * Unit tests enforce that the elements of the cache are always - * long enough to contain the longest possible valid handshake_type, but - * for safety we still handle the case where we need to truncate. - */ - char *p = names[handshake_type]; - size_t remaining = MAX_HANDSHAKE_TYPE_LEN; - for (size_t i = 0; i < handshake_labels_len; i++) { - bool label_applies = handshake_type & (1 << i); - if (label_applies) { - size_t bytes_to_copy = S2N_MIN(remaining, strlen(handshake_labels[i])); - PTR_CHECKED_MEMCPY(p, handshake_labels[i], bytes_to_copy); - p[bytes_to_copy] = '\0'; - p += bytes_to_copy; - remaining -= bytes_to_copy; - } - } - - if (p != names[handshake_type] && '|' == *(p - 1)) { - *(p - 1) = '\0'; - } - - return names[handshake_type]; -} - -S2N_RESULT s2n_handshake_message_send(struct s2n_connection *conn, uint8_t content_type, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - struct s2n_stuffer *in = &conn->handshake.io; - - uint32_t size = s2n_stuffer_data_available(in); - if (size == 0) { - return S2N_RESULT_OK; - } - - if (s2n_connection_is_quic_enabled(conn)) { - RESULT_GUARD(s2n_quic_write_handshake_message(conn)); - RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); - return S2N_RESULT_OK; - } - - struct iovec iov = { 0 }; - iov.iov_len = size; - iov.iov_base = s2n_stuffer_raw_read(in, size); - RESULT_ENSURE_REF(iov.iov_base); - RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(in, size)); - - uint32_t total_bytes_written = 0; - while (total_bytes_written < size) { - int bytes_written = s2n_record_writev(conn, content_type, &iov, 1, - total_bytes_written, size - total_bytes_written); - RESULT_GUARD_POSIX(bytes_written); - total_bytes_written += bytes_written; - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(in, bytes_written)); - RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); - } - return S2N_RESULT_OK; -} - -/* Writing is relatively straight forward, simply write each message out as a record, - * we may fragment a message across multiple records, but we never coalesce multiple - * messages into single records. - * Precondition: secure outbound I/O has already been flushed - */ -static int s2n_handshake_write_io(struct s2n_connection *conn) -{ - uint8_t record_type = EXPECTED_RECORD_TYPE(conn); - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - /* Populate handshake.io with header/payload for the current state, once. - * Check wiped instead of s2n_stuffer_data_available to differentiate between the initial call - * to s2n_handshake_write_io and a repeated call after an EWOULDBLOCK. - */ - if (s2n_stuffer_is_wiped(&conn->handshake.io)) { - if (record_type == TLS_HANDSHAKE) { - POSIX_GUARD(s2n_handshake_write_header(&conn->handshake.io, ACTIVE_STATE(conn).message_type)); - } - POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn)); - if (record_type == TLS_HANDSHAKE) { - POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); - } - } - - POSIX_GUARD_RESULT(s2n_handshake_message_send(conn, record_type, &blocked)); - if (record_type == TLS_HANDSHAKE) { - POSIX_GUARD_RESULT(s2n_handshake_transcript_update(conn)); - } - - /* We're done sending the last record, reset everything */ - POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - - /* Update the secrets, if necessary */ - POSIX_GUARD_RESULT(s2n_tls13_secrets_update(conn)); - POSIX_GUARD_RESULT(s2n_tls13_key_schedule_update(conn)); - - /* Advance the state machine */ - POSIX_GUARD(s2n_advance_message(conn)); - - return S2N_SUCCESS; -} - -/* - * Returns: - * 1 - more data is needed to complete the handshake message. - * 0 - we read the whole handshake message. - * -1 - error processing the handshake message. - */ -static int s2n_read_full_handshake_message(struct s2n_connection *conn, uint8_t *message_type) -{ - uint32_t current_handshake_data = s2n_stuffer_data_available(&conn->handshake.io); - if (current_handshake_data < TLS_HANDSHAKE_HEADER_LENGTH) { - /* The message may be so badly fragmented that we don't even read the full header, take - * what we can and then continue to the next record read iteration. - */ - if (s2n_stuffer_data_available(&conn->in) < (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data)) { - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); - return 1; - } - - /* Get the remainder of the header */ - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data))); - } - - uint32_t handshake_message_length = 0; - POSIX_GUARD_RESULT(s2n_handshake_parse_header(&conn->handshake.io, message_type, &handshake_message_length)); - - S2N_ERROR_IF(handshake_message_length > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - - uint32_t bytes_to_take = handshake_message_length - s2n_stuffer_data_available(&conn->handshake.io); - bytes_to_take = S2N_MIN(bytes_to_take, s2n_stuffer_data_available(&conn->in)); - - /* If the record is handshake data, add it to the handshake buffer */ - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, bytes_to_take)); - - /* If we have the whole handshake message, then success */ - if (s2n_stuffer_data_available(&conn->handshake.io) == handshake_message_length) { - return 0; - } - - /* We don't have the whole message, so we'll need to go again */ - POSIX_GUARD(s2n_stuffer_reread(&conn->handshake.io)); - - return 1; -} - -static int s2n_handshake_handle_sslv2(struct s2n_connection *conn) -{ - S2N_ERROR_IF(ACTIVE_MESSAGE(conn) != CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); - - /* Add the message to our handshake hashes */ - struct s2n_blob hashed = { 0 }; - POSIX_GUARD(s2n_blob_init(&hashed, conn->header_in.blob.data + 2, 3)); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); - - hashed.data = conn->in.blob.data; - hashed.size = s2n_stuffer_data_available(&conn->in); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); - - /* Handle an SSLv2 client hello */ - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); - conn->client_hello.sslv2 = true; - /* Execute the state machine handler */ - int r = ACTIVE_STATE(conn).handler[conn->mode](conn); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); - - /* Advance the state machine */ - POSIX_GUARD(s2n_advance_message(conn)); - - return S2N_SUCCESS; -} - -static int s2n_try_delete_session_cache(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (s2n_allowed_to_cache_connection(conn) > 0) { - conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); - } - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_finish_read(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - RESULT_GUARD(s2n_handshake_transcript_update(conn)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->handshake.io)); - RESULT_GUARD(s2n_tls13_secrets_update(conn)); - RESULT_GUARD(s2n_tls13_key_schedule_update(conn)); - RESULT_GUARD_POSIX(s2n_advance_message(conn)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_handshake_app_data_recv(struct s2n_connection *conn) -{ - if (conn->early_data_expected) { - RESULT_GUARD(s2n_early_data_validate_recv(conn)); - RESULT_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); - } - - if (conn->handshake.renegotiation) { - RESULT_GUARD(s2n_renegotiate_validate(conn)); - /* During renegotiation, Application Data may only be received until - * the server acknowledges the new handshake with a ServerHello. - */ - RESULT_ENSURE(ACTIVE_MESSAGE(conn) == SERVER_HELLO, S2N_ERR_BAD_MESSAGE); - RESULT_BAIL(S2N_ERR_APP_DATA_BLOCKED); - } - - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); -} - -static int s2n_handshake_message_process(struct s2n_connection *conn, uint8_t record_type) -{ - POSIX_ENSURE_REF(conn); - - uint8_t message_type = 0; - while (s2n_stuffer_data_available(&conn->in)) { - /* We're done with negotiating but we have trailing data in this record. Bail on the handshake. */ - S2N_ERROR_IF(EXPECTED_RECORD_TYPE(conn) == TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE); - int r = 0; - POSIX_GUARD((r = s2n_read_full_handshake_message(conn, &message_type))); - - /* Do we need more data? This happens for message fragmentation */ - if (r == 1) { - /* Break out of this inner loop, but since we're not changing the state, the - * outer loop in s2n_handshake_io() will read another record. - */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - return S2N_SUCCESS; - } - - if (conn->mode == S2N_CLIENT) { - s2n_cert_auth_type client_cert_auth_type = { 0 }; - POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - /* If client auth is optional, we initially assume it will not be requested. - * If we received a request, switch to a client auth handshake. - */ - if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) { - POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST); - POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE); - POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); - } - - /* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus" - * message even if it has sent a "status_request" extension in the ServerHello message. - */ - if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS - && message_type != TLS_SERVER_CERT_STATUS) { - POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS)); - } - } - - /* - *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4 - *# The one message that is not bound by these ordering rules - *# is the HelloRequest message, which can be sent at any time, but which - *# SHOULD be ignored by the client if it arrives in the middle of a handshake. - */ - if (message_type == TLS_HELLO_REQUEST) { - POSIX_GUARD_RESULT(s2n_client_hello_request_validate(conn)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - continue; - } - - /* Check for missing Certificate Requests to surface a more specific error */ - if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) { - POSIX_ENSURE(message_type == TLS_CERT_REQ, - S2N_ERR_MISSING_CERT_REQUEST); - } - - POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); - - /* Call the relevant handler */ - WITH_ERROR_BLINDING(conn, POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn))); - - /* Advance the state machine */ - POSIX_GUARD_RESULT(s2n_finish_read(conn)); - } - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - return S2N_SUCCESS; -} - -/* Reading is a little more complicated than writing as the TLS RFCs allow content - * types to be interleaved at the record layer. We may get an alert message - * during the handshake phase, or messages of types that we don't support (e.g. - * HEARTBEAT messages), or during renegotiations we may even get application - * data messages that need to be handled by the application. - */ -static int s2n_handshake_read_io(struct s2n_connection *conn) -{ - uint8_t record_type = 0; - int isSSLv2 = 0; - - /* Fill conn->in stuffer necessary for the handshake. - * If using TCP, read a record. If using QUIC, read a message. */ - if (s2n_connection_is_quic_enabled(conn)) { - record_type = TLS_HANDSHAKE; - uint8_t message_type = 0; - POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); - } else { - int r = s2n_read_full_record(conn, &record_type, &isSSLv2); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# If the client attempts a 0-RTT handshake but the server - *# rejects it, the server will generally not have the 0-RTT record - *# protection keys and must instead use trial decryption (either with - *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in - *# the case of a HelloRetryRequest) to find the first non-0-RTT message. - *# - *# If the server chooses to accept the "early_data" extension, then it - *# MUST comply with the same error-handling requirements specified for - *# all records when processing early data records. Specifically, if the - *# server fails to decrypt a 0-RTT record following an accepted - *# "early_data" extension, it MUST terminate the connection with a - *# "bad_record_mac" alert as per Section 5.2. - */ - if ((r < S2N_SUCCESS) && (s2n_errno == S2N_ERR_EARLY_DATA_TRIAL_DECRYPT)) { - POSIX_GUARD(s2n_stuffer_reread(&conn->in)); - POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, s2n_stuffer_data_available(&conn->in))); - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - return S2N_SUCCESS; - } - POSIX_GUARD(r); - } - - if (isSSLv2) { - S2N_ERROR_IF(record_type != SSLv2_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_handshake_handle_sslv2(conn)); - } - - /* Now we have a record, but it could be a partial fragment of a message, or it might - * contain several messages. - */ - - if (record_type == TLS_APPLICATION_DATA) { - POSIX_GUARD_RESULT(s2n_handshake_app_data_recv(conn)); - } else if (record_type == TLS_CHANGE_CIPHER_SPEC) { - /* TLS1.3 can receive unexpected CCS messages at any point in the handshake - * due to a peer operating in middlebox compatibility mode. - * However, when operating in QUIC mode, S2N should not accept ANY CCS messages, - * including these unexpected ones.*/ - if (!IS_TLS13_HANDSHAKE(conn) || s2n_connection_is_quic_enabled(conn)) { - POSIX_ENSURE(EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC, S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); - } - - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) != 1, S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); - POSIX_GUARD(CCS_STATE(conn).handler[conn->mode](conn)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - /* Advance the state machine if this was an expected message */ - if (EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && !CONNECTION_IS_WRITER(conn)) { - POSIX_GUARD(s2n_advance_message(conn)); - } - - return S2N_SUCCESS; - } else if (record_type != TLS_HANDSHAKE) { - if (record_type == TLS_ALERT) { - POSIX_GUARD(s2n_process_alert_fragment(conn)); - } - - /* Ignore record types that we don't support */ - - /* We're done with the record, wipe it */ - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - return S2N_SUCCESS; - } - - /* Record is a handshake message */ - S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_handshake_message_process(conn, record_type)); - - return S2N_SUCCESS; -} - -static int s2n_handle_retry_state(struct s2n_connection *conn) -{ - /* If we were blocked reading or writing a record, then the handler is waiting on - * external data. The handler will know how to continue, so we should call the - * handler right away. We aren't going to read more handshake data yet or proceed - * to the next handler because the current message has not finished processing. */ - s2n_errno = S2N_ERR_OK; - const int r = ACTIVE_STATE(conn).handler[conn->mode](conn); - - if (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno)) { - /* If the handler is still waiting for data, return control to the caller. */ - S2N_ERROR_PRESERVE_ERRNO(); - } - - /* Resume the handshake */ - conn->handshake.paused = false; - - if (CONNECTION_IS_WRITER(conn)) { - POSIX_GUARD(r); - - /* If we're the writer and handler just finished, update the record header if - * needed and let the s2n_handshake_write_io write the data to the socket */ - if (EXPECTED_RECORD_TYPE(conn) == TLS_HANDSHAKE) { - POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); - } - } else { - if (r < S2N_SUCCESS && conn->session_id_len) { - s2n_try_delete_session_cache(conn); - } - WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); - - /* The read handler processed the message successfully, we are done with this - * message. Advance the state machine. */ - POSIX_GUARD_RESULT(s2n_finish_read(conn)); - - /* We may need to handle remaining handshake messages in the record */ - POSIX_GUARD(s2n_handshake_message_process(conn, TLS_HANDSHAKE)); - } - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_set_blocked_error_from_errno(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(blocked); - - if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { - *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; - conn->handshake.paused = true; - } else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) { - *blocked = S2N_BLOCKED_ON_EARLY_DATA; - } - - return S2N_RESULT_OK; -} - -bool s2n_handshake_is_complete(struct s2n_connection *conn) -{ - /* A deserialized connection implies that the handshake is complete because - * connections cannot be serialized before completing the handshake. */ - return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); -} - -int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(blocked); - - while (!s2n_handshake_is_complete(conn) && ACTIVE_MESSAGE(conn) != conn->handshake.end_of_messages) { - errno = 0; - s2n_errno = S2N_ERR_OK; - - /* Flush any pending I/O or alert messages */ - POSIX_GUARD(s2n_flush(conn, blocked)); - - POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX), S2N_ERR_CLOSED); - - /* If the handshake was paused, retry the current message */ - if (conn->handshake.paused) { - *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; - const int retry_result = s2n_handle_retry_state(conn); - if (retry_result != S2N_SUCCESS) { - POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); - S2N_ERROR_PRESERVE_ERRNO(); - } - - continue; - } - - if (CONNECTION_IS_WRITER(conn)) { - *blocked = S2N_BLOCKED_ON_WRITE; - const int write_result = s2n_handshake_write_io(conn); - - if (write_result < S2N_SUCCESS) { - if (!S2N_ERROR_IS_BLOCKING(s2n_errno)) { - /* Non-retryable write error. The peer might have sent an alert. Try and read it. */ - const int write_errno = errno; - const int write_s2n_errno = s2n_errno; - struct s2n_debug_info write_s2n_debug_info = _s2n_debug_info; - - if (s2n_handshake_read_io(conn) < 0 && s2n_errno == S2N_ERR_ALERT) { - /* s2n_handshake_read_io has set s2n_errno */ - S2N_ERROR_PRESERVE_ERRNO(); - } else { - /* Let the write error take precedence if we didn't read an alert. */ - errno = write_errno; - s2n_errno = write_s2n_errno; - _s2n_debug_info = write_s2n_debug_info; - S2N_ERROR_PRESERVE_ERRNO(); - } - } - - POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); - - S2N_ERROR_PRESERVE_ERRNO(); - } - } else { - *blocked = S2N_BLOCKED_ON_READ; - const int read_result = s2n_handshake_read_io(conn); - - if (read_result < S2N_SUCCESS) { - /* One blocking condition is waiting on the session resumption cache. */ - /* So we don't want to delete anything if we are blocked. */ - if (!S2N_ERROR_IS_BLOCKING(s2n_errno) && conn->session_id_len) { - s2n_try_delete_session_cache(conn); - } - - POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); - - S2N_ERROR_PRESERVE_ERRNO(); - } - } - } - - if (ACTIVE_STATE(conn).writer == 'B') { - /* Clean up handshake secrets */ - POSIX_GUARD_RESULT(s2n_tls13_secrets_clean(conn)); - - /* Send any pending post-handshake messages */ - POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); - - /* If the handshake has just ended, free up memory */ - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); - } - - *blocked = S2N_NOT_BLOCKED; - - return S2N_SUCCESS; -} - -int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(!conn->negotiate_in_use, S2N_ERR_REENTRANCY); - conn->negotiate_in_use = true; - - /* We use the default monotonic clock so that we can avoid referencing any - * item on the config until after the client hello callback is invoked. */ - uint64_t negotiate_start = 0; - POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_start)); - if (conn->handshake_event.handshake_start_ns == 0) { - conn->handshake_event.handshake_start_ns = negotiate_start; - } - - int result = s2n_negotiate_impl(conn, blocked); - - /* finish up sending and receiving */ - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); - - uint64_t negotiate_end = 0; - POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_end)); - conn->handshake_event.handshake_time_ns += negotiate_end - negotiate_start; - - if (result == S2N_SUCCESS) { - conn->handshake_event.handshake_end_ns = negotiate_end; - POSIX_GUARD_RESULT(s2n_event_handshake_populate(conn, &conn->handshake_event)); - POSIX_GUARD_RESULT(s2n_event_handshake_send(conn, &conn->handshake_event)); - } - - conn->negotiate_in_use = false; - return result; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls13_key_schedule.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_events.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +/* clang-format off */ +struct s2n_handshake_action { + uint8_t record_type; + uint8_t message_type; + char writer; /* 'S' or 'C' for server or client, 'B' for both */ + int (*handler[2]) (struct s2n_connection * conn); +}; + +static int s2n_always_fail_send(struct s2n_connection *conn) +{ + /* This state should never be sending a handshake message. */ + POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); +} + +static int s2n_always_fail_recv(struct s2n_connection *conn) +{ + /* This state should never have an incoming handshake message. */ + POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); +} + +/* Client and Server handlers for each message type we support. + * See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7 for the list of handshake message types + */ +static struct s2n_handshake_action state_machine[] = { + /* message_type_t = {Record type Message type Writer S2N_SERVER S2N_CLIENT } */ + [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, + [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, + [SERVER_NEW_SESSION_TICKET] = {TLS_HANDSHAKE, TLS_SERVER_NEW_SESSION_TICKET,'S', {s2n_server_nst_send, s2n_server_nst_recv}}, + [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, + [SERVER_CERT_STATUS] = {TLS_HANDSHAKE, TLS_SERVER_CERT_STATUS, 'S', {s2n_server_status_send, s2n_server_status_recv}}, + [SERVER_KEY] = {TLS_HANDSHAKE, TLS_SERVER_KEY, 'S', {s2n_server_key_send, s2n_server_key_recv}}, + [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_cert_req_send, s2n_cert_req_recv}}, + [SERVER_HELLO_DONE] = {TLS_HANDSHAKE, TLS_SERVER_HELLO_DONE, 'S', {s2n_server_done_send, s2n_server_done_recv}}, + [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, + [CLIENT_KEY] = {TLS_HANDSHAKE, TLS_CLIENT_KEY, 'C', {s2n_client_key_recv, s2n_client_key_send}}, + [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_client_cert_verify_recv, s2n_client_cert_verify_send}}, + [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_client_ccs_recv, s2n_ccs_send}}, + [CLIENT_NPN] = {TLS_HANDSHAKE, TLS_NPN, 'C', {s2n_next_protocol_recv, s2n_next_protocol_send}}, + [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_client_finished_recv, s2n_client_finished_send}}, + [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_server_ccs_recv}}, + [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_server_finished_send, s2n_server_finished_recv}}, + [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, +}; + +/* + * Client and Server handlers for TLS1.3. + */ +static struct s2n_handshake_action tls13_state_machine[] = { + /* message_type_t = {Record type, Message type, Writer, {Server handler, client handler} } */ + [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, + [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, + [HELLO_RETRY_MSG] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_retry_send, s2n_server_hello_retry_recv}}, + [ENCRYPTED_EXTENSIONS] = {TLS_HANDSHAKE, TLS_ENCRYPTED_EXTENSIONS, 'S', {s2n_encrypted_extensions_send, s2n_encrypted_extensions_recv}}, + [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_tls13_cert_req_send, s2n_tls13_cert_req_recv}}, + [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, + [SERVER_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'S', {s2n_tls13_cert_verify_send, s2n_tls13_cert_verify_recv}}, + [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_tls13_server_finished_send, s2n_tls13_server_finished_recv}}, + + [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, + [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_tls13_cert_verify_recv, s2n_tls13_cert_verify_send}}, + [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_tls13_client_finished_recv, s2n_tls13_client_finished_send}}, + [END_OF_EARLY_DATA] = {TLS_HANDSHAKE, TLS_END_OF_EARLY_DATA, 'C', {s2n_end_of_early_data_recv, s2n_end_of_early_data_send}}, + + /* Not used by TLS1.3, except to maintain middlebox compatibility */ + [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_basic_ccs_recv, s2n_ccs_send}}, + [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_basic_ccs_recv}}, + + [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, +}; + +#define MESSAGE_NAME_ENTRY(msg) [msg] = #msg + +static const char *message_names[] = { + MESSAGE_NAME_ENTRY(CLIENT_HELLO), + MESSAGE_NAME_ENTRY(SERVER_HELLO), + MESSAGE_NAME_ENTRY(ENCRYPTED_EXTENSIONS), + MESSAGE_NAME_ENTRY(SERVER_NEW_SESSION_TICKET), + MESSAGE_NAME_ENTRY(SERVER_CERT), + MESSAGE_NAME_ENTRY(SERVER_CERT_STATUS), + MESSAGE_NAME_ENTRY(SERVER_CERT_VERIFY), + MESSAGE_NAME_ENTRY(SERVER_KEY), + MESSAGE_NAME_ENTRY(SERVER_CERT_REQ), + MESSAGE_NAME_ENTRY(SERVER_HELLO_DONE), + MESSAGE_NAME_ENTRY(CLIENT_CERT), + MESSAGE_NAME_ENTRY(CLIENT_KEY), + MESSAGE_NAME_ENTRY(CLIENT_CERT_VERIFY), + MESSAGE_NAME_ENTRY(CLIENT_CHANGE_CIPHER_SPEC), + MESSAGE_NAME_ENTRY(CLIENT_FINISHED), + MESSAGE_NAME_ENTRY(SERVER_CHANGE_CIPHER_SPEC), + MESSAGE_NAME_ENTRY(SERVER_FINISHED), + MESSAGE_NAME_ENTRY(HELLO_RETRY_MSG), + MESSAGE_NAME_ENTRY(END_OF_EARLY_DATA), + MESSAGE_NAME_ENTRY(APPLICATION_DATA), + MESSAGE_NAME_ENTRY(CLIENT_NPN), +}; + +/* We support different ordering of TLS Handshake messages, depending on what is being negotiated. There's also a dummy "INITIAL" handshake + * that everything starts out as until we know better. + */ + +static message_type_t handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { + [INITIAL] = { + CLIENT_HELLO, + SERVER_HELLO + }, + + [NEGOTIATED] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA}, + + [NEGOTIATED | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + APPLICATION_DATA}, + + [NEGOTIATED | FULL_HANDSHAKE ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_NPN ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] ={ + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, + CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH| WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, + CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, + SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + APPLICATION_DATA + }, +}; + +/* + * This selection of handshakes resembles the standard set, but with changes made to support tls1.3. + * + * The CHANGE_CIPHER_SPEC messages are included only for middlebox compatibility. + * See https://tools.ietf.org/html/rfc8446#appendix-D.4 + */ +static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { + [INITIAL] = { + CLIENT_HELLO, + SERVER_HELLO + }, + + [INITIAL | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO + }, + + [INITIAL | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG + }, + + [INITIAL | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG + }, + + [NEGOTIATED] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | WITH_EARLY_DATA] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + END_OF_EARLY_DATA, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT | WITH_EARLY_DATA] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, END_OF_EARLY_DATA, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS | WITH_EARLY_DATA] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + END_OF_EARLY_DATA, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT] = { + CLIENT_HELLO, + SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { + CLIENT_HELLO, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, + + [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { + CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, + SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, + CLIENT_CERT, CLIENT_FINISHED, + APPLICATION_DATA + }, +}; +/* clang-format on */ + +#define MAX_HANDSHAKE_TYPE_LEN 142 +/* The handshake type labels used for TLS 1.0 - TLS 1.2, e.g. `NEGOTIATED|WITH_SESSION_TICKET` */ +static char handshake_type_str_tls12ish[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; + +/* The handshake type labels used for TLS 1.3, e.g. `NEGOTIATED|HELLO_RETRY_REQUEST` */ +static char handshake_type_str_tls13[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; + +static const char *tls12_handshake_type_names[] = { + "NEGOTIATED|", + "FULL_HANDSHAKE|", + "CLIENT_AUTH|", + "NO_CLIENT_CERT|", + "TLS12_PERFECT_FORWARD_SECRECY|", + "OCSP_STATUS|", + "WITH_SESSION_TICKET|", + "WITH_NPN|", +}; + +static const char *tls13_handshake_type_names[] = { + "NEGOTIATED|", + "FULL_HANDSHAKE|", + "CLIENT_AUTH|", + "NO_CLIENT_CERT|", + "HELLO_RETRY_REQUEST|", + "MIDDLEBOX_COMPAT|", + "WITH_EARLY_DATA|", + "EARLY_CLIENT_CCS|", +}; + +#define IS_TLS13_HANDSHAKE(conn) ((conn)->handshake.state_machine == S2N_STATE_MACHINE_TLS13) + +#define ACTIVE_STATE_MACHINE(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_state_machine : state_machine) +#define ACTIVE_HANDSHAKES(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_handshakes : handshakes) + +#define ACTIVE_MESSAGE(conn) ACTIVE_HANDSHAKES(conn)[(conn)->handshake.handshake_type][(conn)->handshake.message_number] + +#define ACTIVE_STATE(conn) ACTIVE_STATE_MACHINE(conn)[ACTIVE_MESSAGE((conn))] + +#define CCS_STATE(conn) (((conn)->mode == S2N_CLIENT) ? \ + ACTIVE_STATE_MACHINE(conn)[SERVER_CHANGE_CIPHER_SPEC] : \ + ACTIVE_STATE_MACHINE(conn)[CLIENT_CHANGE_CIPHER_SPEC]) + +#define EXPECTED_RECORD_TYPE(conn) ACTIVE_STATE(conn).record_type +#define EXPECTED_MESSAGE_TYPE(conn) ACTIVE_STATE(conn).message_type + +#define CONNECTION_WRITER(conn) (conn->mode == S2N_CLIENT ? 'C' : 'S') +#define CONNECTION_IS_WRITER(conn) (ACTIVE_STATE(conn).writer == CONNECTION_WRITER(conn)) + +/* Only used in our test cases. */ +message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn) +{ + return ACTIVE_MESSAGE(conn); +} + +static int s2n_advance_message(struct s2n_connection *conn) +{ + /* Get the mode: 'C'lient or 'S'erver */ + char previous_writer = ACTIVE_STATE(conn).writer; + char this_mode = CONNECTION_WRITER(conn); + + /* Actually advance the message number */ + conn->handshake.message_number++; + + /* When reading and using TLS1.3, skip optional change_cipher_spec states. */ + if (ACTIVE_STATE(conn).writer != this_mode && EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && IS_TLS13_HANDSHAKE(conn)) { + conn->handshake.message_number++; + } + + /* Set TCP_QUICKACK to avoid artificial delay during the handshake */ + POSIX_GUARD(s2n_socket_quickack(conn)); + + /* If optimized io hasn't been enabled or if the caller started out with a corked socket, + * we don't mess with it + */ + if (!conn->corked_io || s2n_socket_was_corked(conn)) { + return S2N_SUCCESS; + } + + /* Are we changing I/O directions */ + if (ACTIVE_STATE(conn).writer == previous_writer || ACTIVE_STATE(conn).writer == 'A') { + return S2N_SUCCESS; + } + + /* We're the new writer */ + if (ACTIVE_STATE(conn).writer == this_mode) { + if (s2n_connection_is_managed_corked(conn)) { + /* Set TCP_CORK/NOPUSH */ + POSIX_GUARD(s2n_socket_write_cork(conn)); + } + + return S2N_SUCCESS; + } + + /* We're the new reader, or we reached the "B" writer stage indicating that + we're at the application data stage - uncork the data */ + if (s2n_connection_is_managed_corked(conn)) { + POSIX_GUARD(s2n_socket_write_uncork(conn)); + } + + return S2N_SUCCESS; +} + +int s2n_generate_new_client_session_id(struct s2n_connection *conn) +{ + if (conn->mode == S2N_SERVER) { + struct s2n_blob session_id = { 0 }; + POSIX_GUARD(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* Generate a new session id */ + POSIX_GUARD_RESULT(s2n_get_public_random_data(&session_id)); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + } + + return S2N_SUCCESS; +} + +/* Lets the server flag whether a HelloRetryRequest is needed while processing extensions */ +int s2n_set_hello_retry_required(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_INVALID_HELLO_RETRY); + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls13_flag(conn, HELLO_RETRY_REQUEST)); + + /* HelloRetryRequests also indicate rejection of early data. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# A server which receives an "early_data" extension MUST behave in one + *# of three ways: + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# - Request that the client send another ClientHello by responding + *# with a HelloRetryRequest. + **/ + if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { + POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + } + + return S2N_SUCCESS; +} + +bool s2n_is_hello_retry_message(struct s2n_connection *conn) +{ + return (conn != NULL && s2n_result_is_ok(s2n_handshake_validate(&(conn->handshake))) && ACTIVE_MESSAGE(conn) == HELLO_RETRY_MSG); +} + +bool s2n_is_hello_retry_handshake(struct s2n_connection *conn) +{ + return IS_HELLO_RETRY_HANDSHAKE(conn); +} + +static S2N_RESULT s2n_conn_set_tls13_handshake_type(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* Most handshake type flags should be reset before we calculate the handshake type, + * in order to handle changes during retries. + * However, flags that have already affected the message order must be kept to avoid + * rewriting the past. + */ + conn->handshake.handshake_type &= (HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS); + + /* A handshake type has been negotiated */ + RESULT_GUARD(s2n_handshake_type_set_flag(conn, NEGOTIATED)); + + if (conn->psk_params.chosen_psk == NULL) { + RESULT_GUARD(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + } + + if (conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { + conn->handshake.handshake_type |= WITH_EARLY_DATA; + } + + s2n_cert_auth_type client_cert_auth_type; + RESULT_GUARD_POSIX(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED + && IS_FULL_HANDSHAKE(conn)) { + /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ + RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE + && IS_FULL_HANDSHAKE(conn)) { + /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ + RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } + + if (s2n_is_middlebox_compat_enabled(conn)) { + RESULT_GUARD(s2n_handshake_type_set_tls13_flag(conn, MIDDLEBOX_COMPAT)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_validate_ems_status(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + s2n_extension_type_id ems_ext_id = 0; + RESULT_GUARD_POSIX(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + bool ems_extension_recv = S2N_CBIT_TEST(conn->extension_requests_received, ems_ext_id); + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *# If the original session used the "extended_master_secret" + *# extension but the new ClientHello does not contain it, the server + *# MUST abort the abbreviated handshake. + **/ + if (conn->ems_negotiated) { + RESULT_ENSURE(ems_extension_recv, S2N_ERR_MISSING_EXTENSION); + } + + /* Since we're discarding the resumption ticket, ignore EMS value from the ticket */ + conn->ems_negotiated = ems_extension_recv; + + return S2N_RESULT_OK; +} + +int s2n_conn_set_handshake_type(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + POSIX_GUARD_RESULT(s2n_conn_choose_state_machine(conn, conn->actual_protocol_version)); + + if (IS_TLS13_HANDSHAKE(conn)) { + POSIX_GUARD_RESULT(s2n_conn_set_tls13_handshake_type(conn)); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_handshake_type_reset(conn)); + + /* A handshake type has been negotiated */ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NEGOTIATED)); + + s2n_cert_auth_type client_cert_auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED) { + /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE) { + /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } + + if (conn->npn_negotiated) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_NPN)); + } + + if (conn->config->use_tickets) { + if (conn->session_ticket_status == S2N_DECRYPT_TICKET) { + /* We reuse the session if a valid TLS12 ticket is provided. + * Otherwise, we will perform a full handshake and then generate + * a new session ticket. */ + if (s2n_result_is_ok(s2n_resume_decrypt_session(conn, &conn->client_ticket_to_decrypt))) { + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); + + /* Set up the handshake to send a session ticket since a valid ticket was not provided */ + if (s2n_result_is_ok(s2n_config_is_encrypt_key_available(conn->config))) { + conn->session_ticket_status = S2N_NEW_TICKET; + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); + } + + /* If a session ticket is presented by the client, then skip lookup in Session ID server cache */ + goto skip_cache_lookup; + } + + if (conn->session_ticket_status == S2N_NEW_TICKET) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); + } + } + + /* If a TLS session is resumed, the Server should respond in its ServerHello with the same SessionId the + * Client sent in the ClientHello. */ + if (conn->actual_protocol_version <= S2N_TLS12 && conn->mode == S2N_SERVER && s2n_allowed_to_cache_connection(conn)) { + int r = s2n_resume_from_cache(conn); + if (r == S2N_SUCCESS || (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno))) { + return r; + } + POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); + } + +skip_cache_lookup: + if (conn->mode == S2N_CLIENT && conn->client_session_resumed == 1) { + return S2N_SUCCESS; + } + + /* If we're doing full handshake, generate a new session id. */ + POSIX_GUARD(s2n_generate_new_client_session_id(conn)); + + /* If we get this far, it's a full handshake */ + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + + bool is_ephemeral = false; + POSIX_GUARD_RESULT(s2n_kex_is_ephemeral(conn->secure->cipher_suite->key_exchange_alg, &is_ephemeral)); + if (is_ephemeral) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, TLS12_PERFECT_FORWARD_SECRECY)); + } + + if (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)) { + POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, OCSP_STATUS)); + } + + return S2N_SUCCESS; +} + +int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn) +{ + s2n_cert_auth_type client_cert_auth_type; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_MISSING_CLIENT_CERT); + + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NO_CLIENT_CERT)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_conn_choose_state_machine(struct s2n_connection *conn, uint8_t protocol_version) +{ + RESULT_ENSURE_REF(conn); + + /* This should never be called before we know what version we're on */ + RESULT_ENSURE_NE(protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + if (protocol_version == S2N_TLS13) { + /* State machine should not change once set */ + RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS12); + conn->handshake.state_machine = S2N_STATE_MACHINE_TLS13; + } else { + /* State machine should not change once set */ + RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS13); + conn->handshake.state_machine = S2N_STATE_MACHINE_TLS12; + } + + return S2N_RESULT_OK; +} + +const char *s2n_connection_get_last_message_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_GUARD_RESULT(s2n_handshake_validate(&(conn->handshake))); + return message_names[ACTIVE_MESSAGE(conn)]; +} + +const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn) +{ + PTR_ENSURE_REF(conn); + PTR_PRECONDITION(s2n_handshake_validate(&(conn->handshake))); + + uint32_t handshake_type = conn->handshake.handshake_type; + + if (handshake_type == INITIAL) { + return "INITIAL"; + } + + const char **handshake_labels = tls13_handshake_type_names; + size_t handshake_labels_len = s2n_array_len(tls13_handshake_type_names); + char(*names)[MAX_HANDSHAKE_TYPE_LEN] = handshake_type_str_tls13; + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + handshake_labels = tls12_handshake_type_names; + handshake_labels_len = s2n_array_len(tls12_handshake_type_names); + names = handshake_type_str_tls12ish; + } + + /* Not all handshake strings will be created already. If the handshake string + * is not null, we can just return the handshake. Otherwise we have to compute + * it down below. */ + if (names[handshake_type][0] != '\0') { + return names[handshake_type]; + } + + /* Compute the cached string by concatenating each applicable handshake_type. + * + * Unit tests enforce that the elements of the cache are always + * long enough to contain the longest possible valid handshake_type, but + * for safety we still handle the case where we need to truncate. + */ + char *p = names[handshake_type]; + size_t remaining = MAX_HANDSHAKE_TYPE_LEN; + for (size_t i = 0; i < handshake_labels_len; i++) { + bool label_applies = handshake_type & (1 << i); + if (label_applies) { + size_t bytes_to_copy = S2N_MIN(remaining, strlen(handshake_labels[i])); + PTR_CHECKED_MEMCPY(p, handshake_labels[i], bytes_to_copy); + p[bytes_to_copy] = '\0'; + p += bytes_to_copy; + remaining -= bytes_to_copy; + } + } + + if (p != names[handshake_type] && '|' == *(p - 1)) { + *(p - 1) = '\0'; + } + + return names[handshake_type]; +} + +S2N_RESULT s2n_handshake_message_send(struct s2n_connection *conn, uint8_t content_type, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + struct s2n_stuffer *in = &conn->handshake.io; + + uint32_t size = s2n_stuffer_data_available(in); + if (size == 0) { + return S2N_RESULT_OK; + } + + if (s2n_connection_is_quic_enabled(conn)) { + RESULT_GUARD(s2n_quic_write_handshake_message(conn)); + RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); + return S2N_RESULT_OK; + } + + struct iovec iov = { 0 }; + iov.iov_len = size; + iov.iov_base = s2n_stuffer_raw_read(in, size); + RESULT_ENSURE_REF(iov.iov_base); + RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(in, size)); + + uint32_t total_bytes_written = 0; + while (total_bytes_written < size) { + int bytes_written = s2n_record_writev(conn, content_type, &iov, 1, + total_bytes_written, size - total_bytes_written); + RESULT_GUARD_POSIX(bytes_written); + total_bytes_written += bytes_written; + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(in, bytes_written)); + RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); + } + return S2N_RESULT_OK; +} + +/* Writing is relatively straight forward, simply write each message out as a record, + * we may fragment a message across multiple records, but we never coalesce multiple + * messages into single records. + * Precondition: secure outbound I/O has already been flushed + */ +static int s2n_handshake_write_io(struct s2n_connection *conn) +{ + uint8_t record_type = EXPECTED_RECORD_TYPE(conn); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Populate handshake.io with header/payload for the current state, once. + * Check wiped instead of s2n_stuffer_data_available to differentiate between the initial call + * to s2n_handshake_write_io and a repeated call after an EWOULDBLOCK. + */ + if (s2n_stuffer_is_wiped(&conn->handshake.io)) { + if (record_type == TLS_HANDSHAKE) { + POSIX_GUARD(s2n_handshake_write_header(&conn->handshake.io, ACTIVE_STATE(conn).message_type)); + } + POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn)); + if (record_type == TLS_HANDSHAKE) { + POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); + } + } + + POSIX_GUARD_RESULT(s2n_handshake_message_send(conn, record_type, &blocked)); + if (record_type == TLS_HANDSHAKE) { + POSIX_GUARD_RESULT(s2n_handshake_transcript_update(conn)); + } + + /* We're done sending the last record, reset everything */ + POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + + /* Update the secrets, if necessary */ + POSIX_GUARD_RESULT(s2n_tls13_secrets_update(conn)); + POSIX_GUARD_RESULT(s2n_tls13_key_schedule_update(conn)); + + /* Advance the state machine */ + POSIX_GUARD(s2n_advance_message(conn)); + + return S2N_SUCCESS; +} + +/* + * Returns: + * 1 - more data is needed to complete the handshake message. + * 0 - we read the whole handshake message. + * -1 - error processing the handshake message. + */ +static int s2n_read_full_handshake_message(struct s2n_connection *conn, uint8_t *message_type) +{ + uint32_t current_handshake_data = s2n_stuffer_data_available(&conn->handshake.io); + if (current_handshake_data < TLS_HANDSHAKE_HEADER_LENGTH) { + /* The message may be so badly fragmented that we don't even read the full header, take + * what we can and then continue to the next record read iteration. + */ + if (s2n_stuffer_data_available(&conn->in) < (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data)) { + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); + return 1; + } + + /* Get the remainder of the header */ + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data))); + } + + uint32_t handshake_message_length = 0; + POSIX_GUARD_RESULT(s2n_handshake_parse_header(&conn->handshake.io, message_type, &handshake_message_length)); + + S2N_ERROR_IF(handshake_message_length > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); + + uint32_t bytes_to_take = handshake_message_length - s2n_stuffer_data_available(&conn->handshake.io); + bytes_to_take = S2N_MIN(bytes_to_take, s2n_stuffer_data_available(&conn->in)); + + /* If the record is handshake data, add it to the handshake buffer */ + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, bytes_to_take)); + + /* If we have the whole handshake message, then success */ + if (s2n_stuffer_data_available(&conn->handshake.io) == handshake_message_length) { + return 0; + } + + /* We don't have the whole message, so we'll need to go again */ + POSIX_GUARD(s2n_stuffer_reread(&conn->handshake.io)); + + return 1; +} + +static int s2n_handshake_handle_sslv2(struct s2n_connection *conn) +{ + S2N_ERROR_IF(ACTIVE_MESSAGE(conn) != CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); + + /* Add the message to our handshake hashes */ + struct s2n_blob hashed = { 0 }; + POSIX_GUARD(s2n_blob_init(&hashed, conn->header_in.blob.data + 2, 3)); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); + + hashed.data = conn->in.blob.data; + hashed.size = s2n_stuffer_data_available(&conn->in); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); + + /* Handle an SSLv2 client hello */ + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); + conn->client_hello.sslv2 = true; + /* Execute the state machine handler */ + int r = ACTIVE_STATE(conn).handler[conn->mode](conn); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); + + /* Advance the state machine */ + POSIX_GUARD(s2n_advance_message(conn)); + + return S2N_SUCCESS; +} + +static int s2n_try_delete_session_cache(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (s2n_allowed_to_cache_connection(conn) > 0) { + conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_finish_read(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD(s2n_handshake_transcript_update(conn)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->handshake.io)); + RESULT_GUARD(s2n_tls13_secrets_update(conn)); + RESULT_GUARD(s2n_tls13_key_schedule_update(conn)); + RESULT_GUARD_POSIX(s2n_advance_message(conn)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_handshake_app_data_recv(struct s2n_connection *conn) +{ + if (conn->early_data_expected) { + RESULT_GUARD(s2n_early_data_validate_recv(conn)); + RESULT_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); + } + + if (conn->handshake.renegotiation) { + RESULT_GUARD(s2n_renegotiate_validate(conn)); + /* During renegotiation, Application Data may only be received until + * the server acknowledges the new handshake with a ServerHello. + */ + RESULT_ENSURE(ACTIVE_MESSAGE(conn) == SERVER_HELLO, S2N_ERR_BAD_MESSAGE); + RESULT_BAIL(S2N_ERR_APP_DATA_BLOCKED); + } + + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); +} + +static int s2n_handshake_message_process(struct s2n_connection *conn, uint8_t record_type) +{ + POSIX_ENSURE_REF(conn); + + uint8_t message_type = 0; + while (s2n_stuffer_data_available(&conn->in)) { + /* We're done with negotiating but we have trailing data in this record. Bail on the handshake. */ + S2N_ERROR_IF(EXPECTED_RECORD_TYPE(conn) == TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE); + int r = 0; + POSIX_GUARD((r = s2n_read_full_handshake_message(conn, &message_type))); + + /* Do we need more data? This happens for message fragmentation */ + if (r == 1) { + /* Break out of this inner loop, but since we're not changing the state, the + * outer loop in s2n_handshake_io() will read another record. + */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + return S2N_SUCCESS; + } + + if (conn->mode == S2N_CLIENT) { + s2n_cert_auth_type client_cert_auth_type = { 0 }; + POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + /* If client auth is optional, we initially assume it will not be requested. + * If we received a request, switch to a client auth handshake. + */ + if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) { + POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST); + POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE); + POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); + } + + /* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus" + * message even if it has sent a "status_request" extension in the ServerHello message. + */ + if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS + && message_type != TLS_SERVER_CERT_STATUS) { + POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS)); + } + } + + /* + *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4 + *# The one message that is not bound by these ordering rules + *# is the HelloRequest message, which can be sent at any time, but which + *# SHOULD be ignored by the client if it arrives in the middle of a handshake. + */ + if (message_type == TLS_HELLO_REQUEST) { + POSIX_GUARD_RESULT(s2n_client_hello_request_validate(conn)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + continue; + } + + /* Check for missing Certificate Requests to surface a more specific error */ + if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) { + POSIX_ENSURE(message_type == TLS_CERT_REQ, + S2N_ERR_MISSING_CERT_REQUEST); + } + + POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); + + /* Call the relevant handler */ + WITH_ERROR_BLINDING(conn, POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn))); + + /* Advance the state machine */ + POSIX_GUARD_RESULT(s2n_finish_read(conn)); + } + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + return S2N_SUCCESS; +} + +/* Reading is a little more complicated than writing as the TLS RFCs allow content + * types to be interleaved at the record layer. We may get an alert message + * during the handshake phase, or messages of types that we don't support (e.g. + * HEARTBEAT messages), or during renegotiations we may even get application + * data messages that need to be handled by the application. + */ +static int s2n_handshake_read_io(struct s2n_connection *conn) +{ + uint8_t record_type = 0; + int isSSLv2 = 0; + + /* Fill conn->in stuffer necessary for the handshake. + * If using TCP, read a record. If using QUIC, read a message. */ + if (s2n_connection_is_quic_enabled(conn)) { + record_type = TLS_HANDSHAKE; + uint8_t message_type = 0; + POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); + } else { + int r = s2n_read_full_record(conn, &record_type, &isSSLv2); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# If the client attempts a 0-RTT handshake but the server + *# rejects it, the server will generally not have the 0-RTT record + *# protection keys and must instead use trial decryption (either with + *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in + *# the case of a HelloRetryRequest) to find the first non-0-RTT message. + *# + *# If the server chooses to accept the "early_data" extension, then it + *# MUST comply with the same error-handling requirements specified for + *# all records when processing early data records. Specifically, if the + *# server fails to decrypt a 0-RTT record following an accepted + *# "early_data" extension, it MUST terminate the connection with a + *# "bad_record_mac" alert as per Section 5.2. + */ + if ((r < S2N_SUCCESS) && (s2n_errno == S2N_ERR_EARLY_DATA_TRIAL_DECRYPT)) { + POSIX_GUARD(s2n_stuffer_reread(&conn->in)); + POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, s2n_stuffer_data_available(&conn->in))); + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + return S2N_SUCCESS; + } + POSIX_GUARD(r); + } + + if (isSSLv2) { + S2N_ERROR_IF(record_type != SSLv2_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_handshake_handle_sslv2(conn)); + } + + /* Now we have a record, but it could be a partial fragment of a message, or it might + * contain several messages. + */ + + if (record_type == TLS_APPLICATION_DATA) { + POSIX_GUARD_RESULT(s2n_handshake_app_data_recv(conn)); + } else if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* TLS1.3 can receive unexpected CCS messages at any point in the handshake + * due to a peer operating in middlebox compatibility mode. + * However, when operating in QUIC mode, S2N should not accept ANY CCS messages, + * including these unexpected ones.*/ + if (!IS_TLS13_HANDSHAKE(conn) || s2n_connection_is_quic_enabled(conn)) { + POSIX_ENSURE(EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); + } + + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) != 1, S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); + POSIX_GUARD(CCS_STATE(conn).handler[conn->mode](conn)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + /* Advance the state machine if this was an expected message */ + if (EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && !CONNECTION_IS_WRITER(conn)) { + POSIX_GUARD(s2n_advance_message(conn)); + } + + return S2N_SUCCESS; + } else if (record_type != TLS_HANDSHAKE) { + if (record_type == TLS_ALERT) { + POSIX_GUARD(s2n_process_alert_fragment(conn)); + } + + /* Ignore record types that we don't support */ + + /* We're done with the record, wipe it */ + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + return S2N_SUCCESS; + } + + /* Record is a handshake message */ + S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_handshake_message_process(conn, record_type)); + + return S2N_SUCCESS; +} + +static int s2n_handle_retry_state(struct s2n_connection *conn) +{ + /* If we were blocked reading or writing a record, then the handler is waiting on + * external data. The handler will know how to continue, so we should call the + * handler right away. We aren't going to read more handshake data yet or proceed + * to the next handler because the current message has not finished processing. */ + s2n_errno = S2N_ERR_OK; + const int r = ACTIVE_STATE(conn).handler[conn->mode](conn); + + if (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno)) { + /* If the handler is still waiting for data, return control to the caller. */ + S2N_ERROR_PRESERVE_ERRNO(); + } + + /* Resume the handshake */ + conn->handshake.paused = false; + + if (CONNECTION_IS_WRITER(conn)) { + POSIX_GUARD(r); + + /* If we're the writer and handler just finished, update the record header if + * needed and let the s2n_handshake_write_io write the data to the socket */ + if (EXPECTED_RECORD_TYPE(conn) == TLS_HANDSHAKE) { + POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); + } + } else { + if (r < S2N_SUCCESS && conn->session_id_len) { + s2n_try_delete_session_cache(conn); + } + WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); + + /* The read handler processed the message successfully, we are done with this + * message. Advance the state machine. */ + POSIX_GUARD_RESULT(s2n_finish_read(conn)); + + /* We may need to handle remaining handshake messages in the record */ + POSIX_GUARD(s2n_handshake_message_process(conn, TLS_HANDSHAKE)); + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_set_blocked_error_from_errno(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(blocked); + + if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { + *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; + conn->handshake.paused = true; + } else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) { + *blocked = S2N_BLOCKED_ON_EARLY_DATA; + } + + return S2N_RESULT_OK; +} + +bool s2n_handshake_is_complete(struct s2n_connection *conn) +{ + /* A deserialized connection implies that the handshake is complete because + * connections cannot be serialized before completing the handshake. */ + return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); +} + +int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(blocked); + + while (!s2n_handshake_is_complete(conn) && ACTIVE_MESSAGE(conn) != conn->handshake.end_of_messages) { + errno = 0; + s2n_errno = S2N_ERR_OK; + + /* Flush any pending I/O or alert messages */ + POSIX_GUARD(s2n_flush(conn, blocked)); + + POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX), S2N_ERR_CLOSED); + + /* If the handshake was paused, retry the current message */ + if (conn->handshake.paused) { + *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; + const int retry_result = s2n_handle_retry_state(conn); + if (retry_result != S2N_SUCCESS) { + POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); + S2N_ERROR_PRESERVE_ERRNO(); + } + + continue; + } + + if (CONNECTION_IS_WRITER(conn)) { + *blocked = S2N_BLOCKED_ON_WRITE; + const int write_result = s2n_handshake_write_io(conn); + + if (write_result < S2N_SUCCESS) { + if (!S2N_ERROR_IS_BLOCKING(s2n_errno)) { + /* Non-retryable write error. The peer might have sent an alert. Try and read it. */ + const int write_errno = errno; + const int write_s2n_errno = s2n_errno; + struct s2n_debug_info write_s2n_debug_info = _s2n_debug_info; + + if (s2n_handshake_read_io(conn) < 0 && s2n_errno == S2N_ERR_ALERT) { + /* s2n_handshake_read_io has set s2n_errno */ + S2N_ERROR_PRESERVE_ERRNO(); + } else { + /* Let the write error take precedence if we didn't read an alert. */ + errno = write_errno; + s2n_errno = write_s2n_errno; + _s2n_debug_info = write_s2n_debug_info; + S2N_ERROR_PRESERVE_ERRNO(); + } + } + + POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); + + S2N_ERROR_PRESERVE_ERRNO(); + } + } else { + *blocked = S2N_BLOCKED_ON_READ; + const int read_result = s2n_handshake_read_io(conn); + + if (read_result < S2N_SUCCESS) { + /* One blocking condition is waiting on the session resumption cache. */ + /* So we don't want to delete anything if we are blocked. */ + if (!S2N_ERROR_IS_BLOCKING(s2n_errno) && conn->session_id_len) { + s2n_try_delete_session_cache(conn); + } + + POSIX_GUARD_RESULT(s2n_set_blocked_error_from_errno(conn, blocked)); + + S2N_ERROR_PRESERVE_ERRNO(); + } + } + } + + if (ACTIVE_STATE(conn).writer == 'B') { + /* Clean up handshake secrets */ + POSIX_GUARD_RESULT(s2n_tls13_secrets_clean(conn)); + + /* Send any pending post-handshake messages */ + POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); + + /* If the handshake has just ended, free up memory */ + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); + } + + *blocked = S2N_NOT_BLOCKED; + + return S2N_SUCCESS; +} + +int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(!conn->negotiate_in_use, S2N_ERR_REENTRANCY); + conn->negotiate_in_use = true; + + /* We use the default monotonic clock so that we can avoid referencing any + * item on the config until after the client hello callback is invoked. */ + uint64_t negotiate_start = 0; + POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_start)); + if (conn->handshake_event.handshake_start_ns == 0) { + conn->handshake_event.handshake_start_ns = negotiate_start; + } + + int result = s2n_negotiate_impl(conn, blocked); + + /* finish up sending and receiving */ + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); + + uint64_t negotiate_end = 0; + POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_end)); + conn->handshake_event.handshake_time_ns += negotiate_end - negotiate_start; + + if (result == S2N_SUCCESS) { + conn->handshake_event.handshake_end_ns = negotiate_end; + POSIX_GUARD_RESULT(s2n_event_handshake_populate(conn, &conn->handshake_event)); + POSIX_GUARD_RESULT(s2n_event_handshake_send(conn, &conn->handshake_event)); + } + + conn->negotiate_in_use = false; + return result; +} diff --git a/tls/s2n_handshake_transcript.c b/tls/s2n_handshake_transcript.c index 2ebec134fdc..7b44852aae1 100644 --- a/tls/s2n_handshake_transcript.c +++ b/tls/s2n_handshake_transcript.c @@ -1,130 +1,130 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "stuffer/s2n_stuffer.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_blob.h" - -/* Length of the synthetic message header */ -#define MESSAGE_HASH_HEADER_LENGTH 4 - -S2N_RESULT s2n_handshake_transcript_update(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_stuffer message = conn->handshake.io; - RESULT_GUARD_POSIX(s2n_stuffer_reread(&message)); - - struct s2n_blob data = { 0 }; - uint32_t len = s2n_stuffer_data_available(&message); - uint8_t *bytes = s2n_stuffer_raw_read(&message, len); - RESULT_ENSURE_REF(bytes); - RESULT_GUARD_POSIX(s2n_blob_init(&data, bytes, len)); - - RESULT_GUARD_POSIX(s2n_conn_update_handshake_hashes(conn, &data)); - return S2N_RESULT_OK; -} - -int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(data); - struct s2n_handshake_hashes *hashes = conn->handshake.hashes; - POSIX_ENSURE_REF(hashes); - - /* MD5 and SHA1 are not permitted in FIPS mode, but an exception is made in - * order to continue to support TLS1.0 and TLS1.1. NIST SP 800-52r1 approves - * their continued use for the signature check in the CertificateVerify message - * and the PRF when negotiating TLS1.0 or TLS1.1 (see footnotes 15 and 20, - * and section 3.3.2) - */ - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5)) { - POSIX_GUARD(s2n_hash_update(&hashes->md5, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha1, data->data, data->size)); - } - - const uint8_t md5_sha1_required = - (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5) - && s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)); - - if (md5_sha1_required) { - POSIX_GUARD(s2n_hash_update(&hashes->md5_sha1, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA224)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha224, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA256)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha256, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA384)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha384, data->data, data->size)); - } - - if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA512)) { - POSIX_GUARD(s2n_hash_update(&hashes->sha512, data->data, data->size)); - } - - return S2N_SUCCESS; -} - -/* When a HelloRetryRequest message is used, the hash transcript needs to be recreated. - * This is done with a synthetic message header, and the hash of ClientHello1. - * - * https://tools.ietf.org/html/rfc8446#section-4.4.1 - */ -int s2n_server_hello_retry_recreate_transcript(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - struct s2n_handshake_hashes *hashes = conn->handshake.hashes; - POSIX_ENSURE_REF(hashes); - - s2n_tls13_connection_keys(keys, conn); - uint8_t hash_digest_length = keys.size; - - /* Create the MessageHash (our synthetic message) */ - uint8_t msghdr[MESSAGE_HASH_HEADER_LENGTH] = { 0 }; - msghdr[0] = TLS_MESSAGE_HASH; - msghdr[MESSAGE_HASH_HEADER_LENGTH - 1] = hash_digest_length; - - /* Grab the current transcript hash to use as the ClientHello1 value. */ - struct s2n_hash_state *client_hello1_hash = &hashes->hash_workspace; - uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, client_hello1_hash)); - POSIX_GUARD(s2n_hash_digest(client_hello1_hash, client_hello1_digest_out, hash_digest_length)); - - /* Step 1: Reset the hash state */ - POSIX_GUARD_RESULT(s2n_handshake_reset_hash_state(conn, keys.hash_algorithm)); - - /* Step 2: Update the transcript with the synthetic message */ - struct s2n_blob msg_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&msg_blob, msghdr, MESSAGE_HASH_HEADER_LENGTH)); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); - - /* Step 3: Update the transcript with the ClientHello1 hash */ - POSIX_GUARD(s2n_blob_init(&msg_blob, client_hello1_digest_out, hash_digest_length)); - POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "stuffer/s2n_stuffer.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_blob.h" + +/* Length of the synthetic message header */ +#define MESSAGE_HASH_HEADER_LENGTH 4 + +S2N_RESULT s2n_handshake_transcript_update(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_stuffer message = conn->handshake.io; + RESULT_GUARD_POSIX(s2n_stuffer_reread(&message)); + + struct s2n_blob data = { 0 }; + uint32_t len = s2n_stuffer_data_available(&message); + uint8_t *bytes = s2n_stuffer_raw_read(&message, len); + RESULT_ENSURE_REF(bytes); + RESULT_GUARD_POSIX(s2n_blob_init(&data, bytes, len)); + + RESULT_GUARD_POSIX(s2n_conn_update_handshake_hashes(conn, &data)); + return S2N_RESULT_OK; +} + +int s2n_conn_update_handshake_hashes(struct s2n_connection *conn, struct s2n_blob *data) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(data); + struct s2n_handshake_hashes *hashes = conn->handshake.hashes; + POSIX_ENSURE_REF(hashes); + + /* MD5 and SHA1 are not permitted in FIPS mode, but an exception is made in + * order to continue to support TLS1.0 and TLS1.1. NIST SP 800-52r1 approves + * their continued use for the signature check in the CertificateVerify message + * and the PRF when negotiating TLS1.0 or TLS1.1 (see footnotes 15 and 20, + * and section 3.3.2) + */ + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5)) { + POSIX_GUARD(s2n_hash_update(&hashes->md5, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha1, data->data, data->size)); + } + + const uint8_t md5_sha1_required = + (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_MD5) + && s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA1)); + + if (md5_sha1_required) { + POSIX_GUARD(s2n_hash_update(&hashes->md5_sha1, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA224)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha224, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA256)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha256, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA384)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha384, data->data, data->size)); + } + + if (s2n_handshake_is_hash_required(&conn->handshake, S2N_HASH_SHA512)) { + POSIX_GUARD(s2n_hash_update(&hashes->sha512, data->data, data->size)); + } + + return S2N_SUCCESS; +} + +/* When a HelloRetryRequest message is used, the hash transcript needs to be recreated. + * This is done with a synthetic message header, and the hash of ClientHello1. + * + * https://tools.ietf.org/html/rfc8446#section-4.4.1 + */ +int s2n_server_hello_retry_recreate_transcript(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + struct s2n_handshake_hashes *hashes = conn->handshake.hashes; + POSIX_ENSURE_REF(hashes); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + + /* Create the MessageHash (our synthetic message) */ + uint8_t msghdr[MESSAGE_HASH_HEADER_LENGTH] = { 0 }; + msghdr[0] = TLS_MESSAGE_HASH; + msghdr[MESSAGE_HASH_HEADER_LENGTH - 1] = hash_digest_length; + + /* Grab the current transcript hash to use as the ClientHello1 value. */ + struct s2n_hash_state *client_hello1_hash = &hashes->hash_workspace; + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, client_hello1_hash)); + POSIX_GUARD(s2n_hash_digest(client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + + /* Step 1: Reset the hash state */ + POSIX_GUARD_RESULT(s2n_handshake_reset_hash_state(conn, keys.hash_algorithm)); + + /* Step 2: Update the transcript with the synthetic message */ + struct s2n_blob msg_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&msg_blob, msghdr, MESSAGE_HASH_HEADER_LENGTH)); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); + + /* Step 3: Update the transcript with the ClientHello1 hash */ + POSIX_GUARD(s2n_blob_init(&msg_blob, client_hello1_digest_out, hash_digest_length)); + POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &msg_blob)); + + return S2N_SUCCESS; +} diff --git a/tls/s2n_ktls.h b/tls/s2n_ktls.h index 846016afdfe..e4658fa865c 100644 --- a/tls/s2n_ktls.h +++ b/tls/s2n_ktls.h @@ -1,81 +1,81 @@ -/* - * 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 - -#include "api/unstable/ktls.h" -#include "tls/s2n_connection.h" -/* Define headers needed to enable and use kTLS. - * - * The inline header definitions are required to compile kTLS specific code. - * kTLS has been tested on linux. For all other platforms, kTLS is marked as - * unsupported, and will return an unsupported error. - */ -#include "tls/s2n_ktls_parameters.h" - -/* A set of kTLS configurations representing the combination of sending - * and receiving. - */ -typedef enum { - /* Enable kTLS for the send socket. */ - S2N_KTLS_MODE_SEND, - /* Enable kTLS for the receive socket. */ - S2N_KTLS_MODE_RECV, -} s2n_ktls_mode; - -bool s2n_ktls_is_supported_on_platform(); -S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd); - -int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len); -ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, - ssize_t count, ssize_t offs, s2n_blocked_status *blocked); -int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, - const struct iovec *in, int in_count, size_t offs, size_t to_write); -int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type); -S2N_RESULT s2n_ktls_key_update_send(struct s2n_connection *conn, size_t bytes_requested); -S2N_RESULT s2n_ktls_key_update_process(struct s2n_connection *conn); -S2N_RESULT s2n_ktls_set_estimated_sequence_number(struct s2n_connection *conn, size_t bytes_written); -S2N_RESULT s2n_ktls_check_estimated_record_limit(struct s2n_connection *conn, size_t bytes_requested); - -int s2n_connection_ktls_enable_send(struct s2n_connection *conn); -int s2n_connection_ktls_enable_recv(struct s2n_connection *conn); - -#ifndef _WIN32 - -#if !defined(_MSC_VER) - #include -#endif - -/* These use POSIX socket types not available on Windows */ -S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, - size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written); -S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, - size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read); - -/* Testing */ -typedef int (*s2n_setsockopt_fn)(int socket, int level, int option_name, const void *option_value, - socklen_t option_len); -S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb); -typedef ssize_t (*s2n_ktls_sendmsg_fn)(void *io_context, const struct msghdr *msg); -typedef ssize_t (*s2n_ktls_recvmsg_fn)(void *io_context, struct msghdr *msg); -S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, - void *send_ctx); -S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, - void *recv_ctx); -void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode); - -int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, - size_t *bytes_written, s2n_blocked_status *blocked); -#endif /* _WIN32 */ +/* + * 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 + +#include "api/unstable/ktls.h" +#include "tls/s2n_connection.h" +/* Define headers needed to enable and use kTLS. + * + * The inline header definitions are required to compile kTLS specific code. + * kTLS has been tested on linux. For all other platforms, kTLS is marked as + * unsupported, and will return an unsupported error. + */ +#include "tls/s2n_ktls_parameters.h" + +/* A set of kTLS configurations representing the combination of sending + * and receiving. + */ +typedef enum { + /* Enable kTLS for the send socket. */ + S2N_KTLS_MODE_SEND, + /* Enable kTLS for the receive socket. */ + S2N_KTLS_MODE_RECV, +} s2n_ktls_mode; + +bool s2n_ktls_is_supported_on_platform(); +S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd); + +int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len); +ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, + ssize_t count, ssize_t offs, s2n_blocked_status *blocked); +int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, + const struct iovec *in, int in_count, size_t offs, size_t to_write); +int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type); +S2N_RESULT s2n_ktls_key_update_send(struct s2n_connection *conn, size_t bytes_requested); +S2N_RESULT s2n_ktls_key_update_process(struct s2n_connection *conn); +S2N_RESULT s2n_ktls_set_estimated_sequence_number(struct s2n_connection *conn, size_t bytes_written); +S2N_RESULT s2n_ktls_check_estimated_record_limit(struct s2n_connection *conn, size_t bytes_requested); + +int s2n_connection_ktls_enable_send(struct s2n_connection *conn); +int s2n_connection_ktls_enable_recv(struct s2n_connection *conn); + +#ifndef _WIN32 + +#if !defined(_MSC_VER) + #include +#endif + +/* These use POSIX socket types not available on Windows */ +S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, + size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written); +S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, + size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read); + +/* Testing */ +typedef int (*s2n_setsockopt_fn)(int socket, int level, int option_name, const void *option_value, + socklen_t option_len); +S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb); +typedef ssize_t (*s2n_ktls_sendmsg_fn)(void *io_context, const struct msghdr *msg); +typedef ssize_t (*s2n_ktls_recvmsg_fn)(void *io_context, struct msghdr *msg); +S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, + void *send_ctx); +S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, + void *recv_ctx); +void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode); + +int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, + size_t *bytes_written, s2n_blocked_status *blocked); +#endif /* _WIN32 */ diff --git a/tls/s2n_ktls_io.c b/tls/s2n_ktls_io.c index 30e088da355..b5b45d74391 100644 --- a/tls/s2n_ktls_io.c +++ b/tls/s2n_ktls_io.c @@ -1,499 +1,499 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* kTLS I/O is not supported on Windows. */ -#ifndef _WIN32 - - #if defined(__FreeBSD__) || defined(__APPLE__) - /* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html - * The POSIX standard does not define the CMSG_LEN and CMSG_SPACE macros. FreeBSD - * and APPLE check and disable these macros if the _POSIX_C_SOURCE flag is set. - * - * Since s2n-tls already unsets the _POSIX_C_SOURCE in other files and is not - * POSIX compliant, we continue the pattern here. - */ - #undef _POSIX_C_SOURCE - #endif -#if !defined(_MSC_VER) - #include -#endif - - #ifdef S2N_LINUX_SENDFILE - #include - #endif - - #include "error/s2n_errno.h" - #include "tls/s2n_ktls.h" - #include "tls/s2n_tls.h" - #include "utils/s2n_io.h" - #include "utils/s2n_result.h" - #include "utils/s2n_safety.h" - #include "utils/s2n_socket.h" - - /* record_type is of type uint8_t */ - #define S2N_KTLS_RECORD_TYPE_SIZE (sizeof(uint8_t)) - #define S2N_KTLS_CONTROL_BUFFER_SIZE (CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)) - - #define S2N_MAX_STACK_IOVECS 16 - #define S2N_MAX_STACK_IOVECS_MEM (S2N_MAX_STACK_IOVECS * sizeof(struct iovec)) - -/* Used to override sendmsg and recvmsg for testing. */ -static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg); -static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg); -s2n_ktls_sendmsg_fn s2n_sendmsg_fn = s2n_ktls_default_sendmsg; -s2n_ktls_recvmsg_fn s2n_recvmsg_fn = s2n_ktls_default_recvmsg; - -S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, - void *send_ctx) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(send_ctx); - RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); - conn->send_io_context = send_ctx; - s2n_sendmsg_fn = send_cb; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, - void *recv_ctx) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(recv_ctx); - RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); - conn->recv_io_context = recv_ctx; - s2n_recvmsg_fn = recv_cb; - return S2N_RESULT_OK; -} - -static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(msg); - - const struct s2n_socket_read_io_context *peer_socket_ctx = io_context; - POSIX_ENSURE_REF(peer_socket_ctx); - int fd = peer_socket_ctx->fd; - - return recvmsg(fd, msg, 0); -} - -static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(msg); - - const struct s2n_socket_write_io_context *peer_socket_ctx = io_context; - POSIX_ENSURE_REF(peer_socket_ctx); - int fd = peer_socket_ctx->fd; - - return sendmsg(fd, msg, 0); -} - -S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, - int cmsg_type, uint8_t record_type) -{ - RESULT_ENSURE_REF(msg); - RESULT_ENSURE_REF(buf); - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * To create ancillary data, first initialize the msg_controllen - * member of the msghdr with the length of the control message - * buffer. - */ - msg->msg_control = buf; - msg->msg_controllen = buf_size; - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * Use CMSG_FIRSTHDR() on the msghdr to get the first - * control message and CMSG_NXTHDR() to get all subsequent ones. - */ - struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); - RESULT_ENSURE_REF(hdr); - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * In each control message, initialize cmsg_len (with CMSG_LEN()), the - * other cmsghdr header fields, and the data portion using - * CMSG_DATA(). - */ - hdr->cmsg_len = CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE); - hdr->cmsg_level = S2N_SOL_TLS; - hdr->cmsg_type = cmsg_type; - *CMSG_DATA(hdr) = record_type; - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * Finally, the msg_controllen field of the msghdr - * should be set to the sum of the CMSG_SPACE() of the length of all - * control messages in the buffer - */ - RESULT_ENSURE_GTE(msg->msg_controllen, CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)); - msg->msg_controllen = CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE); - - return S2N_RESULT_OK; -} - -/* Expect to receive a single cmsghdr containing the TLS record_type. - * - * s2n-tls allocates enough space to receive a single cmsghdr. Since this is - * used to get the record_type when receiving over kTLS (enabled via - * `s2n_connection_ktls_enable_recv`), the application should not configure - * the socket to receive additional control messages. In the event s2n-tls - * can not retrieve the record_type, it is safer to drop the record. - */ -S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type) -{ - RESULT_ENSURE_REF(msg); - RESULT_ENSURE_REF(record_type); - - /* https://man7.org/linux/man-pages/man3/recvmsg.3p.html - * MSG_CTRUNC Control data was truncated. - */ - if (msg->msg_flags & MSG_CTRUNC) { - RESULT_BAIL(S2N_ERR_KTLS_BAD_CMSG); - } - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * To create ancillary data, first initialize the msg_controllen - * member of the msghdr with the length of the control message - * buffer. - */ - RESULT_ENSURE(msg->msg_control, S2N_ERR_SAFETY); - RESULT_ENSURE(msg->msg_controllen >= CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_SAFETY); - - /* https://man7.org/linux/man-pages/man3/cmsg.3.html - * Use CMSG_FIRSTHDR() on the msghdr to get the first - * control message and CMSG_NXTHDR() to get all subsequent ones. - */ - struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); - RESULT_ENSURE(hdr, S2N_ERR_KTLS_BAD_CMSG); - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * In each control message, initialize cmsg_len (with CMSG_LEN()), the - * other cmsghdr header fields, and the data portion using - * CMSG_DATA(). - */ - RESULT_ENSURE(hdr->cmsg_level == S2N_SOL_TLS, S2N_ERR_KTLS_BAD_CMSG); - RESULT_ENSURE(hdr->cmsg_type == cmsg_type, S2N_ERR_KTLS_BAD_CMSG); - RESULT_ENSURE(hdr->cmsg_len == CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_KTLS_BAD_CMSG); - *record_type = *CMSG_DATA(hdr); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, - size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written) -{ - RESULT_ENSURE_REF(bytes_written); - RESULT_ENSURE_REF(blocked); - RESULT_ENSURE(msg_iov != NULL || msg_iovlen == 0, S2N_ERR_NULL); - - *blocked = S2N_BLOCKED_ON_WRITE; - *bytes_written = 0; - - struct msghdr msg = { - /* msghdr requires a non-const iovec. This is safe because s2n-tls does - * not modify msg_iov after this point. - */ - .msg_iov = (struct iovec *) (uintptr_t) msg_iov, - .msg_iovlen = msg_iovlen, - }; - - char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; - RESULT_GUARD(s2n_ktls_set_control_data(&msg, control_data, sizeof(control_data), - S2N_TLS_SET_RECORD_TYPE, record_type)); - - ssize_t result = 0; - S2N_IO_RETRY_EINTR(result, s2n_sendmsg_fn(io_context, &msg)); - RESULT_GUARD(s2n_io_check_write_result(result)); - - *blocked = S2N_NOT_BLOCKED; - *bytes_written = result; - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, - size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read) -{ - RESULT_ENSURE_REF(record_type); - RESULT_ENSURE_REF(bytes_read); - RESULT_ENSURE_REF(blocked); - RESULT_ENSURE_REF(buf); - /* Ensure that buf_len is > 0 since trying to receive 0 bytes does not - * make sense and a return value of `0` from recvmsg is treated as EOF. - */ - RESULT_ENSURE_GT(buf_len, 0); - - *blocked = S2N_BLOCKED_ON_READ; - *record_type = 0; - *bytes_read = 0; - struct iovec msg_iov = { - .iov_base = buf, - .iov_len = buf_len - }; - struct msghdr msg = { - .msg_iov = &msg_iov, - .msg_iovlen = 1, - }; - - /* - * https://man7.org/linux/man-pages/man3/cmsg.3.html - * To create ancillary data, first initialize the msg_controllen - * member of the msghdr with the length of the control message - * buffer. - */ - char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; - msg.msg_controllen = sizeof(control_data); - msg.msg_control = control_data; - - ssize_t result = 0; - S2N_IO_RETRY_EINTR(result, s2n_recvmsg_fn(io_context, &msg)); - RESULT_GUARD(s2n_io_check_read_result(result)); - - RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type)); - - *blocked = S2N_NOT_BLOCKED; - *bytes_read = result; - return S2N_RESULT_OK; -} - -/* The iovec array `bufs` is constant and owned by the application. - * - * However, we need to apply the given offset to `bufs`. That may involve - * updating the iov_base and iov_len of entries in `bufs` to reflect the bytes - * already sent. Because `bufs` is constant, we need to instead copy `bufs` and - * modify the copy. - * - * Since one of the primary benefits of kTLS is that we avoid buffering application - * data and can pass application data as-is to the kernel, we try to limit the - * situations where we need to copy `bufs` and use stack memory where possible. - * - * Note: We are copying an array of iovecs here, NOT the scattered application - * data the iovecs reference. On Linux, the maximum data copied would be - * 1024 (IOV_MAX on Linux) * 16 (sizeof(struct iovec)) = ~16KB. - * - * To avoid any copies when using a large number of iovecs, applications should - * call s2n_sendv instead of s2n_sendv_with_offset. - */ -static S2N_RESULT s2n_ktls_update_bufs_with_offset(const struct iovec **bufs, size_t *count, - size_t offs, struct s2n_blob *mem) -{ - RESULT_ENSURE_REF(bufs); - RESULT_ENSURE_REF(count); - RESULT_ENSURE(*bufs != NULL || *count == 0, S2N_ERR_NULL); - RESULT_ENSURE_REF(mem); - - size_t skipped = 0; - while (offs > 0) { - /* If we need to skip more iovecs than actually exist, - * then the offset is too large and therefore invalid. - */ - RESULT_ENSURE(skipped < *count, S2N_ERR_INVALID_ARGUMENT); - - size_t iov_len = (*bufs)[skipped].iov_len; - - /* This is the last iovec affected by the offset. */ - if (offs < iov_len) { - break; - } - - offs -= iov_len; - skipped++; - } - - *count = (*count) - skipped; - if (*count == 0) { - return S2N_RESULT_OK; - } - - *bufs = &(*bufs)[skipped]; - if (offs == 0) { - return S2N_RESULT_OK; - } - - size_t size = (*count) * (sizeof(struct iovec)); - /* If possible, use the existing stack memory in `mem` for the copy. - * Otherwise, we need to allocate sufficient new heap memory. */ - if (size > mem->size) { - RESULT_GUARD_POSIX(s2n_alloc(mem, size)); - } - - struct iovec *new_bufs = (struct iovec *) (void *) mem->data; - RESULT_CHECKED_MEMCPY(new_bufs, *bufs, size); - new_bufs[0].iov_base = (uint8_t *) new_bufs[0].iov_base + offs; - new_bufs[0].iov_len = new_bufs[0].iov_len - offs; - *bufs = new_bufs; - - return S2N_RESULT_OK; -} - -ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, - ssize_t count_in, ssize_t offs_in, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(count_in >= 0, S2N_ERR_INVALID_ARGUMENT); - size_t count = count_in; - POSIX_ENSURE(offs_in >= 0, S2N_ERR_INVALID_ARGUMENT); - size_t offs = offs_in; - - ssize_t total_bytes = 0; - POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count_in, offs_in, &total_bytes)); - POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, total_bytes)); - - /* The order of new_bufs and new_bufs_mem matters. See https://github.com/aws/s2n-tls/issues/4354 */ - uint8_t new_bufs_mem[S2N_MAX_STACK_IOVECS_MEM] = { 0 }; - DEFER_CLEANUP(struct s2n_blob new_bufs = { 0 }, s2n_free_or_wipe); - POSIX_GUARD(s2n_blob_init(&new_bufs, new_bufs_mem, sizeof(new_bufs_mem))); - if (offs > 0) { - POSIX_GUARD_RESULT(s2n_ktls_update_bufs_with_offset(&bufs, &count, offs, &new_bufs)); - } - - size_t bytes_written = 0; - POSIX_GUARD_RESULT(s2n_ktls_sendmsg(conn->send_io_context, TLS_APPLICATION_DATA, - bufs, count, blocked, &bytes_written)); - - POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, bytes_written)); - return bytes_written; -} - -int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(buf); - - /* For now, all control records are assumed to be alerts. - * We can set the record_type on the io_context in the future. - */ - const uint8_t record_type = TLS_ALERT; - - const struct iovec iov = { - .iov_base = (void *) (uintptr_t) buf, - .iov_len = len, - }; - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - size_t bytes_written = 0; - - POSIX_GUARD_RESULT(s2n_ktls_sendmsg(io_context, record_type, &iov, 1, - &blocked, &bytes_written)); - - POSIX_ENSURE_LTE(bytes_written, len); - return bytes_written; -} - -int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, - const struct iovec *in, int in_count, size_t offs, size_t to_write) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(in_count > 0, S2N_ERR_INVALID_ARGUMENT); - size_t count = in_count; - POSIX_ENSURE_REF(in); - - /* Currently, ktls only supports sending alerts. - * To also support handshake messages, we would need a way to track record_type. - * We could add a field to the send io context. - */ - POSIX_ENSURE(content_type == TLS_ALERT, S2N_ERR_UNIMPLEMENTED); - - /* When stuffers automatically resize, they allocate a potentially large - * chunk of memory to avoid repeated resizes. - * Since ktls only uses conn->out for control messages (alerts and eventually - * handshake messages), we expect infrequent small writes with conn->out - * freed in between. Since we're therefore more concerned with the size of - * the allocation than the frequency, use a more accurate size for each write. - */ - POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->out, to_write)); - - POSIX_GUARD(s2n_stuffer_writev_bytes(&conn->out, in, count, offs, to_write)); - return to_write; -} - -int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, - size_t *bytes_written, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(blocked); - *blocked = S2N_BLOCKED_ON_WRITE; - POSIX_ENSURE_REF(bytes_written); - *bytes_written = 0; - POSIX_ENSURE_REF(conn); - POSIX_ENSURE(conn->ktls_send_enabled, S2N_ERR_KTLS_UNSUPPORTED_CONN); - POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, count)); - - int out_fd = 0; - POSIX_GUARD_RESULT(s2n_ktls_get_file_descriptor(conn, S2N_KTLS_MODE_SEND, &out_fd)); - - #ifdef S2N_LINUX_SENDFILE - /* https://man7.org/linux/man-pages/man2/sendfile.2.html */ - ssize_t result = 0; - S2N_IO_RETRY_EINTR(result, sendfile(out_fd, in_fd, &offset, count)); - POSIX_GUARD_RESULT(s2n_io_check_write_result(result)); - *bytes_written = result; - #else - POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); - #endif - - POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, *bytes_written)); - *blocked = S2N_NOT_BLOCKED; - return S2N_SUCCESS; -} - -int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(record_type); - - /* If any unread data remains in conn->in, it must be application data that - * couldn't be returned due to the size of the application's provided buffer. - */ - if (s2n_stuffer_data_available(&conn->in)) { - *record_type = TLS_APPLICATION_DATA; - return S2N_SUCCESS; - } - - POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_DEFAULT_FRAGMENT_LENGTH)); - - struct s2n_stuffer record_stuffer = conn->buffer_in; - size_t len = s2n_stuffer_space_remaining(&record_stuffer); - uint8_t *buf = s2n_stuffer_raw_write(&record_stuffer, len); - POSIX_ENSURE_REF(buf); - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - size_t bytes_read = 0; - - /* Since recvmsg is responsible for decrypting the record in ktls, - * we apply blinding to the recvmsg call. - */ - s2n_result result = s2n_ktls_recvmsg(conn->recv_io_context, record_type, - buf, len, &blocked, &bytes_read); - WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); - - POSIX_GUARD(s2n_stuffer_skip_write(&conn->buffer_in, bytes_read)); - - /* We don't care about returning a full fragment because we don't need to decrypt. - * kTLS handled decryption already. - * So we can always set conn->in equal to the full buffer_in. - */ - POSIX_GUARD_RESULT(s2n_recv_in_init(conn, bytes_read, bytes_read)); - return S2N_SUCCESS; -} - -#endif - -/* Suppress empty translation unit warning when compiled on Windows. */ -#pragma clang diagnostic ignored "-Wempty-translation-unit" +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* kTLS I/O is not supported on Windows. */ +#ifndef _WIN32 + + #if defined(__FreeBSD__) || defined(__APPLE__) + /* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html + * The POSIX standard does not define the CMSG_LEN and CMSG_SPACE macros. FreeBSD + * and APPLE check and disable these macros if the _POSIX_C_SOURCE flag is set. + * + * Since s2n-tls already unsets the _POSIX_C_SOURCE in other files and is not + * POSIX compliant, we continue the pattern here. + */ + #undef _POSIX_C_SOURCE + #endif +#if !defined(_MSC_VER) + #include +#endif + + #ifdef S2N_LINUX_SENDFILE + #include + #endif + + #include "error/s2n_errno.h" + #include "tls/s2n_ktls.h" + #include "tls/s2n_tls.h" + #include "utils/s2n_io.h" + #include "utils/s2n_result.h" + #include "utils/s2n_safety.h" + #include "utils/s2n_socket.h" + + /* record_type is of type uint8_t */ + #define S2N_KTLS_RECORD_TYPE_SIZE (sizeof(uint8_t)) + #define S2N_KTLS_CONTROL_BUFFER_SIZE (CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)) + + #define S2N_MAX_STACK_IOVECS 16 + #define S2N_MAX_STACK_IOVECS_MEM (S2N_MAX_STACK_IOVECS * sizeof(struct iovec)) + +/* Used to override sendmsg and recvmsg for testing. */ +static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg); +static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg); +s2n_ktls_sendmsg_fn s2n_sendmsg_fn = s2n_ktls_default_sendmsg; +s2n_ktls_recvmsg_fn s2n_recvmsg_fn = s2n_ktls_default_recvmsg; + +S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, + void *send_ctx) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(send_ctx); + RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); + conn->send_io_context = send_ctx; + s2n_sendmsg_fn = send_cb; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, + void *recv_ctx) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(recv_ctx); + RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); + conn->recv_io_context = recv_ctx; + s2n_recvmsg_fn = recv_cb; + return S2N_RESULT_OK; +} + +static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(msg); + + const struct s2n_socket_read_io_context *peer_socket_ctx = io_context; + POSIX_ENSURE_REF(peer_socket_ctx); + int fd = peer_socket_ctx->fd; + + return recvmsg(fd, msg, 0); +} + +static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(msg); + + const struct s2n_socket_write_io_context *peer_socket_ctx = io_context; + POSIX_ENSURE_REF(peer_socket_ctx); + int fd = peer_socket_ctx->fd; + + return sendmsg(fd, msg, 0); +} + +S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, + int cmsg_type, uint8_t record_type) +{ + RESULT_ENSURE_REF(msg); + RESULT_ENSURE_REF(buf); + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * To create ancillary data, first initialize the msg_controllen + * member of the msghdr with the length of the control message + * buffer. + */ + msg->msg_control = buf; + msg->msg_controllen = buf_size; + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * Use CMSG_FIRSTHDR() on the msghdr to get the first + * control message and CMSG_NXTHDR() to get all subsequent ones. + */ + struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); + RESULT_ENSURE_REF(hdr); + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * In each control message, initialize cmsg_len (with CMSG_LEN()), the + * other cmsghdr header fields, and the data portion using + * CMSG_DATA(). + */ + hdr->cmsg_len = CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE); + hdr->cmsg_level = S2N_SOL_TLS; + hdr->cmsg_type = cmsg_type; + *CMSG_DATA(hdr) = record_type; + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * Finally, the msg_controllen field of the msghdr + * should be set to the sum of the CMSG_SPACE() of the length of all + * control messages in the buffer + */ + RESULT_ENSURE_GTE(msg->msg_controllen, CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE)); + msg->msg_controllen = CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE); + + return S2N_RESULT_OK; +} + +/* Expect to receive a single cmsghdr containing the TLS record_type. + * + * s2n-tls allocates enough space to receive a single cmsghdr. Since this is + * used to get the record_type when receiving over kTLS (enabled via + * `s2n_connection_ktls_enable_recv`), the application should not configure + * the socket to receive additional control messages. In the event s2n-tls + * can not retrieve the record_type, it is safer to drop the record. + */ +S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type) +{ + RESULT_ENSURE_REF(msg); + RESULT_ENSURE_REF(record_type); + + /* https://man7.org/linux/man-pages/man3/recvmsg.3p.html + * MSG_CTRUNC Control data was truncated. + */ + if (msg->msg_flags & MSG_CTRUNC) { + RESULT_BAIL(S2N_ERR_KTLS_BAD_CMSG); + } + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * To create ancillary data, first initialize the msg_controllen + * member of the msghdr with the length of the control message + * buffer. + */ + RESULT_ENSURE(msg->msg_control, S2N_ERR_SAFETY); + RESULT_ENSURE(msg->msg_controllen >= CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_SAFETY); + + /* https://man7.org/linux/man-pages/man3/cmsg.3.html + * Use CMSG_FIRSTHDR() on the msghdr to get the first + * control message and CMSG_NXTHDR() to get all subsequent ones. + */ + struct cmsghdr *hdr = CMSG_FIRSTHDR(msg); + RESULT_ENSURE(hdr, S2N_ERR_KTLS_BAD_CMSG); + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * In each control message, initialize cmsg_len (with CMSG_LEN()), the + * other cmsghdr header fields, and the data portion using + * CMSG_DATA(). + */ + RESULT_ENSURE(hdr->cmsg_level == S2N_SOL_TLS, S2N_ERR_KTLS_BAD_CMSG); + RESULT_ENSURE(hdr->cmsg_type == cmsg_type, S2N_ERR_KTLS_BAD_CMSG); + RESULT_ENSURE(hdr->cmsg_len == CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_KTLS_BAD_CMSG); + *record_type = *CMSG_DATA(hdr); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov, + size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written) +{ + RESULT_ENSURE_REF(bytes_written); + RESULT_ENSURE_REF(blocked); + RESULT_ENSURE(msg_iov != NULL || msg_iovlen == 0, S2N_ERR_NULL); + + *blocked = S2N_BLOCKED_ON_WRITE; + *bytes_written = 0; + + struct msghdr msg = { + /* msghdr requires a non-const iovec. This is safe because s2n-tls does + * not modify msg_iov after this point. + */ + .msg_iov = (struct iovec *) (uintptr_t) msg_iov, + .msg_iovlen = msg_iovlen, + }; + + char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; + RESULT_GUARD(s2n_ktls_set_control_data(&msg, control_data, sizeof(control_data), + S2N_TLS_SET_RECORD_TYPE, record_type)); + + ssize_t result = 0; + S2N_IO_RETRY_EINTR(result, s2n_sendmsg_fn(io_context, &msg)); + RESULT_GUARD(s2n_io_check_write_result(result)); + + *blocked = S2N_NOT_BLOCKED; + *bytes_written = result; + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf, + size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read) +{ + RESULT_ENSURE_REF(record_type); + RESULT_ENSURE_REF(bytes_read); + RESULT_ENSURE_REF(blocked); + RESULT_ENSURE_REF(buf); + /* Ensure that buf_len is > 0 since trying to receive 0 bytes does not + * make sense and a return value of `0` from recvmsg is treated as EOF. + */ + RESULT_ENSURE_GT(buf_len, 0); + + *blocked = S2N_BLOCKED_ON_READ; + *record_type = 0; + *bytes_read = 0; + struct iovec msg_iov = { + .iov_base = buf, + .iov_len = buf_len + }; + struct msghdr msg = { + .msg_iov = &msg_iov, + .msg_iovlen = 1, + }; + + /* + * https://man7.org/linux/man-pages/man3/cmsg.3.html + * To create ancillary data, first initialize the msg_controllen + * member of the msghdr with the length of the control message + * buffer. + */ + char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 }; + msg.msg_controllen = sizeof(control_data); + msg.msg_control = control_data; + + ssize_t result = 0; + S2N_IO_RETRY_EINTR(result, s2n_recvmsg_fn(io_context, &msg)); + RESULT_GUARD(s2n_io_check_read_result(result)); + + RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type)); + + *blocked = S2N_NOT_BLOCKED; + *bytes_read = result; + return S2N_RESULT_OK; +} + +/* The iovec array `bufs` is constant and owned by the application. + * + * However, we need to apply the given offset to `bufs`. That may involve + * updating the iov_base and iov_len of entries in `bufs` to reflect the bytes + * already sent. Because `bufs` is constant, we need to instead copy `bufs` and + * modify the copy. + * + * Since one of the primary benefits of kTLS is that we avoid buffering application + * data and can pass application data as-is to the kernel, we try to limit the + * situations where we need to copy `bufs` and use stack memory where possible. + * + * Note: We are copying an array of iovecs here, NOT the scattered application + * data the iovecs reference. On Linux, the maximum data copied would be + * 1024 (IOV_MAX on Linux) * 16 (sizeof(struct iovec)) = ~16KB. + * + * To avoid any copies when using a large number of iovecs, applications should + * call s2n_sendv instead of s2n_sendv_with_offset. + */ +static S2N_RESULT s2n_ktls_update_bufs_with_offset(const struct iovec **bufs, size_t *count, + size_t offs, struct s2n_blob *mem) +{ + RESULT_ENSURE_REF(bufs); + RESULT_ENSURE_REF(count); + RESULT_ENSURE(*bufs != NULL || *count == 0, S2N_ERR_NULL); + RESULT_ENSURE_REF(mem); + + size_t skipped = 0; + while (offs > 0) { + /* If we need to skip more iovecs than actually exist, + * then the offset is too large and therefore invalid. + */ + RESULT_ENSURE(skipped < *count, S2N_ERR_INVALID_ARGUMENT); + + size_t iov_len = (*bufs)[skipped].iov_len; + + /* This is the last iovec affected by the offset. */ + if (offs < iov_len) { + break; + } + + offs -= iov_len; + skipped++; + } + + *count = (*count) - skipped; + if (*count == 0) { + return S2N_RESULT_OK; + } + + *bufs = &(*bufs)[skipped]; + if (offs == 0) { + return S2N_RESULT_OK; + } + + size_t size = (*count) * (sizeof(struct iovec)); + /* If possible, use the existing stack memory in `mem` for the copy. + * Otherwise, we need to allocate sufficient new heap memory. */ + if (size > mem->size) { + RESULT_GUARD_POSIX(s2n_alloc(mem, size)); + } + + struct iovec *new_bufs = (struct iovec *) (void *) mem->data; + RESULT_CHECKED_MEMCPY(new_bufs, *bufs, size); + new_bufs[0].iov_base = (uint8_t *) new_bufs[0].iov_base + offs; + new_bufs[0].iov_len = new_bufs[0].iov_len - offs; + *bufs = new_bufs; + + return S2N_RESULT_OK; +} + +ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, + ssize_t count_in, ssize_t offs_in, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(count_in >= 0, S2N_ERR_INVALID_ARGUMENT); + size_t count = count_in; + POSIX_ENSURE(offs_in >= 0, S2N_ERR_INVALID_ARGUMENT); + size_t offs = offs_in; + + ssize_t total_bytes = 0; + POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count_in, offs_in, &total_bytes)); + POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, total_bytes)); + + /* The order of new_bufs and new_bufs_mem matters. See https://github.com/aws/s2n-tls/issues/4354 */ + uint8_t new_bufs_mem[S2N_MAX_STACK_IOVECS_MEM] = { 0 }; + DEFER_CLEANUP(struct s2n_blob new_bufs = { 0 }, s2n_free_or_wipe); + POSIX_GUARD(s2n_blob_init(&new_bufs, new_bufs_mem, sizeof(new_bufs_mem))); + if (offs > 0) { + POSIX_GUARD_RESULT(s2n_ktls_update_bufs_with_offset(&bufs, &count, offs, &new_bufs)); + } + + size_t bytes_written = 0; + POSIX_GUARD_RESULT(s2n_ktls_sendmsg(conn->send_io_context, TLS_APPLICATION_DATA, + bufs, count, blocked, &bytes_written)); + + POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, bytes_written)); + return bytes_written; +} + +int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(buf); + + /* For now, all control records are assumed to be alerts. + * We can set the record_type on the io_context in the future. + */ + const uint8_t record_type = TLS_ALERT; + + const struct iovec iov = { + .iov_base = (void *) (uintptr_t) buf, + .iov_len = len, + }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + + POSIX_GUARD_RESULT(s2n_ktls_sendmsg(io_context, record_type, &iov, 1, + &blocked, &bytes_written)); + + POSIX_ENSURE_LTE(bytes_written, len); + return bytes_written; +} + +int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type, + const struct iovec *in, int in_count, size_t offs, size_t to_write) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(in_count > 0, S2N_ERR_INVALID_ARGUMENT); + size_t count = in_count; + POSIX_ENSURE_REF(in); + + /* Currently, ktls only supports sending alerts. + * To also support handshake messages, we would need a way to track record_type. + * We could add a field to the send io context. + */ + POSIX_ENSURE(content_type == TLS_ALERT, S2N_ERR_UNIMPLEMENTED); + + /* When stuffers automatically resize, they allocate a potentially large + * chunk of memory to avoid repeated resizes. + * Since ktls only uses conn->out for control messages (alerts and eventually + * handshake messages), we expect infrequent small writes with conn->out + * freed in between. Since we're therefore more concerned with the size of + * the allocation than the frequency, use a more accurate size for each write. + */ + POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->out, to_write)); + + POSIX_GUARD(s2n_stuffer_writev_bytes(&conn->out, in, count, offs, to_write)); + return to_write; +} + +int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count, + size_t *bytes_written, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(blocked); + *blocked = S2N_BLOCKED_ON_WRITE; + POSIX_ENSURE_REF(bytes_written); + *bytes_written = 0; + POSIX_ENSURE_REF(conn); + POSIX_ENSURE(conn->ktls_send_enabled, S2N_ERR_KTLS_UNSUPPORTED_CONN); + POSIX_GUARD_RESULT(s2n_ktls_key_update_send(conn, count)); + + int out_fd = 0; + POSIX_GUARD_RESULT(s2n_ktls_get_file_descriptor(conn, S2N_KTLS_MODE_SEND, &out_fd)); + + #ifdef S2N_LINUX_SENDFILE + /* https://man7.org/linux/man-pages/man2/sendfile.2.html */ + ssize_t result = 0; + S2N_IO_RETRY_EINTR(result, sendfile(out_fd, in_fd, &offset, count)); + POSIX_GUARD_RESULT(s2n_io_check_write_result(result)); + *bytes_written = result; + #else + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); + #endif + + POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, *bytes_written)); + *blocked = S2N_NOT_BLOCKED; + return S2N_SUCCESS; +} + +int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(record_type); + + /* If any unread data remains in conn->in, it must be application data that + * couldn't be returned due to the size of the application's provided buffer. + */ + if (s2n_stuffer_data_available(&conn->in)) { + *record_type = TLS_APPLICATION_DATA; + return S2N_SUCCESS; + } + + POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_DEFAULT_FRAGMENT_LENGTH)); + + struct s2n_stuffer record_stuffer = conn->buffer_in; + size_t len = s2n_stuffer_space_remaining(&record_stuffer); + uint8_t *buf = s2n_stuffer_raw_write(&record_stuffer, len); + POSIX_ENSURE_REF(buf); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_read = 0; + + /* Since recvmsg is responsible for decrypting the record in ktls, + * we apply blinding to the recvmsg call. + */ + s2n_result result = s2n_ktls_recvmsg(conn->recv_io_context, record_type, + buf, len, &blocked, &bytes_read); + WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); + + POSIX_GUARD(s2n_stuffer_skip_write(&conn->buffer_in, bytes_read)); + + /* We don't care about returning a full fragment because we don't need to decrypt. + * kTLS handled decryption already. + * So we can always set conn->in equal to the full buffer_in. + */ + POSIX_GUARD_RESULT(s2n_recv_in_init(conn, bytes_read, bytes_read)); + return S2N_SUCCESS; +} + +#endif + +/* Suppress empty translation unit warning when compiled on Windows. */ +#pragma clang diagnostic ignored "-Wempty-translation-unit" diff --git a/tls/s2n_ocsp_stapling.c b/tls/s2n_ocsp_stapling.c index dcf3478fa6d..82acf719d08 100644 --- a/tls/s2n_ocsp_stapling.c +++ b/tls/s2n_ocsp_stapling.c @@ -1,41 +1,41 @@ -/* - * 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. - */ - -#if !defined(_MSC_VER) -#include -#endif - -#include "error/s2n_errno.h" -#include "tls/extensions/s2n_cert_status.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_x509_validator.h" -#include "utils/s2n_safety.h" - -int s2n_server_status_send(struct s2n_connection *conn) -{ - if (s2n_server_can_send_ocsp(conn)) { - POSIX_GUARD(s2n_cert_status_send(conn, &conn->handshake.io)); - } - - return 0; -} - -int s2n_server_status_recv(struct s2n_connection *conn) -{ - return s2n_cert_status_recv(conn, &conn->handshake.io); -} +/* + * 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. + */ + +#if !defined(_MSC_VER) +#include +#endif + +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_cert_status.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_x509_validator.h" +#include "utils/s2n_safety.h" + +int s2n_server_status_send(struct s2n_connection *conn) +{ + if (s2n_server_can_send_ocsp(conn)) { + POSIX_GUARD(s2n_cert_status_send(conn, &conn->handshake.io)); + } + + return 0; +} + +int s2n_server_status_recv(struct s2n_connection *conn) +{ + return s2n_cert_status_recv(conn, &conn->handshake.io); +} diff --git a/tls/s2n_post_handshake.c b/tls/s2n_post_handshake.c index 6d4a58baa59..f783bddcffe 100644 --- a/tls/s2n_post_handshake.c +++ b/tls/s2n_post_handshake.c @@ -1,201 +1,201 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "error/s2n_errno.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_key_update.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_post_handshake_process(struct s2n_connection *conn, struct s2n_stuffer *in, uint8_t message_type) -{ - RESULT_ENSURE_REF(conn); - - switch (message_type) { - case TLS_KEY_UPDATE: - RESULT_GUARD_POSIX(s2n_key_update_recv(conn, in)); - break; - case TLS_SERVER_NEW_SESSION_TICKET: - RESULT_GUARD(s2n_tls13_server_nst_recv(conn, in)); - break; - case TLS_HELLO_REQUEST: - RESULT_GUARD(s2n_client_hello_request_recv(conn)); - break; - case TLS_CERT_REQ: - /* - * s2n-tls does not support post-handshake authentication. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.2 - *# A client that receives a CertificateRequest message without having - *# sent the "post_handshake_auth" extension MUST send an - *# "unexpected_message" fatal alert. - */ - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); - default: - /* All other messages are unexpected */ - RESULT_BAIL(S2N_ERR_BAD_MESSAGE); - } - - return S2N_RESULT_OK; -} - -/* - * Read a handshake message from conn->in. - * - * Handshake messages can be fragmented, meaning that a single message - * may be split between multiple records. conn->in only holds a single - * record at a time, so we may need to call this method multiple - * times to construct the complete message. We store the partial message - * in conn->post_handshake.in between calls. - */ -S2N_RESULT s2n_post_handshake_message_recv(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_stuffer *in = &conn->in; - struct s2n_stuffer *message = &conn->post_handshake.in; - uint8_t message_type = 0; - uint32_t message_len = 0; - - /* We always start reading from the beginning of the message. - * Reset the read progress, but keep the write progress since - * there may already be a partial message stored in `message`. - */ - RESULT_GUARD_POSIX(s2n_stuffer_reread(message)); - - /* At minimum, the message stuffer needs to have enough space to read the header. - * For small messages like KeyUpdate and HelloRequest, this is all the space we will need. - */ - if (s2n_stuffer_is_freed(message)) { - struct s2n_blob b = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&b, conn->post_handshake.header_in, - sizeof(conn->post_handshake.header_in))); - RESULT_GUARD_POSIX(s2n_stuffer_init(message, &b)); - } - - /* Try to copy the header into the message stuffer. - * The message stuffer may already contain some or all of the header if - * we have read fragments of this message from previous records. - */ - if (s2n_stuffer_data_available(message) < TLS_HANDSHAKE_HEADER_LENGTH) { - uint32_t remaining = TLS_HANDSHAKE_HEADER_LENGTH - s2n_stuffer_data_available(message); - uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); - RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); - } - RESULT_ENSURE(s2n_stuffer_data_available(message) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_IO_BLOCKED); - - /* Parse the header */ - RESULT_GUARD(s2n_handshake_parse_header(message, &message_type, &message_len)); - RESULT_ENSURE(message_len == 0 || s2n_stuffer_data_available(in), S2N_ERR_IO_BLOCKED); - RESULT_ENSURE(message_len <= S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - - /* If the message body is not fragmented, just process it directly from conn->in. - * This will be the most common case, and does not require us to allocate any new memory. - */ - if (s2n_stuffer_data_available(message) == 0 && s2n_stuffer_data_available(in) >= message_len) { - struct s2n_stuffer full_message = { 0 }; - struct s2n_blob full_message_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&full_message_blob, s2n_stuffer_raw_read(in, message_len), message_len)); - RESULT_GUARD_POSIX(s2n_stuffer_init(&full_message, &full_message_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&full_message, message_len)); - RESULT_GUARD(s2n_post_handshake_process(conn, &full_message, message_type)); - return S2N_RESULT_OK; - } - - /* If the message body is fragmented, then the current fragment will be wiped from conn->in - * in order to read the next record. So the message stuffer needs enough space to store - * the full message as we reconstruct it from multiple records. - * For large messages like NewSessionTicket, this will require allocating new memory. - */ - if (s2n_stuffer_space_remaining(message) < message_len) { - /* We want to avoid servers allocating memory in response to post-handshake messages - * to avoid a potential DDOS / resource exhaustion attack. - * - * Currently, s2n-tls servers only support the KeyUpdate message, - * which should never require additional memory to parse. - */ - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); - - uint32_t total_size = message_len + TLS_HANDSHAKE_HEADER_LENGTH; - if (message->alloced) { - RESULT_GUARD_POSIX(s2n_stuffer_resize(message, total_size)); - } else { - /* Manually convert our static stuffer to a growable stuffer */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(message, total_size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(message, conn->post_handshake.header_in, TLS_HANDSHAKE_HEADER_LENGTH)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(message, TLS_HANDSHAKE_HEADER_LENGTH)); - } - } - - /* Try to copy the message body into the message stuffer. - * The message stuffer may already contain some of the message body if - * we have already read fragments from previous records. - */ - if (s2n_stuffer_data_available(message) < message_len) { - uint32_t remaining = message_len - s2n_stuffer_data_available(message); - uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); - RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); - } - RESULT_ENSURE(s2n_stuffer_data_available(message) == message_len, S2N_ERR_IO_BLOCKED); - - /* Now that the full message body is available, process it. */ - RESULT_GUARD(s2n_post_handshake_process(conn, message, message_type)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_post_handshake_recv(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - while (s2n_stuffer_data_available(&conn->in)) { - RESULT_GUARD(s2n_post_handshake_message_recv(conn)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->post_handshake.in)); - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_post_handshake_write_records(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - struct s2n_stuffer *message = &conn->handshake.io; - - /* Flush any existing records before we write a new handshake record. - * We do not support buffering multiple handshake records. - */ - if (s2n_stuffer_data_available(message)) { - RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); - } - - RESULT_GUARD(s2n_handshake_message_send(conn, TLS_HANDSHAKE, blocked)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(message)); - return S2N_RESULT_OK; -} - -int s2n_post_handshake_send(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - - /* Currently, we only support TLS1.3 post-handshake messages. */ - if (conn->actual_protocol_version < S2N_TLS13) { - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_post_handshake_write_records(conn, blocked)); - - POSIX_GUARD(s2n_key_update_send(conn, blocked)); - POSIX_GUARD_RESULT(s2n_tls13_server_nst_send(conn, blocked)); - - POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "error/s2n_errno.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_key_update.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_post_handshake_process(struct s2n_connection *conn, struct s2n_stuffer *in, uint8_t message_type) +{ + RESULT_ENSURE_REF(conn); + + switch (message_type) { + case TLS_KEY_UPDATE: + RESULT_GUARD_POSIX(s2n_key_update_recv(conn, in)); + break; + case TLS_SERVER_NEW_SESSION_TICKET: + RESULT_GUARD(s2n_tls13_server_nst_recv(conn, in)); + break; + case TLS_HELLO_REQUEST: + RESULT_GUARD(s2n_client_hello_request_recv(conn)); + break; + case TLS_CERT_REQ: + /* + * s2n-tls does not support post-handshake authentication. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.2 + *# A client that receives a CertificateRequest message without having + *# sent the "post_handshake_auth" extension MUST send an + *# "unexpected_message" fatal alert. + */ + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); + default: + /* All other messages are unexpected */ + RESULT_BAIL(S2N_ERR_BAD_MESSAGE); + } + + return S2N_RESULT_OK; +} + +/* + * Read a handshake message from conn->in. + * + * Handshake messages can be fragmented, meaning that a single message + * may be split between multiple records. conn->in only holds a single + * record at a time, so we may need to call this method multiple + * times to construct the complete message. We store the partial message + * in conn->post_handshake.in between calls. + */ +S2N_RESULT s2n_post_handshake_message_recv(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_stuffer *in = &conn->in; + struct s2n_stuffer *message = &conn->post_handshake.in; + uint8_t message_type = 0; + uint32_t message_len = 0; + + /* We always start reading from the beginning of the message. + * Reset the read progress, but keep the write progress since + * there may already be a partial message stored in `message`. + */ + RESULT_GUARD_POSIX(s2n_stuffer_reread(message)); + + /* At minimum, the message stuffer needs to have enough space to read the header. + * For small messages like KeyUpdate and HelloRequest, this is all the space we will need. + */ + if (s2n_stuffer_is_freed(message)) { + struct s2n_blob b = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&b, conn->post_handshake.header_in, + sizeof(conn->post_handshake.header_in))); + RESULT_GUARD_POSIX(s2n_stuffer_init(message, &b)); + } + + /* Try to copy the header into the message stuffer. + * The message stuffer may already contain some or all of the header if + * we have read fragments of this message from previous records. + */ + if (s2n_stuffer_data_available(message) < TLS_HANDSHAKE_HEADER_LENGTH) { + uint32_t remaining = TLS_HANDSHAKE_HEADER_LENGTH - s2n_stuffer_data_available(message); + uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); + } + RESULT_ENSURE(s2n_stuffer_data_available(message) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_IO_BLOCKED); + + /* Parse the header */ + RESULT_GUARD(s2n_handshake_parse_header(message, &message_type, &message_len)); + RESULT_ENSURE(message_len == 0 || s2n_stuffer_data_available(in), S2N_ERR_IO_BLOCKED); + RESULT_ENSURE(message_len <= S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); + + /* If the message body is not fragmented, just process it directly from conn->in. + * This will be the most common case, and does not require us to allocate any new memory. + */ + if (s2n_stuffer_data_available(message) == 0 && s2n_stuffer_data_available(in) >= message_len) { + struct s2n_stuffer full_message = { 0 }; + struct s2n_blob full_message_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&full_message_blob, s2n_stuffer_raw_read(in, message_len), message_len)); + RESULT_GUARD_POSIX(s2n_stuffer_init(&full_message, &full_message_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&full_message, message_len)); + RESULT_GUARD(s2n_post_handshake_process(conn, &full_message, message_type)); + return S2N_RESULT_OK; + } + + /* If the message body is fragmented, then the current fragment will be wiped from conn->in + * in order to read the next record. So the message stuffer needs enough space to store + * the full message as we reconstruct it from multiple records. + * For large messages like NewSessionTicket, this will require allocating new memory. + */ + if (s2n_stuffer_space_remaining(message) < message_len) { + /* We want to avoid servers allocating memory in response to post-handshake messages + * to avoid a potential DDOS / resource exhaustion attack. + * + * Currently, s2n-tls servers only support the KeyUpdate message, + * which should never require additional memory to parse. + */ + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); + + uint32_t total_size = message_len + TLS_HANDSHAKE_HEADER_LENGTH; + if (message->alloced) { + RESULT_GUARD_POSIX(s2n_stuffer_resize(message, total_size)); + } else { + /* Manually convert our static stuffer to a growable stuffer */ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(message, total_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(message, conn->post_handshake.header_in, TLS_HANDSHAKE_HEADER_LENGTH)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(message, TLS_HANDSHAKE_HEADER_LENGTH)); + } + } + + /* Try to copy the message body into the message stuffer. + * The message stuffer may already contain some of the message body if + * we have already read fragments from previous records. + */ + if (s2n_stuffer_data_available(message) < message_len) { + uint32_t remaining = message_len - s2n_stuffer_data_available(message); + uint32_t to_read = S2N_MIN(remaining, s2n_stuffer_data_available(in)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read)); + } + RESULT_ENSURE(s2n_stuffer_data_available(message) == message_len, S2N_ERR_IO_BLOCKED); + + /* Now that the full message body is available, process it. */ + RESULT_GUARD(s2n_post_handshake_process(conn, message, message_type)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_post_handshake_recv(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + while (s2n_stuffer_data_available(&conn->in)) { + RESULT_GUARD(s2n_post_handshake_message_recv(conn)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->post_handshake.in)); + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_post_handshake_write_records(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + struct s2n_stuffer *message = &conn->handshake.io; + + /* Flush any existing records before we write a new handshake record. + * We do not support buffering multiple handshake records. + */ + if (s2n_stuffer_data_available(message)) { + RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); + } + + RESULT_GUARD(s2n_handshake_message_send(conn, TLS_HANDSHAKE, blocked)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(message)); + return S2N_RESULT_OK; +} + +int s2n_post_handshake_send(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + + /* Currently, we only support TLS1.3 post-handshake messages. */ + if (conn->actual_protocol_version < S2N_TLS13) { + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_post_handshake_write_records(conn, blocked)); + + POSIX_GUARD(s2n_key_update_send(conn, blocked)); + POSIX_GUARD_RESULT(s2n_tls13_server_nst_send(conn, blocked)); + + POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); + return S2N_SUCCESS; +} diff --git a/tls/s2n_prf.c b/tls/s2n_prf.c index a352d10fb16..f02b7183dca 100644 --- a/tls/s2n_prf.c +++ b/tls/s2n_prf.c @@ -1,852 +1,852 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_prf.h" - -#include -#include -#include -#include - -#include "crypto/s2n_fips.h" -#include "crypto/s2n_hash.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_prf_libcrypto.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto_constants.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* The s2n p_hash implementation is abstracted to allow for separate implementations. - * Currently the only implementation uses s2n-tls's custom HMAC implementation. - */ -struct s2n_p_hash_hmac { - int (*alloc)(struct s2n_prf_working_space *ws); - int (*init)(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret); - int (*update)(struct s2n_prf_working_space *ws, const void *data, uint32_t size); - int (*final)(struct s2n_prf_working_space *ws, void *digest, uint32_t size); - int (*reset)(struct s2n_prf_working_space *ws); - int (*cleanup)(struct s2n_prf_working_space *ws); - int (*free)(struct s2n_prf_working_space *ws); -}; - -S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, - s2n_hash_algorithm hash_alg, struct s2n_blob *output); -S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, - struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); - -S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(key_material); - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - RESULT_ENSURE_REF(cipher); - - uint8_t mac_size = 0; - uint32_t key_size = 0; - uint32_t iv_size = 0; - - /* MAC size */ - if (cipher->type == S2N_COMPOSITE) { - mac_size = cipher->io.comp.mac_key_size; - } else { - RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->record_alg->hmac_alg, &mac_size)); - } - - /* KEY size */ - key_size = cipher->key_material_size; - - /* Only AEAD ciphers have implicit IVs for TLS >= 1.1 */ - if (conn->actual_protocol_version <= S2N_TLS10 || cipher->type == S2N_AEAD) { - /* IV size */ - switch (cipher->type) { - case S2N_AEAD: - iv_size = cipher->io.aead.fixed_iv_size; - break; - case S2N_CBC: - iv_size = cipher->io.cbc.block_size; - break; - case S2N_COMPOSITE: - iv_size = cipher->io.comp.block_size; - break; - /* No-op for stream ciphers */ - default: - break; - } - } - - struct s2n_stuffer key_material_stuffer = { 0 }; - struct s2n_blob key_material_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&key_material_blob, key_material->key_block, sizeof(key_material->key_block))); - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&key_material_stuffer, &key_material_blob)); - - /* initialize key_material blobs; incrementing ptr to point to the next slice of memory */ - uint8_t *ptr = NULL; - /* MAC */ - ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_mac, ptr, mac_size)); - - ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_mac, ptr, mac_size)); - - /* KEY */ - ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_key, ptr, key_size)); - - ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_key, ptr, key_size)); - - /* IV */ - ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_iv, ptr, iv_size)); - - ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); - RESULT_ENSURE_REF(ptr); - RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_iv, ptr, iv_size)); - - return S2N_RESULT_OK; -} - -/* SSLv3 PRF uses MD5 and SHA-1 in a custom hash-based construction (not - * HMAC). The use of weak hash algorithms is inherent to the SSLv3 protocol - * specification. SSLv3 is disabled by default and not recommended. - */ -static int s2n_prf_sslv3(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *seed_a, - struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - struct s2n_hash_state *workspace = &conn->handshake.hashes->hash_workspace; - - uint32_t outputlen = out->size; - uint8_t *output = out->data; - uint8_t iteration = 1; - - uint8_t md5_digest[MD5_DIGEST_LENGTH] = { 0 }, sha_digest[SHA_DIGEST_LENGTH] = { 0 }; - - uint8_t A = 'A'; - while (outputlen) { - struct s2n_hash_state *sha1 = workspace; - POSIX_GUARD(s2n_hash_reset(sha1)); - POSIX_GUARD(s2n_hash_init(sha1, S2N_HASH_SHA1)); - - for (int i = 0; i < iteration; i++) { - POSIX_GUARD(s2n_hash_update(sha1, &A, 1)); - } - - POSIX_GUARD(s2n_hash_update(sha1, secret->data, secret->size)); - POSIX_GUARD(s2n_hash_update(sha1, seed_a->data, seed_a->size)); - - if (seed_b) { - POSIX_GUARD(s2n_hash_update(sha1, seed_b->data, seed_b->size)); - if (seed_c) { - POSIX_GUARD(s2n_hash_update(sha1, seed_c->data, seed_c->size)); - } - } - - POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, sizeof(sha_digest))); - - struct s2n_hash_state *md5 = workspace; - POSIX_GUARD(s2n_hash_reset(md5)); - POSIX_GUARD(s2n_hash_init(md5, S2N_HASH_MD5)); - POSIX_GUARD(s2n_hash_update(md5, secret->data, secret->size)); - POSIX_GUARD(s2n_hash_update(md5, sha_digest, sizeof(sha_digest))); - POSIX_GUARD(s2n_hash_digest(md5, md5_digest, sizeof(md5_digest))); - - uint32_t bytes_to_copy = S2N_MIN(outputlen, sizeof(md5_digest)); - - POSIX_CHECKED_MEMCPY(output, md5_digest, bytes_to_copy); - - outputlen -= bytes_to_copy; - output += bytes_to_copy; - - /* Increment the letter */ - A++; - iteration++; - } - - return 0; -} - -static int s2n_hmac_p_hash_new(struct s2n_prf_working_space *ws) -{ - POSIX_GUARD(s2n_hmac_new(&ws->p_hash.s2n_hmac)); - return s2n_hmac_init(&ws->p_hash.s2n_hmac, S2N_HMAC_NONE, NULL, 0); -} - -static int s2n_hmac_p_hash_init(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret) -{ - return s2n_hmac_init(&ws->p_hash.s2n_hmac, alg, secret->data, secret->size); -} - -static int s2n_hmac_p_hash_update(struct s2n_prf_working_space *ws, const void *data, uint32_t size) -{ - return s2n_hmac_update(&ws->p_hash.s2n_hmac, data, size); -} - -static int s2n_hmac_p_hash_digest(struct s2n_prf_working_space *ws, void *digest, uint32_t size) -{ - return s2n_hmac_digest(&ws->p_hash.s2n_hmac, digest, size); -} - -static int s2n_hmac_p_hash_reset(struct s2n_prf_working_space *ws) -{ - /* If we actually initialized s2n_hmac, wipe it. - * A valid, initialized s2n_hmac_state will have a valid block size. - */ - if (ws->p_hash.s2n_hmac.hash_block_size != 0) { - return s2n_hmac_reset(&ws->p_hash.s2n_hmac); - } - return S2N_SUCCESS; -} - -static int s2n_hmac_p_hash_cleanup(struct s2n_prf_working_space *ws) -{ - return s2n_hmac_p_hash_reset(ws); -} - -static int s2n_hmac_p_hash_free(struct s2n_prf_working_space *ws) -{ - return s2n_hmac_free(&ws->p_hash.s2n_hmac); -} - -static const struct s2n_p_hash_hmac s2n_internal_p_hash_hmac = { - .alloc = &s2n_hmac_p_hash_new, - .init = &s2n_hmac_p_hash_init, - .update = &s2n_hmac_p_hash_update, - .final = &s2n_hmac_p_hash_digest, - .reset = &s2n_hmac_p_hash_reset, - .cleanup = &s2n_hmac_p_hash_cleanup, - .free = &s2n_hmac_p_hash_free, -}; - -/* - * For now, use the internal s2n-tls hmac abstraction. - * However, that is a custom implementation of hmac built on hashes. - * Ideally we should stop using our custom implementation here and switch - * to using a libcrypto implementation. Unfortunately, what each libcrypto - * can support varies a lot for HMACs. - * - * For historical reference, there used to be two other hmac implementations: - * https://github.com/aws/s2n-tls/blob/711ee0df658cd7c44088cf7a1b20a9f3cf5296d6/tls/s2n_prf.c#L174-L337 - * Both implementations have compatibility issues with one or more libcryptos. - */ -const struct s2n_p_hash_hmac *s2n_get_hmac_implementation() -{ - return &s2n_internal_p_hash_hmac; -} - -static int s2n_p_hash(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret, struct s2n_blob *label, - struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - uint8_t digest_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(alg, &digest_size)); - - const struct s2n_p_hash_hmac *hmac = s2n_get_hmac_implementation(); - POSIX_ENSURE_REF(hmac); - - /* First compute hmac(secret + A(0)) */ - POSIX_GUARD(hmac->init(ws, alg, secret)); - POSIX_GUARD(hmac->update(ws, label->data, label->size)); - POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); - - if (seed_b) { - POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); - if (seed_c) { - POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); - } - } - POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); - - uint32_t outputlen = out->size; - uint8_t *output = out->data; - - while (outputlen) { - /* Now compute hmac(secret + A(N - 1) + seed) */ - POSIX_GUARD(hmac->reset(ws)); - POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); - - /* Add the label + seed and compute this round's A */ - POSIX_GUARD(hmac->update(ws, label->data, label->size)); - POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); - if (seed_b) { - POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); - if (seed_c) { - POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); - } - } - - POSIX_GUARD(hmac->final(ws, ws->digest1, digest_size)); - - uint32_t bytes_to_xor = S2N_MIN(outputlen, digest_size); - - for (size_t i = 0; i < bytes_to_xor; i++) { - *output ^= ws->digest1[i]; - output++; - outputlen--; - } - - /* Stash a digest of A(N), in A(N), for the next round */ - POSIX_GUARD(hmac->reset(ws)); - POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); - POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); - } - - POSIX_GUARD(hmac->cleanup(ws)); - - return 0; -} - -S2N_RESULT s2n_prf_new(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_EQ(conn->prf_space, NULL); - - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_realloc(&mem, sizeof(struct s2n_prf_working_space))); - RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); - conn->prf_space = (struct s2n_prf_working_space *) (void *) mem.data; - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - /* Allocate the hmac state */ - const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); - RESULT_ENSURE_REF(hmac_impl); - RESULT_GUARD_POSIX(hmac_impl->alloc(conn->prf_space)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_wipe(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->prf_space); - - const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); - RESULT_ENSURE_REF(hmac_impl); - RESULT_GUARD_POSIX(hmac_impl->reset(conn->prf_space)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_free(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (conn->prf_space == NULL) { - return S2N_RESULT_OK; - } - - const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); - RESULT_ENSURE_REF(hmac_impl); - RESULT_GUARD_POSIX(hmac_impl->free(conn->prf_space)); - - RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &conn->prf_space, sizeof(struct s2n_prf_working_space))); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_custom(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, - struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - /* We zero the out blob because p_hash works by XOR'ing with the existing - * buffer. This is a little convoluted but means we can avoid dynamic memory - * allocation. When we call p_hash once (in the TLS1.2 case) it will produce - * the right values. When we call it twice in the regular case, the two - * outputs will be XORd just ass the TLS 1.0 and 1.1 RFCs require. - */ - RESULT_GUARD_POSIX(s2n_blob_zero(out)); - - if (conn->actual_protocol_version == S2N_TLS12) { - RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, conn->secure->cipher_suite->prf_alg, secret, label, seed_a, - seed_b, seed_c, out)); - return S2N_RESULT_OK; - } - - struct s2n_blob half_secret = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&half_secret, secret->data, (secret->size + 1) / 2)); - - RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_MD5, &half_secret, label, seed_a, seed_b, seed_c, out)); - half_secret.data += secret->size - half_secret.size; - RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_SHA1, &half_secret, label, seed_a, seed_b, seed_c, out)); - - return S2N_RESULT_OK; -} - -int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, struct s2n_blob *seed_a, - struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_REF(conn->prf_space); - POSIX_ENSURE_REF(secret); - POSIX_ENSURE_REF(label); - POSIX_ENSURE_REF(out); - - /* seed_a is always required, seed_b is optional, if seed_c is provided seed_b must also be provided */ - POSIX_ENSURE(seed_a != NULL, S2N_ERR_PRF_INVALID_SEED); - POSIX_ENSURE(S2N_IMPLIES(seed_c != NULL, seed_b != NULL), S2N_ERR_PRF_INVALID_SEED); - - if (conn->actual_protocol_version == S2N_SSLv3) { - POSIX_GUARD(s2n_prf_sslv3(conn, secret, seed_a, seed_b, seed_c, out)); - return S2N_SUCCESS; - } - - /* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the - * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. - */ - if (s2n_is_in_fips_mode()) { - POSIX_GUARD_RESULT(s2n_prf_libcrypto(conn, secret, label, seed_a, seed_b, seed_c, out)); - return S2N_SUCCESS; - } - - POSIX_GUARD_RESULT(s2n_prf_custom(conn, secret, label, seed_a, seed_b, seed_c, out)); - - return S2N_SUCCESS; -} - -int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) -{ - POSIX_ENSURE_REF(conn); - - struct s2n_blob client_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); - struct s2n_blob server_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); - struct s2n_blob master_secret = { 0 }; - POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - uint8_t master_secret_label[] = "master secret"; - struct s2n_blob label = { 0 }; - POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); - - return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, NULL, &master_secret); -} - -int s2n_prf_hybrid_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) -{ - POSIX_ENSURE_REF(conn); - - struct s2n_blob client_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); - struct s2n_blob server_random = { 0 }; - POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); - struct s2n_blob master_secret = { 0 }; - POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - uint8_t master_secret_label[] = "hybrid master secret"; - struct s2n_blob label = { 0 }; - POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); - - return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, &conn->kex_params.client_key_exchange_message, &master_secret); -} - -int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_KEY); - - if (!conn->ems_negotiated) { - POSIX_GUARD(s2n_prf_tls_master_secret(conn, premaster_secret)); - return S2N_SUCCESS; - } - - /* Only the client writes the Client Key Exchange message */ - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); - } - struct s2n_stuffer client_key_message = conn->handshake.io; - POSIX_GUARD(s2n_stuffer_reread(&client_key_message)); - uint32_t client_key_message_size = s2n_stuffer_data_available(&client_key_message); - struct s2n_blob client_key_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&client_key_blob, client_key_message.blob.data, client_key_message_size)); - - uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob digest = { 0 }; - POSIX_GUARD(s2n_blob_init(&digest, data, sizeof(data))); - if (conn->actual_protocol_version < S2N_TLS12) { - uint8_t sha1_data[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob sha1_digest = { 0 }; - POSIX_GUARD(s2n_blob_init(&sha1_digest, sha1_data, sizeof(sha1_data))); - POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_MD5, &digest)); - POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_SHA1, &sha1_digest)); - POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, &sha1_digest)); - } else { - s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; - s2n_hash_algorithm hash_alg = 0; - POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); - POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, hash_alg, &digest)); - POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, NULL)); - } - return S2N_SUCCESS; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc7627#section-4 - *# When the extended master secret extension is negotiated in a full - *# handshake, the "master_secret" is computed as - *# - *# master_secret = PRF(pre_master_secret, "extended master secret", - *# session_hash) - *# [0..47]; - */ -S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_blob extended_master_secret = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&extended_master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - uint8_t extended_master_secret_label[] = "extended master secret"; - /* Subtract one from the label size to remove the "\0" */ - struct s2n_blob label = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&label, extended_master_secret_label, sizeof(extended_master_secret_label) - 1)); - - RESULT_GUARD_POSIX(s2n_prf(conn, premaster_secret, &label, session_hash, sha1_hash, NULL, &extended_master_secret)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->handshake.hashes); - RESULT_ENSURE_REF(message); - RESULT_ENSURE_REF(output); - - struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; - RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); - RESULT_GUARD_POSIX(s2n_hash_update(hash_state, message->data, message->size)); - - uint8_t digest_size = 0; - RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_alg, &digest_size)); - RESULT_ENSURE_GTE(output->size, digest_size); - RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, output->data, digest_size)); - output->size = digest_size; - - return S2N_RESULT_OK; -} - -static int s2n_prf_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], struct s2n_hash_state *hash_workspace, uint8_t *out) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - uint8_t xorpad1[48] = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; - uint8_t xorpad2[48] = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c }; - uint8_t *md5_digest = out; - uint8_t *sha_digest = out + MD5_DIGEST_LENGTH; - - POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH)); - - struct s2n_hash_state *md5 = hash_workspace; - POSIX_GUARD(s2n_hash_copy(md5, &conn->handshake.hashes->md5)); - POSIX_GUARD(s2n_hash_update(md5, prefix, 4)); - POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(md5, xorpad1, 48)); - POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(md5)); - POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(md5, xorpad2, 48)); - POSIX_GUARD(s2n_hash_update(md5, md5_digest, MD5_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(md5)); - - struct s2n_hash_state *sha1 = hash_workspace; - POSIX_GUARD(s2n_hash_copy(sha1, &conn->handshake.hashes->sha1)); - POSIX_GUARD(s2n_hash_update(sha1, prefix, 4)); - POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(sha1, xorpad1, 40)); - POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(sha1)); - POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - POSIX_GUARD(s2n_hash_update(sha1, xorpad2, 40)); - POSIX_GUARD(s2n_hash_update(sha1, sha_digest, SHA_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); - POSIX_GUARD(s2n_hash_reset(sha1)); - - return 0; -} - -static int s2n_prf_sslv3_client_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - uint8_t prefix[4] = { 0x43, 0x4c, 0x4e, 0x54 }; - - return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.client_finished); -} - -static int s2n_prf_sslv3_server_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->handshake.hashes); - - uint8_t prefix[4] = { 0x53, 0x52, 0x56, 0x52 }; - - return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.server_finished); -} - -int s2n_prf_client_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->handshake.hashes); - - struct s2n_blob master_secret, md5, sha; - uint8_t md5_digest[MD5_DIGEST_LENGTH]; - uint8_t sha_digest[SHA384_DIGEST_LENGTH]; - uint8_t client_finished_label[] = "client finished"; - struct s2n_blob client_finished = { 0 }; - struct s2n_blob label = { 0 }; - - if (conn->actual_protocol_version == S2N_SSLv3) { - return s2n_prf_sslv3_client_finished(conn); - } - - client_finished.data = conn->handshake.client_finished; - client_finished.size = S2N_TLS_FINISHED_LEN; - POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, client_finished.size)); - label.data = client_finished_label; - label.size = sizeof(client_finished_label) - 1; - - master_secret.data = conn->secrets.version.tls12.master_secret; - master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); - if (conn->actual_protocol_version == S2N_TLS12) { - switch (conn->secure->cipher_suite->prf_alg) { - case S2N_HMAC_SHA256: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); - sha.size = SHA256_DIGEST_LENGTH; - break; - case S2N_HMAC_SHA384: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); - sha.size = SHA384_DIGEST_LENGTH; - break; - default: - POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); - } - - sha.data = sha_digest; - return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &client_finished); - } - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); - md5.data = md5_digest; - md5.size = MD5_DIGEST_LENGTH; - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); - sha.data = sha_digest; - sha.size = SHA_DIGEST_LENGTH; - - return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &client_finished); -} - -int s2n_prf_server_finished(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->handshake.hashes); - - struct s2n_blob master_secret, md5, sha; - uint8_t md5_digest[MD5_DIGEST_LENGTH]; - uint8_t sha_digest[SHA384_DIGEST_LENGTH]; - uint8_t server_finished_label[] = "server finished"; - struct s2n_blob server_finished = { 0 }; - struct s2n_blob label = { 0 }; - - if (conn->actual_protocol_version == S2N_SSLv3) { - return s2n_prf_sslv3_server_finished(conn); - } - - server_finished.data = conn->handshake.server_finished; - server_finished.size = S2N_TLS_FINISHED_LEN; - POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, server_finished.size)); - label.data = server_finished_label; - label.size = sizeof(server_finished_label) - 1; - - master_secret.data = conn->secrets.version.tls12.master_secret; - master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); - if (conn->actual_protocol_version == S2N_TLS12) { - switch (conn->secure->cipher_suite->prf_alg) { - case S2N_HMAC_SHA256: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); - sha.size = SHA256_DIGEST_LENGTH; - break; - case S2N_HMAC_SHA384: - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); - sha.size = SHA384_DIGEST_LENGTH; - break; - default: - POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); - } - - sha.data = sha_digest; - return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &server_finished); - } - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); - md5.data = md5_digest; - md5.size = MD5_DIGEST_LENGTH; - - POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); - POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); - sha.data = sha_digest; - sha.size = SHA_DIGEST_LENGTH; - - return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &server_finished); -} - -static int s2n_prf_make_client_key(struct s2n_connection *conn, struct s2n_key_material *key_material) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - POSIX_ENSURE_REF(cipher); - POSIX_ENSURE_REF(cipher->set_encryption_key); - POSIX_ENSURE_REF(cipher->set_decryption_key); - - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->client_key, &key_material->client_key)); - } else { - POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->client_key, &key_material->client_key)); - } - - return 0; -} - -static int s2n_prf_make_server_key(struct s2n_connection *conn, struct s2n_key_material *key_material) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); - const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; - POSIX_ENSURE_REF(cipher); - POSIX_ENSURE_REF(cipher->set_encryption_key); - POSIX_ENSURE_REF(cipher->set_decryption_key); - - if (conn->mode == S2N_SERVER) { - POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->server_key, &key_material->server_key)); - } else { - POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->server_key, &key_material->server_key)); - } - - return 0; -} - -S2N_RESULT s2n_prf_generate_key_material(struct s2n_connection *conn, struct s2n_key_material *key_material) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(key_material); - - struct s2n_blob client_random = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); - struct s2n_blob server_random = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); - struct s2n_blob master_secret = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); - - struct s2n_blob label = { 0 }; - uint8_t key_expansion_label[] = "key expansion"; - RESULT_GUARD_POSIX(s2n_blob_init(&label, key_expansion_label, sizeof(key_expansion_label) - 1)); - - RESULT_GUARD(s2n_key_material_init(key_material, conn)); - struct s2n_blob prf_out = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&prf_out, key_material->key_block, sizeof(key_material->key_block))); - RESULT_GUARD_POSIX(s2n_prf(conn, &master_secret, &label, &server_random, &client_random, NULL, &prf_out)); - - return S2N_RESULT_OK; -} - -int s2n_prf_key_expansion(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite; - POSIX_ENSURE_REF(cipher_suite); - POSIX_ENSURE_REF(cipher_suite->record_alg); - const struct s2n_cipher *cipher = cipher_suite->record_alg->cipher; - POSIX_ENSURE_REF(cipher); - - struct s2n_key_material key_material = { 0 }; - POSIX_GUARD_RESULT(s2n_prf_generate_key_material(conn, &key_material)); - - POSIX_ENSURE(cipher_suite->available, S2N_ERR_PRF_INVALID_ALGORITHM); - POSIX_GUARD_RESULT(cipher->init(&conn->secure->client_key)); - POSIX_GUARD_RESULT(cipher->init(&conn->secure->server_key)); - - /* Seed the client MAC */ - POSIX_GUARD(s2n_hmac_reset(&conn->secure->client_record_mac)); - POSIX_GUARD(s2n_hmac_init( - &conn->secure->client_record_mac, - cipher_suite->record_alg->hmac_alg, - key_material.client_mac.data, - key_material.client_mac.size)); - - /* Seed the server MAC */ - POSIX_GUARD(s2n_hmac_reset(&conn->secure->server_record_mac)); - POSIX_GUARD(s2n_hmac_init( - &conn->secure->server_record_mac, - conn->secure->cipher_suite->record_alg->hmac_alg, - key_material.server_mac.data, - key_material.server_mac.size)); - - /* Make the client key */ - POSIX_GUARD(s2n_prf_make_client_key(conn, &key_material)); - - /* Make the server key */ - POSIX_GUARD(s2n_prf_make_server_key(conn, &key_material)); - - /* Composite CBC does MAC inside the cipher, pass it the MAC key. - * Must happen after setting encryption/decryption keys. - */ - if (cipher->type == S2N_COMPOSITE) { - POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->client_key, key_material.client_mac.data, key_material.client_mac.size)); - POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->server_key, key_material.server_mac.data, key_material.server_mac.size)); - } - - /* set IV */ - POSIX_ENSURE_EQ(key_material.client_iv.size, key_material.server_iv.size); - POSIX_ENSURE_LTE(key_material.client_iv.size, S2N_TLS_MAX_IV_LEN); - POSIX_CHECKED_MEMCPY(conn->secure->client_implicit_iv, key_material.client_iv.data, key_material.client_iv.size); - POSIX_CHECKED_MEMCPY(conn->secure->server_implicit_iv, key_material.server_iv.data, key_material.server_iv.size); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_prf.h" + +#include +#include +#include +#include + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_prf_libcrypto.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto_constants.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* The s2n p_hash implementation is abstracted to allow for separate implementations. + * Currently the only implementation uses s2n-tls's custom HMAC implementation. + */ +struct s2n_p_hash_hmac { + int (*alloc)(struct s2n_prf_working_space *ws); + int (*init)(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret); + int (*update)(struct s2n_prf_working_space *ws, const void *data, uint32_t size); + int (*final)(struct s2n_prf_working_space *ws, void *digest, uint32_t size); + int (*reset)(struct s2n_prf_working_space *ws); + int (*cleanup)(struct s2n_prf_working_space *ws); + int (*free)(struct s2n_prf_working_space *ws); +}; + +S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, + s2n_hash_algorithm hash_alg, struct s2n_blob *output); +S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, + struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash); + +S2N_RESULT s2n_key_material_init(struct s2n_key_material *key_material, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(key_material); + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + RESULT_ENSURE_REF(cipher); + + uint8_t mac_size = 0; + uint32_t key_size = 0; + uint32_t iv_size = 0; + + /* MAC size */ + if (cipher->type == S2N_COMPOSITE) { + mac_size = cipher->io.comp.mac_key_size; + } else { + RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->record_alg->hmac_alg, &mac_size)); + } + + /* KEY size */ + key_size = cipher->key_material_size; + + /* Only AEAD ciphers have implicit IVs for TLS >= 1.1 */ + if (conn->actual_protocol_version <= S2N_TLS10 || cipher->type == S2N_AEAD) { + /* IV size */ + switch (cipher->type) { + case S2N_AEAD: + iv_size = cipher->io.aead.fixed_iv_size; + break; + case S2N_CBC: + iv_size = cipher->io.cbc.block_size; + break; + case S2N_COMPOSITE: + iv_size = cipher->io.comp.block_size; + break; + /* No-op for stream ciphers */ + default: + break; + } + } + + struct s2n_stuffer key_material_stuffer = { 0 }; + struct s2n_blob key_material_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&key_material_blob, key_material->key_block, sizeof(key_material->key_block))); + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&key_material_stuffer, &key_material_blob)); + + /* initialize key_material blobs; incrementing ptr to point to the next slice of memory */ + uint8_t *ptr = NULL; + /* MAC */ + ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_mac, ptr, mac_size)); + + ptr = s2n_stuffer_raw_read(&key_material_stuffer, mac_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_mac, ptr, mac_size)); + + /* KEY */ + ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_key, ptr, key_size)); + + ptr = s2n_stuffer_raw_read(&key_material_stuffer, key_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_key, ptr, key_size)); + + /* IV */ + ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->client_iv, ptr, iv_size)); + + ptr = s2n_stuffer_raw_read(&key_material_stuffer, iv_size); + RESULT_ENSURE_REF(ptr); + RESULT_GUARD_POSIX(s2n_blob_init(&key_material->server_iv, ptr, iv_size)); + + return S2N_RESULT_OK; +} + +/* SSLv3 PRF uses MD5 and SHA-1 in a custom hash-based construction (not + * HMAC). The use of weak hash algorithms is inherent to the SSLv3 protocol + * specification. SSLv3 is disabled by default and not recommended. + */ +static int s2n_prf_sslv3(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *seed_a, + struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + struct s2n_hash_state *workspace = &conn->handshake.hashes->hash_workspace; + + uint32_t outputlen = out->size; + uint8_t *output = out->data; + uint8_t iteration = 1; + + uint8_t md5_digest[MD5_DIGEST_LENGTH] = { 0 }, sha_digest[SHA_DIGEST_LENGTH] = { 0 }; + + uint8_t A = 'A'; + while (outputlen) { + struct s2n_hash_state *sha1 = workspace; + POSIX_GUARD(s2n_hash_reset(sha1)); + POSIX_GUARD(s2n_hash_init(sha1, S2N_HASH_SHA1)); + + for (int i = 0; i < iteration; i++) { + POSIX_GUARD(s2n_hash_update(sha1, &A, 1)); + } + + POSIX_GUARD(s2n_hash_update(sha1, secret->data, secret->size)); + POSIX_GUARD(s2n_hash_update(sha1, seed_a->data, seed_a->size)); + + if (seed_b) { + POSIX_GUARD(s2n_hash_update(sha1, seed_b->data, seed_b->size)); + if (seed_c) { + POSIX_GUARD(s2n_hash_update(sha1, seed_c->data, seed_c->size)); + } + } + + POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, sizeof(sha_digest))); + + struct s2n_hash_state *md5 = workspace; + POSIX_GUARD(s2n_hash_reset(md5)); + POSIX_GUARD(s2n_hash_init(md5, S2N_HASH_MD5)); + POSIX_GUARD(s2n_hash_update(md5, secret->data, secret->size)); + POSIX_GUARD(s2n_hash_update(md5, sha_digest, sizeof(sha_digest))); + POSIX_GUARD(s2n_hash_digest(md5, md5_digest, sizeof(md5_digest))); + + uint32_t bytes_to_copy = S2N_MIN(outputlen, sizeof(md5_digest)); + + POSIX_CHECKED_MEMCPY(output, md5_digest, bytes_to_copy); + + outputlen -= bytes_to_copy; + output += bytes_to_copy; + + /* Increment the letter */ + A++; + iteration++; + } + + return 0; +} + +static int s2n_hmac_p_hash_new(struct s2n_prf_working_space *ws) +{ + POSIX_GUARD(s2n_hmac_new(&ws->p_hash.s2n_hmac)); + return s2n_hmac_init(&ws->p_hash.s2n_hmac, S2N_HMAC_NONE, NULL, 0); +} + +static int s2n_hmac_p_hash_init(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret) +{ + return s2n_hmac_init(&ws->p_hash.s2n_hmac, alg, secret->data, secret->size); +} + +static int s2n_hmac_p_hash_update(struct s2n_prf_working_space *ws, const void *data, uint32_t size) +{ + return s2n_hmac_update(&ws->p_hash.s2n_hmac, data, size); +} + +static int s2n_hmac_p_hash_digest(struct s2n_prf_working_space *ws, void *digest, uint32_t size) +{ + return s2n_hmac_digest(&ws->p_hash.s2n_hmac, digest, size); +} + +static int s2n_hmac_p_hash_reset(struct s2n_prf_working_space *ws) +{ + /* If we actually initialized s2n_hmac, wipe it. + * A valid, initialized s2n_hmac_state will have a valid block size. + */ + if (ws->p_hash.s2n_hmac.hash_block_size != 0) { + return s2n_hmac_reset(&ws->p_hash.s2n_hmac); + } + return S2N_SUCCESS; +} + +static int s2n_hmac_p_hash_cleanup(struct s2n_prf_working_space *ws) +{ + return s2n_hmac_p_hash_reset(ws); +} + +static int s2n_hmac_p_hash_free(struct s2n_prf_working_space *ws) +{ + return s2n_hmac_free(&ws->p_hash.s2n_hmac); +} + +static const struct s2n_p_hash_hmac s2n_internal_p_hash_hmac = { + .alloc = &s2n_hmac_p_hash_new, + .init = &s2n_hmac_p_hash_init, + .update = &s2n_hmac_p_hash_update, + .final = &s2n_hmac_p_hash_digest, + .reset = &s2n_hmac_p_hash_reset, + .cleanup = &s2n_hmac_p_hash_cleanup, + .free = &s2n_hmac_p_hash_free, +}; + +/* + * For now, use the internal s2n-tls hmac abstraction. + * However, that is a custom implementation of hmac built on hashes. + * Ideally we should stop using our custom implementation here and switch + * to using a libcrypto implementation. Unfortunately, what each libcrypto + * can support varies a lot for HMACs. + * + * For historical reference, there used to be two other hmac implementations: + * https://github.com/aws/s2n-tls/blob/711ee0df658cd7c44088cf7a1b20a9f3cf5296d6/tls/s2n_prf.c#L174-L337 + * Both implementations have compatibility issues with one or more libcryptos. + */ +const struct s2n_p_hash_hmac *s2n_get_hmac_implementation() +{ + return &s2n_internal_p_hash_hmac; +} + +static int s2n_p_hash(struct s2n_prf_working_space *ws, s2n_hmac_algorithm alg, struct s2n_blob *secret, struct s2n_blob *label, + struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + uint8_t digest_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(alg, &digest_size)); + + const struct s2n_p_hash_hmac *hmac = s2n_get_hmac_implementation(); + POSIX_ENSURE_REF(hmac); + + /* First compute hmac(secret + A(0)) */ + POSIX_GUARD(hmac->init(ws, alg, secret)); + POSIX_GUARD(hmac->update(ws, label->data, label->size)); + POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); + + if (seed_b) { + POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); + if (seed_c) { + POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); + } + } + POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); + + uint32_t outputlen = out->size; + uint8_t *output = out->data; + + while (outputlen) { + /* Now compute hmac(secret + A(N - 1) + seed) */ + POSIX_GUARD(hmac->reset(ws)); + POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); + + /* Add the label + seed and compute this round's A */ + POSIX_GUARD(hmac->update(ws, label->data, label->size)); + POSIX_GUARD(hmac->update(ws, seed_a->data, seed_a->size)); + if (seed_b) { + POSIX_GUARD(hmac->update(ws, seed_b->data, seed_b->size)); + if (seed_c) { + POSIX_GUARD(hmac->update(ws, seed_c->data, seed_c->size)); + } + } + + POSIX_GUARD(hmac->final(ws, ws->digest1, digest_size)); + + uint32_t bytes_to_xor = S2N_MIN(outputlen, digest_size); + + for (size_t i = 0; i < bytes_to_xor; i++) { + *output ^= ws->digest1[i]; + output++; + outputlen--; + } + + /* Stash a digest of A(N), in A(N), for the next round */ + POSIX_GUARD(hmac->reset(ws)); + POSIX_GUARD(hmac->update(ws, ws->digest0, digest_size)); + POSIX_GUARD(hmac->final(ws, ws->digest0, digest_size)); + } + + POSIX_GUARD(hmac->cleanup(ws)); + + return 0; +} + +S2N_RESULT s2n_prf_new(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_EQ(conn->prf_space, NULL); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_realloc(&mem, sizeof(struct s2n_prf_working_space))); + RESULT_GUARD_POSIX(s2n_blob_zero(&mem)); + conn->prf_space = (struct s2n_prf_working_space *) (void *) mem.data; + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + /* Allocate the hmac state */ + const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); + RESULT_ENSURE_REF(hmac_impl); + RESULT_GUARD_POSIX(hmac_impl->alloc(conn->prf_space)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_wipe(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->prf_space); + + const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); + RESULT_ENSURE_REF(hmac_impl); + RESULT_GUARD_POSIX(hmac_impl->reset(conn->prf_space)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_free(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (conn->prf_space == NULL) { + return S2N_RESULT_OK; + } + + const struct s2n_p_hash_hmac *hmac_impl = s2n_get_hmac_implementation(); + RESULT_ENSURE_REF(hmac_impl); + RESULT_GUARD_POSIX(hmac_impl->free(conn->prf_space)); + + RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &conn->prf_space, sizeof(struct s2n_prf_working_space))); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_custom(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, + struct s2n_blob *seed_a, struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + /* We zero the out blob because p_hash works by XOR'ing with the existing + * buffer. This is a little convoluted but means we can avoid dynamic memory + * allocation. When we call p_hash once (in the TLS1.2 case) it will produce + * the right values. When we call it twice in the regular case, the two + * outputs will be XORd just ass the TLS 1.0 and 1.1 RFCs require. + */ + RESULT_GUARD_POSIX(s2n_blob_zero(out)); + + if (conn->actual_protocol_version == S2N_TLS12) { + RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, conn->secure->cipher_suite->prf_alg, secret, label, seed_a, + seed_b, seed_c, out)); + return S2N_RESULT_OK; + } + + struct s2n_blob half_secret = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&half_secret, secret->data, (secret->size + 1) / 2)); + + RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_MD5, &half_secret, label, seed_a, seed_b, seed_c, out)); + half_secret.data += secret->size - half_secret.size; + RESULT_GUARD_POSIX(s2n_p_hash(conn->prf_space, S2N_HMAC_SHA1, &half_secret, label, seed_a, seed_b, seed_c, out)); + + return S2N_RESULT_OK; +} + +int s2n_prf(struct s2n_connection *conn, struct s2n_blob *secret, struct s2n_blob *label, struct s2n_blob *seed_a, + struct s2n_blob *seed_b, struct s2n_blob *seed_c, struct s2n_blob *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->prf_space); + POSIX_ENSURE_REF(secret); + POSIX_ENSURE_REF(label); + POSIX_ENSURE_REF(out); + + /* seed_a is always required, seed_b is optional, if seed_c is provided seed_b must also be provided */ + POSIX_ENSURE(seed_a != NULL, S2N_ERR_PRF_INVALID_SEED); + POSIX_ENSURE(S2N_IMPLIES(seed_c != NULL, seed_b != NULL), S2N_ERR_PRF_INVALID_SEED); + + if (conn->actual_protocol_version == S2N_SSLv3) { + POSIX_GUARD(s2n_prf_sslv3(conn, secret, seed_a, seed_b, seed_c, out)); + return S2N_SUCCESS; + } + + /* By default, s2n-tls uses a custom PRF implementation. When operating in FIPS mode, the + * FIPS-validated libcrypto implementation is used instead, if an implementation is provided. + */ + if (s2n_is_in_fips_mode()) { + POSIX_GUARD_RESULT(s2n_prf_libcrypto(conn, secret, label, seed_a, seed_b, seed_c, out)); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_prf_custom(conn, secret, label, seed_a, seed_b, seed_c, out)); + + return S2N_SUCCESS; +} + +int s2n_prf_tls_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) +{ + POSIX_ENSURE_REF(conn); + + struct s2n_blob client_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); + struct s2n_blob server_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); + struct s2n_blob master_secret = { 0 }; + POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + uint8_t master_secret_label[] = "master secret"; + struct s2n_blob label = { 0 }; + POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); + + return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, NULL, &master_secret); +} + +int s2n_prf_hybrid_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) +{ + POSIX_ENSURE_REF(conn); + + struct s2n_blob client_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); + struct s2n_blob server_random = { 0 }; + POSIX_GUARD(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); + struct s2n_blob master_secret = { 0 }; + POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + uint8_t master_secret_label[] = "hybrid master secret"; + struct s2n_blob label = { 0 }; + POSIX_GUARD(s2n_blob_init(&label, master_secret_label, sizeof(master_secret_label) - 1)); + + return s2n_prf(conn, premaster_secret, &label, &client_random, &server_random, &conn->kex_params.client_key_exchange_message, &master_secret); +} + +int s2n_prf_calculate_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_KEY); + + if (!conn->ems_negotiated) { + POSIX_GUARD(s2n_prf_tls_master_secret(conn, premaster_secret)); + return S2N_SUCCESS; + } + + /* Only the client writes the Client Key Exchange message */ + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); + } + struct s2n_stuffer client_key_message = conn->handshake.io; + POSIX_GUARD(s2n_stuffer_reread(&client_key_message)); + uint32_t client_key_message_size = s2n_stuffer_data_available(&client_key_message); + struct s2n_blob client_key_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&client_key_blob, client_key_message.blob.data, client_key_message_size)); + + uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest = { 0 }; + POSIX_GUARD(s2n_blob_init(&digest, data, sizeof(data))); + if (conn->actual_protocol_version < S2N_TLS12) { + uint8_t sha1_data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob sha1_digest = { 0 }; + POSIX_GUARD(s2n_blob_init(&sha1_digest, sha1_data, sizeof(sha1_data))); + POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_MD5, &digest)); + POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, S2N_HASH_SHA1, &sha1_digest)); + POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, &sha1_digest)); + } else { + s2n_hmac_algorithm prf_alg = conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg = 0; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + POSIX_GUARD_RESULT(s2n_prf_get_digest_for_ems(conn, &client_key_blob, hash_alg, &digest)); + POSIX_GUARD_RESULT(s2n_prf_tls_extended_master_secret(conn, premaster_secret, &digest, NULL)); + } + return S2N_SUCCESS; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc7627#section-4 + *# When the extended master secret extension is negotiated in a full + *# handshake, the "master_secret" is computed as + *# + *# master_secret = PRF(pre_master_secret, "extended master secret", + *# session_hash) + *# [0..47]; + */ +S2N_RESULT s2n_prf_tls_extended_master_secret(struct s2n_connection *conn, struct s2n_blob *premaster_secret, struct s2n_blob *session_hash, struct s2n_blob *sha1_hash) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_blob extended_master_secret = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&extended_master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + uint8_t extended_master_secret_label[] = "extended master secret"; + /* Subtract one from the label size to remove the "\0" */ + struct s2n_blob label = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&label, extended_master_secret_label, sizeof(extended_master_secret_label) - 1)); + + RESULT_GUARD_POSIX(s2n_prf(conn, premaster_secret, &label, session_hash, sha1_hash, NULL, &extended_master_secret)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_prf_get_digest_for_ems(struct s2n_connection *conn, struct s2n_blob *message, s2n_hash_algorithm hash_alg, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->handshake.hashes); + RESULT_ENSURE_REF(message); + RESULT_ENSURE_REF(output); + + struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; + RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); + RESULT_GUARD_POSIX(s2n_hash_update(hash_state, message->data, message->size)); + + uint8_t digest_size = 0; + RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_alg, &digest_size)); + RESULT_ENSURE_GTE(output->size, digest_size); + RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, output->data, digest_size)); + output->size = digest_size; + + return S2N_RESULT_OK; +} + +static int s2n_prf_sslv3_finished(struct s2n_connection *conn, uint8_t prefix[4], struct s2n_hash_state *hash_workspace, uint8_t *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + uint8_t xorpad1[48] = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 }; + uint8_t xorpad2[48] = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c }; + uint8_t *md5_digest = out; + uint8_t *sha_digest = out + MD5_DIGEST_LENGTH; + + POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH)); + + struct s2n_hash_state *md5 = hash_workspace; + POSIX_GUARD(s2n_hash_copy(md5, &conn->handshake.hashes->md5)); + POSIX_GUARD(s2n_hash_update(md5, prefix, 4)); + POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(md5, xorpad1, 48)); + POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(md5)); + POSIX_GUARD(s2n_hash_update(md5, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(md5, xorpad2, 48)); + POSIX_GUARD(s2n_hash_update(md5, md5_digest, MD5_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_digest(md5, md5_digest, MD5_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(md5)); + + struct s2n_hash_state *sha1 = hash_workspace; + POSIX_GUARD(s2n_hash_copy(sha1, &conn->handshake.hashes->sha1)); + POSIX_GUARD(s2n_hash_update(sha1, prefix, 4)); + POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(sha1, xorpad1, 40)); + POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(sha1)); + POSIX_GUARD(s2n_hash_update(sha1, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + POSIX_GUARD(s2n_hash_update(sha1, xorpad2, 40)); + POSIX_GUARD(s2n_hash_update(sha1, sha_digest, SHA_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_digest(sha1, sha_digest, SHA_DIGEST_LENGTH)); + POSIX_GUARD(s2n_hash_reset(sha1)); + + return 0; +} + +static int s2n_prf_sslv3_client_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + uint8_t prefix[4] = { 0x43, 0x4c, 0x4e, 0x54 }; + + return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.client_finished); +} + +static int s2n_prf_sslv3_server_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->handshake.hashes); + + uint8_t prefix[4] = { 0x53, 0x52, 0x56, 0x52 }; + + return s2n_prf_sslv3_finished(conn, prefix, &conn->handshake.hashes->hash_workspace, conn->handshake.server_finished); +} + +int s2n_prf_client_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->handshake.hashes); + + struct s2n_blob master_secret, md5, sha; + uint8_t md5_digest[MD5_DIGEST_LENGTH]; + uint8_t sha_digest[SHA384_DIGEST_LENGTH]; + uint8_t client_finished_label[] = "client finished"; + struct s2n_blob client_finished = { 0 }; + struct s2n_blob label = { 0 }; + + if (conn->actual_protocol_version == S2N_SSLv3) { + return s2n_prf_sslv3_client_finished(conn); + } + + client_finished.data = conn->handshake.client_finished; + client_finished.size = S2N_TLS_FINISHED_LEN; + POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, client_finished.size)); + label.data = client_finished_label; + label.size = sizeof(client_finished_label) - 1; + + master_secret.data = conn->secrets.version.tls12.master_secret; + master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); + if (conn->actual_protocol_version == S2N_TLS12) { + switch (conn->secure->cipher_suite->prf_alg) { + case S2N_HMAC_SHA256: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); + sha.size = SHA256_DIGEST_LENGTH; + break; + case S2N_HMAC_SHA384: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); + sha.size = SHA384_DIGEST_LENGTH; + break; + default: + POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); + } + + sha.data = sha_digest; + return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &client_finished); + } + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); + md5.data = md5_digest; + md5.size = MD5_DIGEST_LENGTH; + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); + sha.data = sha_digest; + sha.size = SHA_DIGEST_LENGTH; + + return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &client_finished); +} + +int s2n_prf_server_finished(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->handshake.hashes); + + struct s2n_blob master_secret, md5, sha; + uint8_t md5_digest[MD5_DIGEST_LENGTH]; + uint8_t sha_digest[SHA384_DIGEST_LENGTH]; + uint8_t server_finished_label[] = "server finished"; + struct s2n_blob server_finished = { 0 }; + struct s2n_blob label = { 0 }; + + if (conn->actual_protocol_version == S2N_SSLv3) { + return s2n_prf_sslv3_server_finished(conn); + } + + server_finished.data = conn->handshake.server_finished; + server_finished.size = S2N_TLS_FINISHED_LEN; + POSIX_GUARD_RESULT(s2n_handshake_set_finished_len(conn, server_finished.size)); + label.data = server_finished_label; + label.size = sizeof(server_finished_label) - 1; + + master_secret.data = conn->secrets.version.tls12.master_secret; + master_secret.size = sizeof(conn->secrets.version.tls12.master_secret); + if (conn->actual_protocol_version == S2N_TLS12) { + switch (conn->secure->cipher_suite->prf_alg) { + case S2N_HMAC_SHA256: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha256)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA256_DIGEST_LENGTH)); + sha.size = SHA256_DIGEST_LENGTH; + break; + case S2N_HMAC_SHA384: + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha384)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA384_DIGEST_LENGTH)); + sha.size = SHA384_DIGEST_LENGTH; + break; + default: + POSIX_BAIL(S2N_ERR_PRF_INVALID_ALGORITHM); + } + + sha.data = sha_digest; + return s2n_prf(conn, &master_secret, &label, &sha, NULL, NULL, &server_finished); + } + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->md5)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, md5_digest, MD5_DIGEST_LENGTH)); + md5.data = md5_digest; + md5.size = MD5_DIGEST_LENGTH; + + POSIX_GUARD(s2n_hash_copy(&conn->handshake.hashes->hash_workspace, &conn->handshake.hashes->sha1)); + POSIX_GUARD(s2n_hash_digest(&conn->handshake.hashes->hash_workspace, sha_digest, SHA_DIGEST_LENGTH)); + sha.data = sha_digest; + sha.size = SHA_DIGEST_LENGTH; + + return s2n_prf(conn, &master_secret, &label, &md5, &sha, NULL, &server_finished); +} + +static int s2n_prf_make_client_key(struct s2n_connection *conn, struct s2n_key_material *key_material) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + POSIX_ENSURE_REF(cipher); + POSIX_ENSURE_REF(cipher->set_encryption_key); + POSIX_ENSURE_REF(cipher->set_decryption_key); + + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->client_key, &key_material->client_key)); + } else { + POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->client_key, &key_material->client_key)); + } + + return 0; +} + +static int s2n_prf_make_server_key(struct s2n_connection *conn, struct s2n_key_material *key_material) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + POSIX_ENSURE_REF(cipher); + POSIX_ENSURE_REF(cipher->set_encryption_key); + POSIX_ENSURE_REF(cipher->set_decryption_key); + + if (conn->mode == S2N_SERVER) { + POSIX_GUARD_RESULT(cipher->set_encryption_key(&conn->secure->server_key, &key_material->server_key)); + } else { + POSIX_GUARD_RESULT(cipher->set_decryption_key(&conn->secure->server_key, &key_material->server_key)); + } + + return 0; +} + +S2N_RESULT s2n_prf_generate_key_material(struct s2n_connection *conn, struct s2n_key_material *key_material) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(key_material); + + struct s2n_blob client_random = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&client_random, conn->client_hello.random, sizeof(conn->client_hello.random))); + struct s2n_blob server_random = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&server_random, conn->handshake_params.server_random, sizeof(conn->handshake_params.server_random))); + struct s2n_blob master_secret = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&master_secret, conn->secrets.version.tls12.master_secret, sizeof(conn->secrets.version.tls12.master_secret))); + + struct s2n_blob label = { 0 }; + uint8_t key_expansion_label[] = "key expansion"; + RESULT_GUARD_POSIX(s2n_blob_init(&label, key_expansion_label, sizeof(key_expansion_label) - 1)); + + RESULT_GUARD(s2n_key_material_init(key_material, conn)); + struct s2n_blob prf_out = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&prf_out, key_material->key_block, sizeof(key_material->key_block))); + RESULT_GUARD_POSIX(s2n_prf(conn, &master_secret, &label, &server_random, &client_random, NULL, &prf_out)); + + return S2N_RESULT_OK; +} + +int s2n_prf_key_expansion(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite; + POSIX_ENSURE_REF(cipher_suite); + POSIX_ENSURE_REF(cipher_suite->record_alg); + const struct s2n_cipher *cipher = cipher_suite->record_alg->cipher; + POSIX_ENSURE_REF(cipher); + + struct s2n_key_material key_material = { 0 }; + POSIX_GUARD_RESULT(s2n_prf_generate_key_material(conn, &key_material)); + + POSIX_ENSURE(cipher_suite->available, S2N_ERR_PRF_INVALID_ALGORITHM); + POSIX_GUARD_RESULT(cipher->init(&conn->secure->client_key)); + POSIX_GUARD_RESULT(cipher->init(&conn->secure->server_key)); + + /* Seed the client MAC */ + POSIX_GUARD(s2n_hmac_reset(&conn->secure->client_record_mac)); + POSIX_GUARD(s2n_hmac_init( + &conn->secure->client_record_mac, + cipher_suite->record_alg->hmac_alg, + key_material.client_mac.data, + key_material.client_mac.size)); + + /* Seed the server MAC */ + POSIX_GUARD(s2n_hmac_reset(&conn->secure->server_record_mac)); + POSIX_GUARD(s2n_hmac_init( + &conn->secure->server_record_mac, + conn->secure->cipher_suite->record_alg->hmac_alg, + key_material.server_mac.data, + key_material.server_mac.size)); + + /* Make the client key */ + POSIX_GUARD(s2n_prf_make_client_key(conn, &key_material)); + + /* Make the server key */ + POSIX_GUARD(s2n_prf_make_server_key(conn, &key_material)); + + /* Composite CBC does MAC inside the cipher, pass it the MAC key. + * Must happen after setting encryption/decryption keys. + */ + if (cipher->type == S2N_COMPOSITE) { + POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->client_key, key_material.client_mac.data, key_material.client_mac.size)); + POSIX_GUARD(cipher->io.comp.set_mac_write_key(&conn->secure->server_key, key_material.server_mac.data, key_material.server_mac.size)); + } + + /* set IV */ + POSIX_ENSURE_EQ(key_material.client_iv.size, key_material.server_iv.size); + POSIX_ENSURE_LTE(key_material.client_iv.size, S2N_TLS_MAX_IV_LEN); + POSIX_CHECKED_MEMCPY(conn->secure->client_implicit_iv, key_material.client_iv.data, key_material.client_iv.size); + POSIX_CHECKED_MEMCPY(conn->secure->server_implicit_iv, key_material.server_iv.data, key_material.server_iv.size); + + return 0; +} diff --git a/tls/s2n_psk.c b/tls/s2n_psk.c index f25d89d0e85..e99d03bf8e1 100644 --- a/tls/s2n_psk.c +++ b/tls/s2n_psk.c @@ -1,712 +1,712 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_tls13_keys.h" -#include "tls/extensions/s2n_extension_type.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls13_secrets.h" -#include "utils/s2n_array.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_psk_init(struct s2n_psk *psk, s2n_psk_type type) -{ - RESULT_ENSURE_MUT(psk); - - RESULT_CHECKED_MEMSET(psk, 0, sizeof(struct s2n_psk)); - psk->hmac_alg = S2N_HMAC_SHA256; - psk->type = type; - - return S2N_RESULT_OK; -} - -struct s2n_psk *s2n_external_psk_new() -{ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_psk))); - - struct s2n_psk *psk = (struct s2n_psk *) (void *) mem.data; - PTR_GUARD_RESULT(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); - - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - return psk; -} - -int s2n_psk_set_identity(struct s2n_psk *psk, const uint8_t *identity, uint16_t identity_size) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(identity); - POSIX_ENSURE(identity_size != 0, S2N_ERR_INVALID_ARGUMENT); - - POSIX_GUARD(s2n_realloc(&psk->identity, identity_size)); - POSIX_CHECKED_MEMCPY(psk->identity.data, identity, identity_size); - - return S2N_SUCCESS; -} - -int s2n_psk_set_secret(struct s2n_psk *psk, const uint8_t *secret, uint16_t secret_size) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(secret); - POSIX_ENSURE(secret_size != 0, S2N_ERR_INVALID_ARGUMENT); - - /* There are a number of application level errors that might result in an - * all-zero secret accidentally getting used. Error if that happens. - */ - bool secret_is_all_zero = true; - for (uint16_t i = 0; i < secret_size; i++) { - secret_is_all_zero = secret_is_all_zero && secret[i] == 0; - } - POSIX_ENSURE(!secret_is_all_zero, S2N_ERR_INVALID_ARGUMENT); - - POSIX_GUARD(s2n_realloc(&psk->secret, secret_size)); - POSIX_CHECKED_MEMCPY(psk->secret.data, secret, secret_size); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_psk_clone(struct s2n_psk *new_psk, struct s2n_psk *original_psk) -{ - if (original_psk == NULL) { - return S2N_RESULT_OK; - } - RESULT_ENSURE_REF(new_psk); - - struct s2n_psk psk_copy = *new_psk; - - /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ - *new_psk = *original_psk; - new_psk->identity = psk_copy.identity; - new_psk->secret = psk_copy.secret; - new_psk->early_secret = psk_copy.early_secret; - new_psk->early_data_config = psk_copy.early_data_config; - - /* Clone / realloc blobs */ - RESULT_GUARD_POSIX(s2n_psk_set_identity(new_psk, original_psk->identity.data, original_psk->identity.size)); - RESULT_GUARD_POSIX(s2n_psk_set_secret(new_psk, original_psk->secret.data, original_psk->secret.size)); - RESULT_GUARD_POSIX(s2n_realloc(&new_psk->early_secret, original_psk->early_secret.size)); - RESULT_CHECKED_MEMCPY(new_psk->early_secret.data, original_psk->early_secret.data, original_psk->early_secret.size); - RESULT_GUARD(s2n_early_data_config_clone(new_psk, &original_psk->early_data_config)); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_psk_wipe(struct s2n_psk *psk) -{ - if (psk == NULL) { - return S2N_RESULT_OK; - } - - RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); - RESULT_GUARD_POSIX(s2n_free(&psk->identity)); - RESULT_GUARD_POSIX(s2n_free(&psk->secret)); - RESULT_GUARD(s2n_early_data_config_free(&psk->early_data_config)); - - return S2N_RESULT_OK; -} - -int s2n_psk_free(struct s2n_psk **psk) -{ - if (psk == NULL) { - return S2N_SUCCESS; - } - POSIX_GUARD_RESULT(s2n_psk_wipe(*psk)); - return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_psk)); -} - -S2N_RESULT s2n_psk_parameters_init(struct s2n_psk_parameters *params) -{ - RESULT_ENSURE_REF(params); - RESULT_CHECKED_MEMSET(params, 0, sizeof(struct s2n_psk_parameters)); - RESULT_GUARD(s2n_array_init(¶ms->psk_list, sizeof(struct s2n_psk))); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_psk_offered_psk_size(struct s2n_psk *psk, uint32_t *size) -{ - *size = sizeof(uint16_t) /* identity size */ - + sizeof(uint32_t) /* obfuscated ticket age */ - + sizeof(uint8_t); /* binder size */ - - RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk->identity.size, size)); - - uint8_t binder_size = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &binder_size)); - RESULT_GUARD_POSIX(s2n_add_overflow(*size, binder_size, size)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_psk_parameters_offered_psks_size(struct s2n_psk_parameters *params, uint32_t *size) -{ - RESULT_ENSURE_REF(params); - RESULT_ENSURE_REF(size); - - *size = sizeof(uint16_t) /* identity list size */ - + sizeof(uint16_t) /* binder list size */; - - for (uint32_t i = 0; i < params->psk_list.len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - - uint32_t psk_size = 0; - RESULT_GUARD(s2n_psk_offered_psk_size(psk, &psk_size)); - RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk_size, size)); - } - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_psk_parameters_wipe(struct s2n_psk_parameters *params) -{ - RESULT_ENSURE_REF(params); - - for (size_t i = 0; i < params->psk_list.len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); - RESULT_GUARD(s2n_psk_wipe(psk)); - } - RESULT_GUARD_POSIX(s2n_free(¶ms->psk_list.mem)); - RESULT_GUARD(s2n_psk_parameters_init(params)); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_psk_parameters_wipe_secrets(struct s2n_psk_parameters *params) -{ - RESULT_ENSURE_REF(params); - - for (size_t i = 0; i < params->psk_list.len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); - RESULT_GUARD_POSIX(s2n_free(&psk->secret)); - } - - return S2N_RESULT_OK; -} - -bool s2n_offered_psk_list_has_next(struct s2n_offered_psk_list *psk_list) -{ - return psk_list != NULL && s2n_stuffer_data_available(&psk_list->wire_data) > 0; -} - -S2N_RESULT s2n_offered_psk_list_read_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) -{ - RESULT_ENSURE_REF(psk_list); - RESULT_ENSURE_REF(psk_list->conn); - RESULT_ENSURE_MUT(psk); - - uint16_t identity_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&psk_list->wire_data, &identity_size)); - RESULT_ENSURE_GT(identity_size, 0); - - uint8_t *identity_data = NULL; - identity_data = s2n_stuffer_raw_read(&psk_list->wire_data, identity_size); - RESULT_ENSURE_REF(identity_data); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 - *# For identities established externally, an obfuscated_ticket_age of 0 SHOULD be - *# used, and servers MUST ignore the value. - */ - if (psk_list->conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&psk_list->wire_data, sizeof(uint32_t))); - } else { - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&psk_list->wire_data, &psk->obfuscated_ticket_age)); - } - - RESULT_GUARD_POSIX(s2n_blob_init(&psk->identity, identity_data, identity_size)); - psk->wire_index = psk_list->wire_index; - - RESULT_ENSURE(psk_list->wire_index < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); - psk_list->wire_index++; - return S2N_RESULT_OK; -} - -int s2n_offered_psk_list_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) -{ - POSIX_ENSURE_REF(psk_list); - POSIX_ENSURE_REF(psk); - *psk = (struct s2n_offered_psk){ 0 }; - POSIX_ENSURE(s2n_offered_psk_list_has_next(psk_list), S2N_ERR_STUFFER_OUT_OF_DATA); - POSIX_ENSURE(s2n_result_is_ok(s2n_offered_psk_list_read_next(psk_list, psk)), S2N_ERR_BAD_MESSAGE); - return S2N_SUCCESS; -} - -int s2n_offered_psk_list_reread(struct s2n_offered_psk_list *psk_list) -{ - POSIX_ENSURE_REF(psk_list); - psk_list->wire_index = 0; - return s2n_stuffer_reread(&psk_list->wire_data); -} - -/* Match a PSK identity received from the client against the server's known PSK identities. - * This method compares a single client identity to all server identities. - * - * While both the client's offered identities and whether a match was found are public, we should make an attempt - * to keep the server's known identities a secret. We will make comparisons to the server's identities constant - * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). - * - * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, - * and an attacker could probably guess the server's known identities just by observing the public identities - * sent by clients. - */ -static S2N_RESULT s2n_match_psk_identity(struct s2n_array *known_psks, const struct s2n_blob *wire_identity, - struct s2n_psk **match) -{ - RESULT_ENSURE_REF(match); - RESULT_ENSURE_REF(wire_identity); - RESULT_ENSURE_REF(known_psks); - *match = NULL; - for (size_t i = 0; i < known_psks->len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(known_psks, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - RESULT_ENSURE_REF(psk->identity.data); - RESULT_ENSURE_REF(wire_identity->data); - uint32_t compare_size = S2N_MIN(wire_identity->size, psk->identity.size); - if (s2n_constant_time_equals(psk->identity.data, wire_identity->data, compare_size) - & (psk->identity.size == wire_identity->size) & (!*match)) { - *match = psk; - } - } - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 - *# For PSKs provisioned via NewSessionTicket, a server MUST validate - *# that the ticket age for the selected PSK identity (computed by - *# subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age - *# modulo 2^32) is within a small tolerance of the time since the ticket - *# was issued (see Section 8). - **/ -static S2N_RESULT s2n_validate_ticket_lifetime(struct s2n_connection *conn, uint32_t obfuscated_ticket_age, uint32_t ticket_age_add) -{ - RESULT_ENSURE_REF(conn); - - if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { - return S2N_RESULT_OK; - } - - /* Subtract the ticket_age_add value from the ticket age in milliseconds. The resulting uint32_t value - * may wrap, resulting in the modulo 2^32 operation. */ - uint32_t ticket_age_in_millis = obfuscated_ticket_age - ticket_age_add; - uint32_t session_lifetime_in_millis = conn->config->session_state_lifetime_in_nanos / ONE_MILLISEC_IN_NANOS; - RESULT_ENSURE(ticket_age_in_millis < session_lifetime_in_millis, S2N_ERR_INVALID_SESSION_TICKET); - - return S2N_RESULT_OK; -} - -int s2n_offered_psk_list_choose_psk(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) -{ - POSIX_ENSURE_REF(psk_list); - POSIX_ENSURE_REF(psk_list->conn); - - struct s2n_psk_parameters *psk_params = &psk_list->conn->psk_params; - struct s2n_stuffer ticket_stuffer = { 0 }; - - if (!psk) { - psk_params->chosen_psk = NULL; - return S2N_SUCCESS; - } - - if (psk_params->type == S2N_PSK_TYPE_RESUMPTION && psk_list->conn->config->use_tickets) { - POSIX_GUARD(s2n_stuffer_init(&ticket_stuffer, &psk->identity)); - POSIX_GUARD(s2n_stuffer_skip_write(&ticket_stuffer, psk->identity.size)); - - /* s2n_resume_decrypt_session appends a new PSK with the decrypted values. */ - POSIX_GUARD_RESULT(s2n_resume_decrypt_session(psk_list->conn, &ticket_stuffer)); - } - - struct s2n_psk *chosen_psk = NULL; - POSIX_GUARD_RESULT(s2n_match_psk_identity(&psk_params->psk_list, &psk->identity, &chosen_psk)); - POSIX_ENSURE_REF(chosen_psk); - POSIX_GUARD_RESULT(s2n_validate_ticket_lifetime(psk_list->conn, psk->obfuscated_ticket_age, chosen_psk->ticket_age_add)); - psk_params->chosen_psk = chosen_psk; - psk_params->chosen_psk_wire_index = psk->wire_index; - - return S2N_SUCCESS; -} - -struct s2n_offered_psk *s2n_offered_psk_new() -{ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_offered_psk))); - PTR_GUARD_POSIX(s2n_blob_zero(&mem)); - - struct s2n_offered_psk *psk = (struct s2n_offered_psk *) (void *) mem.data; - - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - return psk; -} - -int s2n_offered_psk_free(struct s2n_offered_psk **psk) -{ - if (psk == NULL) { - return S2N_SUCCESS; - } - return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_offered_psk)); -} - -int s2n_offered_psk_get_identity(struct s2n_offered_psk *psk, uint8_t **identity, uint16_t *size) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(psk->identity.data); - POSIX_ENSURE_REF(identity); - POSIX_ENSURE_REF(size); - *identity = psk->identity.data; - *size = psk->identity.size; - return S2N_SUCCESS; -} - -/* The binder hash is computed by hashing the concatenation of the current transcript - * and a partial ClientHello that does not include the binders themselves. - */ -int s2n_psk_calculate_binder_hash(struct s2n_connection *conn, s2n_hmac_algorithm hmac_alg, - const struct s2n_blob *partial_client_hello, struct s2n_blob *output_binder_hash) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(partial_client_hello); - POSIX_ENSURE_REF(output_binder_hash); - struct s2n_handshake_hashes *hashes = conn->handshake.hashes; - POSIX_ENSURE_REF(hashes); - - /* Retrieve the current transcript. - * The current transcript will be empty unless this handshake included a HelloRetryRequest. */ - s2n_hash_algorithm hash_alg = S2N_HASH_NONE; - struct s2n_hash_state *hash_state = &hashes->hash_workspace; - POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); - - /* Add the partial client hello to the transcript. */ - POSIX_GUARD(s2n_hash_update(hash_state, partial_client_hello->data, partial_client_hello->size)); - - /* Get the transcript digest */ - POSIX_GUARD(s2n_hash_digest(hash_state, output_binder_hash->data, output_binder_hash->size)); - - return S2N_SUCCESS; -} - -/* The binder is computed in the same way as the Finished message - * (https://tools.ietf.org/html/rfc8446#section-4.4.4) but with the BaseKey being the binder_key - * derived via the key schedule from the corresponding PSK which is being offered - * (https://tools.ietf.org/html/rfc8446#section-7.1) - */ -int s2n_psk_calculate_binder(struct s2n_psk *psk, const struct s2n_blob *binder_hash, - struct s2n_blob *output_binder) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(binder_hash); - POSIX_ENSURE_REF(output_binder); - - DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); - POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); - POSIX_ENSURE_EQ(binder_hash->size, psk_keys.size); - POSIX_ENSURE_EQ(output_binder->size, psk_keys.size); - - /* Derive the binder key */ - POSIX_GUARD_RESULT(s2n_derive_binder_key(psk, &psk_keys.derive_secret)); - POSIX_GUARD(s2n_blob_init(&psk_keys.extract_secret, psk->early_secret.data, psk_keys.size)); - struct s2n_blob *binder_key = &psk_keys.derive_secret; - - /* Expand the binder key into the finished key */ - s2n_tls13_key_blob(finished_key, psk_keys.size); - POSIX_GUARD(s2n_tls13_derive_finished_key(&psk_keys, binder_key, &finished_key)); - - /* HMAC the binder hash with the binder finished key */ - POSIX_GUARD(s2n_hkdf_extract(&psk_keys.hmac, psk_keys.hmac_algorithm, &finished_key, binder_hash, output_binder)); - - return S2N_SUCCESS; -} - -int s2n_psk_verify_binder(struct s2n_connection *conn, struct s2n_psk *psk, - const struct s2n_blob *partial_client_hello, struct s2n_blob *binder_to_verify) -{ - POSIX_ENSURE_REF(psk); - POSIX_ENSURE_REF(binder_to_verify); - - DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); - POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); - POSIX_ENSURE_EQ(binder_to_verify->size, psk_keys.size); - - /* Calculate the binder hash from the transcript */ - s2n_tls13_key_blob(binder_hash, psk_keys.size); - POSIX_GUARD(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, &binder_hash)); - - /* Calculate the expected binder from the binder hash */ - s2n_tls13_key_blob(expected_binder, psk_keys.size); - POSIX_GUARD(s2n_psk_calculate_binder(psk, &binder_hash, &expected_binder)); - - /* Verify the expected binder matches the given binder. - * This operation must be constant time. */ - POSIX_GUARD(s2n_tls13_mac_verify(&psk_keys, &expected_binder, binder_to_verify)); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_psk_write_binder(struct s2n_connection *conn, struct s2n_psk *psk, - const struct s2n_blob *binder_hash, struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(binder_hash); - - struct s2n_blob binder = { 0 }; - uint8_t binder_data[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&binder, binder_data, binder_hash->size)); - - RESULT_GUARD_POSIX(s2n_psk_calculate_binder(psk, binder_hash, &binder)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, binder.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write(out, &binder)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_psk_write_binder_list(struct s2n_connection *conn, const struct s2n_blob *partial_client_hello, - struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(partial_client_hello); - RESULT_ENSURE_REF(conn->secure); - - struct s2n_psk_parameters *psk_params = &conn->psk_params; - struct s2n_array *psk_list = &psk_params->psk_list; - - /* Setup memory to hold the binder hashes. We potentially need one for - * every hash algorithm. */ - uint8_t binder_hashes_data[S2N_HASH_ALGS_COUNT][S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - struct s2n_blob binder_hashes[S2N_HASH_ALGS_COUNT] = { 0 }; - - struct s2n_stuffer_reservation binder_list_size = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(out, &binder_list_size)); - - /* Write binder for every psk */ - for (size_t i = 0; i < psk_list->len; i++) { - struct s2n_psk *psk = NULL; - RESULT_GUARD(s2n_array_get(psk_list, i, (void **) &psk)); - RESULT_ENSURE_REF(psk); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 - *# In addition, in its updated ClientHello, the client SHOULD NOT offer - *# any pre-shared keys associated with a hash other than that of the - *# selected cipher suite. This allows the client to avoid having to - *# compute partial hash transcripts for multiple hashes in the second - *# ClientHello. - */ - if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { - continue; - } - - /* Retrieve or calculate the binder hash. */ - struct s2n_blob *binder_hash = &binder_hashes[psk->hmac_alg]; - if (binder_hash->size == 0) { - uint8_t hash_size = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); - RESULT_GUARD_POSIX(s2n_blob_init(binder_hash, binder_hashes_data[psk->hmac_alg], hash_size)); - RESULT_GUARD_POSIX(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, binder_hash)); - } - - RESULT_GUARD(s2n_psk_write_binder(conn, psk, binder_hash, out)); - } - RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&binder_list_size)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_finish_psk_extension(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - if (!conn->psk_params.binder_list_size) { - return S2N_RESULT_OK; - } - - struct s2n_stuffer *client_hello = &conn->handshake.io; - struct s2n_psk_parameters *psk_params = &conn->psk_params; - - /* Fill in the correct message size. */ - RESULT_GUARD_POSIX(s2n_handshake_finish_header(client_hello)); - - /* Remove the empty space allocated for the binder list. - * It was originally added to ensure the extension / extension list / message sizes - * were properly calculated. */ - RESULT_GUARD_POSIX(s2n_stuffer_wipe_n(client_hello, psk_params->binder_list_size)); - - /* Store the partial client hello for use in calculating the binder hash. */ - struct s2n_blob partial_client_hello = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&partial_client_hello, client_hello->blob.data, - s2n_stuffer_data_available(client_hello))); - - RESULT_GUARD(s2n_psk_write_binder_list(conn, &partial_client_hello, client_hello)); - - /* Reset binder list size. - * This is important because the psk extension can be removed during a retry. - */ - conn->psk_params.binder_list_size = 0; - - return S2N_RESULT_OK; -} - -int s2n_psk_set_hmac(struct s2n_psk *psk, s2n_psk_hmac hmac) -{ - POSIX_ENSURE_REF(psk); - switch (hmac) { - case S2N_PSK_HMAC_SHA256: - psk->hmac_alg = S2N_HMAC_SHA256; - break; - case S2N_PSK_HMAC_SHA384: - psk->hmac_alg = S2N_HMAC_SHA384; - break; - default: - POSIX_BAIL(S2N_ERR_HMAC_INVALID_ALGORITHM); - } - return S2N_SUCCESS; -} - -S2N_RESULT s2n_connection_set_psk_type(struct s2n_connection *conn, s2n_psk_type type) -{ - RESULT_ENSURE_REF(conn); - if (conn->psk_params.psk_list.len != 0) { - RESULT_ENSURE(conn->psk_params.type == type, S2N_ERR_PSK_MODE); - } - conn->psk_params.type = type; - return S2N_RESULT_OK; -} - -int s2n_connection_append_psk(struct s2n_connection *conn, struct s2n_psk *input_psk) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(input_psk); - POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, input_psk->type)); - - struct s2n_array *psk_list = &conn->psk_params.psk_list; - - /* Check for duplicate identities */ - for (uint32_t j = 0; j < psk_list->len; j++) { - struct s2n_psk *existing_psk = NULL; - POSIX_GUARD_RESULT(s2n_array_get(psk_list, j, (void **) &existing_psk)); - POSIX_ENSURE_REF(existing_psk); - - bool duplicate = existing_psk->identity.size == input_psk->identity.size - && memcmp(existing_psk->identity.data, input_psk->identity.data, existing_psk->identity.size) == 0; - POSIX_ENSURE(!duplicate, S2N_ERR_DUPLICATE_PSK_IDENTITIES); - } - - /* Verify the PSK list will fit in the ClientHello pre_shared_key extension */ - if (conn->mode == S2N_CLIENT) { - uint32_t list_size = 0; - POSIX_GUARD_RESULT(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &list_size)); - - uint32_t psk_size = 0; - POSIX_GUARD_RESULT(s2n_psk_offered_psk_size(input_psk, &psk_size)); - - POSIX_ENSURE(list_size + psk_size + S2N_EXTENSION_HEADER_LENGTH <= UINT16_MAX, S2N_ERR_OFFERED_PSKS_TOO_LONG); - } - - DEFER_CLEANUP(struct s2n_psk new_psk = { 0 }, s2n_psk_wipe); - POSIX_ENSURE(s2n_result_is_ok(s2n_psk_clone(&new_psk, input_psk)), S2N_ERR_INVALID_ARGUMENT); - POSIX_GUARD_RESULT(s2n_array_insert_and_copy(psk_list, psk_list->len, &new_psk)); - - ZERO_TO_DISABLE_DEFER_CLEANUP(new_psk); - return S2N_SUCCESS; -} - -int s2n_config_set_psk_mode(struct s2n_config *config, s2n_psk_mode mode) -{ - POSIX_ENSURE_REF(config); - config->psk_mode = mode; - return S2N_SUCCESS; -} - -int s2n_connection_set_psk_mode(struct s2n_connection *conn, s2n_psk_mode mode) -{ - POSIX_ENSURE_REF(conn); - s2n_psk_type type = 0; - switch (mode) { - case S2N_PSK_MODE_RESUMPTION: - type = S2N_PSK_TYPE_RESUMPTION; - break; - case S2N_PSK_MODE_EXTERNAL: - type = S2N_PSK_TYPE_EXTERNAL; - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); - break; - } - POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, type)); - conn->psk_mode_overridden = true; - return S2N_SUCCESS; -} - -int s2n_connection_get_negotiated_psk_identity_length(struct s2n_connection *conn, uint16_t *identity_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(identity_length); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - - if (chosen_psk == NULL) { - *identity_length = 0; - } else { - *identity_length = chosen_psk->identity.size; - } - - return S2N_SUCCESS; -} - -int s2n_connection_get_negotiated_psk_identity(struct s2n_connection *conn, uint8_t *identity, - uint16_t max_identity_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(identity); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - - if (chosen_psk == NULL) { - return S2N_SUCCESS; - } - - POSIX_ENSURE(chosen_psk->identity.size <= max_identity_length, S2N_ERR_INSUFFICIENT_MEM_SIZE); - POSIX_CHECKED_MEMCPY(identity, chosen_psk->identity.data, chosen_psk->identity.size); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_psk_validate_keying_material(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - if (!chosen_psk || chosen_psk->type != S2N_PSK_TYPE_RESUMPTION) { - return S2N_RESULT_OK; - } - - /* - * The minimum ticket lifetime is 1s, because ticket_lifetime is given - * in seconds and 0 indicates that the ticket should be immediately discarded. - */ - uint32_t min_lifetime = ONE_SEC_IN_NANOS; - - uint64_t current_time = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); - RESULT_ENSURE(chosen_psk->keying_material_expiration > current_time + min_lifetime, S2N_ERR_KEYING_MATERIAL_EXPIRED); - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_tls13_keys.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls13_secrets.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_psk_init(struct s2n_psk *psk, s2n_psk_type type) +{ + RESULT_ENSURE_MUT(psk); + + RESULT_CHECKED_MEMSET(psk, 0, sizeof(struct s2n_psk)); + psk->hmac_alg = S2N_HMAC_SHA256; + psk->type = type; + + return S2N_RESULT_OK; +} + +struct s2n_psk *s2n_external_psk_new() +{ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_psk))); + + struct s2n_psk *psk = (struct s2n_psk *) (void *) mem.data; + PTR_GUARD_RESULT(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + return psk; +} + +int s2n_psk_set_identity(struct s2n_psk *psk, const uint8_t *identity, uint16_t identity_size) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(identity); + POSIX_ENSURE(identity_size != 0, S2N_ERR_INVALID_ARGUMENT); + + POSIX_GUARD(s2n_realloc(&psk->identity, identity_size)); + POSIX_CHECKED_MEMCPY(psk->identity.data, identity, identity_size); + + return S2N_SUCCESS; +} + +int s2n_psk_set_secret(struct s2n_psk *psk, const uint8_t *secret, uint16_t secret_size) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(secret); + POSIX_ENSURE(secret_size != 0, S2N_ERR_INVALID_ARGUMENT); + + /* There are a number of application level errors that might result in an + * all-zero secret accidentally getting used. Error if that happens. + */ + bool secret_is_all_zero = true; + for (uint16_t i = 0; i < secret_size; i++) { + secret_is_all_zero = secret_is_all_zero && secret[i] == 0; + } + POSIX_ENSURE(!secret_is_all_zero, S2N_ERR_INVALID_ARGUMENT); + + POSIX_GUARD(s2n_realloc(&psk->secret, secret_size)); + POSIX_CHECKED_MEMCPY(psk->secret.data, secret, secret_size); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_psk_clone(struct s2n_psk *new_psk, struct s2n_psk *original_psk) +{ + if (original_psk == NULL) { + return S2N_RESULT_OK; + } + RESULT_ENSURE_REF(new_psk); + + struct s2n_psk psk_copy = *new_psk; + + /* Copy all fields from the old_config EXCEPT the blobs, which we need to reallocate. */ + *new_psk = *original_psk; + new_psk->identity = psk_copy.identity; + new_psk->secret = psk_copy.secret; + new_psk->early_secret = psk_copy.early_secret; + new_psk->early_data_config = psk_copy.early_data_config; + + /* Clone / realloc blobs */ + RESULT_GUARD_POSIX(s2n_psk_set_identity(new_psk, original_psk->identity.data, original_psk->identity.size)); + RESULT_GUARD_POSIX(s2n_psk_set_secret(new_psk, original_psk->secret.data, original_psk->secret.size)); + RESULT_GUARD_POSIX(s2n_realloc(&new_psk->early_secret, original_psk->early_secret.size)); + RESULT_CHECKED_MEMCPY(new_psk->early_secret.data, original_psk->early_secret.data, original_psk->early_secret.size); + RESULT_GUARD(s2n_early_data_config_clone(new_psk, &original_psk->early_data_config)); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_psk_wipe(struct s2n_psk *psk) +{ + if (psk == NULL) { + return S2N_RESULT_OK; + } + + RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); + RESULT_GUARD_POSIX(s2n_free(&psk->identity)); + RESULT_GUARD_POSIX(s2n_free(&psk->secret)); + RESULT_GUARD(s2n_early_data_config_free(&psk->early_data_config)); + + return S2N_RESULT_OK; +} + +int s2n_psk_free(struct s2n_psk **psk) +{ + if (psk == NULL) { + return S2N_SUCCESS; + } + POSIX_GUARD_RESULT(s2n_psk_wipe(*psk)); + return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_psk)); +} + +S2N_RESULT s2n_psk_parameters_init(struct s2n_psk_parameters *params) +{ + RESULT_ENSURE_REF(params); + RESULT_CHECKED_MEMSET(params, 0, sizeof(struct s2n_psk_parameters)); + RESULT_GUARD(s2n_array_init(¶ms->psk_list, sizeof(struct s2n_psk))); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_psk_offered_psk_size(struct s2n_psk *psk, uint32_t *size) +{ + *size = sizeof(uint16_t) /* identity size */ + + sizeof(uint32_t) /* obfuscated ticket age */ + + sizeof(uint8_t); /* binder size */ + + RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk->identity.size, size)); + + uint8_t binder_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &binder_size)); + RESULT_GUARD_POSIX(s2n_add_overflow(*size, binder_size, size)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_psk_parameters_offered_psks_size(struct s2n_psk_parameters *params, uint32_t *size) +{ + RESULT_ENSURE_REF(params); + RESULT_ENSURE_REF(size); + + *size = sizeof(uint16_t) /* identity list size */ + + sizeof(uint16_t) /* binder list size */; + + for (uint32_t i = 0; i < params->psk_list.len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + + uint32_t psk_size = 0; + RESULT_GUARD(s2n_psk_offered_psk_size(psk, &psk_size)); + RESULT_GUARD_POSIX(s2n_add_overflow(*size, psk_size, size)); + } + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_psk_parameters_wipe(struct s2n_psk_parameters *params) +{ + RESULT_ENSURE_REF(params); + + for (size_t i = 0; i < params->psk_list.len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); + RESULT_GUARD(s2n_psk_wipe(psk)); + } + RESULT_GUARD_POSIX(s2n_free(¶ms->psk_list.mem)); + RESULT_GUARD(s2n_psk_parameters_init(params)); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_psk_parameters_wipe_secrets(struct s2n_psk_parameters *params) +{ + RESULT_ENSURE_REF(params); + + for (size_t i = 0; i < params->psk_list.len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(¶ms->psk_list, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + RESULT_GUARD_POSIX(s2n_free(&psk->early_secret)); + RESULT_GUARD_POSIX(s2n_free(&psk->secret)); + } + + return S2N_RESULT_OK; +} + +bool s2n_offered_psk_list_has_next(struct s2n_offered_psk_list *psk_list) +{ + return psk_list != NULL && s2n_stuffer_data_available(&psk_list->wire_data) > 0; +} + +S2N_RESULT s2n_offered_psk_list_read_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) +{ + RESULT_ENSURE_REF(psk_list); + RESULT_ENSURE_REF(psk_list->conn); + RESULT_ENSURE_MUT(psk); + + uint16_t identity_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&psk_list->wire_data, &identity_size)); + RESULT_ENSURE_GT(identity_size, 0); + + uint8_t *identity_data = NULL; + identity_data = s2n_stuffer_raw_read(&psk_list->wire_data, identity_size); + RESULT_ENSURE_REF(identity_data); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11 + *# For identities established externally, an obfuscated_ticket_age of 0 SHOULD be + *# used, and servers MUST ignore the value. + */ + if (psk_list->conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&psk_list->wire_data, sizeof(uint32_t))); + } else { + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&psk_list->wire_data, &psk->obfuscated_ticket_age)); + } + + RESULT_GUARD_POSIX(s2n_blob_init(&psk->identity, identity_data, identity_size)); + psk->wire_index = psk_list->wire_index; + + RESULT_ENSURE(psk_list->wire_index < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); + psk_list->wire_index++; + return S2N_RESULT_OK; +} + +int s2n_offered_psk_list_next(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) +{ + POSIX_ENSURE_REF(psk_list); + POSIX_ENSURE_REF(psk); + *psk = (struct s2n_offered_psk){ 0 }; + POSIX_ENSURE(s2n_offered_psk_list_has_next(psk_list), S2N_ERR_STUFFER_OUT_OF_DATA); + POSIX_ENSURE(s2n_result_is_ok(s2n_offered_psk_list_read_next(psk_list, psk)), S2N_ERR_BAD_MESSAGE); + return S2N_SUCCESS; +} + +int s2n_offered_psk_list_reread(struct s2n_offered_psk_list *psk_list) +{ + POSIX_ENSURE_REF(psk_list); + psk_list->wire_index = 0; + return s2n_stuffer_reread(&psk_list->wire_data); +} + +/* Match a PSK identity received from the client against the server's known PSK identities. + * This method compares a single client identity to all server identities. + * + * While both the client's offered identities and whether a match was found are public, we should make an attempt + * to keep the server's known identities a secret. We will make comparisons to the server's identities constant + * time (to hide partial matches) and not end the search early when a match is found (to hide the ordering). + * + * Keeping these comparisons constant time is not high priority. There's no known attack using these timings, + * and an attacker could probably guess the server's known identities just by observing the public identities + * sent by clients. + */ +static S2N_RESULT s2n_match_psk_identity(struct s2n_array *known_psks, const struct s2n_blob *wire_identity, + struct s2n_psk **match) +{ + RESULT_ENSURE_REF(match); + RESULT_ENSURE_REF(wire_identity); + RESULT_ENSURE_REF(known_psks); + *match = NULL; + for (size_t i = 0; i < known_psks->len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(known_psks, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + RESULT_ENSURE_REF(psk->identity.data); + RESULT_ENSURE_REF(wire_identity->data); + uint32_t compare_size = S2N_MIN(wire_identity->size, psk->identity.size); + if (s2n_constant_time_equals(psk->identity.data, wire_identity->data, compare_size) + & (psk->identity.size == wire_identity->size) & (!*match)) { + *match = psk; + } + } + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 + *# For PSKs provisioned via NewSessionTicket, a server MUST validate + *# that the ticket age for the selected PSK identity (computed by + *# subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age + *# modulo 2^32) is within a small tolerance of the time since the ticket + *# was issued (see Section 8). + **/ +static S2N_RESULT s2n_validate_ticket_lifetime(struct s2n_connection *conn, uint32_t obfuscated_ticket_age, uint32_t ticket_age_add) +{ + RESULT_ENSURE_REF(conn); + + if (conn->psk_params.type == S2N_PSK_TYPE_EXTERNAL) { + return S2N_RESULT_OK; + } + + /* Subtract the ticket_age_add value from the ticket age in milliseconds. The resulting uint32_t value + * may wrap, resulting in the modulo 2^32 operation. */ + uint32_t ticket_age_in_millis = obfuscated_ticket_age - ticket_age_add; + uint32_t session_lifetime_in_millis = conn->config->session_state_lifetime_in_nanos / ONE_MILLISEC_IN_NANOS; + RESULT_ENSURE(ticket_age_in_millis < session_lifetime_in_millis, S2N_ERR_INVALID_SESSION_TICKET); + + return S2N_RESULT_OK; +} + +int s2n_offered_psk_list_choose_psk(struct s2n_offered_psk_list *psk_list, struct s2n_offered_psk *psk) +{ + POSIX_ENSURE_REF(psk_list); + POSIX_ENSURE_REF(psk_list->conn); + + struct s2n_psk_parameters *psk_params = &psk_list->conn->psk_params; + struct s2n_stuffer ticket_stuffer = { 0 }; + + if (!psk) { + psk_params->chosen_psk = NULL; + return S2N_SUCCESS; + } + + if (psk_params->type == S2N_PSK_TYPE_RESUMPTION && psk_list->conn->config->use_tickets) { + POSIX_GUARD(s2n_stuffer_init(&ticket_stuffer, &psk->identity)); + POSIX_GUARD(s2n_stuffer_skip_write(&ticket_stuffer, psk->identity.size)); + + /* s2n_resume_decrypt_session appends a new PSK with the decrypted values. */ + POSIX_GUARD_RESULT(s2n_resume_decrypt_session(psk_list->conn, &ticket_stuffer)); + } + + struct s2n_psk *chosen_psk = NULL; + POSIX_GUARD_RESULT(s2n_match_psk_identity(&psk_params->psk_list, &psk->identity, &chosen_psk)); + POSIX_ENSURE_REF(chosen_psk); + POSIX_GUARD_RESULT(s2n_validate_ticket_lifetime(psk_list->conn, psk->obfuscated_ticket_age, chosen_psk->ticket_age_add)); + psk_params->chosen_psk = chosen_psk; + psk_params->chosen_psk_wire_index = psk->wire_index; + + return S2N_SUCCESS; +} + +struct s2n_offered_psk *s2n_offered_psk_new() +{ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_offered_psk))); + PTR_GUARD_POSIX(s2n_blob_zero(&mem)); + + struct s2n_offered_psk *psk = (struct s2n_offered_psk *) (void *) mem.data; + + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + return psk; +} + +int s2n_offered_psk_free(struct s2n_offered_psk **psk) +{ + if (psk == NULL) { + return S2N_SUCCESS; + } + return s2n_free_object((uint8_t **) psk, sizeof(struct s2n_offered_psk)); +} + +int s2n_offered_psk_get_identity(struct s2n_offered_psk *psk, uint8_t **identity, uint16_t *size) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(psk->identity.data); + POSIX_ENSURE_REF(identity); + POSIX_ENSURE_REF(size); + *identity = psk->identity.data; + *size = psk->identity.size; + return S2N_SUCCESS; +} + +/* The binder hash is computed by hashing the concatenation of the current transcript + * and a partial ClientHello that does not include the binders themselves. + */ +int s2n_psk_calculate_binder_hash(struct s2n_connection *conn, s2n_hmac_algorithm hmac_alg, + const struct s2n_blob *partial_client_hello, struct s2n_blob *output_binder_hash) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(partial_client_hello); + POSIX_ENSURE_REF(output_binder_hash); + struct s2n_handshake_hashes *hashes = conn->handshake.hashes; + POSIX_ENSURE_REF(hashes); + + /* Retrieve the current transcript. + * The current transcript will be empty unless this handshake included a HelloRetryRequest. */ + s2n_hash_algorithm hash_alg = S2N_HASH_NONE; + struct s2n_hash_state *hash_state = &hashes->hash_workspace; + POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, hash_alg, hash_state)); + + /* Add the partial client hello to the transcript. */ + POSIX_GUARD(s2n_hash_update(hash_state, partial_client_hello->data, partial_client_hello->size)); + + /* Get the transcript digest */ + POSIX_GUARD(s2n_hash_digest(hash_state, output_binder_hash->data, output_binder_hash->size)); + + return S2N_SUCCESS; +} + +/* The binder is computed in the same way as the Finished message + * (https://tools.ietf.org/html/rfc8446#section-4.4.4) but with the BaseKey being the binder_key + * derived via the key schedule from the corresponding PSK which is being offered + * (https://tools.ietf.org/html/rfc8446#section-7.1) + */ +int s2n_psk_calculate_binder(struct s2n_psk *psk, const struct s2n_blob *binder_hash, + struct s2n_blob *output_binder) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(binder_hash); + POSIX_ENSURE_REF(output_binder); + + DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); + POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); + POSIX_ENSURE_EQ(binder_hash->size, psk_keys.size); + POSIX_ENSURE_EQ(output_binder->size, psk_keys.size); + + /* Derive the binder key */ + POSIX_GUARD_RESULT(s2n_derive_binder_key(psk, &psk_keys.derive_secret)); + POSIX_GUARD(s2n_blob_init(&psk_keys.extract_secret, psk->early_secret.data, psk_keys.size)); + struct s2n_blob *binder_key = &psk_keys.derive_secret; + + /* Expand the binder key into the finished key */ + s2n_tls13_key_blob(finished_key, psk_keys.size); + POSIX_GUARD(s2n_tls13_derive_finished_key(&psk_keys, binder_key, &finished_key)); + + /* HMAC the binder hash with the binder finished key */ + POSIX_GUARD(s2n_hkdf_extract(&psk_keys.hmac, psk_keys.hmac_algorithm, &finished_key, binder_hash, output_binder)); + + return S2N_SUCCESS; +} + +int s2n_psk_verify_binder(struct s2n_connection *conn, struct s2n_psk *psk, + const struct s2n_blob *partial_client_hello, struct s2n_blob *binder_to_verify) +{ + POSIX_ENSURE_REF(psk); + POSIX_ENSURE_REF(binder_to_verify); + + DEFER_CLEANUP(struct s2n_tls13_keys psk_keys, s2n_tls13_keys_free); + POSIX_GUARD(s2n_tls13_keys_init(&psk_keys, psk->hmac_alg)); + POSIX_ENSURE_EQ(binder_to_verify->size, psk_keys.size); + + /* Calculate the binder hash from the transcript */ + s2n_tls13_key_blob(binder_hash, psk_keys.size); + POSIX_GUARD(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, &binder_hash)); + + /* Calculate the expected binder from the binder hash */ + s2n_tls13_key_blob(expected_binder, psk_keys.size); + POSIX_GUARD(s2n_psk_calculate_binder(psk, &binder_hash, &expected_binder)); + + /* Verify the expected binder matches the given binder. + * This operation must be constant time. */ + POSIX_GUARD(s2n_tls13_mac_verify(&psk_keys, &expected_binder, binder_to_verify)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_psk_write_binder(struct s2n_connection *conn, struct s2n_psk *psk, + const struct s2n_blob *binder_hash, struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(binder_hash); + + struct s2n_blob binder = { 0 }; + uint8_t binder_data[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&binder, binder_data, binder_hash->size)); + + RESULT_GUARD_POSIX(s2n_psk_calculate_binder(psk, binder_hash, &binder)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, binder.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write(out, &binder)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_psk_write_binder_list(struct s2n_connection *conn, const struct s2n_blob *partial_client_hello, + struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(partial_client_hello); + RESULT_ENSURE_REF(conn->secure); + + struct s2n_psk_parameters *psk_params = &conn->psk_params; + struct s2n_array *psk_list = &psk_params->psk_list; + + /* Setup memory to hold the binder hashes. We potentially need one for + * every hash algorithm. */ + uint8_t binder_hashes_data[S2N_HASH_ALGS_COUNT][S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + struct s2n_blob binder_hashes[S2N_HASH_ALGS_COUNT] = { 0 }; + + struct s2n_stuffer_reservation binder_list_size = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(out, &binder_list_size)); + + /* Write binder for every psk */ + for (size_t i = 0; i < psk_list->len; i++) { + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(psk_list, i, (void **) &psk)); + RESULT_ENSURE_REF(psk); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4 + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. This allows the client to avoid having to + *# compute partial hash transcripts for multiple hashes in the second + *# ClientHello. + */ + if (s2n_is_hello_retry_handshake(conn) && conn->secure->cipher_suite->prf_alg != psk->hmac_alg) { + continue; + } + + /* Retrieve or calculate the binder hash. */ + struct s2n_blob *binder_hash = &binder_hashes[psk->hmac_alg]; + if (binder_hash->size == 0) { + uint8_t hash_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(psk->hmac_alg, &hash_size)); + RESULT_GUARD_POSIX(s2n_blob_init(binder_hash, binder_hashes_data[psk->hmac_alg], hash_size)); + RESULT_GUARD_POSIX(s2n_psk_calculate_binder_hash(conn, psk->hmac_alg, partial_client_hello, binder_hash)); + } + + RESULT_GUARD(s2n_psk_write_binder(conn, psk, binder_hash, out)); + } + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&binder_list_size)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_finish_psk_extension(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + if (!conn->psk_params.binder_list_size) { + return S2N_RESULT_OK; + } + + struct s2n_stuffer *client_hello = &conn->handshake.io; + struct s2n_psk_parameters *psk_params = &conn->psk_params; + + /* Fill in the correct message size. */ + RESULT_GUARD_POSIX(s2n_handshake_finish_header(client_hello)); + + /* Remove the empty space allocated for the binder list. + * It was originally added to ensure the extension / extension list / message sizes + * were properly calculated. */ + RESULT_GUARD_POSIX(s2n_stuffer_wipe_n(client_hello, psk_params->binder_list_size)); + + /* Store the partial client hello for use in calculating the binder hash. */ + struct s2n_blob partial_client_hello = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&partial_client_hello, client_hello->blob.data, + s2n_stuffer_data_available(client_hello))); + + RESULT_GUARD(s2n_psk_write_binder_list(conn, &partial_client_hello, client_hello)); + + /* Reset binder list size. + * This is important because the psk extension can be removed during a retry. + */ + conn->psk_params.binder_list_size = 0; + + return S2N_RESULT_OK; +} + +int s2n_psk_set_hmac(struct s2n_psk *psk, s2n_psk_hmac hmac) +{ + POSIX_ENSURE_REF(psk); + switch (hmac) { + case S2N_PSK_HMAC_SHA256: + psk->hmac_alg = S2N_HMAC_SHA256; + break; + case S2N_PSK_HMAC_SHA384: + psk->hmac_alg = S2N_HMAC_SHA384; + break; + default: + POSIX_BAIL(S2N_ERR_HMAC_INVALID_ALGORITHM); + } + return S2N_SUCCESS; +} + +S2N_RESULT s2n_connection_set_psk_type(struct s2n_connection *conn, s2n_psk_type type) +{ + RESULT_ENSURE_REF(conn); + if (conn->psk_params.psk_list.len != 0) { + RESULT_ENSURE(conn->psk_params.type == type, S2N_ERR_PSK_MODE); + } + conn->psk_params.type = type; + return S2N_RESULT_OK; +} + +int s2n_connection_append_psk(struct s2n_connection *conn, struct s2n_psk *input_psk) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(input_psk); + POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, input_psk->type)); + + struct s2n_array *psk_list = &conn->psk_params.psk_list; + + /* Check for duplicate identities */ + for (uint32_t j = 0; j < psk_list->len; j++) { + struct s2n_psk *existing_psk = NULL; + POSIX_GUARD_RESULT(s2n_array_get(psk_list, j, (void **) &existing_psk)); + POSIX_ENSURE_REF(existing_psk); + + bool duplicate = existing_psk->identity.size == input_psk->identity.size + && memcmp(existing_psk->identity.data, input_psk->identity.data, existing_psk->identity.size) == 0; + POSIX_ENSURE(!duplicate, S2N_ERR_DUPLICATE_PSK_IDENTITIES); + } + + /* Verify the PSK list will fit in the ClientHello pre_shared_key extension */ + if (conn->mode == S2N_CLIENT) { + uint32_t list_size = 0; + POSIX_GUARD_RESULT(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &list_size)); + + uint32_t psk_size = 0; + POSIX_GUARD_RESULT(s2n_psk_offered_psk_size(input_psk, &psk_size)); + + POSIX_ENSURE(list_size + psk_size + S2N_EXTENSION_HEADER_LENGTH <= UINT16_MAX, S2N_ERR_OFFERED_PSKS_TOO_LONG); + } + + DEFER_CLEANUP(struct s2n_psk new_psk = { 0 }, s2n_psk_wipe); + POSIX_ENSURE(s2n_result_is_ok(s2n_psk_clone(&new_psk, input_psk)), S2N_ERR_INVALID_ARGUMENT); + POSIX_GUARD_RESULT(s2n_array_insert_and_copy(psk_list, psk_list->len, &new_psk)); + + ZERO_TO_DISABLE_DEFER_CLEANUP(new_psk); + return S2N_SUCCESS; +} + +int s2n_config_set_psk_mode(struct s2n_config *config, s2n_psk_mode mode) +{ + POSIX_ENSURE_REF(config); + config->psk_mode = mode; + return S2N_SUCCESS; +} + +int s2n_connection_set_psk_mode(struct s2n_connection *conn, s2n_psk_mode mode) +{ + POSIX_ENSURE_REF(conn); + s2n_psk_type type = 0; + switch (mode) { + case S2N_PSK_MODE_RESUMPTION: + type = S2N_PSK_TYPE_RESUMPTION; + break; + case S2N_PSK_MODE_EXTERNAL: + type = S2N_PSK_TYPE_EXTERNAL; + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); + break; + } + POSIX_GUARD_RESULT(s2n_connection_set_psk_type(conn, type)); + conn->psk_mode_overridden = true; + return S2N_SUCCESS; +} + +int s2n_connection_get_negotiated_psk_identity_length(struct s2n_connection *conn, uint16_t *identity_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(identity_length); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + + if (chosen_psk == NULL) { + *identity_length = 0; + } else { + *identity_length = chosen_psk->identity.size; + } + + return S2N_SUCCESS; +} + +int s2n_connection_get_negotiated_psk_identity(struct s2n_connection *conn, uint8_t *identity, + uint16_t max_identity_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(identity); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + + if (chosen_psk == NULL) { + return S2N_SUCCESS; + } + + POSIX_ENSURE(chosen_psk->identity.size <= max_identity_length, S2N_ERR_INSUFFICIENT_MEM_SIZE); + POSIX_CHECKED_MEMCPY(identity, chosen_psk->identity.data, chosen_psk->identity.size); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_psk_validate_keying_material(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + if (!chosen_psk || chosen_psk->type != S2N_PSK_TYPE_RESUMPTION) { + return S2N_RESULT_OK; + } + + /* + * The minimum ticket lifetime is 1s, because ticket_lifetime is given + * in seconds and 0 indicates that the ticket should be immediately discarded. + */ + uint32_t min_lifetime = ONE_SEC_IN_NANOS; + + uint64_t current_time = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); + RESULT_ENSURE(chosen_psk->keying_material_expiration > current_time + min_lifetime, S2N_ERR_KEYING_MATERIAL_EXPIRED); + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_quic_support.c b/tls/s2n_quic_support.c index 6db154b11ae..120561caf50 100644 --- a/tls/s2n_quic_support.c +++ b/tls/s2n_quic_support.c @@ -1,183 +1,183 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_quic_support.h" - -#include "tls/s2n_connection.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -/* When reading and writing records with TCP, S2N sets its input and output buffers - * to the maximum record fragment size to prevent resizing those buffers later. - * - * However, because S2N with QUIC reads and writes messages instead of records, - * the "maximum size" for the input and output buffers would be the maximum message size: 64k. - * Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful. - * - * Instead, we intentionally choose a smaller size and accept that an abnormally large message - * could cause the buffer to resize. */ -#define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH - -S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length); - -int s2n_config_enable_quic(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - config->quic_enabled = true; - return S2N_SUCCESS; -} - -int s2n_connection_enable_quic(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn)); - /* QUIC support is not currently compatible with recv_buffering */ - POSIX_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); - conn->quic_enabled = true; - return S2N_SUCCESS; -} - -bool s2n_connection_is_quic_enabled(struct s2n_connection *conn) -{ - return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled); -} - -bool s2n_connection_are_session_tickets_enabled(struct s2n_connection *conn) -{ - return conn && conn->config && conn->config->use_tickets; -} - -int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn, - const uint8_t *data_buffer, uint16_t data_len) -{ - POSIX_ENSURE_REF(conn); - - POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); - POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len)); - POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len); - - return S2N_SUCCESS; -} - -int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn, - const uint8_t **data_buffer, uint16_t *data_len) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(data_buffer); - POSIX_ENSURE_REF(data_len); - - *data_buffer = conn->peer_quic_transport_parameters.data; - *data_len = conn->peer_quic_transport_parameters.size; - - return S2N_SUCCESS; -} - -int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(cb_func); - - conn->secret_cb = cb_func; - conn->secret_cb_context = ctx; - - return S2N_SUCCESS; -} - -/* Currently we need an API that quic can call to process post-handshake messages. Ideally - * we could re-use the s2n_recv API but that function needs to be refactored to support quic. - * For now we just call this API. - */ -int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - - *blocked = S2N_BLOCKED_ON_READ; - - uint8_t message_type = 0; - /* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used - * for sending post-handshake messages. This could cause a concurrency issue if we start both sending - * and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake - * message that is both sent and received in quic (servers only send session tickets - * and clients only receive session tickets.) Therefore it is safe for us - * to use the stuffer here. - */ - POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); - - /* The only post-handshake messages we support from QUIC currently are session tickets */ - POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC); - POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type)); - - /* Successfully read the message, wipe the header and message buffer */ - POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - - *blocked = S2N_NOT_BLOCKED; - - return S2N_SUCCESS; -} - -/* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records. - * This method sets up the S2N input buffers to match the results of using s2n_read_full_record. - */ -S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) -{ - RESULT_ENSURE_REF(conn); - /* The use of handshake.io here would complicate recv_buffering, and there's - * no real use case for recv_buffering when QUIC is handling the IO. - */ - RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); - - /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); - - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); - - uint32_t message_len = 0; - RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len)); - /* Reset the read cursor rather than wiping the stuffer. During the - * handshake, s2n_read_full_handshake_message expects the header to still - * be present in handshake.io so it can accumulate the full message. - */ - RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); - - RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); - - /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. - * Therefore buffer_in will always contain exactly message_len bytes of data. - * So we don't need to handle the possibility of extra data in buffer_in. - */ - RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); - RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); - return S2N_RESULT_OK; -} - -/* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records. - * This method sets up the S2N output buffer to match the result of using s2n_record_write. - */ -S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); - - RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out, - s2n_stuffer_data_available(&conn->handshake.io))); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_quic_support.h" + +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* When reading and writing records with TCP, S2N sets its input and output buffers + * to the maximum record fragment size to prevent resizing those buffers later. + * + * However, because S2N with QUIC reads and writes messages instead of records, + * the "maximum size" for the input and output buffers would be the maximum message size: 64k. + * Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful. + * + * Instead, we intentionally choose a smaller size and accept that an abnormally large message + * could cause the buffer to resize. */ +#define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH + +S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length); + +int s2n_config_enable_quic(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + config->quic_enabled = true; + return S2N_SUCCESS; +} + +int s2n_connection_enable_quic(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn)); + /* QUIC support is not currently compatible with recv_buffering */ + POSIX_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); + conn->quic_enabled = true; + return S2N_SUCCESS; +} + +bool s2n_connection_is_quic_enabled(struct s2n_connection *conn) +{ + return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled); +} + +bool s2n_connection_are_session_tickets_enabled(struct s2n_connection *conn) +{ + return conn && conn->config && conn->config->use_tickets; +} + +int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn, + const uint8_t *data_buffer, uint16_t data_len) +{ + POSIX_ENSURE_REF(conn); + + POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); + POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len)); + POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len); + + return S2N_SUCCESS; +} + +int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn, + const uint8_t **data_buffer, uint16_t *data_len) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(data_buffer); + POSIX_ENSURE_REF(data_len); + + *data_buffer = conn->peer_quic_transport_parameters.data; + *data_len = conn->peer_quic_transport_parameters.size; + + return S2N_SUCCESS; +} + +int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(cb_func); + + conn->secret_cb = cb_func; + conn->secret_cb_context = ctx; + + return S2N_SUCCESS; +} + +/* Currently we need an API that quic can call to process post-handshake messages. Ideally + * we could re-use the s2n_recv API but that function needs to be refactored to support quic. + * For now we just call this API. + */ +int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + + *blocked = S2N_BLOCKED_ON_READ; + + uint8_t message_type = 0; + /* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used + * for sending post-handshake messages. This could cause a concurrency issue if we start both sending + * and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake + * message that is both sent and received in quic (servers only send session tickets + * and clients only receive session tickets.) Therefore it is safe for us + * to use the stuffer here. + */ + POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); + + /* The only post-handshake messages we support from QUIC currently are session tickets */ + POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC); + POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type)); + + /* Successfully read the message, wipe the header and message buffer */ + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + + *blocked = S2N_NOT_BLOCKED; + + return S2N_SUCCESS; +} + +/* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records. + * This method sets up the S2N input buffers to match the results of using s2n_read_full_record. + */ +S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) +{ + RESULT_ENSURE_REF(conn); + /* The use of handshake.io here would complicate recv_buffering, and there's + * no real use case for recv_buffering when QUIC is handling the IO. + */ + RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); + + /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); + + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); + + uint32_t message_len = 0; + RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len)); + /* Reset the read cursor rather than wiping the stuffer. During the + * handshake, s2n_read_full_handshake_message expects the header to still + * be present in handshake.io so it can accumulate the full message. + */ + RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); + + RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); + + /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. + * Therefore buffer_in will always contain exactly message_len bytes of data. + * So we don't need to handle the possibility of extra data in buffer_in. + */ + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); + RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); + return S2N_RESULT_OK; +} + +/* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records. + * This method sets up the S2N output buffer to match the result of using s2n_record_write. + */ +S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); + + RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out, + s2n_stuffer_data_available(&conn->handshake.io))); + return S2N_RESULT_OK; +} diff --git a/tls/s2n_record_read.c b/tls/s2n_record_read.c index 527a950e195..fa838480e31 100644 --- a/tls/s2n_record_read.c +++ b/tls/s2n_record_read.c @@ -1,288 +1,288 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_record_read.h" - -#include "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_sequence.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -int s2n_sslv2_record_header_parse( - struct s2n_connection *conn, - uint8_t *record_type, - uint8_t *client_protocol_version, - uint16_t *fragment_length) -{ - struct s2n_stuffer *header_in = &conn->header_in; - - POSIX_ENSURE(s2n_stuffer_data_available(header_in) >= S2N_TLS_RECORD_HEADER_LENGTH, - S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_stuffer_read_uint16(header_in, fragment_length)); - - /* The first bit of the SSLv2 message would usually indicate whether the - * length is 2 bytes long or 3 bytes long. - * See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt - * - * However, s2n-tls only supports SSLv2 for ClientHellos as defined in the - * TLS1.2 RFC. In that case, the first bit must always be set to distinguish - * SSLv2 from non-SSLv2 headers. The length is always 2 bytes. - * See https://datatracker.ietf.org/doc/html/rfc5246#appendix-E.2 - * - * Since the first bit is not actually used to indicate length, we need to - * remove it from the length. - * - *= https://www.rfc-editor.org/rfc/rfc5246#appendix-E.2 - *# msg_length - *# The highest bit MUST be 1; the remaining bits contain the length - *# of the following data in bytes. - */ - POSIX_ENSURE(*fragment_length & S2N_TLS_SSLV2_HEADER_FLAG_UINT16, S2N_ERR_BAD_MESSAGE); - *fragment_length ^= S2N_TLS_SSLV2_HEADER_FLAG_UINT16; - - /* We read 5 bytes into header_in because we expected a standard, non-SSLv2 record header - * instead of an SSLv2 message. We have therefore already read 3 bytes of the payload. - * We need to adjust "fragment_length" to account for the bytes we have already - * read so that we will only attempt to read the remainder of the payload on - * our next call to conn->recv. - */ - POSIX_ENSURE(*fragment_length >= s2n_stuffer_data_available(header_in), S2N_ERR_BAD_MESSAGE); - *fragment_length -= s2n_stuffer_data_available(header_in); - - /* By reading 5 bytes for a standard header we have also read the first - * 3 bytes of the SSLv2 ClientHello message. - * So we now need to parse those three bytes. - * - * The first field of an SSLv2 ClientHello is the msg_type. - * This is always '1', matching the ClientHello msg_type used by later - * handshake messages. - */ - POSIX_GUARD(s2n_stuffer_read_uint8(header_in, record_type)); - - /* - * The second field of an SSLv2 ClientHello is the version. - * - * The protocol version read here will likely not be SSLv2, since we only - * accept SSLv2 ClientHellos offering higher protocol versions. - * See s2n_sslv2_client_hello_parse. - */ - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; - POSIX_GUARD(s2n_stuffer_read_bytes(header_in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - *client_protocol_version = (protocol_version[0] * 10) + protocol_version[1]; - - POSIX_GUARD(s2n_stuffer_reread(header_in)); - return 0; -} - -int s2n_record_header_parse( - struct s2n_connection *conn, - uint8_t *content_type, - uint16_t *fragment_length) -{ - struct s2n_stuffer *in = &conn->header_in; - - S2N_ERROR_IF(s2n_stuffer_data_available(in) < S2N_TLS_RECORD_HEADER_LENGTH, S2N_ERR_BAD_MESSAGE); - - POSIX_GUARD(s2n_stuffer_read_uint8(in, content_type)); - - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - const uint8_t version = (protocol_version[0] * 10) + protocol_version[1]; - /* We record the protocol version in the first record seen by the server for fingerprinting usecases */ - if (!conn->client_hello.record_version_recorded) { - conn->client_hello.legacy_record_version = version; - conn->client_hello.record_version_recorded = 1; - } - - /* https://tools.ietf.org/html/rfc5246#appendix-E.1 states that servers must accept any value {03,XX} as the record - * layer version number for the first TLS record. There is some ambiguity here because the client does not know - * what version to use in the record header prior to receiving the ServerHello. Some client implementations may use - * a garbage value(not {03,XX}) in the ClientHello. - * Choose to be lenient to these clients. After protocol negotiation, we will enforce that all record versions - * match the negotiated version. - */ - - S2N_ERROR_IF(conn->actual_protocol_version_established && S2N_MIN(conn->actual_protocol_version, S2N_TLS12) /* check against legacy record version (1.2) in tls 1.3 */ - != version, - S2N_ERR_BAD_MESSAGE); - - /* Some servers send fragments that are above the maximum length (e.g. - * Openssl 1.0.1), so we don't check if the fragment length is > - * S2N_TLS_MAXIMUM_FRAGMENT_LENGTH. We allow up to 2^16. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *= type=exception - *= reason=Incorrect implementations exist in the wild. Ignoring instead. - *# The length MUST NOT exceed 2^14 bytes. An - *# endpoint that receives a record that exceeds this length MUST - *# terminate the connection with a "record_overflow" alert. - */ - POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length)); - POSIX_GUARD(s2n_stuffer_reread(in)); - - return 0; -} - -/* In TLS 1.3, handle CCS message as unprotected records all the time. - * https://tools.ietf.org/html/rfc8446#section-5 - * - * In TLS 1.2 and TLS 1.3 Alert messages are plaintext or encrypted - * depending on the context of the connection. If we receive an encrypted - * alert, the record type is TLS_APPLICATION_DATA at this point. It will - * be decrypted and processed in s2n_handshake_io. We may receive a - * plaintext alert if we hit an error before the handshake completed - * (like a certificate failed to validate). - * https://tools.ietf.org/html/rfc8446#section-6 - * - * This function is specific to TLS 1.3 to avoid changing the behavior - * of existing interpretation of TLS 1.2 alerts. */ -static bool s2n_is_tls13_plaintext_content(struct s2n_connection *conn, uint8_t content_type) -{ - return conn->actual_protocol_version == S2N_TLS13 && (content_type == TLS_ALERT || content_type == TLS_CHANGE_CIPHER_SPEC); -} - -int s2n_record_parse(struct s2n_connection *conn) -{ - uint8_t content_type = 0; - uint16_t encrypted_length = 0; - POSIX_GUARD(s2n_record_header_parse(conn, &content_type, &encrypted_length)); - - struct s2n_crypto_parameters *current_client_crypto = conn->client; - struct s2n_crypto_parameters *current_server_crypto = conn->server; - if (s2n_is_tls13_plaintext_content(conn, content_type)) { - POSIX_ENSURE_REF(conn->initial); - conn->client = conn->initial; - conn->server = conn->initial; - } - - const struct s2n_cipher_suite *cipher_suite = conn->client->cipher_suite; - uint8_t *implicit_iv = conn->client->client_implicit_iv; - struct s2n_hmac_state *mac = &conn->client->client_record_mac; - uint8_t *sequence_number = conn->client->client_sequence_number; - struct s2n_session_key *session_key = &conn->client->client_key; - - if (conn->mode == S2N_CLIENT) { - cipher_suite = conn->server->cipher_suite; - implicit_iv = conn->server->server_implicit_iv; - mac = &conn->server->server_record_mac; - sequence_number = conn->server->server_sequence_number; - session_key = &conn->server->server_key; - } - - if (s2n_is_tls13_plaintext_content(conn, content_type)) { - conn->client = current_client_crypto; - conn->server = current_server_crypto; - } - - /* The NULL stream cipher MUST NEVER be used for ApplicationData. - * If ApplicationData is unencrypted, we can't trust it. */ - if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { - POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_DECRYPT); - } - - switch (cipher_suite->record_alg->cipher->type) { - case S2N_AEAD: - POSIX_GUARD(s2n_record_parse_aead(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - case S2N_CBC: - POSIX_GUARD(s2n_record_parse_cbc(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - case S2N_COMPOSITE: - POSIX_GUARD(s2n_record_parse_composite(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - case S2N_STREAM: - POSIX_GUARD(s2n_record_parse_stream(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); - break; - default: - POSIX_BAIL(S2N_ERR_CIPHER_TYPE); - break; - } - - return 0; -} - -int s2n_tls13_parse_record_type(struct s2n_stuffer *stuffer, uint8_t *record_type) -{ - uint32_t bytes_left = s2n_stuffer_data_available(stuffer); - - /* From rfc8446 Section 5.4 - * The presence of padding does not change the overall record size - * limitations: the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 - * + 1 octets - * - * Certain versions of Java can generate inner plaintexts with lengths up to - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) - * However, after the padding is stripped, the result will always be no more than - * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 - */ - S2N_ERROR_IF(bytes_left > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - - /* set cursor to the end of the stuffer */ - POSIX_GUARD(s2n_stuffer_skip_read(stuffer, bytes_left)); - - /* Record type should have values greater than zero. - * If zero, treat as padding, keep reading and wiping from the back - * until a non-zero value is found - */ - *record_type = 0; - while (*record_type == 0) { - /* back the cursor by one to read off the last byte */ - POSIX_GUARD(s2n_stuffer_rewind_read(stuffer, 1)); - - /* set the record type */ - POSIX_GUARD(s2n_stuffer_read_uint8(stuffer, record_type)); - - /* wipe the last byte at the end of the stuffer */ - POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1)); - } - - /* only the original plaintext should remain */ - /* now reset the read cursor at where it should be */ - POSIX_GUARD(s2n_stuffer_reread(stuffer)); - - /* Even in the incorrect case above with up to 16 extra bytes, we should never see too much data after unpadding */ - S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); - - return 0; -} - -S2N_RESULT s2n_record_wipe(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->header_in)); - RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->in)); - conn->in_status = ENCRYPTED; - - /* Release the memory in conn->in, which un-taints buffer_in */ - RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); - conn->buffer_in.tainted = false; - - /* Reclaim any memory in buffer_in if possible. - * We want to avoid an expensive shift / copy later if possible. - */ - if (s2n_stuffer_is_consumed(&conn->buffer_in)) { - RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&conn->buffer_in)); - } - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_record_read.h" + +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int s2n_sslv2_record_header_parse( + struct s2n_connection *conn, + uint8_t *record_type, + uint8_t *client_protocol_version, + uint16_t *fragment_length) +{ + struct s2n_stuffer *header_in = &conn->header_in; + + POSIX_ENSURE(s2n_stuffer_data_available(header_in) >= S2N_TLS_RECORD_HEADER_LENGTH, + S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_stuffer_read_uint16(header_in, fragment_length)); + + /* The first bit of the SSLv2 message would usually indicate whether the + * length is 2 bytes long or 3 bytes long. + * See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt + * + * However, s2n-tls only supports SSLv2 for ClientHellos as defined in the + * TLS1.2 RFC. In that case, the first bit must always be set to distinguish + * SSLv2 from non-SSLv2 headers. The length is always 2 bytes. + * See https://datatracker.ietf.org/doc/html/rfc5246#appendix-E.2 + * + * Since the first bit is not actually used to indicate length, we need to + * remove it from the length. + * + *= https://www.rfc-editor.org/rfc/rfc5246#appendix-E.2 + *# msg_length + *# The highest bit MUST be 1; the remaining bits contain the length + *# of the following data in bytes. + */ + POSIX_ENSURE(*fragment_length & S2N_TLS_SSLV2_HEADER_FLAG_UINT16, S2N_ERR_BAD_MESSAGE); + *fragment_length ^= S2N_TLS_SSLV2_HEADER_FLAG_UINT16; + + /* We read 5 bytes into header_in because we expected a standard, non-SSLv2 record header + * instead of an SSLv2 message. We have therefore already read 3 bytes of the payload. + * We need to adjust "fragment_length" to account for the bytes we have already + * read so that we will only attempt to read the remainder of the payload on + * our next call to conn->recv. + */ + POSIX_ENSURE(*fragment_length >= s2n_stuffer_data_available(header_in), S2N_ERR_BAD_MESSAGE); + *fragment_length -= s2n_stuffer_data_available(header_in); + + /* By reading 5 bytes for a standard header we have also read the first + * 3 bytes of the SSLv2 ClientHello message. + * So we now need to parse those three bytes. + * + * The first field of an SSLv2 ClientHello is the msg_type. + * This is always '1', matching the ClientHello msg_type used by later + * handshake messages. + */ + POSIX_GUARD(s2n_stuffer_read_uint8(header_in, record_type)); + + /* + * The second field of an SSLv2 ClientHello is the version. + * + * The protocol version read here will likely not be SSLv2, since we only + * accept SSLv2 ClientHellos offering higher protocol versions. + * See s2n_sslv2_client_hello_parse. + */ + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_read_bytes(header_in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + *client_protocol_version = (protocol_version[0] * 10) + protocol_version[1]; + + POSIX_GUARD(s2n_stuffer_reread(header_in)); + return 0; +} + +int s2n_record_header_parse( + struct s2n_connection *conn, + uint8_t *content_type, + uint16_t *fragment_length) +{ + struct s2n_stuffer *in = &conn->header_in; + + S2N_ERROR_IF(s2n_stuffer_data_available(in) < S2N_TLS_RECORD_HEADER_LENGTH, S2N_ERR_BAD_MESSAGE); + + POSIX_GUARD(s2n_stuffer_read_uint8(in, content_type)); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + const uint8_t version = (protocol_version[0] * 10) + protocol_version[1]; + /* We record the protocol version in the first record seen by the server for fingerprinting usecases */ + if (!conn->client_hello.record_version_recorded) { + conn->client_hello.legacy_record_version = version; + conn->client_hello.record_version_recorded = 1; + } + + /* https://tools.ietf.org/html/rfc5246#appendix-E.1 states that servers must accept any value {03,XX} as the record + * layer version number for the first TLS record. There is some ambiguity here because the client does not know + * what version to use in the record header prior to receiving the ServerHello. Some client implementations may use + * a garbage value(not {03,XX}) in the ClientHello. + * Choose to be lenient to these clients. After protocol negotiation, we will enforce that all record versions + * match the negotiated version. + */ + + S2N_ERROR_IF(conn->actual_protocol_version_established && S2N_MIN(conn->actual_protocol_version, S2N_TLS12) /* check against legacy record version (1.2) in tls 1.3 */ + != version, + S2N_ERR_BAD_MESSAGE); + + /* Some servers send fragments that are above the maximum length (e.g. + * Openssl 1.0.1), so we don't check if the fragment length is > + * S2N_TLS_MAXIMUM_FRAGMENT_LENGTH. We allow up to 2^16. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *= type=exception + *= reason=Incorrect implementations exist in the wild. Ignoring instead. + *# The length MUST NOT exceed 2^14 bytes. An + *# endpoint that receives a record that exceeds this length MUST + *# terminate the connection with a "record_overflow" alert. + */ + POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length)); + POSIX_GUARD(s2n_stuffer_reread(in)); + + return 0; +} + +/* In TLS 1.3, handle CCS message as unprotected records all the time. + * https://tools.ietf.org/html/rfc8446#section-5 + * + * In TLS 1.2 and TLS 1.3 Alert messages are plaintext or encrypted + * depending on the context of the connection. If we receive an encrypted + * alert, the record type is TLS_APPLICATION_DATA at this point. It will + * be decrypted and processed in s2n_handshake_io. We may receive a + * plaintext alert if we hit an error before the handshake completed + * (like a certificate failed to validate). + * https://tools.ietf.org/html/rfc8446#section-6 + * + * This function is specific to TLS 1.3 to avoid changing the behavior + * of existing interpretation of TLS 1.2 alerts. */ +static bool s2n_is_tls13_plaintext_content(struct s2n_connection *conn, uint8_t content_type) +{ + return conn->actual_protocol_version == S2N_TLS13 && (content_type == TLS_ALERT || content_type == TLS_CHANGE_CIPHER_SPEC); +} + +int s2n_record_parse(struct s2n_connection *conn) +{ + uint8_t content_type = 0; + uint16_t encrypted_length = 0; + POSIX_GUARD(s2n_record_header_parse(conn, &content_type, &encrypted_length)); + + struct s2n_crypto_parameters *current_client_crypto = conn->client; + struct s2n_crypto_parameters *current_server_crypto = conn->server; + if (s2n_is_tls13_plaintext_content(conn, content_type)) { + POSIX_ENSURE_REF(conn->initial); + conn->client = conn->initial; + conn->server = conn->initial; + } + + const struct s2n_cipher_suite *cipher_suite = conn->client->cipher_suite; + uint8_t *implicit_iv = conn->client->client_implicit_iv; + struct s2n_hmac_state *mac = &conn->client->client_record_mac; + uint8_t *sequence_number = conn->client->client_sequence_number; + struct s2n_session_key *session_key = &conn->client->client_key; + + if (conn->mode == S2N_CLIENT) { + cipher_suite = conn->server->cipher_suite; + implicit_iv = conn->server->server_implicit_iv; + mac = &conn->server->server_record_mac; + sequence_number = conn->server->server_sequence_number; + session_key = &conn->server->server_key; + } + + if (s2n_is_tls13_plaintext_content(conn, content_type)) { + conn->client = current_client_crypto; + conn->server = current_server_crypto; + } + + /* The NULL stream cipher MUST NEVER be used for ApplicationData. + * If ApplicationData is unencrypted, we can't trust it. */ + if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { + POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_DECRYPT); + } + + switch (cipher_suite->record_alg->cipher->type) { + case S2N_AEAD: + POSIX_GUARD(s2n_record_parse_aead(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + case S2N_CBC: + POSIX_GUARD(s2n_record_parse_cbc(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + case S2N_COMPOSITE: + POSIX_GUARD(s2n_record_parse_composite(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + case S2N_STREAM: + POSIX_GUARD(s2n_record_parse_stream(cipher_suite, conn, content_type, encrypted_length, implicit_iv, mac, sequence_number, session_key)); + break; + default: + POSIX_BAIL(S2N_ERR_CIPHER_TYPE); + break; + } + + return 0; +} + +int s2n_tls13_parse_record_type(struct s2n_stuffer *stuffer, uint8_t *record_type) +{ + uint32_t bytes_left = s2n_stuffer_data_available(stuffer); + + /* From rfc8446 Section 5.4 + * The presence of padding does not change the overall record size + * limitations: the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 + * + 1 octets + * + * Certain versions of Java can generate inner plaintexts with lengths up to + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) + * However, after the padding is stripped, the result will always be no more than + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 + */ + S2N_ERROR_IF(bytes_left > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + /* set cursor to the end of the stuffer */ + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, bytes_left)); + + /* Record type should have values greater than zero. + * If zero, treat as padding, keep reading and wiping from the back + * until a non-zero value is found + */ + *record_type = 0; + while (*record_type == 0) { + /* back the cursor by one to read off the last byte */ + POSIX_GUARD(s2n_stuffer_rewind_read(stuffer, 1)); + + /* set the record type */ + POSIX_GUARD(s2n_stuffer_read_uint8(stuffer, record_type)); + + /* wipe the last byte at the end of the stuffer */ + POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1)); + } + + /* only the original plaintext should remain */ + /* now reset the read cursor at where it should be */ + POSIX_GUARD(s2n_stuffer_reread(stuffer)); + + /* Even in the incorrect case above with up to 16 extra bytes, we should never see too much data after unpadding */ + S2N_ERROR_IF(s2n_stuffer_data_available(stuffer) > S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1, S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + return 0; +} + +S2N_RESULT s2n_record_wipe(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->header_in)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->in)); + conn->in_status = ENCRYPTED; + + /* Release the memory in conn->in, which un-taints buffer_in */ + RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); + conn->buffer_in.tainted = false; + + /* Reclaim any memory in buffer_in if possible. + * We want to avoid an expensive shift / copy later if possible. + */ + if (s2n_stuffer_is_consumed(&conn->buffer_in)) { + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&conn->buffer_in)); + } + return S2N_RESULT_OK; +} diff --git a/tls/s2n_record_read_stream.c b/tls/s2n_record_read_stream.c index 6117a2b22d5..90a4aec9a3b 100644 --- a/tls/s2n_record_read_stream.c +++ b/tls/s2n_record_read_stream.c @@ -1,94 +1,94 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_sequence.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_record_read.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_safety.h" - -int s2n_record_parse_stream( - const struct s2n_cipher_suite *cipher_suite, - struct s2n_connection *conn, - uint8_t content_type, - uint16_t encrypted_length, - uint8_t *implicit_iv, - struct s2n_hmac_state *mac, - uint8_t *sequence_number, - struct s2n_session_key *session_key) -{ - /* Add the header to the HMAC */ - uint8_t *header = s2n_stuffer_raw_read(&conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH); - POSIX_ENSURE_REF(header); - - struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_read(&conn->in, encrypted_length) }; - POSIX_ENSURE_REF(en.data); - - uint16_t payload_length = encrypted_length; - uint8_t mac_digest_size = 0; - POSIX_GUARD(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); - - POSIX_ENSURE_GTE(payload_length, mac_digest_size); - payload_length -= mac_digest_size; - - /* Decrypt stuff! */ - POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.decrypt(session_key, &en, &en)); - - /* Update the MAC */ - header[3] = (payload_length >> 8); - header[4] = payload_length & 0xff; - POSIX_GUARD(s2n_hmac_reset(mac)); - POSIX_GUARD(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - - if (conn->actual_protocol_version == S2N_SSLv3) { - POSIX_GUARD(s2n_hmac_update(mac, header, 1)); - POSIX_GUARD(s2n_hmac_update(mac, header + 3, 2)); - } else { - POSIX_GUARD(s2n_hmac_update(mac, header, S2N_TLS_RECORD_HEADER_LENGTH)); - } - - struct s2n_blob seq = { .data = sequence_number, .size = S2N_TLS_SEQUENCE_NUM_LEN }; - POSIX_GUARD(s2n_increment_sequence_number(&seq)); - - /* MAC check for streaming ciphers - no padding */ - POSIX_GUARD(s2n_hmac_update(mac, en.data, payload_length)); - - uint8_t check_digest[S2N_MAX_DIGEST_LEN]; - POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); - POSIX_GUARD(s2n_hmac_digest(mac, check_digest, mac_digest_size)); - - if (s2n_hmac_digest_verify(en.data + payload_length, check_digest, mac_digest_size) < 0) { - POSIX_BAIL(S2N_ERR_BAD_MESSAGE); - } - - /* O.k., we've successfully read and decrypted the record, now we need to align the stuffer - * for reading the plaintext data. - */ - POSIX_GUARD(s2n_stuffer_reread(&conn->in)); - POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); - - /* Truncate and wipe the MAC and any padding */ - POSIX_GUARD(s2n_stuffer_wipe_n(&conn->in, s2n_stuffer_data_available(&conn->in) - payload_length)); - conn->in_status = PLAINTEXT; - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_record_read.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int s2n_record_parse_stream( + const struct s2n_cipher_suite *cipher_suite, + struct s2n_connection *conn, + uint8_t content_type, + uint16_t encrypted_length, + uint8_t *implicit_iv, + struct s2n_hmac_state *mac, + uint8_t *sequence_number, + struct s2n_session_key *session_key) +{ + /* Add the header to the HMAC */ + uint8_t *header = s2n_stuffer_raw_read(&conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH); + POSIX_ENSURE_REF(header); + + struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_read(&conn->in, encrypted_length) }; + POSIX_ENSURE_REF(en.data); + + uint16_t payload_length = encrypted_length; + uint8_t mac_digest_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); + + POSIX_ENSURE_GTE(payload_length, mac_digest_size); + payload_length -= mac_digest_size; + + /* Decrypt stuff! */ + POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.decrypt(session_key, &en, &en)); + + /* Update the MAC */ + header[3] = (payload_length >> 8); + header[4] = payload_length & 0xff; + POSIX_GUARD(s2n_hmac_reset(mac)); + POSIX_GUARD(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + if (conn->actual_protocol_version == S2N_SSLv3) { + POSIX_GUARD(s2n_hmac_update(mac, header, 1)); + POSIX_GUARD(s2n_hmac_update(mac, header + 3, 2)); + } else { + POSIX_GUARD(s2n_hmac_update(mac, header, S2N_TLS_RECORD_HEADER_LENGTH)); + } + + struct s2n_blob seq = { .data = sequence_number, .size = S2N_TLS_SEQUENCE_NUM_LEN }; + POSIX_GUARD(s2n_increment_sequence_number(&seq)); + + /* MAC check for streaming ciphers - no padding */ + POSIX_GUARD(s2n_hmac_update(mac, en.data, payload_length)); + + uint8_t check_digest[S2N_MAX_DIGEST_LEN]; + POSIX_ENSURE_LTE(mac_digest_size, sizeof(check_digest)); + POSIX_GUARD(s2n_hmac_digest(mac, check_digest, mac_digest_size)); + + if (s2n_hmac_digest_verify(en.data + payload_length, check_digest, mac_digest_size) < 0) { + POSIX_BAIL(S2N_ERR_BAD_MESSAGE); + } + + /* O.k., we've successfully read and decrypted the record, now we need to align the stuffer + * for reading the plaintext data. + */ + POSIX_GUARD(s2n_stuffer_reread(&conn->in)); + POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); + + /* Truncate and wipe the MAC and any padding */ + POSIX_GUARD(s2n_stuffer_wipe_n(&conn->in, s2n_stuffer_data_available(&conn->in) - payload_length)); + conn->in_status = PLAINTEXT; + + return 0; +} diff --git a/tls/s2n_record_write.c b/tls/s2n_record_write.c index 1916fb1b955..958f144693b 100644 --- a/tls/s2n_record_write.c +++ b/tls/s2n_record_write.c @@ -1,643 +1,643 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "crypto/s2n_cipher.h" -#include "crypto/s2n_hmac.h" -#include "crypto/s2n_sequence.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_record.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -extern uint8_t s2n_unknown_protocol_version; - -/* In TLS1.3 the record type is obfuscated as APPLICATION_DATA once the handshake begins to be encrypted. - * The real record type is encrypted and written in the final byte of the record. - * In TLS1.2 the record type is always cleartext. */ -#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type) - -/* How much overhead does the IV, MAC, TAG and padding bytes introduce ? */ -static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_MUT(out); - struct s2n_crypto_parameters *active = conn->server; - - if (conn->mode == S2N_CLIENT) { - active = conn->client; - } - - uint8_t extra = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(active->cipher_suite->record_alg->hmac_alg, &extra)); - - if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { - /* Subtract one for the padding length byte */ - extra += 1; - - if (conn->actual_protocol_version > S2N_TLS10) { - extra += active->cipher_suite->record_alg->cipher->io.cbc.record_iv_size; - } - } else if (active->cipher_suite->record_alg->cipher->type == S2N_AEAD) { - extra += active->cipher_suite->record_alg->cipher->io.aead.tag_size; - extra += active->cipher_suite->record_alg->cipher->io.aead.record_iv_size; - } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE && conn->actual_protocol_version > S2N_TLS10) { - extra += active->cipher_suite->record_alg->cipher->io.comp.record_iv_size; - } - - *out = extra; - - return S2N_RESULT_OK; -} - -/* This function returns maximum size of plaintext data to write for the payload. - * Record overheads are not included here. - */ -S2N_RESULT s2n_record_max_write_payload_size(struct s2n_connection *conn, uint16_t *max_fragment_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_MUT(max_fragment_size); - RESULT_ENSURE(conn->max_outgoing_fragment_length > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - - *max_fragment_size = S2N_MIN(conn->max_outgoing_fragment_length, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); - - /* If a custom send buffer is configured, ensure it will be large enough for the payload. - * That may mean we need a smaller fragment size. - */ - uint32_t send_buffer_override = conn->config->send_buffer_size_override; - if (send_buffer_override) { - uint16_t max_record_size = 0; - RESULT_GUARD(s2n_record_max_write_size(conn, *max_fragment_size, &max_record_size)); - if (send_buffer_override < max_record_size) { - size_t overhead = (max_record_size - *max_fragment_size); - RESULT_ENSURE_GT(send_buffer_override, overhead); - *max_fragment_size = send_buffer_override - overhead; - } - } - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_record_max_write_size(struct s2n_connection *conn, uint16_t max_fragment_size, uint16_t *max_record_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_MUT(max_record_size); - - if (!IS_NEGOTIATED(conn)) { - *max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(max_fragment_size); - } else if (conn->actual_protocol_version < S2N_TLS13) { - *max_record_size = S2N_TLS12_MAX_RECORD_LEN_FOR(max_fragment_size); - } else { - *max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(max_fragment_size); - } - return S2N_RESULT_OK; -} - -/* Find the largest size that will fit within an ethernet frame for a "small" payload */ -S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16_t *payload_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_MUT(payload_size); - - /* remove ethernet, TCP/IP and TLS header overheads */ - const uint16_t min_outgoing_fragment_length = ETH_MTU - (conn->ipv6 ? IP_V6_HEADER_LENGTH : IP_V4_HEADER_LENGTH) - - TCP_HEADER_LENGTH - TCP_OPTIONS_LENGTH - S2N_TLS_RECORD_HEADER_LENGTH; - - RESULT_ENSURE(min_outgoing_fragment_length <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - uint16_t size = min_outgoing_fragment_length; - - const struct s2n_crypto_parameters *active = conn->mode == S2N_CLIENT ? conn->client : conn->server; - - /* Round the fragment size down to be block aligned */ - if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { - size -= size % active->cipher_suite->record_alg->cipher->io.cbc.block_size; - } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - size -= size % active->cipher_suite->record_alg->cipher->io.comp.block_size; - /* Composite digest length */ - size -= active->cipher_suite->record_alg->cipher->io.comp.mac_key_size; - /* Padding length byte */ - size -= 1; - } - - /* If TLS1.3, remove content type */ - if (conn->actual_protocol_version >= S2N_TLS13) { - RESULT_ENSURE(size > S2N_TLS_CONTENT_TYPE_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - size -= S2N_TLS_CONTENT_TYPE_LENGTH; - } - - /* subtract overheads of a TLS record */ - uint16_t overhead = 0; - RESULT_GUARD(s2n_tls_record_overhead(conn, &overhead)); - RESULT_ENSURE(size > overhead, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - size -= overhead; - - RESULT_ENSURE(size > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); - RESULT_ENSURE(size <= ETH_MTU, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - - *payload_size = size; - - return S2N_RESULT_OK; -} - -int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out) -{ - uint8_t record_protocol_version = conn->actual_protocol_version; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *# This version value is historical, deriving from the use of 0x0301 for - *# TLS 1.0 and 0x0300 for SSL 3.0. In order to maximize backward - *# compatibility, a record containing an initial ClientHello SHOULD have - *# version 0x0301 (reflecting TLS 1.0) - * - * We set actual_protocol_version early for clients, but we do not - * use that assumed value here in case we are talking to a legacy - * server that expects TLS1.0. - * - * Both TLS 1.3 early data and a deserialized connection will - * send data without the server_protocol_version being known. However, - * the record type would be set to APPLICATION_DATA in their cases - * so this check is avoided. - **/ - if (conn->server_protocol_version == s2n_unknown_protocol_version - && record_type == TLS_HANDSHAKE) { - record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS10); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *# legacy_record_version: MUST be set to 0x0303 for all records - *# generated by a TLS 1.3 implementation other than an initial - *# ClientHello (i.e., one not generated after a HelloRetryRequest), - *# where it MAY also be 0x0301 for compatibility purposes. - **/ - record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS12); - - /* Never send an empty protocol version. - * If the protocol version is unknown, default to TLS1.0 like we do for initial ClientHellos. - */ - if (record_protocol_version == s2n_unknown_protocol_version) { - record_protocol_version = S2N_TLS10; - } - - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - protocol_version[0] = record_protocol_version / 10; - protocol_version[1] = record_protocol_version % 10; - - POSIX_GUARD(s2n_stuffer_write_bytes(out, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - - return 0; -} - -static inline int s2n_record_encrypt( - struct s2n_connection *conn, - const struct s2n_cipher_suite *cipher_suite, - struct s2n_session_key *session_key, - struct s2n_blob *iv, - struct s2n_blob *aad, - struct s2n_blob *en, - uint8_t *implicit_iv, uint16_t block_size) -{ - POSIX_ENSURE_REF(en->data); - - switch (cipher_suite->record_alg->cipher->type) { - case S2N_STREAM: - POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.encrypt(session_key, en, en)); - break; - case S2N_CBC: - POSIX_GUARD(cipher_suite->record_alg->cipher->io.cbc.encrypt(session_key, iv, en, en)); - - /* Copy the last encrypted block to be the next IV */ - if (conn->actual_protocol_version < S2N_TLS11) { - POSIX_ENSURE_GTE(en->size, block_size); - POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); - } - break; - case S2N_AEAD: - POSIX_GUARD(cipher_suite->record_alg->cipher->io.aead.encrypt(session_key, iv, aad, en, en)); - break; - case S2N_COMPOSITE: - /* This will: compute mac, append padding, append padding length, and encrypt */ - POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.encrypt(session_key, iv, en, en)); - - /* Copy the last encrypted block to be the next IV */ - POSIX_ENSURE_GTE(en->size, block_size); - POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); - break; - default: - POSIX_BAIL(S2N_ERR_CIPHER_TYPE); - break; - } - - return 0; -} - -static S2N_RESULT s2n_record_write_mac(struct s2n_connection *conn, struct s2n_blob *record_header, - struct s2n_blob *plaintext, struct s2n_stuffer *out, uint32_t *bytes_written) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->server); - RESULT_ENSURE_REF(conn->client); - RESULT_ENSURE_REF(record_header); - RESULT_ENSURE_REF(plaintext); - RESULT_ENSURE_REF(out); - RESULT_ENSURE_REF(bytes_written); - *bytes_written = 0; - - struct s2n_hmac_state *mac = &conn->server->server_record_mac; - const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; - uint8_t *sequence_number = conn->server->server_sequence_number; - - if (conn->mode == S2N_CLIENT) { - mac = &conn->client->client_record_mac; - cipher_suite = conn->client->cipher_suite; - sequence_number = conn->client->client_sequence_number; - } - - RESULT_ENSURE_REF(cipher_suite); - RESULT_ENSURE_REF(cipher_suite->record_alg); - - if (cipher_suite->record_alg->hmac_alg == S2N_HMAC_NONE) { - /* If the S2N_HMAC_NONE algorithm is specified, a MAC should not be explicitly written. - * This is the case for AEAD and Composite cipher types, where the MAC is written as part - * of encryption. This is also the case for plaintext handshake records, where the null - * stream cipher is used. - */ - return S2N_RESULT_OK; - } - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# The MAC is generated as: - *# - *# MAC(MAC_write_key, seq_num + - */ - RESULT_GUARD_POSIX(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - - struct s2n_stuffer header_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&header_stuffer, record_header)); - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.type + - */ - void *record_type_byte = s2n_stuffer_raw_read(&header_stuffer, sizeof(uint8_t)); - RESULT_ENSURE_REF(record_type_byte); - RESULT_GUARD_POSIX(s2n_hmac_update(mac, record_type_byte, sizeof(uint8_t))); - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.version + - */ - void *protocol_version_bytes = s2n_stuffer_raw_read(&header_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN); - RESULT_ENSURE_REF(protocol_version_bytes); - if (conn->actual_protocol_version > S2N_SSLv3) { - /* SSLv3 doesn't include the protocol version in the MAC. */ - RESULT_GUARD_POSIX(s2n_hmac_update(mac, protocol_version_bytes, S2N_TLS_PROTOCOL_VERSION_LEN)); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.length + - * - * Note that the length field refers to the length of the plaintext content, not the length of - * TLSCiphertext fragment written to the record header, which accounts for additional fields - * such as the padding and MAC. - */ - uint8_t content_length_bytes[sizeof(uint16_t)] = { 0 }; - struct s2n_blob content_length_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&content_length_blob, content_length_bytes, sizeof(content_length_bytes))); - struct s2n_stuffer content_length_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&content_length_stuffer, &content_length_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&content_length_stuffer, plaintext->size)); - RESULT_GUARD_POSIX(s2n_hmac_update(mac, content_length_bytes, sizeof(content_length_bytes))); - - /** - *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 - *# TLSCompressed.fragment); - *# - *# where "+" denotes concatenation. - */ - RESULT_GUARD_POSIX(s2n_hmac_update(mac, plaintext->data, plaintext->size)); - - uint8_t mac_digest_size = 0; - RESULT_GUARD_POSIX(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); - uint8_t *digest = s2n_stuffer_raw_write(out, mac_digest_size); - RESULT_ENSURE_REF(digest); - RESULT_GUARD_POSIX(s2n_hmac_digest(mac, digest, mac_digest_size)); - *bytes_written = mac_digest_size; - - RESULT_GUARD_POSIX(s2n_hmac_reset(mac)); - - return S2N_RESULT_OK; -} - -int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const struct iovec *in, int in_count, size_t offs, size_t to_write) -{ - if (conn->ktls_send_enabled) { - return s2n_ktls_record_writev(conn, content_type, in, in_count, offs, to_write); - } - - struct s2n_blob iv = { 0 }; - uint8_t padding = 0; - uint16_t block_size = 0; - uint8_t aad_iv[S2N_TLS_MAX_IV_LEN] = { 0 }; - - /* In TLS 1.3, handle CCS message as unprotected records */ - struct s2n_crypto_parameters *current_client_crypto = conn->client; - struct s2n_crypto_parameters *current_server_crypto = conn->server; - if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { - POSIX_ENSURE_REF(conn->initial); - conn->client = conn->initial; - conn->server = conn->initial; - } - - uint8_t *sequence_number = conn->server->server_sequence_number; - struct s2n_session_key *session_key = &conn->server->server_key; - const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; - uint8_t *implicit_iv = conn->server->server_implicit_iv; - - if (conn->mode == S2N_CLIENT) { - sequence_number = conn->client->client_sequence_number; - session_key = &conn->client->client_key; - cipher_suite = conn->client->cipher_suite; - implicit_iv = conn->client->client_implicit_iv; - } - - /* The NULL stream cipher MUST NEVER be used for ApplicationData. - * Writing ApplicationData unencrypted defeats the purpose of TLS. */ - if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { - POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_ENCRYPT); - } - - const int is_tls13_record = cipher_suite->record_alg->flags & S2N_TLS13_RECORD_AEAD_NONCE; - s2n_stack_blob(aad, is_tls13_record ? S2N_TLS13_AAD_LEN : S2N_TLS_MAX_AAD_LEN, S2N_TLS_MAX_AAD_LEN); - - /* If we aren't buffering multiple records, then the output stuffer should be empty. */ - if (!conn->multirecord_send) { - POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - } - - /* Before we do anything, we need to figure out what the length of the - * fragment is going to be. - */ - uint16_t max_write_payload_size = 0; - POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_write_payload_size)); - const uint16_t data_bytes_to_take = S2N_MIN(to_write, max_write_payload_size); - - uint16_t extra = 0; - POSIX_GUARD_RESULT(s2n_tls_record_overhead(conn, &extra)); - - /* If we have padding to worry about, figure that out too */ - if (cipher_suite->record_alg->cipher->type == S2N_CBC) { - block_size = cipher_suite->record_alg->cipher->io.cbc.block_size; - if (((data_bytes_to_take + extra) % block_size)) { - padding = block_size - ((data_bytes_to_take + extra) % block_size); - } - } else if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - block_size = cipher_suite->record_alg->cipher->io.comp.block_size; - } - - if (s2n_stuffer_is_freed(&conn->out)) { - /* If the output buffer has not been allocated yet, allocate - * at least enough memory to hold a record with the local maximum fragment length. - * - * The local maximum fragment length is: - * 1) The local default configured for new connections - * 2) The local value set by the user via s2n_connection_prefer_throughput() - * or s2n_connection_prefer_low_latency() - * 3) On the server, the minimum of the local value and the value negotiated with the - * client via the max_fragment_length extension - * - * Because this only occurs if the output buffer has not been allocated, - * it does NOT resize existing buffers. - */ - uint16_t max_wire_record_size = 0; - POSIX_GUARD_RESULT(s2n_record_max_write_size(conn, max_write_payload_size, &max_wire_record_size)); - - uint32_t buffer_size = S2N_MAX(conn->config->send_buffer_size_override, max_wire_record_size); - POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); - } - - /* A record only local stuffer used to avoid tainting the conn->out stuffer or overwriting - * previous records. It should be used to add an individual record to the out stuffer. - */ - struct s2n_blob record_blob = { 0 }; - struct s2n_stuffer record_stuffer = { 0 }; - POSIX_GUARD(s2n_blob_init(&record_blob, - conn->out.blob.data + conn->out.write_cursor, - s2n_stuffer_space_remaining(&conn->out))); - POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob)); - - /* Now that we know the length, start writing the record */ - uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type); - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type)); - POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer)); - - /* Compute non-payload parts of the MAC(seq num, type, proto vers, fragment length) for composite ciphers. - * Composite "encrypt" will MAC the payload data and fill in padding. - */ - if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - /* Only fragment length is needed for MAC, but the EVP ctrl function needs fragment length + eiv len. */ - uint16_t payload_and_eiv_len = data_bytes_to_take; - if (conn->actual_protocol_version > S2N_TLS10) { - payload_and_eiv_len += block_size; - } - - /* Outputs number of extra bytes required for MAC and padding */ - int pad_and_mac_len = 0; - POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.initial_hmac(session_key, sequence_number, content_type, conn->actual_protocol_version, - payload_and_eiv_len, &pad_and_mac_len)); - extra += pad_and_mac_len; - } - - /* TLS 1.3 protected record occupies one extra byte for content type */ - if (is_tls13_record) { - extra += S2N_TLS_CONTENT_TYPE_LENGTH; - } - - /* Rewrite the length to be the actual fragment length */ - const uint16_t actual_fragment_length = data_bytes_to_take + padding + extra; - /* ensure actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH <= max record length */ - const uint16_t max_record_length = is_tls13_record ? S2N_TLS13_MAXIMUM_RECORD_LENGTH : S2N_TLS_MAXIMUM_RECORD_LENGTH; - S2N_ERROR_IF(actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH > max_record_length, S2N_ERR_RECORD_LENGTH_TOO_LARGE); - POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, actual_fragment_length)); - - /* If we're AEAD, write the sequence number as an IV, and generate the AAD */ - if (cipher_suite->record_alg->cipher->type == S2N_AEAD) { - struct s2n_stuffer iv_stuffer = { 0 }; - POSIX_GUARD(s2n_blob_init(&iv, aad_iv, sizeof(aad_iv))); - POSIX_GUARD(s2n_stuffer_init(&iv_stuffer, &iv)); - - if (cipher_suite->record_alg->flags & S2N_TLS12_AES_GCM_AEAD_NONCE) { - /* Partially explicit nonce. See RFC 5288 Section 3 */ - POSIX_GUARD(s2n_stuffer_write_bytes(&record_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, implicit_iv, cipher_suite->record_alg->cipher->io.aead.fixed_iv_size)); - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - } else if (cipher_suite->record_alg->flags & S2N_TLS12_CHACHA_POLY_AEAD_NONCE || is_tls13_record) { - /* Fully implicit nonce. See RFC7905 Section 2 */ - uint8_t four_zeroes[4] = { 0 }; - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, four_zeroes, 4)); - POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - for (int i = 0; i < cipher_suite->record_alg->cipher->io.aead.fixed_iv_size; i++) { - aad_iv[i] = aad_iv[i] ^ implicit_iv[i]; - } - } else { - POSIX_BAIL(S2N_ERR_INVALID_NONCE_TYPE); - } - - /* Set the IV size to the amount of data written */ - iv.size = s2n_stuffer_data_available(&iv_stuffer); - if (is_tls13_record) { - POSIX_GUARD_RESULT(s2n_tls13_aead_aad_init(data_bytes_to_take + S2N_TLS_CONTENT_TYPE_LENGTH, cipher_suite->record_alg->cipher->io.aead.tag_size, &aad)); - } else { - POSIX_GUARD_RESULT(s2n_aead_aad_init(conn, sequence_number, content_type, data_bytes_to_take, &aad)); - } - } else if (cipher_suite->record_alg->cipher->type == S2N_CBC || cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - POSIX_GUARD(s2n_blob_init(&iv, implicit_iv, block_size)); - - /* For TLS1.1/1.2; write the IV with random data */ - if (conn->actual_protocol_version > S2N_TLS10) { - POSIX_GUARD_RESULT(s2n_get_public_random_data(&iv)); - if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { - /* Write a separate random block to the record. This will be used along with the previously generated - * iv blob to generate the final explicit_iv for this record. - * - * How? Openssl's AES-CBC stitched encrypt populates the first block of application data with: - * AES(Key, XOR(iv, initial_block)) - * - * If we make initial_block a random block unrelated to random_iv, explicit IV for this record - * is random value based on the two random blobs we just generated: - * AES(Key, XOR(random_iv, explicit_iv_placeholder) == AES(Key, XOR(random_iv, random_iv2)) - * - * NOTE: We can't use the same random IV blob as both the initial block and IV since it will result in: - * AES(Key, XOR(random_iv, random_iv)) == AES(Key, 0), which will be shared by all records in this session. - */ - struct s2n_blob explicit_iv_placeholder = { 0 }; - uint8_t zero_block[S2N_TLS_MAX_IV_LEN] = { 0 }; - POSIX_GUARD(s2n_blob_init(&explicit_iv_placeholder, zero_block, block_size)); - POSIX_GUARD_RESULT(s2n_get_public_random_data(&explicit_iv_placeholder)); - POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &explicit_iv_placeholder)); - } else { - /* We can write the explicit IV directly to the record for non composite CBC because - * s2n starts AES *after* the explicit IV. - */ - POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &iv)); - } - } - } - - /* Write the plaintext data */ - POSIX_GUARD(s2n_stuffer_writev_bytes(&record_stuffer, in, in_count, offs, data_bytes_to_take)); - void *orig_write_ptr = record_stuffer.blob.data + record_stuffer.write_cursor - data_bytes_to_take; - - /* Write the MAC */ - struct s2n_blob header_blob = { 0 }; - POSIX_GUARD(s2n_blob_slice(&record_blob, &header_blob, 0, S2N_TLS_RECORD_HEADER_LENGTH)); - struct s2n_blob plaintext_blob = { 0 }; - POSIX_GUARD(s2n_blob_init(&plaintext_blob, orig_write_ptr, data_bytes_to_take)); - uint32_t mac_digest_size = 0; - POSIX_GUARD_RESULT(s2n_record_write_mac(conn, &header_blob, &plaintext_blob, &record_stuffer, &mac_digest_size)); - - /* We are done with this sequence number, so we can increment it */ - struct s2n_blob seq = { 0 }; - POSIX_GUARD(s2n_blob_init(&seq, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); - POSIX_GUARD(s2n_increment_sequence_number(&seq)); - - /* Write content type for TLS 1.3 record (RFC 8446 Section 5.2) */ - if (is_tls13_record) { - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, content_type)); - } - - if (cipher_suite->record_alg->cipher->type == S2N_CBC) { - /* Include padding bytes, each with the value 'p', and - * include an extra padding length byte, also with the value 'p'. - */ - for (int i = 0; i <= padding; i++) { - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, padding)); - } - } - - /* Rewind to rewrite/encrypt the packet */ - POSIX_GUARD(s2n_stuffer_rewrite(&record_stuffer)); - - /* Skip the header */ - POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, S2N_TLS_RECORD_HEADER_LENGTH)); - - uint16_t encrypted_length = data_bytes_to_take + mac_digest_size; - switch (cipher_suite->record_alg->cipher->type) { - case S2N_AEAD: - POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, cipher_suite->record_alg->cipher->io.aead.record_iv_size)); - encrypted_length += cipher_suite->record_alg->cipher->io.aead.tag_size; - if (is_tls13_record) { - /* one extra byte for content type */ - encrypted_length += S2N_TLS_CONTENT_TYPE_LENGTH; - } - break; - case S2N_CBC: - if (conn->actual_protocol_version > S2N_TLS10) { - /* Leave the IV alone and unencrypted */ - POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, iv.size)); - } - /* Encrypt the padding and the padding length byte too */ - encrypted_length += padding + 1; - break; - case S2N_COMPOSITE: - /* Composite CBC expects a pointer starting at explicit IV: [Explicit IV | fragment | MAC | padding | padding len ] - * extra will account for the explicit IV len(if applicable), MAC digest len, padding len + padding byte. - */ - encrypted_length += extra; - break; - default: - break; - } - - /* Check that stuffer have enough space to write encrypted record, because raw_write cannot expand tainted stuffer */ - S2N_ERROR_IF(s2n_stuffer_space_remaining(&record_stuffer) < encrypted_length, S2N_ERR_RECORD_STUFFER_SIZE); - - /* Do the encryption */ - struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_write(&record_stuffer, encrypted_length) }; - POSIX_GUARD(s2n_record_encrypt(conn, cipher_suite, session_key, &iv, &aad, &en, implicit_iv, block_size)); - - /* Sync the out stuffer write cursor with the record stuffer. */ - POSIX_GUARD(s2n_stuffer_skip_write(&conn->out, s2n_stuffer_data_available(&record_stuffer))); - - if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { - conn->client = current_client_crypto; - conn->server = current_server_crypto; - } - - return data_bytes_to_take; -} - -S2N_RESULT s2n_record_write(struct s2n_connection *conn, uint8_t content_type, struct s2n_blob *in) -{ - struct iovec iov; - iov.iov_base = in->data; - iov.iov_len = in->size; - int written = s2n_record_writev(conn, content_type, &iov, 1, 0, in->size); - RESULT_GUARD_POSIX(written); - RESULT_ENSURE((uint32_t) written == in->size, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * 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 "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_record.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +extern uint8_t s2n_unknown_protocol_version; + +/* In TLS1.3 the record type is obfuscated as APPLICATION_DATA once the handshake begins to be encrypted. + * The real record type is encrypted and written in the final byte of the record. + * In TLS1.2 the record type is always cleartext. */ +#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type) + +/* How much overhead does the IV, MAC, TAG and padding bytes introduce ? */ +static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_MUT(out); + struct s2n_crypto_parameters *active = conn->server; + + if (conn->mode == S2N_CLIENT) { + active = conn->client; + } + + uint8_t extra = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(active->cipher_suite->record_alg->hmac_alg, &extra)); + + if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { + /* Subtract one for the padding length byte */ + extra += 1; + + if (conn->actual_protocol_version > S2N_TLS10) { + extra += active->cipher_suite->record_alg->cipher->io.cbc.record_iv_size; + } + } else if (active->cipher_suite->record_alg->cipher->type == S2N_AEAD) { + extra += active->cipher_suite->record_alg->cipher->io.aead.tag_size; + extra += active->cipher_suite->record_alg->cipher->io.aead.record_iv_size; + } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE && conn->actual_protocol_version > S2N_TLS10) { + extra += active->cipher_suite->record_alg->cipher->io.comp.record_iv_size; + } + + *out = extra; + + return S2N_RESULT_OK; +} + +/* This function returns maximum size of plaintext data to write for the payload. + * Record overheads are not included here. + */ +S2N_RESULT s2n_record_max_write_payload_size(struct s2n_connection *conn, uint16_t *max_fragment_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_MUT(max_fragment_size); + RESULT_ENSURE(conn->max_outgoing_fragment_length > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + + *max_fragment_size = S2N_MIN(conn->max_outgoing_fragment_length, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* If a custom send buffer is configured, ensure it will be large enough for the payload. + * That may mean we need a smaller fragment size. + */ + uint32_t send_buffer_override = conn->config->send_buffer_size_override; + if (send_buffer_override) { + uint16_t max_record_size = 0; + RESULT_GUARD(s2n_record_max_write_size(conn, *max_fragment_size, &max_record_size)); + if (send_buffer_override < max_record_size) { + size_t overhead = (max_record_size - *max_fragment_size); + RESULT_ENSURE_GT(send_buffer_override, overhead); + *max_fragment_size = send_buffer_override - overhead; + } + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_record_max_write_size(struct s2n_connection *conn, uint16_t max_fragment_size, uint16_t *max_record_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_MUT(max_record_size); + + if (!IS_NEGOTIATED(conn)) { + *max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(max_fragment_size); + } else if (conn->actual_protocol_version < S2N_TLS13) { + *max_record_size = S2N_TLS12_MAX_RECORD_LEN_FOR(max_fragment_size); + } else { + *max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(max_fragment_size); + } + return S2N_RESULT_OK; +} + +/* Find the largest size that will fit within an ethernet frame for a "small" payload */ +S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16_t *payload_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_MUT(payload_size); + + /* remove ethernet, TCP/IP and TLS header overheads */ + const uint16_t min_outgoing_fragment_length = ETH_MTU - (conn->ipv6 ? IP_V6_HEADER_LENGTH : IP_V4_HEADER_LENGTH) + - TCP_HEADER_LENGTH - TCP_OPTIONS_LENGTH - S2N_TLS_RECORD_HEADER_LENGTH; + + RESULT_ENSURE(min_outgoing_fragment_length <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + uint16_t size = min_outgoing_fragment_length; + + const struct s2n_crypto_parameters *active = conn->mode == S2N_CLIENT ? conn->client : conn->server; + + /* Round the fragment size down to be block aligned */ + if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) { + size -= size % active->cipher_suite->record_alg->cipher->io.cbc.block_size; + } else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + size -= size % active->cipher_suite->record_alg->cipher->io.comp.block_size; + /* Composite digest length */ + size -= active->cipher_suite->record_alg->cipher->io.comp.mac_key_size; + /* Padding length byte */ + size -= 1; + } + + /* If TLS1.3, remove content type */ + if (conn->actual_protocol_version >= S2N_TLS13) { + RESULT_ENSURE(size > S2N_TLS_CONTENT_TYPE_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + size -= S2N_TLS_CONTENT_TYPE_LENGTH; + } + + /* subtract overheads of a TLS record */ + uint16_t overhead = 0; + RESULT_GUARD(s2n_tls_record_overhead(conn, &overhead)); + RESULT_ENSURE(size > overhead, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + size -= overhead; + + RESULT_ENSURE(size > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + RESULT_ENSURE(size <= ETH_MTU, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + + *payload_size = size; + + return S2N_RESULT_OK; +} + +int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out) +{ + uint8_t record_protocol_version = conn->actual_protocol_version; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *# This version value is historical, deriving from the use of 0x0301 for + *# TLS 1.0 and 0x0300 for SSL 3.0. In order to maximize backward + *# compatibility, a record containing an initial ClientHello SHOULD have + *# version 0x0301 (reflecting TLS 1.0) + * + * We set actual_protocol_version early for clients, but we do not + * use that assumed value here in case we are talking to a legacy + * server that expects TLS1.0. + * + * Both TLS 1.3 early data and a deserialized connection will + * send data without the server_protocol_version being known. However, + * the record type would be set to APPLICATION_DATA in their cases + * so this check is avoided. + **/ + if (conn->server_protocol_version == s2n_unknown_protocol_version + && record_type == TLS_HANDSHAKE) { + record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS10); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *# legacy_record_version: MUST be set to 0x0303 for all records + *# generated by a TLS 1.3 implementation other than an initial + *# ClientHello (i.e., one not generated after a HelloRetryRequest), + *# where it MAY also be 0x0301 for compatibility purposes. + **/ + record_protocol_version = S2N_MIN(record_protocol_version, S2N_TLS12); + + /* Never send an empty protocol version. + * If the protocol version is unknown, default to TLS1.0 like we do for initial ClientHellos. + */ + if (record_protocol_version == s2n_unknown_protocol_version) { + record_protocol_version = S2N_TLS10; + } + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + protocol_version[0] = record_protocol_version / 10; + protocol_version[1] = record_protocol_version % 10; + + POSIX_GUARD(s2n_stuffer_write_bytes(out, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + return 0; +} + +static inline int s2n_record_encrypt( + struct s2n_connection *conn, + const struct s2n_cipher_suite *cipher_suite, + struct s2n_session_key *session_key, + struct s2n_blob *iv, + struct s2n_blob *aad, + struct s2n_blob *en, + uint8_t *implicit_iv, uint16_t block_size) +{ + POSIX_ENSURE_REF(en->data); + + switch (cipher_suite->record_alg->cipher->type) { + case S2N_STREAM: + POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.encrypt(session_key, en, en)); + break; + case S2N_CBC: + POSIX_GUARD(cipher_suite->record_alg->cipher->io.cbc.encrypt(session_key, iv, en, en)); + + /* Copy the last encrypted block to be the next IV */ + if (conn->actual_protocol_version < S2N_TLS11) { + POSIX_ENSURE_GTE(en->size, block_size); + POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); + } + break; + case S2N_AEAD: + POSIX_GUARD(cipher_suite->record_alg->cipher->io.aead.encrypt(session_key, iv, aad, en, en)); + break; + case S2N_COMPOSITE: + /* This will: compute mac, append padding, append padding length, and encrypt */ + POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.encrypt(session_key, iv, en, en)); + + /* Copy the last encrypted block to be the next IV */ + POSIX_ENSURE_GTE(en->size, block_size); + POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size); + break; + default: + POSIX_BAIL(S2N_ERR_CIPHER_TYPE); + break; + } + + return 0; +} + +static S2N_RESULT s2n_record_write_mac(struct s2n_connection *conn, struct s2n_blob *record_header, + struct s2n_blob *plaintext, struct s2n_stuffer *out, uint32_t *bytes_written) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->server); + RESULT_ENSURE_REF(conn->client); + RESULT_ENSURE_REF(record_header); + RESULT_ENSURE_REF(plaintext); + RESULT_ENSURE_REF(out); + RESULT_ENSURE_REF(bytes_written); + *bytes_written = 0; + + struct s2n_hmac_state *mac = &conn->server->server_record_mac; + const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; + uint8_t *sequence_number = conn->server->server_sequence_number; + + if (conn->mode == S2N_CLIENT) { + mac = &conn->client->client_record_mac; + cipher_suite = conn->client->cipher_suite; + sequence_number = conn->client->client_sequence_number; + } + + RESULT_ENSURE_REF(cipher_suite); + RESULT_ENSURE_REF(cipher_suite->record_alg); + + if (cipher_suite->record_alg->hmac_alg == S2N_HMAC_NONE) { + /* If the S2N_HMAC_NONE algorithm is specified, a MAC should not be explicitly written. + * This is the case for AEAD and Composite cipher types, where the MAC is written as part + * of encryption. This is also the case for plaintext handshake records, where the null + * stream cipher is used. + */ + return S2N_RESULT_OK; + } + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# The MAC is generated as: + *# + *# MAC(MAC_write_key, seq_num + + */ + RESULT_GUARD_POSIX(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + struct s2n_stuffer header_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&header_stuffer, record_header)); + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.type + + */ + void *record_type_byte = s2n_stuffer_raw_read(&header_stuffer, sizeof(uint8_t)); + RESULT_ENSURE_REF(record_type_byte); + RESULT_GUARD_POSIX(s2n_hmac_update(mac, record_type_byte, sizeof(uint8_t))); + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.version + + */ + void *protocol_version_bytes = s2n_stuffer_raw_read(&header_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN); + RESULT_ENSURE_REF(protocol_version_bytes); + if (conn->actual_protocol_version > S2N_SSLv3) { + /* SSLv3 doesn't include the protocol version in the MAC. */ + RESULT_GUARD_POSIX(s2n_hmac_update(mac, protocol_version_bytes, S2N_TLS_PROTOCOL_VERSION_LEN)); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.length + + * + * Note that the length field refers to the length of the plaintext content, not the length of + * TLSCiphertext fragment written to the record header, which accounts for additional fields + * such as the padding and MAC. + */ + uint8_t content_length_bytes[sizeof(uint16_t)] = { 0 }; + struct s2n_blob content_length_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&content_length_blob, content_length_bytes, sizeof(content_length_bytes))); + struct s2n_stuffer content_length_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&content_length_stuffer, &content_length_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&content_length_stuffer, plaintext->size)); + RESULT_GUARD_POSIX(s2n_hmac_update(mac, content_length_bytes, sizeof(content_length_bytes))); + + /** + *= https://www.rfc-editor.org/rfc/rfc5246#section-6.2.3.1 + *# TLSCompressed.fragment); + *# + *# where "+" denotes concatenation. + */ + RESULT_GUARD_POSIX(s2n_hmac_update(mac, plaintext->data, plaintext->size)); + + uint8_t mac_digest_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(mac->alg, &mac_digest_size)); + uint8_t *digest = s2n_stuffer_raw_write(out, mac_digest_size); + RESULT_ENSURE_REF(digest); + RESULT_GUARD_POSIX(s2n_hmac_digest(mac, digest, mac_digest_size)); + *bytes_written = mac_digest_size; + + RESULT_GUARD_POSIX(s2n_hmac_reset(mac)); + + return S2N_RESULT_OK; +} + +int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const struct iovec *in, int in_count, size_t offs, size_t to_write) +{ + if (conn->ktls_send_enabled) { + return s2n_ktls_record_writev(conn, content_type, in, in_count, offs, to_write); + } + + struct s2n_blob iv = { 0 }; + uint8_t padding = 0; + uint16_t block_size = 0; + uint8_t aad_iv[S2N_TLS_MAX_IV_LEN] = { 0 }; + + /* In TLS 1.3, handle CCS message as unprotected records */ + struct s2n_crypto_parameters *current_client_crypto = conn->client; + struct s2n_crypto_parameters *current_server_crypto = conn->server; + if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { + POSIX_ENSURE_REF(conn->initial); + conn->client = conn->initial; + conn->server = conn->initial; + } + + uint8_t *sequence_number = conn->server->server_sequence_number; + struct s2n_session_key *session_key = &conn->server->server_key; + const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite; + uint8_t *implicit_iv = conn->server->server_implicit_iv; + + if (conn->mode == S2N_CLIENT) { + sequence_number = conn->client->client_sequence_number; + session_key = &conn->client->client_key; + cipher_suite = conn->client->cipher_suite; + implicit_iv = conn->client->client_implicit_iv; + } + + /* The NULL stream cipher MUST NEVER be used for ApplicationData. + * Writing ApplicationData unencrypted defeats the purpose of TLS. */ + if (cipher_suite->record_alg->cipher == &s2n_null_cipher) { + POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_ENCRYPT); + } + + const int is_tls13_record = cipher_suite->record_alg->flags & S2N_TLS13_RECORD_AEAD_NONCE; + s2n_stack_blob(aad, is_tls13_record ? S2N_TLS13_AAD_LEN : S2N_TLS_MAX_AAD_LEN, S2N_TLS_MAX_AAD_LEN); + + /* If we aren't buffering multiple records, then the output stuffer should be empty. */ + if (!conn->multirecord_send) { + POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + } + + /* Before we do anything, we need to figure out what the length of the + * fragment is going to be. + */ + uint16_t max_write_payload_size = 0; + POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_write_payload_size)); + const uint16_t data_bytes_to_take = S2N_MIN(to_write, max_write_payload_size); + + uint16_t extra = 0; + POSIX_GUARD_RESULT(s2n_tls_record_overhead(conn, &extra)); + + /* If we have padding to worry about, figure that out too */ + if (cipher_suite->record_alg->cipher->type == S2N_CBC) { + block_size = cipher_suite->record_alg->cipher->io.cbc.block_size; + if (((data_bytes_to_take + extra) % block_size)) { + padding = block_size - ((data_bytes_to_take + extra) % block_size); + } + } else if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + block_size = cipher_suite->record_alg->cipher->io.comp.block_size; + } + + if (s2n_stuffer_is_freed(&conn->out)) { + /* If the output buffer has not been allocated yet, allocate + * at least enough memory to hold a record with the local maximum fragment length. + * + * The local maximum fragment length is: + * 1) The local default configured for new connections + * 2) The local value set by the user via s2n_connection_prefer_throughput() + * or s2n_connection_prefer_low_latency() + * 3) On the server, the minimum of the local value and the value negotiated with the + * client via the max_fragment_length extension + * + * Because this only occurs if the output buffer has not been allocated, + * it does NOT resize existing buffers. + */ + uint16_t max_wire_record_size = 0; + POSIX_GUARD_RESULT(s2n_record_max_write_size(conn, max_write_payload_size, &max_wire_record_size)); + + uint32_t buffer_size = S2N_MAX(conn->config->send_buffer_size_override, max_wire_record_size); + POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); + } + + /* A record only local stuffer used to avoid tainting the conn->out stuffer or overwriting + * previous records. It should be used to add an individual record to the out stuffer. + */ + struct s2n_blob record_blob = { 0 }; + struct s2n_stuffer record_stuffer = { 0 }; + POSIX_GUARD(s2n_blob_init(&record_blob, + conn->out.blob.data + conn->out.write_cursor, + s2n_stuffer_space_remaining(&conn->out))); + POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob)); + + /* Now that we know the length, start writing the record */ + uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type); + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type)); + POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer)); + + /* Compute non-payload parts of the MAC(seq num, type, proto vers, fragment length) for composite ciphers. + * Composite "encrypt" will MAC the payload data and fill in padding. + */ + if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + /* Only fragment length is needed for MAC, but the EVP ctrl function needs fragment length + eiv len. */ + uint16_t payload_and_eiv_len = data_bytes_to_take; + if (conn->actual_protocol_version > S2N_TLS10) { + payload_and_eiv_len += block_size; + } + + /* Outputs number of extra bytes required for MAC and padding */ + int pad_and_mac_len = 0; + POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.initial_hmac(session_key, sequence_number, content_type, conn->actual_protocol_version, + payload_and_eiv_len, &pad_and_mac_len)); + extra += pad_and_mac_len; + } + + /* TLS 1.3 protected record occupies one extra byte for content type */ + if (is_tls13_record) { + extra += S2N_TLS_CONTENT_TYPE_LENGTH; + } + + /* Rewrite the length to be the actual fragment length */ + const uint16_t actual_fragment_length = data_bytes_to_take + padding + extra; + /* ensure actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH <= max record length */ + const uint16_t max_record_length = is_tls13_record ? S2N_TLS13_MAXIMUM_RECORD_LENGTH : S2N_TLS_MAXIMUM_RECORD_LENGTH; + S2N_ERROR_IF(actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH > max_record_length, S2N_ERR_RECORD_LENGTH_TOO_LARGE); + POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, actual_fragment_length)); + + /* If we're AEAD, write the sequence number as an IV, and generate the AAD */ + if (cipher_suite->record_alg->cipher->type == S2N_AEAD) { + struct s2n_stuffer iv_stuffer = { 0 }; + POSIX_GUARD(s2n_blob_init(&iv, aad_iv, sizeof(aad_iv))); + POSIX_GUARD(s2n_stuffer_init(&iv_stuffer, &iv)); + + if (cipher_suite->record_alg->flags & S2N_TLS12_AES_GCM_AEAD_NONCE) { + /* Partially explicit nonce. See RFC 5288 Section 3 */ + POSIX_GUARD(s2n_stuffer_write_bytes(&record_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, implicit_iv, cipher_suite->record_alg->cipher->io.aead.fixed_iv_size)); + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + } else if (cipher_suite->record_alg->flags & S2N_TLS12_CHACHA_POLY_AEAD_NONCE || is_tls13_record) { + /* Fully implicit nonce. See RFC7905 Section 2 */ + uint8_t four_zeroes[4] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, four_zeroes, 4)); + POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + for (int i = 0; i < cipher_suite->record_alg->cipher->io.aead.fixed_iv_size; i++) { + aad_iv[i] = aad_iv[i] ^ implicit_iv[i]; + } + } else { + POSIX_BAIL(S2N_ERR_INVALID_NONCE_TYPE); + } + + /* Set the IV size to the amount of data written */ + iv.size = s2n_stuffer_data_available(&iv_stuffer); + if (is_tls13_record) { + POSIX_GUARD_RESULT(s2n_tls13_aead_aad_init(data_bytes_to_take + S2N_TLS_CONTENT_TYPE_LENGTH, cipher_suite->record_alg->cipher->io.aead.tag_size, &aad)); + } else { + POSIX_GUARD_RESULT(s2n_aead_aad_init(conn, sequence_number, content_type, data_bytes_to_take, &aad)); + } + } else if (cipher_suite->record_alg->cipher->type == S2N_CBC || cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + POSIX_GUARD(s2n_blob_init(&iv, implicit_iv, block_size)); + + /* For TLS1.1/1.2; write the IV with random data */ + if (conn->actual_protocol_version > S2N_TLS10) { + POSIX_GUARD_RESULT(s2n_get_public_random_data(&iv)); + if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) { + /* Write a separate random block to the record. This will be used along with the previously generated + * iv blob to generate the final explicit_iv for this record. + * + * How? Openssl's AES-CBC stitched encrypt populates the first block of application data with: + * AES(Key, XOR(iv, initial_block)) + * + * If we make initial_block a random block unrelated to random_iv, explicit IV for this record + * is random value based on the two random blobs we just generated: + * AES(Key, XOR(random_iv, explicit_iv_placeholder) == AES(Key, XOR(random_iv, random_iv2)) + * + * NOTE: We can't use the same random IV blob as both the initial block and IV since it will result in: + * AES(Key, XOR(random_iv, random_iv)) == AES(Key, 0), which will be shared by all records in this session. + */ + struct s2n_blob explicit_iv_placeholder = { 0 }; + uint8_t zero_block[S2N_TLS_MAX_IV_LEN] = { 0 }; + POSIX_GUARD(s2n_blob_init(&explicit_iv_placeholder, zero_block, block_size)); + POSIX_GUARD_RESULT(s2n_get_public_random_data(&explicit_iv_placeholder)); + POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &explicit_iv_placeholder)); + } else { + /* We can write the explicit IV directly to the record for non composite CBC because + * s2n starts AES *after* the explicit IV. + */ + POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &iv)); + } + } + } + + /* Write the plaintext data */ + POSIX_GUARD(s2n_stuffer_writev_bytes(&record_stuffer, in, in_count, offs, data_bytes_to_take)); + void *orig_write_ptr = record_stuffer.blob.data + record_stuffer.write_cursor - data_bytes_to_take; + + /* Write the MAC */ + struct s2n_blob header_blob = { 0 }; + POSIX_GUARD(s2n_blob_slice(&record_blob, &header_blob, 0, S2N_TLS_RECORD_HEADER_LENGTH)); + struct s2n_blob plaintext_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&plaintext_blob, orig_write_ptr, data_bytes_to_take)); + uint32_t mac_digest_size = 0; + POSIX_GUARD_RESULT(s2n_record_write_mac(conn, &header_blob, &plaintext_blob, &record_stuffer, &mac_digest_size)); + + /* We are done with this sequence number, so we can increment it */ + struct s2n_blob seq = { 0 }; + POSIX_GUARD(s2n_blob_init(&seq, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + POSIX_GUARD(s2n_increment_sequence_number(&seq)); + + /* Write content type for TLS 1.3 record (RFC 8446 Section 5.2) */ + if (is_tls13_record) { + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, content_type)); + } + + if (cipher_suite->record_alg->cipher->type == S2N_CBC) { + /* Include padding bytes, each with the value 'p', and + * include an extra padding length byte, also with the value 'p'. + */ + for (int i = 0; i <= padding; i++) { + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, padding)); + } + } + + /* Rewind to rewrite/encrypt the packet */ + POSIX_GUARD(s2n_stuffer_rewrite(&record_stuffer)); + + /* Skip the header */ + POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, S2N_TLS_RECORD_HEADER_LENGTH)); + + uint16_t encrypted_length = data_bytes_to_take + mac_digest_size; + switch (cipher_suite->record_alg->cipher->type) { + case S2N_AEAD: + POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, cipher_suite->record_alg->cipher->io.aead.record_iv_size)); + encrypted_length += cipher_suite->record_alg->cipher->io.aead.tag_size; + if (is_tls13_record) { + /* one extra byte for content type */ + encrypted_length += S2N_TLS_CONTENT_TYPE_LENGTH; + } + break; + case S2N_CBC: + if (conn->actual_protocol_version > S2N_TLS10) { + /* Leave the IV alone and unencrypted */ + POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, iv.size)); + } + /* Encrypt the padding and the padding length byte too */ + encrypted_length += padding + 1; + break; + case S2N_COMPOSITE: + /* Composite CBC expects a pointer starting at explicit IV: [Explicit IV | fragment | MAC | padding | padding len ] + * extra will account for the explicit IV len(if applicable), MAC digest len, padding len + padding byte. + */ + encrypted_length += extra; + break; + default: + break; + } + + /* Check that stuffer have enough space to write encrypted record, because raw_write cannot expand tainted stuffer */ + S2N_ERROR_IF(s2n_stuffer_space_remaining(&record_stuffer) < encrypted_length, S2N_ERR_RECORD_STUFFER_SIZE); + + /* Do the encryption */ + struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_write(&record_stuffer, encrypted_length) }; + POSIX_GUARD(s2n_record_encrypt(conn, cipher_suite, session_key, &iv, &aad, &en, implicit_iv, block_size)); + + /* Sync the out stuffer write cursor with the record stuffer. */ + POSIX_GUARD(s2n_stuffer_skip_write(&conn->out, s2n_stuffer_data_available(&record_stuffer))); + + if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) { + conn->client = current_client_crypto; + conn->server = current_server_crypto; + } + + return data_bytes_to_take; +} + +S2N_RESULT s2n_record_write(struct s2n_connection *conn, uint8_t content_type, struct s2n_blob *in) +{ + struct iovec iov; + iov.iov_base = in->data; + iov.iov_len = in->size; + int written = s2n_record_writev(conn, content_type, &iov, 1, 0, in->size); + RESULT_GUARD_POSIX(written); + RESULT_ENSURE((uint32_t) written == in->size, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + return S2N_RESULT_OK; +} diff --git a/tls/s2n_recv.c b/tls/s2n_recv.c index 096dcd870f5..8b1940ac3d0 100644 --- a/tls/s2n_recv.c +++ b/tls/s2n_recv.c @@ -1,332 +1,332 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -/* Use usleep */ -#define _XOPEN_SOURCE 500 -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_io.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_socket.h" - -S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t total) -{ - RESULT_ENSURE_REF(conn); - - /* If we're going to initialize conn->in to point to more memory than - * is actually readable, make sure that the additional memory exists. - */ - RESULT_ENSURE_LTE(written, total); - uint32_t remaining = total - written; - RESULT_ENSURE_LTE(remaining, s2n_stuffer_space_remaining(&conn->buffer_in)); - - uint8_t *data = s2n_stuffer_raw_read(&conn->buffer_in, written); - RESULT_ENSURE_REF(data); - RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); - RESULT_GUARD_POSIX(s2n_blob_init(&conn->in.blob, data, total)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&conn->in, written)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length) -{ - while (s2n_stuffer_data_available(output) < length) { - uint32_t remaining = length - s2n_stuffer_data_available(output); - if (conn->recv_buffering) { - remaining = S2N_MAX(remaining, s2n_stuffer_space_remaining(output)); - } - errno = 0; - int r = s2n_connection_recv_stuffer(output, conn, remaining); - if (r == 0) { - s2n_atomic_flag_set(&conn->read_closed); - } - RESULT_GUARD(s2n_io_check_read_result(r)); - conn->wire_bytes_in += r; - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_recv_buffer_in(struct s2n_connection *conn, size_t min_size) -{ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_LARGE_FRAGMENT_LENGTH)); - uint32_t buffer_in_available = s2n_stuffer_data_available(&conn->buffer_in); - if (buffer_in_available < min_size) { - uint32_t remaining = min_size - buffer_in_available; - if (s2n_stuffer_space_remaining(&conn->buffer_in) < remaining) { - RESULT_GUARD_POSIX(s2n_stuffer_shift(&conn->buffer_in)); - } - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, min_size)); - } - return S2N_RESULT_OK; -} - -int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2) -{ - *isSSLv2 = 0; - - if (conn->ktls_recv_enabled) { - return s2n_ktls_read_full_record(conn, record_type); - } - - /* If the record has already been decrypted, then leave it alone */ - if (conn->in_status == PLAINTEXT) { - /* Only application data packets count as plaintext */ - *record_type = TLS_APPLICATION_DATA; - return S2N_SUCCESS; - } - - /* Read the record until we at least have a header */ - POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); - uint32_t header_available = s2n_stuffer_data_available(&conn->header_in); - if (header_available < S2N_TLS_RECORD_HEADER_LENGTH) { - uint32_t header_remaining = S2N_TLS_RECORD_HEADER_LENGTH - header_available; - s2n_result ret = s2n_recv_buffer_in(conn, header_remaining); - uint32_t header_read = S2N_MIN(header_remaining, s2n_stuffer_data_available(&conn->buffer_in)); - POSIX_GUARD(s2n_stuffer_copy(&conn->buffer_in, &conn->header_in, header_read)); - POSIX_GUARD_RESULT(ret); - } - - uint16_t fragment_length = 0; - - /* If the first bit is set then this is an SSLv2 record */ - if (conn->header_in.blob.data[0] & S2N_TLS_SSLV2_HEADER_FLAG) { - *isSSLv2 = 1; - WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_sslv2_record_header_parse(conn, record_type, &conn->client_hello.legacy_version, &fragment_length))); - } else { - WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_header_parse(conn, record_type, &fragment_length))); - } - - /* Read enough to have the whole record */ - uint32_t fragment_available = s2n_stuffer_data_available(&conn->in); - if (fragment_available < fragment_length || fragment_length == 0) { - POSIX_GUARD(s2n_stuffer_rewind_read(&conn->buffer_in, fragment_available)); - s2n_result ret = s2n_recv_buffer_in(conn, fragment_length); - uint32_t fragment_read = S2N_MIN(fragment_length, s2n_stuffer_data_available(&conn->buffer_in)); - POSIX_GUARD_RESULT(s2n_recv_in_init(conn, fragment_read, fragment_length)); - POSIX_GUARD_RESULT(ret); - } - - if (*isSSLv2) { - return 0; - } - - /* Decrypt and parse the record */ - if (s2n_early_data_is_trial_decryption_allowed(conn, *record_type)) { - POSIX_ENSURE(s2n_record_parse(conn) >= S2N_SUCCESS, S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); - } else { - WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_parse(conn))); - } - - /* In TLS 1.3, encrypted handshake records would appear to be of record type - * TLS_APPLICATION_DATA. The actual record content type is found after the encrypted - * is decrypted. - */ - if (conn->actual_protocol_version == S2N_TLS13 && *record_type == TLS_APPLICATION_DATA) { - POSIX_GUARD(s2n_tls13_parse_record_type(&conn->in, record_type)); - } - - return 0; -} - -ssize_t s2n_recv_impl(struct s2n_connection *conn, void *buf, ssize_t size_signed, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_GTE(size_signed, 0); - size_t size = size_signed; - ssize_t bytes_read = 0; - struct s2n_blob out = { 0 }; - POSIX_GUARD(s2n_blob_init(&out, (uint8_t *) buf, 0)); - - /* - * Set the `blocked` status to BLOCKED_ON_READ by default - * - * The only case in which it should be updated is on a successful read into the provided buffer. - * - * Unfortunately, the current `blocked` behavior has become ossified by buggy applications that ignore - * error types and only read `blocked`. As such, it's very important to avoid changing how this value is updated - * as it could break applications. - */ - *blocked = S2N_BLOCKED_ON_READ; - - if (!s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#6.1 - *# If a transport-level close - *# is received prior to a "close_notify", the receiver cannot know that - *# all the data that was sent has been received. - * - *= https://www.rfc-editor.org/rfc/rfc8446#6.1 - *# If the application protocol using TLS provides that any data may be - *# carried over the underlying transport after the TLS connection is - *# closed, the TLS implementation MUST receive a "close_notify" alert - *# before indicating end-of-data to the application layer. - */ - POSIX_ENSURE(s2n_atomic_flag_test(&conn->close_notify_received), S2N_ERR_CLOSED); - *blocked = S2N_NOT_BLOCKED; - return 0; - } - - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); - POSIX_GUARD_RESULT(s2n_early_data_validate_recv(conn)); - - while (size && s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { - int isSSLv2 = 0; - uint8_t record_type = 0; - int r = s2n_read_full_record(conn, &record_type, &isSSLv2); - if (r < 0) { - /* Don't propagate the error if we already read some bytes. */ - if (bytes_read && (s2n_errno == S2N_ERR_CLOSED || s2n_errno == S2N_ERR_IO_BLOCKED)) { - break; - } - - /* If we get here, it's an error condition. - * For stateful resumption, invalidate the session on error to prevent resumption with - * potentially corrupted session state. This ensures that a bad session state does not - * lead to repeated failures during resumption attempts. - */ - if (s2n_errno != S2N_ERR_IO_BLOCKED && s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { - conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); - } - - S2N_ERROR_PRESERVE_ERRNO(); - } - - S2N_ERROR_IF(isSSLv2, S2N_ERR_BAD_MESSAGE); - - if (record_type != TLS_HANDSHAKE) { - /* - *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 - *# - Handshake messages MUST NOT be interleaved with other record - *# types. That is, if a handshake message is split over two or more - *# records, there MUST NOT be any other records between them. - */ - POSIX_ENSURE(s2n_stuffer_is_wiped(&conn->post_handshake.in), S2N_ERR_BAD_MESSAGE); - - /* If not handling a handshake message, free the post-handshake memory. - * Post-handshake messages are infrequent enough that we don't want to - * keep a potentially large buffer around unnecessarily. - */ - if (!s2n_stuffer_is_freed(&conn->post_handshake.in)) { - POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); - } - } - - if (record_type != TLS_APPLICATION_DATA) { - switch (record_type) { - case TLS_ALERT: - POSIX_GUARD(s2n_process_alert_fragment(conn)); - break; - case TLS_HANDSHAKE: { - s2n_result result = s2n_post_handshake_recv(conn); - /* Ignore any errors due to insufficient input data from io. - * The next iteration of this loop will attempt to read more input data. - */ - if (s2n_result_is_error(result) && s2n_errno != S2N_ERR_IO_BLOCKED) { - WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); - } - break; - } - } - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - continue; - } - - out.size = S2N_MIN(size, s2n_stuffer_data_available(&conn->in)); - - POSIX_GUARD(s2n_stuffer_erase_and_read(&conn->in, &out)); - bytes_read += out.size; - - out.data += out.size; - size -= out.size; - - /* Are we ready for more encrypted data? */ - if (s2n_stuffer_data_available(&conn->in) == 0) { - POSIX_GUARD_RESULT(s2n_record_wipe(conn)); - } - - /* If we've read some data, return it in legacy mode */ - if (bytes_read && !conn->config->recv_multi_record) { - break; - } - } - - /* Due to the history of this API, some applications depend on the blocked status to know if - * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. - * - * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing - * without conflating being blocked on reading from the OS socket vs blocked on the application's - * buffer size. - */ - if (s2n_stuffer_data_available(&conn->in) == 0) { - *blocked = S2N_NOT_BLOCKED; - } - - return bytes_read; -} - -ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked) -{ - POSIX_ENSURE(!conn->recv_in_use, S2N_ERR_REENTRANCY); - conn->recv_in_use = true; - - ssize_t result = s2n_recv_impl(conn, buf, size, blocked); - POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); - - /* finish the recv call */ - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); - - conn->recv_in_use = false; - return result; -} - -uint32_t s2n_peek(struct s2n_connection *conn) -{ - if (conn == NULL) { - return 0; - } - - /* If we have partially buffered an encrypted record, - * we should not report those bytes as available to read. - */ - if (conn->in_status != PLAINTEXT) { - return 0; - } - - return s2n_stuffer_data_available(&conn->in); -} - -uint32_t s2n_peek_buffered(struct s2n_connection *conn) -{ - if (conn == NULL) { - return 0; - } - return s2n_stuffer_data_available(&conn->buffer_in); -} +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +/* Use usleep */ +#define _XOPEN_SOURCE 500 +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_io.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t total) +{ + RESULT_ENSURE_REF(conn); + + /* If we're going to initialize conn->in to point to more memory than + * is actually readable, make sure that the additional memory exists. + */ + RESULT_ENSURE_LTE(written, total); + uint32_t remaining = total - written; + RESULT_ENSURE_LTE(remaining, s2n_stuffer_space_remaining(&conn->buffer_in)); + + uint8_t *data = s2n_stuffer_raw_read(&conn->buffer_in, written); + RESULT_ENSURE_REF(data); + RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); + RESULT_GUARD_POSIX(s2n_blob_init(&conn->in.blob, data, total)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&conn->in, written)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length) +{ + while (s2n_stuffer_data_available(output) < length) { + uint32_t remaining = length - s2n_stuffer_data_available(output); + if (conn->recv_buffering) { + remaining = S2N_MAX(remaining, s2n_stuffer_space_remaining(output)); + } + errno = 0; + int r = s2n_connection_recv_stuffer(output, conn, remaining); + if (r == 0) { + s2n_atomic_flag_set(&conn->read_closed); + } + RESULT_GUARD(s2n_io_check_read_result(r)); + conn->wire_bytes_in += r; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_recv_buffer_in(struct s2n_connection *conn, size_t min_size) +{ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_LARGE_FRAGMENT_LENGTH)); + uint32_t buffer_in_available = s2n_stuffer_data_available(&conn->buffer_in); + if (buffer_in_available < min_size) { + uint32_t remaining = min_size - buffer_in_available; + if (s2n_stuffer_space_remaining(&conn->buffer_in) < remaining) { + RESULT_GUARD_POSIX(s2n_stuffer_shift(&conn->buffer_in)); + } + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, min_size)); + } + return S2N_RESULT_OK; +} + +int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2) +{ + *isSSLv2 = 0; + + if (conn->ktls_recv_enabled) { + return s2n_ktls_read_full_record(conn, record_type); + } + + /* If the record has already been decrypted, then leave it alone */ + if (conn->in_status == PLAINTEXT) { + /* Only application data packets count as plaintext */ + *record_type = TLS_APPLICATION_DATA; + return S2N_SUCCESS; + } + + /* Read the record until we at least have a header */ + POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); + uint32_t header_available = s2n_stuffer_data_available(&conn->header_in); + if (header_available < S2N_TLS_RECORD_HEADER_LENGTH) { + uint32_t header_remaining = S2N_TLS_RECORD_HEADER_LENGTH - header_available; + s2n_result ret = s2n_recv_buffer_in(conn, header_remaining); + uint32_t header_read = S2N_MIN(header_remaining, s2n_stuffer_data_available(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_copy(&conn->buffer_in, &conn->header_in, header_read)); + POSIX_GUARD_RESULT(ret); + } + + uint16_t fragment_length = 0; + + /* If the first bit is set then this is an SSLv2 record */ + if (conn->header_in.blob.data[0] & S2N_TLS_SSLV2_HEADER_FLAG) { + *isSSLv2 = 1; + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_sslv2_record_header_parse(conn, record_type, &conn->client_hello.legacy_version, &fragment_length))); + } else { + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_header_parse(conn, record_type, &fragment_length))); + } + + /* Read enough to have the whole record */ + uint32_t fragment_available = s2n_stuffer_data_available(&conn->in); + if (fragment_available < fragment_length || fragment_length == 0) { + POSIX_GUARD(s2n_stuffer_rewind_read(&conn->buffer_in, fragment_available)); + s2n_result ret = s2n_recv_buffer_in(conn, fragment_length); + uint32_t fragment_read = S2N_MIN(fragment_length, s2n_stuffer_data_available(&conn->buffer_in)); + POSIX_GUARD_RESULT(s2n_recv_in_init(conn, fragment_read, fragment_length)); + POSIX_GUARD_RESULT(ret); + } + + if (*isSSLv2) { + return 0; + } + + /* Decrypt and parse the record */ + if (s2n_early_data_is_trial_decryption_allowed(conn, *record_type)) { + POSIX_ENSURE(s2n_record_parse(conn) >= S2N_SUCCESS, S2N_ERR_EARLY_DATA_TRIAL_DECRYPT); + } else { + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_parse(conn))); + } + + /* In TLS 1.3, encrypted handshake records would appear to be of record type + * TLS_APPLICATION_DATA. The actual record content type is found after the encrypted + * is decrypted. + */ + if (conn->actual_protocol_version == S2N_TLS13 && *record_type == TLS_APPLICATION_DATA) { + POSIX_GUARD(s2n_tls13_parse_record_type(&conn->in, record_type)); + } + + return 0; +} + +ssize_t s2n_recv_impl(struct s2n_connection *conn, void *buf, ssize_t size_signed, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_GTE(size_signed, 0); + size_t size = size_signed; + ssize_t bytes_read = 0; + struct s2n_blob out = { 0 }; + POSIX_GUARD(s2n_blob_init(&out, (uint8_t *) buf, 0)); + + /* + * Set the `blocked` status to BLOCKED_ON_READ by default + * + * The only case in which it should be updated is on a successful read into the provided buffer. + * + * Unfortunately, the current `blocked` behavior has become ossified by buggy applications that ignore + * error types and only read `blocked`. As such, it's very important to avoid changing how this value is updated + * as it could break applications. + */ + *blocked = S2N_BLOCKED_ON_READ; + + if (!s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#6.1 + *# If a transport-level close + *# is received prior to a "close_notify", the receiver cannot know that + *# all the data that was sent has been received. + * + *= https://www.rfc-editor.org/rfc/rfc8446#6.1 + *# If the application protocol using TLS provides that any data may be + *# carried over the underlying transport after the TLS connection is + *# closed, the TLS implementation MUST receive a "close_notify" alert + *# before indicating end-of-data to the application layer. + */ + POSIX_ENSURE(s2n_atomic_flag_test(&conn->close_notify_received), S2N_ERR_CLOSED); + *blocked = S2N_NOT_BLOCKED; + return 0; + } + + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); + POSIX_GUARD_RESULT(s2n_early_data_validate_recv(conn)); + + while (size && s2n_connection_check_io_status(conn, S2N_IO_READABLE)) { + int isSSLv2 = 0; + uint8_t record_type = 0; + int r = s2n_read_full_record(conn, &record_type, &isSSLv2); + if (r < 0) { + /* Don't propagate the error if we already read some bytes. */ + if (bytes_read && (s2n_errno == S2N_ERR_CLOSED || s2n_errno == S2N_ERR_IO_BLOCKED)) { + break; + } + + /* If we get here, it's an error condition. + * For stateful resumption, invalidate the session on error to prevent resumption with + * potentially corrupted session state. This ensures that a bad session state does not + * lead to repeated failures during resumption attempts. + */ + if (s2n_errno != S2N_ERR_IO_BLOCKED && s2n_allowed_to_cache_connection(conn) && conn->session_id_len) { + conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); + } + + S2N_ERROR_PRESERVE_ERRNO(); + } + + S2N_ERROR_IF(isSSLv2, S2N_ERR_BAD_MESSAGE); + + if (record_type != TLS_HANDSHAKE) { + /* + *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + *# - Handshake messages MUST NOT be interleaved with other record + *# types. That is, if a handshake message is split over two or more + *# records, there MUST NOT be any other records between them. + */ + POSIX_ENSURE(s2n_stuffer_is_wiped(&conn->post_handshake.in), S2N_ERR_BAD_MESSAGE); + + /* If not handling a handshake message, free the post-handshake memory. + * Post-handshake messages are infrequent enough that we don't want to + * keep a potentially large buffer around unnecessarily. + */ + if (!s2n_stuffer_is_freed(&conn->post_handshake.in)) { + POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + } + } + + if (record_type != TLS_APPLICATION_DATA) { + switch (record_type) { + case TLS_ALERT: + POSIX_GUARD(s2n_process_alert_fragment(conn)); + break; + case TLS_HANDSHAKE: { + s2n_result result = s2n_post_handshake_recv(conn); + /* Ignore any errors due to insufficient input data from io. + * The next iteration of this loop will attempt to read more input data. + */ + if (s2n_result_is_error(result) && s2n_errno != S2N_ERR_IO_BLOCKED) { + WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); + } + break; + } + } + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + continue; + } + + out.size = S2N_MIN(size, s2n_stuffer_data_available(&conn->in)); + + POSIX_GUARD(s2n_stuffer_erase_and_read(&conn->in, &out)); + bytes_read += out.size; + + out.data += out.size; + size -= out.size; + + /* Are we ready for more encrypted data? */ + if (s2n_stuffer_data_available(&conn->in) == 0) { + POSIX_GUARD_RESULT(s2n_record_wipe(conn)); + } + + /* If we've read some data, return it in legacy mode */ + if (bytes_read && !conn->config->recv_multi_record) { + break; + } + } + + /* Due to the history of this API, some applications depend on the blocked status to know if + * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. + * + * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing + * without conflating being blocked on reading from the OS socket vs blocked on the application's + * buffer size. + */ + if (s2n_stuffer_data_available(&conn->in) == 0) { + *blocked = S2N_NOT_BLOCKED; + } + + return bytes_read; +} + +ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked) +{ + POSIX_ENSURE(!conn->recv_in_use, S2N_ERR_REENTRANCY); + conn->recv_in_use = true; + + ssize_t result = s2n_recv_impl(conn, buf, size, blocked); + POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); + + /* finish the recv call */ + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); + + conn->recv_in_use = false; + return result; +} + +uint32_t s2n_peek(struct s2n_connection *conn) +{ + if (conn == NULL) { + return 0; + } + + /* If we have partially buffered an encrypted record, + * we should not report those bytes as available to read. + */ + if (conn->in_status != PLAINTEXT) { + return 0; + } + + return s2n_stuffer_data_available(&conn->in); +} + +uint32_t s2n_peek_buffered(struct s2n_connection *conn) +{ + if (conn == NULL) { + return 0; + } + return s2n_stuffer_data_available(&conn->buffer_in); +} diff --git a/tls/s2n_resume.c b/tls/s2n_resume.c index c3f54af0810..c1be4c7fba6 100644 --- a/tls/s2n_resume.c +++ b/tls/s2n_resume.c @@ -1,1094 +1,1094 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -#include "tls/s2n_resume.h" - -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crypto.h" -#include "tls/s2n_tls.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -int s2n_allowed_to_cache_connection(struct s2n_connection *conn) -{ - /* We're unable to cache connections with a Client Cert since we currently don't serialize the Client Cert, - * which means that callers won't have access to the Client's Cert if the connection is resumed. */ - if (s2n_connection_is_client_auth_enabled(conn)) { - return 0; - } - - struct s2n_config *config = conn->config; - - POSIX_ENSURE_REF(config); - return config->use_session_cache; -} - -/* If a protocol version is required before the actual_protocol_version - * is negotiated, we should fall back to resume_protocol_version if available. - * - * This covers the case where the application requests a ticket / session state - * before a NewSessionTicket message has been sent or received. Historically, - * in that case we return the ticket / session state already set for the connection. - * resume_protocol_version represents the protocol version of that existing ticket / state. - */ -static uint8_t s2n_resume_protocol_version(struct s2n_connection *conn) -{ - if (!IS_NEGOTIATED(conn) && conn->resume_protocol_version) { - return conn->resume_protocol_version; - } else { - return conn->actual_protocol_version; - } -} - -static int s2n_tls12_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) -{ - POSIX_ENSURE_REF(to); - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - uint64_t now = 0; - - S2N_ERROR_IF(s2n_stuffer_space_remaining(to) < S2N_TLS12_STATE_SIZE_IN_BYTES, S2N_ERR_STUFFER_IS_FULL); - - /* Get the time */ - POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); - - /* Write the entry */ - POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_SERIALIZED_FORMAT_TLS12_V3)); - POSIX_GUARD(s2n_stuffer_write_uint8(to, s2n_resume_protocol_version(conn))); - POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint64(to, now)); - POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->ems_negotiated)); - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_tls13_serialize_keying_material_expiration(struct s2n_connection *conn, - uint64_t now, struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(out); - - if (conn->mode != S2N_SERVER) { - return S2N_RESULT_OK; - } - - uint64_t expiration_timestamp = now + (conn->server_keying_material_lifetime * (uint64_t) ONE_SEC_IN_NANOS); - - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - if (chosen_psk && chosen_psk->type == S2N_PSK_TYPE_RESUMPTION) { - expiration_timestamp = S2N_MIN(chosen_psk->keying_material_expiration, expiration_timestamp); - } - - RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, expiration_timestamp)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_tls13_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - RESULT_ENSURE_REF(out); - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - - uint64_t current_time = 0; - struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; - - /* Get the time */ - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); - - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, S2N_SERIALIZED_FORMAT_TLS13_V1)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, conn->actual_protocol_version)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, current_time)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, ticket_fields->ticket_age_add)); - RESULT_ENSURE_INCLUSIVE_RANGE(1, ticket_fields->session_secret.size, UINT8_MAX); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, ticket_fields->session_secret.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, ticket_fields->session_secret.data, ticket_fields->session_secret.size)); - RESULT_GUARD(s2n_tls13_serialize_keying_material_expiration(conn, current_time, out)); - - uint32_t server_max_early_data = 0; - RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, server_max_early_data)); - if (server_max_early_data > 0) { - uint8_t application_protocol_len = strlen(conn->application_protocol); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, application_protocol_len)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, (uint8_t *) conn->application_protocol, application_protocol_len)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, conn->server_early_data_context.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write(out, &conn->server_early_data_context)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) -{ - if (s2n_resume_protocol_version(conn) < S2N_TLS13) { - RESULT_GUARD_POSIX(s2n_tls12_serialize_resumption_state(conn, out)); - } else { - RESULT_GUARD(s2n_tls13_serialize_resumption_state(conn, out)); - } - return S2N_RESULT_OK; -} - -static int s2n_tls12_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - uint8_t protocol_version = 0; - uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - - S2N_ERROR_IF(s2n_stuffer_data_available(from) < S2N_TLS12_STATE_SIZE_IN_BYTES - sizeof(uint8_t), S2N_ERR_STUFFER_OUT_OF_DATA); - - POSIX_GUARD(s2n_stuffer_read_uint8(from, &protocol_version)); - S2N_ERROR_IF(protocol_version != conn->actual_protocol_version, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - - POSIX_GUARD(s2n_stuffer_read_bytes(from, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); - POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN), S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - - uint64_t now = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); - - uint64_t then = 0; - POSIX_GUARD(s2n_stuffer_read_uint64(from, &then)); - S2N_ERROR_IF(then > now, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - S2N_ERROR_IF(now - then > conn->config->session_state_lifetime_in_nanos, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - - POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); - - if (s2n_stuffer_data_available(from)) { - uint8_t ems_negotiated = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(from, &ems_negotiated)); - - /** - *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 - *# o If the original session did not use the "extended_master_secret" - *# extension but the new ClientHello contains the extension, then the - *# server MUST NOT perform the abbreviated handshake. Instead, it - *# SHOULD continue with a full handshake (as described in - *# Section 5.2) to negotiate a new session. - *# - *# o If the original session used the "extended_master_secret" - *# extension but the new ClientHello does not contain it, the server - *# MUST abort the abbreviated handshake. - **/ - if (conn->ems_negotiated != ems_negotiated) { - /* The session ticket needs to have the same EMS state as the current session. If it doesn't - * have the same state, the current session takes the state of the session ticket and errors. - * If the deserialization process errors, we will use this state in a few extra checks - * to determine if we can fallback to a full handshake. - */ - conn->ems_negotiated = ems_negotiated; - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - } - - return S2N_SUCCESS; -} - -static int s2n_client_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) -{ - /* Serialize session ticket */ - if (conn->config->use_tickets && conn->client_ticket.size > 0) { - POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_TICKET)); - POSIX_GUARD(s2n_stuffer_write_uint16(to, conn->client_ticket.size)); - POSIX_GUARD(s2n_stuffer_write(to, &conn->client_ticket)); - } else { - /* Serialize session id */ - POSIX_ENSURE_LT(conn->actual_protocol_version, S2N_TLS13); - POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_ID)); - POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->session_id_len)); - POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->session_id, conn->session_id_len)); - } - - /* Serialize session state */ - POSIX_GUARD_RESULT(s2n_serialize_resumption_state(conn, to)); - - return 0; -} - -static S2N_RESULT s2n_tls12_client_deserialize_session_state(struct s2n_connection *conn, - struct s2n_blob *ticket, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(from); - - /* Operate on a copy of the connection to avoid mutating the connection on - * failure. We have tests in s2n_resume_test.c that prove this level of copy - * is sufficient. - */ - struct s2n_crypto_parameters *secure = conn->secure; - RESULT_ENSURE_REF(secure); - struct s2n_connection temp_conn = *conn; - struct s2n_crypto_parameters temp_secure = *secure; - temp_conn.secure = &temp_secure; - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &temp_conn.resume_protocol_version)); - - uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(from, S2N_TLS_CIPHER_SUITE_LEN); - RESULT_ENSURE_REF(cipher_suite_wire); - RESULT_GUARD_POSIX(s2n_set_cipher_as_client(&temp_conn, cipher_suite_wire)); - - uint64_t then = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &then)); - - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, temp_conn.secrets.version.tls12.master_secret, - S2N_TLS_SECRET_LEN)); - - if (s2n_stuffer_data_available(from)) { - uint8_t ems_negotiated = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &ems_negotiated)); - temp_conn.ems_negotiated = ems_negotiated; - } - - DEFER_CLEANUP(struct s2n_blob client_ticket = { 0 }, s2n_free); - if (ticket) { - RESULT_GUARD_POSIX(s2n_dup(ticket, &client_ticket)); - } - - /* Finally, actually update the connection */ - RESULT_GUARD_POSIX(s2n_free(&conn->client_ticket)); - *secure = temp_secure; - *conn = temp_conn; - conn->secure = secure; - conn->client_ticket = client_ticket; - ZERO_TO_DISABLE_DEFER_CLEANUP(client_ticket); - - return S2N_RESULT_OK; -} - -/* `s2n_validate_ticket_age` is a best effort check that the session ticket is - * less than one week old. - * - * Clock skew between hosts or the possibility of a clock jump prevent this from - * being a precise check. - */ -static S2N_RESULT s2n_validate_ticket_age(uint64_t current_time, uint64_t ticket_issue_time) -{ - /* If the `ticket_issue_time` is in the future, then we are observing clock skew. - * We shouldn't fully reject the ticket, but we assert that the clock skew is - * less than some MAX_ALLOWED_CLOCK_SKEW_SEC - */ - if (current_time < ticket_issue_time) { - uint64_t clock_skew_in_nanos = ticket_issue_time - current_time; - uint64_t clock_skew_in_seconds = clock_skew_in_nanos / ONE_SEC_IN_NANOS; - RESULT_ENSURE(clock_skew_in_seconds <= MAX_ALLOWED_CLOCK_SKEW_SEC, S2N_ERR_INVALID_SESSION_TICKET); - } else { - uint64_t ticket_age_in_nanos = current_time - ticket_issue_time; - uint64_t ticket_age_in_sec = ticket_age_in_nanos / ONE_SEC_IN_NANOS; - RESULT_ENSURE(ticket_age_in_sec <= ONE_WEEK_IN_SEC, S2N_ERR_INVALID_SESSION_TICKET); - } - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_tls13_deserialize_session_state(struct s2n_connection *conn, struct s2n_blob *psk_identity, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(psk_identity); - RESULT_ENSURE_REF(from); - - DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe); - RESULT_GUARD(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION)); - RESULT_GUARD_POSIX(s2n_psk_set_identity(&psk, psk_identity->data, psk_identity->size)); - - uint8_t protocol_version = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &protocol_version)); - RESULT_ENSURE_GTE(protocol_version, S2N_TLS13); - - uint8_t iana_id[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, iana_id, S2N_TLS_CIPHER_SUITE_LEN)); - struct s2n_cipher_suite *cipher_suite = NULL; - RESULT_GUARD(s2n_cipher_suite_from_iana(iana_id, sizeof(iana_id), &cipher_suite)); - RESULT_ENSURE_REF(cipher_suite); - psk.hmac_alg = cipher_suite->prf_alg; - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.ticket_issue_time)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Clients MUST NOT cache - *# tickets for longer than 7 days, regardless of the ticket_lifetime, - *# and MAY delete tickets earlier based on local policy. - */ - uint64_t current_time = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); - RESULT_GUARD(s2n_validate_ticket_age(current_time, psk.ticket_issue_time)); - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &psk.ticket_age_add)); - - uint8_t secret_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &secret_len)); - RESULT_ENSURE_LTE(secret_len, S2N_TLS_SECRET_LEN); - uint8_t *secret_data = s2n_stuffer_raw_read(from, secret_len); - RESULT_ENSURE_REF(secret_data); - RESULT_GUARD_POSIX(s2n_psk_set_secret(&psk, secret_data, secret_len)); - - if (conn->mode == S2N_SERVER) { - RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.keying_material_expiration)); - RESULT_ENSURE(psk.keying_material_expiration > current_time, S2N_ERR_KEYING_MATERIAL_EXPIRED); - } - - uint32_t max_early_data_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &max_early_data_size)); - if (max_early_data_size > 0) { - RESULT_GUARD_POSIX(s2n_psk_configure_early_data(&psk, max_early_data_size, - iana_id[0], iana_id[1])); - - uint8_t app_proto_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &app_proto_size)); - uint8_t *app_proto_data = s2n_stuffer_raw_read(from, app_proto_size); - RESULT_ENSURE_REF(app_proto_data); - RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(&psk, app_proto_data, app_proto_size)); - - uint16_t early_data_context_size = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(from, &early_data_context_size)); - uint8_t *early_data_context_data = s2n_stuffer_raw_read(from, early_data_context_size); - RESULT_ENSURE_REF(early_data_context_data); - RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(&psk, early_data_context_data, early_data_context_size)); - } - - /* Make sure that this connection is configured for resumption PSKs, not external PSKs */ - RESULT_GUARD(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_RESUMPTION)); - /* Remove all previously-set PSKs. To keep the session ticket API behavior consistent - * across protocol versions, we currently only support setting a single resumption PSK. */ - RESULT_GUARD(s2n_psk_parameters_wipe(&conn->psk_params)); - RESULT_GUARD_POSIX(s2n_connection_append_psk(conn, &psk)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_deserialize_resumption_state(struct s2n_connection *conn, - struct s2n_blob *ticket, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(from); - - uint8_t format = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &format)); - - if (format == S2N_SERIALIZED_FORMAT_TLS12_V3) { - if (conn->mode == S2N_SERVER) { - RESULT_GUARD_POSIX(s2n_tls12_deserialize_resumption_state(conn, from)); - } else { - RESULT_GUARD(s2n_tls12_client_deserialize_session_state(conn, ticket, from)); - } - } else if (format == S2N_SERIALIZED_FORMAT_TLS13_V1) { - RESULT_GUARD(s2n_tls13_deserialize_session_state(conn, ticket, from)); - } else { - RESULT_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - conn->set_session = true; - return S2N_RESULT_OK; -} - -static int s2n_client_deserialize_with_session_id(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - uint8_t session_id_len = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(from, &session_id_len)); - - if (session_id_len == 0 || session_id_len > S2N_TLS_SESSION_ID_MAX_LEN - || session_id_len > s2n_stuffer_data_available(from)) { - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - - conn->session_id_len = session_id_len; - POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->session_id, session_id_len)); - - POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, NULL, from)); - - return 0; -} - -static int s2n_client_deserialize_with_session_ticket(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - uint16_t session_ticket_len = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(from, &session_ticket_len)); - - if (session_ticket_len == 0 || session_ticket_len > s2n_stuffer_data_available(from)) { - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - - struct s2n_blob session_ticket = { 0 }; - uint8_t *session_ticket_bytes = s2n_stuffer_raw_read(from, session_ticket_len); - POSIX_ENSURE_REF(session_ticket_bytes); - POSIX_GUARD(s2n_blob_init(&session_ticket, session_ticket_bytes, session_ticket_len)); - - POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, &session_ticket, from)); - return 0; -} - -static int s2n_client_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - uint8_t format = 0; - POSIX_GUARD(s2n_stuffer_read_uint8(from, &format)); - - switch (format) { - case S2N_STATE_WITH_SESSION_ID: - POSIX_GUARD(s2n_client_deserialize_with_session_id(conn, from)); - break; - case S2N_STATE_WITH_SESSION_TICKET: - POSIX_GUARD(s2n_client_deserialize_with_session_ticket(conn, from)); - break; - default: - POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); - } - - return 0; -} - -int s2n_resume_from_cache(struct s2n_connection *conn) -{ - S2N_ERROR_IF(conn->session_id_len == 0, S2N_ERR_SESSION_ID_TOO_SHORT); - S2N_ERROR_IF(conn->session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); - - uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; - struct s2n_blob entry = { 0 }; - POSIX_GUARD(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); - uint64_t size = entry.size; - int result = conn->config->cache_retrieve(conn, conn->config->cache_retrieve_data, conn->session_id, conn->session_id_len, entry.data, &size); - if (result == S2N_CALLBACK_BLOCKED) { - POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - POSIX_ENSURE(result >= S2N_SUCCESS, S2N_ERR_CANCELLED); - - S2N_ERROR_IF(size != entry.size, S2N_ERR_SIZE_MISMATCH); - - struct s2n_stuffer from = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&from, &entry)); - POSIX_GUARD(s2n_stuffer_write(&from, &entry)); - POSIX_GUARD_RESULT(s2n_resume_decrypt_session(conn, &from)); - - return 0; -} - -S2N_RESULT s2n_store_to_cache(struct s2n_connection *conn) -{ - uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; - struct s2n_blob entry = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); - struct s2n_stuffer to = { 0 }; - - /* session_id_len should always be >0 since either the Client provided a SessionId or the Server generated a new - * one for the Client */ - RESULT_ENSURE(conn->session_id_len > 0, S2N_ERR_SESSION_ID_TOO_SHORT); - RESULT_ENSURE(conn->session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); - - RESULT_GUARD_POSIX(s2n_stuffer_init(&to, &entry)); - - struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); - RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &to)); - - /* Store to the cache */ - conn->config->cache_store(conn, conn->config->cache_store_data, S2N_TLS_SESSION_CACHE_TTL, conn->session_id, conn->session_id_len, entry.data, entry.size); - - return S2N_RESULT_OK; -} - -int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(session); - - DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free); - /* size_t is 64-bit integer on 64-bit system, while s2n_alloc's length parameter is a 32-bit integer */ - POSIX_ENSURE(length <= UINT32_MAX, S2N_ERR_INVALID_ARGUMENT); - POSIX_GUARD(s2n_alloc(&session_data, length)); - POSIX_CHECKED_MEMCPY(session_data.data, session, length); - - struct s2n_stuffer from = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&from, &session_data)); - POSIX_GUARD(s2n_stuffer_write(&from, &session_data)); - POSIX_GUARD(s2n_client_deserialize_resumption_state(conn, &from)); - return 0; -} - -int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(session); - - const int len = s2n_connection_get_session_length(conn); - POSIX_GUARD(len); - - if (len == 0) { - return 0; - } - - POSIX_ENSURE((size_t) len <= max_length, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); - - struct s2n_blob serialized_data = { 0 }; - POSIX_GUARD(s2n_blob_init(&serialized_data, session, len)); - POSIX_GUARD(s2n_blob_zero(&serialized_data)); - - struct s2n_stuffer to = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&to, &serialized_data)); - POSIX_GUARD(s2n_client_serialize_resumption_state(conn, &to)); - - return len; -} - -int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - S2N_ERROR_IF(!(conn->config->use_tickets && conn->client_ticket.size > 0), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED); - - /* Session resumption using session ticket */ - return conn->ticket_lifetime_hint; -} - -S2N_RESULT s2n_connection_get_session_state_size(struct s2n_connection *conn, size_t *state_size) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(state_size); - - if (s2n_resume_protocol_version(conn) < S2N_TLS13) { - *state_size = S2N_TLS12_STATE_SIZE_IN_BYTES; - return S2N_RESULT_OK; - } - - *state_size = S2N_TLS13_FIXED_STATE_SIZE; - - uint8_t secret_size = 0; - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); - *state_size += secret_size; - - uint32_t server_max_early_data = 0; - RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); - if (server_max_early_data > 0) { - *state_size += S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE - + strlen(conn->application_protocol) - + conn->server_early_data_context.size; - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_connection_get_session_length_impl(struct s2n_connection *conn, size_t *length) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_REF(length); - *length = 0; - - if (conn->config->use_tickets && conn->client_ticket.size > 0) { - size_t session_state_size = 0; - RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); - *length = S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + session_state_size; - } else if (conn->session_id_len > 0 && conn->actual_protocol_version < S2N_TLS13) { - *length = S2N_STATE_FORMAT_LEN + sizeof(conn->session_id_len) + conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES; - } - return S2N_RESULT_OK; -} - -int s2n_connection_get_session_length(struct s2n_connection *conn) -{ - size_t length = 0; - if (s2n_result_is_ok(s2n_connection_get_session_length_impl(conn, &length))) { - return length; - } - return 0; -} - -int s2n_connection_is_session_resumed(struct s2n_connection *conn) -{ - return conn && IS_RESUMPTION_HANDSHAKE(conn) - && (conn->actual_protocol_version < S2N_TLS13 || conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION); -} - -int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - if (conn->actual_protocol_version >= S2N_TLS13) { - return (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)); - } else { - return IS_OCSP_STAPLED(conn); - } -} - -S2N_RESULT s2n_config_is_encrypt_key_available(struct s2n_config *config) -{ - RESULT_ENSURE_REF(config); - - uint64_t now = 0; - struct s2n_ticket_key *ticket_key = NULL; - RESULT_GUARD(s2n_config_wall_clock(config, &now)); - RESULT_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - RESULT_GUARD(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - for (uint32_t i = ticket_keys_len; i > 0; i--) { - uint32_t idx = i - 1; - RESULT_GUARD(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); - uint64_t key_intro_time = ticket_key->intro_timestamp; - - if (key_intro_time <= now - && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { - return S2N_RESULT_OK; - } - } - - RESULT_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); -} - -/* This function is used in s2n_get_ticket_encrypt_decrypt_key to compute the weight - * of the keys and to choose a single key from all of the encrypt-decrypt keys. - * Higher the weight of the key, higher the probability of being picked. - */ -int s2n_compute_weight_of_encrypt_decrypt_keys(struct s2n_config *config, - uint8_t *encrypt_decrypt_keys_index, - uint8_t num_encrypt_decrypt_keys, - uint64_t now) -{ - double total_weight = 0; - struct s2n_ticket_key_weight ticket_keys_weight[S2N_MAX_TICKET_KEYS]; - struct s2n_ticket_key *ticket_key = NULL; - - /* Compute weight of encrypt-decrypt keys */ - for (int i = 0; i < num_encrypt_decrypt_keys; i++) { - POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[i], (void **) &ticket_key)); - - uint64_t key_intro_time = ticket_key->intro_timestamp; - uint64_t key_encryption_peak_time = key_intro_time + (config->encrypt_decrypt_key_lifetime_in_nanos / 2); - - /* The % of encryption using this key is linearly increasing */ - if (now < key_encryption_peak_time) { - ticket_keys_weight[i].key_weight = now - key_intro_time; - } else { - /* The % of encryption using this key is linearly decreasing */ - ticket_keys_weight[i].key_weight = (config->encrypt_decrypt_key_lifetime_in_nanos / 2) - (now - key_encryption_peak_time); - } - - ticket_keys_weight[i].key_index = encrypt_decrypt_keys_index[i]; - total_weight += ticket_keys_weight[i].key_weight; - } - - /* Pick a random number in [0, 1). Using 53 bits (IEEE 754 double-precision floats). */ - uint64_t random_int = 0; - POSIX_GUARD_RESULT(s2n_public_random(pow(2, 53), &random_int)); - double random = (double) random_int / (double) pow(2, 53); - - /* Compute cumulative weight of encrypt-decrypt keys */ - for (int i = 0; i < num_encrypt_decrypt_keys; i++) { - ticket_keys_weight[i].key_weight = ticket_keys_weight[i].key_weight / total_weight; - - if (i > 0) { - ticket_keys_weight[i].key_weight += ticket_keys_weight[i - 1].key_weight; - } - - if (ticket_keys_weight[i].key_weight > random) { - return ticket_keys_weight[i].key_index; - } - } - - POSIX_BAIL(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED); -} - -/* This function is used in s2n_resume_encrypt_session_ticket in order for s2n to - * choose a key in encrypt-decrypt state from all of the keys added to config - */ -struct s2n_ticket_key *s2n_get_ticket_encrypt_decrypt_key(struct s2n_config *config) -{ - uint8_t num_encrypt_decrypt_keys = 0; - uint8_t encrypt_decrypt_keys_index[S2N_MAX_TICKET_KEYS] = { 0 }; - struct s2n_ticket_key *ticket_key = NULL; - - uint64_t now = 0; - PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - PTR_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - for (uint32_t i = ticket_keys_len; i > 0; i--) { - uint32_t idx = i - 1; - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); - uint64_t key_intro_time = ticket_key->intro_timestamp; - - /* A key can be used at its intro time (<=) and it can be used up to (<) - * its expiration time. - */ - if (key_intro_time <= now - && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { - encrypt_decrypt_keys_index[num_encrypt_decrypt_keys] = idx; - num_encrypt_decrypt_keys++; - } - } - - if (num_encrypt_decrypt_keys == 0) { - PTR_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); - } - - if (num_encrypt_decrypt_keys == 1) { - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[0], (void **) &ticket_key)); - return ticket_key; - } - - int8_t idx = 0; - PTR_GUARD_POSIX(idx = s2n_compute_weight_of_encrypt_decrypt_keys(config, encrypt_decrypt_keys_index, num_encrypt_decrypt_keys, now)); - - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); - return ticket_key; -} - -/* This function is used in s2n_resume_decrypt_session in order for s2n to - * find the matching key that was used for encryption. - */ -struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint8_t name[S2N_TICKET_KEY_NAME_LEN]) -{ - uint64_t now = 0; - struct s2n_ticket_key *ticket_key = NULL; - PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - PTR_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - for (uint32_t i = 0; i < ticket_keys_len; i++) { - PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); - - if (s2n_constant_time_equals(ticket_key->key_name, name, S2N_TICKET_KEY_NAME_LEN)) { - /* Check to see if the key has expired */ - if (now >= ticket_key->intro_timestamp - + config->encrypt_decrypt_key_lifetime_in_nanos - + config->decrypt_key_lifetime_in_nanos) { - return NULL; - } - - return ticket_key; - } - } - - 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_ticket_key *key, struct s2n_stuffer *to) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(to); - - 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, 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)); - RESULT_GUARD(s2n_aes256_gcm.set_encryption_key(&aes_ticket_key, &aes_key_blob)); - - /* Ensure we never encrypt with a zero-filled key */ - uint8_t zero_block[S2N_AES256_KEY_LEN] = { 0 }; - RESULT_ENSURE(!s2n_constant_time_equals(key->aes_key, zero_block, S2N_AES256_KEY_LEN), - S2N_ERR_KEY_CHECK); - - /* Initialize Additional Authenticated Data */ - uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; - struct s2n_blob aad_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); - struct s2n_stuffer aad = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); - 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 }; - RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); - RESULT_GUARD(s2n_get_public_random_data(&iv)); - RESULT_GUARD_POSIX(s2n_stuffer_write(to, &iv)); - - /* Write serialized session state */ - uint32_t plaintext_state_size = s2n_stuffer_data_available(to); - RESULT_GUARD(s2n_serialize_resumption_state(conn, to)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(to, S2N_TLS_GCM_TAG_LEN)); - - /* Initialize blob to be encrypted */ - struct s2n_blob state_blob = { 0 }; - struct s2n_stuffer copy_for_encryption = *to; - RESULT_GUARD_POSIX(s2n_stuffer_skip_read(©_for_encryption, plaintext_state_size)); - uint32_t state_blob_size = s2n_stuffer_data_available(©_for_encryption); - uint8_t *state_blob_data = s2n_stuffer_raw_read(©_for_encryption, state_blob_size); - RESULT_ENSURE_REF(state_blob_data); - RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, state_blob_data, state_blob_size)); - - RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.encrypt(&aes_ticket_key, &iv, &aad_blob, &state_blob, &state_blob)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct s2n_stuffer *from) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(from); - RESULT_ENSURE_REF(conn->config); - - /* 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))); - - struct s2n_ticket_key *key = s2n_find_ticket_key(conn->config, key_name); - /* 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 }; - RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); - RESULT_GUARD_POSIX(s2n_stuffer_read(from, &iv)); - - /* Initialize AES key */ - struct s2n_blob aes_key_blob = { 0 }; - 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)); - RESULT_GUARD(s2n_aes256_gcm.set_decryption_key(&aes_ticket_key, &aes_key_blob)); - - /* Initialize Additional Authenticated Data */ - uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; - struct s2n_blob aad_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); - struct s2n_stuffer aad = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); - 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))); - - /* Initialize blob to be decrypted */ - struct s2n_blob en_blob = { 0 }; - uint32_t en_blob_size = s2n_stuffer_data_available(from); - uint8_t *en_blob_data = s2n_stuffer_raw_read(from, en_blob_size); - RESULT_ENSURE_REF(en_blob_data); - RESULT_GUARD_POSIX(s2n_blob_init(&en_blob, en_blob_data, en_blob_size)); - - RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.decrypt(&aes_ticket_key, &iv, &aad_blob, &en_blob, &en_blob)); - - /* Parse decrypted state */ - struct s2n_blob state_blob = { 0 }; - uint32_t state_blob_size = en_blob_size - S2N_TLS_GCM_TAG_LEN; - RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, en_blob.data, state_blob_size)); - struct s2n_stuffer state_stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&state_stuffer, &state_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&state_stuffer, state_blob_size)); - RESULT_GUARD(s2n_deserialize_resumption_state(conn, &from->blob, &state_stuffer)); - - return S2N_RESULT_OK; -} - -/* This function is used to remove all or just one expired key from server config */ -int s2n_config_wipe_expired_ticket_crypto_keys(struct s2n_config *config, int8_t expired_key_index) -{ - int num_of_expired_keys = 0; - int expired_keys_index[S2N_MAX_TICKET_KEYS]; - struct s2n_ticket_key *ticket_key = NULL; - - if (expired_key_index != -1) { - expired_keys_index[num_of_expired_keys] = expired_key_index; - num_of_expired_keys++; - - goto end; - } - - uint64_t now = 0; - POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); - POSIX_ENSURE_REF(config->ticket_keys); - - uint32_t ticket_keys_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - for (uint32_t i = 0; i < ticket_keys_len; i++) { - POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); - if (now >= ticket_key->intro_timestamp - + config->encrypt_decrypt_key_lifetime_in_nanos - + config->decrypt_key_lifetime_in_nanos) { - expired_keys_index[num_of_expired_keys] = i; - num_of_expired_keys++; - } - } - -end: - for (int j = 0; j < num_of_expired_keys; j++) { - POSIX_GUARD_RESULT(s2n_array_remove(config->ticket_keys, expired_keys_index[j] - j)); - } - - return 0; -} - -int s2n_config_store_ticket_key(struct s2n_config *config, struct s2n_ticket_key *key) -{ - uint32_t ticket_keys_len = 0; - POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); - - /* The ticket key name and secret must both be unique. */ - for (uint32_t i = 0; i < ticket_keys_len; i++) { - struct s2n_ticket_key *other_key = NULL; - POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &other_key)); - POSIX_ENSURE(!s2n_constant_time_equals(key->key_name, other_key->key_name, s2n_array_len(key->key_name)), - S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); - POSIX_ENSURE(!s2n_constant_time_equals(key->aes_key, other_key->aes_key, s2n_array_len(key->aes_key)), - S2N_ERR_TICKET_KEY_NOT_UNIQUE); - } - - POSIX_GUARD_RESULT(s2n_array_insert_and_copy(config->ticket_keys, ticket_keys_len, key)); - return S2N_SUCCESS; -} - -int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num) -{ - POSIX_ENSURE_REF(config); - - config->initial_tickets_to_send = num; - POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, true)); - - return S2N_SUCCESS; -} - -int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num) -{ - POSIX_ENSURE_REF(conn); - POSIX_GUARD_RESULT(s2n_psk_validate_keying_material(conn)); - - uint32_t out = conn->tickets_to_send + num; - POSIX_ENSURE(out <= UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); - conn->tickets_to_send = out; - - return S2N_SUCCESS; -} - -int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(num); - POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); - *num = conn->tickets_sent; - return S2N_SUCCESS; -} - -int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs) -{ - POSIX_ENSURE_REF(conn); - conn->server_keying_material_lifetime = lifetime_in_secs; - return S2N_SUCCESS; -} - -int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx) -{ - POSIX_ENSURE_MUT(config); - - config->session_ticket_cb = callback; - config->session_ticket_ctx = ctx; - return S2N_SUCCESS; -} - -int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len) -{ - POSIX_ENSURE_REF(ticket); - POSIX_ENSURE_MUT(data_len); - - *data_len = ticket->ticket_data.size; - return S2N_SUCCESS; -} - -int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data) -{ - POSIX_ENSURE_REF(ticket); - POSIX_ENSURE_MUT(data); - - POSIX_ENSURE(ticket->ticket_data.size <= max_data_len, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); - POSIX_CHECKED_MEMCPY(data, ticket->ticket_data.data, ticket->ticket_data.size); - - return S2N_SUCCESS; -} - -int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime) -{ - POSIX_ENSURE_REF(ticket); - POSIX_ENSURE_REF(session_lifetime); - - *session_lifetime = ticket->session_lifetime; - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include "tls/s2n_resume.h" + +#include + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +int s2n_allowed_to_cache_connection(struct s2n_connection *conn) +{ + /* We're unable to cache connections with a Client Cert since we currently don't serialize the Client Cert, + * which means that callers won't have access to the Client's Cert if the connection is resumed. */ + if (s2n_connection_is_client_auth_enabled(conn)) { + return 0; + } + + struct s2n_config *config = conn->config; + + POSIX_ENSURE_REF(config); + return config->use_session_cache; +} + +/* If a protocol version is required before the actual_protocol_version + * is negotiated, we should fall back to resume_protocol_version if available. + * + * This covers the case where the application requests a ticket / session state + * before a NewSessionTicket message has been sent or received. Historically, + * in that case we return the ticket / session state already set for the connection. + * resume_protocol_version represents the protocol version of that existing ticket / state. + */ +static uint8_t s2n_resume_protocol_version(struct s2n_connection *conn) +{ + if (!IS_NEGOTIATED(conn) && conn->resume_protocol_version) { + return conn->resume_protocol_version; + } else { + return conn->actual_protocol_version; + } +} + +static int s2n_tls12_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) +{ + POSIX_ENSURE_REF(to); + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + uint64_t now = 0; + + S2N_ERROR_IF(s2n_stuffer_space_remaining(to) < S2N_TLS12_STATE_SIZE_IN_BYTES, S2N_ERR_STUFFER_IS_FULL); + + /* Get the time */ + POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); + + /* Write the entry */ + POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_SERIALIZED_FORMAT_TLS12_V3)); + POSIX_GUARD(s2n_stuffer_write_uint8(to, s2n_resume_protocol_version(conn))); + POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint64(to, now)); + POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->ems_negotiated)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_tls13_serialize_keying_material_expiration(struct s2n_connection *conn, + uint64_t now, struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(out); + + if (conn->mode != S2N_SERVER) { + return S2N_RESULT_OK; + } + + uint64_t expiration_timestamp = now + (conn->server_keying_material_lifetime * (uint64_t) ONE_SEC_IN_NANOS); + + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + if (chosen_psk && chosen_psk->type == S2N_PSK_TYPE_RESUMPTION) { + expiration_timestamp = S2N_MIN(chosen_psk->keying_material_expiration, expiration_timestamp); + } + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, expiration_timestamp)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_tls13_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + RESULT_ENSURE_REF(out); + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + + uint64_t current_time = 0; + struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; + + /* Get the time */ + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, S2N_SERIALIZED_FORMAT_TLS13_V1)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, conn->actual_protocol_version)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, current_time)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, ticket_fields->ticket_age_add)); + RESULT_ENSURE_INCLUSIVE_RANGE(1, ticket_fields->session_secret.size, UINT8_MAX); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, ticket_fields->session_secret.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, ticket_fields->session_secret.data, ticket_fields->session_secret.size)); + RESULT_GUARD(s2n_tls13_serialize_keying_material_expiration(conn, current_time, out)); + + uint32_t server_max_early_data = 0; + RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, server_max_early_data)); + if (server_max_early_data > 0) { + uint8_t application_protocol_len = strlen(conn->application_protocol); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, application_protocol_len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, (uint8_t *) conn->application_protocol, application_protocol_len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, conn->server_early_data_context.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write(out, &conn->server_early_data_context)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + if (s2n_resume_protocol_version(conn) < S2N_TLS13) { + RESULT_GUARD_POSIX(s2n_tls12_serialize_resumption_state(conn, out)); + } else { + RESULT_GUARD(s2n_tls13_serialize_resumption_state(conn, out)); + } + return S2N_RESULT_OK; +} + +static int s2n_tls12_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + uint8_t protocol_version = 0; + uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + + S2N_ERROR_IF(s2n_stuffer_data_available(from) < S2N_TLS12_STATE_SIZE_IN_BYTES - sizeof(uint8_t), S2N_ERR_STUFFER_OUT_OF_DATA); + + POSIX_GUARD(s2n_stuffer_read_uint8(from, &protocol_version)); + S2N_ERROR_IF(protocol_version != conn->actual_protocol_version, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + POSIX_GUARD(s2n_stuffer_read_bytes(from, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN), S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + uint64_t now = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now)); + + uint64_t then = 0; + POSIX_GUARD(s2n_stuffer_read_uint64(from, &then)); + S2N_ERROR_IF(then > now, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + S2N_ERROR_IF(now - then > conn->config->session_state_lifetime_in_nanos, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + + if (s2n_stuffer_data_available(from)) { + uint8_t ems_negotiated = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(from, &ems_negotiated)); + + /** + *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 + *# o If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + *# + *# o If the original session used the "extended_master_secret" + *# extension but the new ClientHello does not contain it, the server + *# MUST abort the abbreviated handshake. + **/ + if (conn->ems_negotiated != ems_negotiated) { + /* The session ticket needs to have the same EMS state as the current session. If it doesn't + * have the same state, the current session takes the state of the session ticket and errors. + * If the deserialization process errors, we will use this state in a few extra checks + * to determine if we can fallback to a full handshake. + */ + conn->ems_negotiated = ems_negotiated; + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + } + + return S2N_SUCCESS; +} + +static int s2n_client_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to) +{ + /* Serialize session ticket */ + if (conn->config->use_tickets && conn->client_ticket.size > 0) { + POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_TICKET)); + POSIX_GUARD(s2n_stuffer_write_uint16(to, conn->client_ticket.size)); + POSIX_GUARD(s2n_stuffer_write(to, &conn->client_ticket)); + } else { + /* Serialize session id */ + POSIX_ENSURE_LT(conn->actual_protocol_version, S2N_TLS13); + POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_ID)); + POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->session_id_len)); + POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->session_id, conn->session_id_len)); + } + + /* Serialize session state */ + POSIX_GUARD_RESULT(s2n_serialize_resumption_state(conn, to)); + + return 0; +} + +static S2N_RESULT s2n_tls12_client_deserialize_session_state(struct s2n_connection *conn, + struct s2n_blob *ticket, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(from); + + /* Operate on a copy of the connection to avoid mutating the connection on + * failure. We have tests in s2n_resume_test.c that prove this level of copy + * is sufficient. + */ + struct s2n_crypto_parameters *secure = conn->secure; + RESULT_ENSURE_REF(secure); + struct s2n_connection temp_conn = *conn; + struct s2n_crypto_parameters temp_secure = *secure; + temp_conn.secure = &temp_secure; + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &temp_conn.resume_protocol_version)); + + uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(from, S2N_TLS_CIPHER_SUITE_LEN); + RESULT_ENSURE_REF(cipher_suite_wire); + RESULT_GUARD_POSIX(s2n_set_cipher_as_client(&temp_conn, cipher_suite_wire)); + + uint64_t then = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &then)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, temp_conn.secrets.version.tls12.master_secret, + S2N_TLS_SECRET_LEN)); + + if (s2n_stuffer_data_available(from)) { + uint8_t ems_negotiated = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &ems_negotiated)); + temp_conn.ems_negotiated = ems_negotiated; + } + + DEFER_CLEANUP(struct s2n_blob client_ticket = { 0 }, s2n_free); + if (ticket) { + RESULT_GUARD_POSIX(s2n_dup(ticket, &client_ticket)); + } + + /* Finally, actually update the connection */ + RESULT_GUARD_POSIX(s2n_free(&conn->client_ticket)); + *secure = temp_secure; + *conn = temp_conn; + conn->secure = secure; + conn->client_ticket = client_ticket; + ZERO_TO_DISABLE_DEFER_CLEANUP(client_ticket); + + return S2N_RESULT_OK; +} + +/* `s2n_validate_ticket_age` is a best effort check that the session ticket is + * less than one week old. + * + * Clock skew between hosts or the possibility of a clock jump prevent this from + * being a precise check. + */ +static S2N_RESULT s2n_validate_ticket_age(uint64_t current_time, uint64_t ticket_issue_time) +{ + /* If the `ticket_issue_time` is in the future, then we are observing clock skew. + * We shouldn't fully reject the ticket, but we assert that the clock skew is + * less than some MAX_ALLOWED_CLOCK_SKEW_SEC + */ + if (current_time < ticket_issue_time) { + uint64_t clock_skew_in_nanos = ticket_issue_time - current_time; + uint64_t clock_skew_in_seconds = clock_skew_in_nanos / ONE_SEC_IN_NANOS; + RESULT_ENSURE(clock_skew_in_seconds <= MAX_ALLOWED_CLOCK_SKEW_SEC, S2N_ERR_INVALID_SESSION_TICKET); + } else { + uint64_t ticket_age_in_nanos = current_time - ticket_issue_time; + uint64_t ticket_age_in_sec = ticket_age_in_nanos / ONE_SEC_IN_NANOS; + RESULT_ENSURE(ticket_age_in_sec <= ONE_WEEK_IN_SEC, S2N_ERR_INVALID_SESSION_TICKET); + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_tls13_deserialize_session_state(struct s2n_connection *conn, struct s2n_blob *psk_identity, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(psk_identity); + RESULT_ENSURE_REF(from); + + DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe); + RESULT_GUARD(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(&psk, psk_identity->data, psk_identity->size)); + + uint8_t protocol_version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &protocol_version)); + RESULT_ENSURE_GTE(protocol_version, S2N_TLS13); + + uint8_t iana_id[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, iana_id, S2N_TLS_CIPHER_SUITE_LEN)); + struct s2n_cipher_suite *cipher_suite = NULL; + RESULT_GUARD(s2n_cipher_suite_from_iana(iana_id, sizeof(iana_id), &cipher_suite)); + RESULT_ENSURE_REF(cipher_suite); + psk.hmac_alg = cipher_suite->prf_alg; + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.ticket_issue_time)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Clients MUST NOT cache + *# tickets for longer than 7 days, regardless of the ticket_lifetime, + *# and MAY delete tickets earlier based on local policy. + */ + uint64_t current_time = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time)); + RESULT_GUARD(s2n_validate_ticket_age(current_time, psk.ticket_issue_time)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &psk.ticket_age_add)); + + uint8_t secret_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &secret_len)); + RESULT_ENSURE_LTE(secret_len, S2N_TLS_SECRET_LEN); + uint8_t *secret_data = s2n_stuffer_raw_read(from, secret_len); + RESULT_ENSURE_REF(secret_data); + RESULT_GUARD_POSIX(s2n_psk_set_secret(&psk, secret_data, secret_len)); + + if (conn->mode == S2N_SERVER) { + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.keying_material_expiration)); + RESULT_ENSURE(psk.keying_material_expiration > current_time, S2N_ERR_KEYING_MATERIAL_EXPIRED); + } + + uint32_t max_early_data_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &max_early_data_size)); + if (max_early_data_size > 0) { + RESULT_GUARD_POSIX(s2n_psk_configure_early_data(&psk, max_early_data_size, + iana_id[0], iana_id[1])); + + uint8_t app_proto_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &app_proto_size)); + uint8_t *app_proto_data = s2n_stuffer_raw_read(from, app_proto_size); + RESULT_ENSURE_REF(app_proto_data); + RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(&psk, app_proto_data, app_proto_size)); + + uint16_t early_data_context_size = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(from, &early_data_context_size)); + uint8_t *early_data_context_data = s2n_stuffer_raw_read(from, early_data_context_size); + RESULT_ENSURE_REF(early_data_context_data); + RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(&psk, early_data_context_data, early_data_context_size)); + } + + /* Make sure that this connection is configured for resumption PSKs, not external PSKs */ + RESULT_GUARD(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_RESUMPTION)); + /* Remove all previously-set PSKs. To keep the session ticket API behavior consistent + * across protocol versions, we currently only support setting a single resumption PSK. */ + RESULT_GUARD(s2n_psk_parameters_wipe(&conn->psk_params)); + RESULT_GUARD_POSIX(s2n_connection_append_psk(conn, &psk)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_deserialize_resumption_state(struct s2n_connection *conn, + struct s2n_blob *ticket, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(from); + + uint8_t format = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &format)); + + if (format == S2N_SERIALIZED_FORMAT_TLS12_V3) { + if (conn->mode == S2N_SERVER) { + RESULT_GUARD_POSIX(s2n_tls12_deserialize_resumption_state(conn, from)); + } else { + RESULT_GUARD(s2n_tls12_client_deserialize_session_state(conn, ticket, from)); + } + } else if (format == S2N_SERIALIZED_FORMAT_TLS13_V1) { + RESULT_GUARD(s2n_tls13_deserialize_session_state(conn, ticket, from)); + } else { + RESULT_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + conn->set_session = true; + return S2N_RESULT_OK; +} + +static int s2n_client_deserialize_with_session_id(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + uint8_t session_id_len = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(from, &session_id_len)); + + if (session_id_len == 0 || session_id_len > S2N_TLS_SESSION_ID_MAX_LEN + || session_id_len > s2n_stuffer_data_available(from)) { + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + + conn->session_id_len = session_id_len; + POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->session_id, session_id_len)); + + POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, NULL, from)); + + return 0; +} + +static int s2n_client_deserialize_with_session_ticket(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + uint16_t session_ticket_len = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(from, &session_ticket_len)); + + if (session_ticket_len == 0 || session_ticket_len > s2n_stuffer_data_available(from)) { + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + + struct s2n_blob session_ticket = { 0 }; + uint8_t *session_ticket_bytes = s2n_stuffer_raw_read(from, session_ticket_len); + POSIX_ENSURE_REF(session_ticket_bytes); + POSIX_GUARD(s2n_blob_init(&session_ticket, session_ticket_bytes, session_ticket_len)); + + POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, &session_ticket, from)); + return 0; +} + +static int s2n_client_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + uint8_t format = 0; + POSIX_GUARD(s2n_stuffer_read_uint8(from, &format)); + + switch (format) { + case S2N_STATE_WITH_SESSION_ID: + POSIX_GUARD(s2n_client_deserialize_with_session_id(conn, from)); + break; + case S2N_STATE_WITH_SESSION_TICKET: + POSIX_GUARD(s2n_client_deserialize_with_session_ticket(conn, from)); + break; + default: + POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + } + + return 0; +} + +int s2n_resume_from_cache(struct s2n_connection *conn) +{ + S2N_ERROR_IF(conn->session_id_len == 0, S2N_ERR_SESSION_ID_TOO_SHORT); + S2N_ERROR_IF(conn->session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); + + uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob entry = { 0 }; + POSIX_GUARD(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + uint64_t size = entry.size; + int result = conn->config->cache_retrieve(conn, conn->config->cache_retrieve_data, conn->session_id, conn->session_id_len, entry.data, &size); + if (result == S2N_CALLBACK_BLOCKED) { + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + POSIX_ENSURE(result >= S2N_SUCCESS, S2N_ERR_CANCELLED); + + S2N_ERROR_IF(size != entry.size, S2N_ERR_SIZE_MISMATCH); + + struct s2n_stuffer from = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&from, &entry)); + POSIX_GUARD(s2n_stuffer_write(&from, &entry)); + POSIX_GUARD_RESULT(s2n_resume_decrypt_session(conn, &from)); + + return 0; +} + +S2N_RESULT s2n_store_to_cache(struct s2n_connection *conn) +{ + uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob entry = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + struct s2n_stuffer to = { 0 }; + + /* session_id_len should always be >0 since either the Client provided a SessionId or the Server generated a new + * one for the Client */ + RESULT_ENSURE(conn->session_id_len > 0, S2N_ERR_SESSION_ID_TOO_SHORT); + RESULT_ENSURE(conn->session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG); + + RESULT_GUARD_POSIX(s2n_stuffer_init(&to, &entry)); + + struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); + RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &to)); + + /* Store to the cache */ + conn->config->cache_store(conn, conn->config->cache_store_data, S2N_TLS_SESSION_CACHE_TTL, conn->session_id, conn->session_id_len, entry.data, entry.size); + + return S2N_RESULT_OK; +} + +int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(session); + + DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free); + /* size_t is 64-bit integer on 64-bit system, while s2n_alloc's length parameter is a 32-bit integer */ + POSIX_ENSURE(length <= UINT32_MAX, S2N_ERR_INVALID_ARGUMENT); + POSIX_GUARD(s2n_alloc(&session_data, length)); + POSIX_CHECKED_MEMCPY(session_data.data, session, length); + + struct s2n_stuffer from = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&from, &session_data)); + POSIX_GUARD(s2n_stuffer_write(&from, &session_data)); + POSIX_GUARD(s2n_client_deserialize_resumption_state(conn, &from)); + return 0; +} + +int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(session); + + const int len = s2n_connection_get_session_length(conn); + POSIX_GUARD(len); + + if (len == 0) { + return 0; + } + + POSIX_ENSURE((size_t) len <= max_length, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); + + struct s2n_blob serialized_data = { 0 }; + POSIX_GUARD(s2n_blob_init(&serialized_data, session, len)); + POSIX_GUARD(s2n_blob_zero(&serialized_data)); + + struct s2n_stuffer to = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&to, &serialized_data)); + POSIX_GUARD(s2n_client_serialize_resumption_state(conn, &to)); + + return len; +} + +int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + S2N_ERROR_IF(!(conn->config->use_tickets && conn->client_ticket.size > 0), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED); + + /* Session resumption using session ticket */ + return conn->ticket_lifetime_hint; +} + +S2N_RESULT s2n_connection_get_session_state_size(struct s2n_connection *conn, size_t *state_size) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(state_size); + + if (s2n_resume_protocol_version(conn) < S2N_TLS13) { + *state_size = S2N_TLS12_STATE_SIZE_IN_BYTES; + return S2N_RESULT_OK; + } + + *state_size = S2N_TLS13_FIXED_STATE_SIZE; + + uint8_t secret_size = 0; + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + *state_size += secret_size; + + uint32_t server_max_early_data = 0; + RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data)); + if (server_max_early_data > 0) { + *state_size += S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE + + strlen(conn->application_protocol) + + conn->server_early_data_context.size; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_get_session_length_impl(struct s2n_connection *conn, size_t *length) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_REF(length); + *length = 0; + + if (conn->config->use_tickets && conn->client_ticket.size > 0) { + size_t session_state_size = 0; + RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); + *length = S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + session_state_size; + } else if (conn->session_id_len > 0 && conn->actual_protocol_version < S2N_TLS13) { + *length = S2N_STATE_FORMAT_LEN + sizeof(conn->session_id_len) + conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES; + } + return S2N_RESULT_OK; +} + +int s2n_connection_get_session_length(struct s2n_connection *conn) +{ + size_t length = 0; + if (s2n_result_is_ok(s2n_connection_get_session_length_impl(conn, &length))) { + return length; + } + return 0; +} + +int s2n_connection_is_session_resumed(struct s2n_connection *conn) +{ + return conn && IS_RESUMPTION_HANDSHAKE(conn) + && (conn->actual_protocol_version < S2N_TLS13 || conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION); +} + +int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (conn->actual_protocol_version >= S2N_TLS13) { + return (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)); + } else { + return IS_OCSP_STAPLED(conn); + } +} + +S2N_RESULT s2n_config_is_encrypt_key_available(struct s2n_config *config) +{ + RESULT_ENSURE_REF(config); + + uint64_t now = 0; + struct s2n_ticket_key *ticket_key = NULL; + RESULT_GUARD(s2n_config_wall_clock(config, &now)); + RESULT_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + RESULT_GUARD(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + for (uint32_t i = ticket_keys_len; i > 0; i--) { + uint32_t idx = i - 1; + RESULT_GUARD(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); + uint64_t key_intro_time = ticket_key->intro_timestamp; + + if (key_intro_time <= now + && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { + return S2N_RESULT_OK; + } + } + + RESULT_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); +} + +/* This function is used in s2n_get_ticket_encrypt_decrypt_key to compute the weight + * of the keys and to choose a single key from all of the encrypt-decrypt keys. + * Higher the weight of the key, higher the probability of being picked. + */ +int s2n_compute_weight_of_encrypt_decrypt_keys(struct s2n_config *config, + uint8_t *encrypt_decrypt_keys_index, + uint8_t num_encrypt_decrypt_keys, + uint64_t now) +{ + double total_weight = 0; + struct s2n_ticket_key_weight ticket_keys_weight[S2N_MAX_TICKET_KEYS]; + struct s2n_ticket_key *ticket_key = NULL; + + /* Compute weight of encrypt-decrypt keys */ + for (int i = 0; i < num_encrypt_decrypt_keys; i++) { + POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[i], (void **) &ticket_key)); + + uint64_t key_intro_time = ticket_key->intro_timestamp; + uint64_t key_encryption_peak_time = key_intro_time + (config->encrypt_decrypt_key_lifetime_in_nanos / 2); + + /* The % of encryption using this key is linearly increasing */ + if (now < key_encryption_peak_time) { + ticket_keys_weight[i].key_weight = now - key_intro_time; + } else { + /* The % of encryption using this key is linearly decreasing */ + ticket_keys_weight[i].key_weight = (config->encrypt_decrypt_key_lifetime_in_nanos / 2) - (now - key_encryption_peak_time); + } + + ticket_keys_weight[i].key_index = encrypt_decrypt_keys_index[i]; + total_weight += ticket_keys_weight[i].key_weight; + } + + /* Pick a random number in [0, 1). Using 53 bits (IEEE 754 double-precision floats). */ + uint64_t random_int = 0; + POSIX_GUARD_RESULT(s2n_public_random(pow(2, 53), &random_int)); + double random = (double) random_int / (double) pow(2, 53); + + /* Compute cumulative weight of encrypt-decrypt keys */ + for (int i = 0; i < num_encrypt_decrypt_keys; i++) { + ticket_keys_weight[i].key_weight = ticket_keys_weight[i].key_weight / total_weight; + + if (i > 0) { + ticket_keys_weight[i].key_weight += ticket_keys_weight[i - 1].key_weight; + } + + if (ticket_keys_weight[i].key_weight > random) { + return ticket_keys_weight[i].key_index; + } + } + + POSIX_BAIL(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED); +} + +/* This function is used in s2n_resume_encrypt_session_ticket in order for s2n to + * choose a key in encrypt-decrypt state from all of the keys added to config + */ +struct s2n_ticket_key *s2n_get_ticket_encrypt_decrypt_key(struct s2n_config *config) +{ + uint8_t num_encrypt_decrypt_keys = 0; + uint8_t encrypt_decrypt_keys_index[S2N_MAX_TICKET_KEYS] = { 0 }; + struct s2n_ticket_key *ticket_key = NULL; + + uint64_t now = 0; + PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + PTR_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + for (uint32_t i = ticket_keys_len; i > 0; i--) { + uint32_t idx = i - 1; + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); + uint64_t key_intro_time = ticket_key->intro_timestamp; + + /* A key can be used at its intro time (<=) and it can be used up to (<) + * its expiration time. + */ + if (key_intro_time <= now + && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) { + encrypt_decrypt_keys_index[num_encrypt_decrypt_keys] = idx; + num_encrypt_decrypt_keys++; + } + } + + if (num_encrypt_decrypt_keys == 0) { + PTR_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + } + + if (num_encrypt_decrypt_keys == 1) { + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[0], (void **) &ticket_key)); + return ticket_key; + } + + int8_t idx = 0; + PTR_GUARD_POSIX(idx = s2n_compute_weight_of_encrypt_decrypt_keys(config, encrypt_decrypt_keys_index, num_encrypt_decrypt_keys, now)); + + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key)); + return ticket_key; +} + +/* This function is used in s2n_resume_decrypt_session in order for s2n to + * find the matching key that was used for encryption. + */ +struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint8_t name[S2N_TICKET_KEY_NAME_LEN]) +{ + uint64_t now = 0; + struct s2n_ticket_key *ticket_key = NULL; + PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + PTR_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + for (uint32_t i = 0; i < ticket_keys_len; i++) { + PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); + + if (s2n_constant_time_equals(ticket_key->key_name, name, S2N_TICKET_KEY_NAME_LEN)) { + /* Check to see if the key has expired */ + if (now >= ticket_key->intro_timestamp + + config->encrypt_decrypt_key_lifetime_in_nanos + + config->decrypt_key_lifetime_in_nanos) { + return NULL; + } + + return ticket_key; + } + } + + 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_ticket_key *key, struct s2n_stuffer *to) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(to); + + 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, 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)); + RESULT_GUARD(s2n_aes256_gcm.set_encryption_key(&aes_ticket_key, &aes_key_blob)); + + /* Ensure we never encrypt with a zero-filled key */ + uint8_t zero_block[S2N_AES256_KEY_LEN] = { 0 }; + RESULT_ENSURE(!s2n_constant_time_equals(key->aes_key, zero_block, S2N_AES256_KEY_LEN), + S2N_ERR_KEY_CHECK); + + /* Initialize Additional Authenticated Data */ + uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; + struct s2n_blob aad_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); + struct s2n_stuffer aad = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); + 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 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); + RESULT_GUARD(s2n_get_public_random_data(&iv)); + RESULT_GUARD_POSIX(s2n_stuffer_write(to, &iv)); + + /* Write serialized session state */ + uint32_t plaintext_state_size = s2n_stuffer_data_available(to); + RESULT_GUARD(s2n_serialize_resumption_state(conn, to)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(to, S2N_TLS_GCM_TAG_LEN)); + + /* Initialize blob to be encrypted */ + struct s2n_blob state_blob = { 0 }; + struct s2n_stuffer copy_for_encryption = *to; + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(©_for_encryption, plaintext_state_size)); + uint32_t state_blob_size = s2n_stuffer_data_available(©_for_encryption); + uint8_t *state_blob_data = s2n_stuffer_raw_read(©_for_encryption, state_blob_size); + RESULT_ENSURE_REF(state_blob_data); + RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, state_blob_data, state_blob_size)); + + RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.encrypt(&aes_ticket_key, &iv, &aad_blob, &state_blob, &state_blob)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct s2n_stuffer *from) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(from); + RESULT_ENSURE_REF(conn->config); + + /* 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))); + + struct s2n_ticket_key *key = s2n_find_ticket_key(conn->config, key_name); + /* 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 }; + RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data))); + RESULT_GUARD_POSIX(s2n_stuffer_read(from, &iv)); + + /* Initialize AES key */ + struct s2n_blob aes_key_blob = { 0 }; + 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)); + RESULT_GUARD(s2n_aes256_gcm.set_decryption_key(&aes_ticket_key, &aes_key_blob)); + + /* Initialize Additional Authenticated Data */ + uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 }; + struct s2n_blob aad_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data))); + struct s2n_stuffer aad = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob)); + 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))); + + /* Initialize blob to be decrypted */ + struct s2n_blob en_blob = { 0 }; + uint32_t en_blob_size = s2n_stuffer_data_available(from); + uint8_t *en_blob_data = s2n_stuffer_raw_read(from, en_blob_size); + RESULT_ENSURE_REF(en_blob_data); + RESULT_GUARD_POSIX(s2n_blob_init(&en_blob, en_blob_data, en_blob_size)); + + RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.decrypt(&aes_ticket_key, &iv, &aad_blob, &en_blob, &en_blob)); + + /* Parse decrypted state */ + struct s2n_blob state_blob = { 0 }; + uint32_t state_blob_size = en_blob_size - S2N_TLS_GCM_TAG_LEN; + RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, en_blob.data, state_blob_size)); + struct s2n_stuffer state_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&state_stuffer, &state_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&state_stuffer, state_blob_size)); + RESULT_GUARD(s2n_deserialize_resumption_state(conn, &from->blob, &state_stuffer)); + + return S2N_RESULT_OK; +} + +/* This function is used to remove all or just one expired key from server config */ +int s2n_config_wipe_expired_ticket_crypto_keys(struct s2n_config *config, int8_t expired_key_index) +{ + int num_of_expired_keys = 0; + int expired_keys_index[S2N_MAX_TICKET_KEYS]; + struct s2n_ticket_key *ticket_key = NULL; + + if (expired_key_index != -1) { + expired_keys_index[num_of_expired_keys] = expired_key_index; + num_of_expired_keys++; + + goto end; + } + + uint64_t now = 0; + POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now)); + POSIX_ENSURE_REF(config->ticket_keys); + + uint32_t ticket_keys_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + for (uint32_t i = 0; i < ticket_keys_len; i++) { + POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key)); + if (now >= ticket_key->intro_timestamp + + config->encrypt_decrypt_key_lifetime_in_nanos + + config->decrypt_key_lifetime_in_nanos) { + expired_keys_index[num_of_expired_keys] = i; + num_of_expired_keys++; + } + } + +end: + for (int j = 0; j < num_of_expired_keys; j++) { + POSIX_GUARD_RESULT(s2n_array_remove(config->ticket_keys, expired_keys_index[j] - j)); + } + + return 0; +} + +int s2n_config_store_ticket_key(struct s2n_config *config, struct s2n_ticket_key *key) +{ + uint32_t ticket_keys_len = 0; + POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len)); + + /* The ticket key name and secret must both be unique. */ + for (uint32_t i = 0; i < ticket_keys_len; i++) { + struct s2n_ticket_key *other_key = NULL; + POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &other_key)); + POSIX_ENSURE(!s2n_constant_time_equals(key->key_name, other_key->key_name, s2n_array_len(key->key_name)), + S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + POSIX_ENSURE(!s2n_constant_time_equals(key->aes_key, other_key->aes_key, s2n_array_len(key->aes_key)), + S2N_ERR_TICKET_KEY_NOT_UNIQUE); + } + + POSIX_GUARD_RESULT(s2n_array_insert_and_copy(config->ticket_keys, ticket_keys_len, key)); + return S2N_SUCCESS; +} + +int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num) +{ + POSIX_ENSURE_REF(config); + + config->initial_tickets_to_send = num; + POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, true)); + + return S2N_SUCCESS; +} + +int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num) +{ + POSIX_ENSURE_REF(conn); + POSIX_GUARD_RESULT(s2n_psk_validate_keying_material(conn)); + + uint32_t out = conn->tickets_to_send + num; + POSIX_ENSURE(out <= UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); + conn->tickets_to_send = out; + + return S2N_SUCCESS; +} + +int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(num); + POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE); + *num = conn->tickets_sent; + return S2N_SUCCESS; +} + +int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs) +{ + POSIX_ENSURE_REF(conn); + conn->server_keying_material_lifetime = lifetime_in_secs; + return S2N_SUCCESS; +} + +int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx) +{ + POSIX_ENSURE_MUT(config); + + config->session_ticket_cb = callback; + config->session_ticket_ctx = ctx; + return S2N_SUCCESS; +} + +int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len) +{ + POSIX_ENSURE_REF(ticket); + POSIX_ENSURE_MUT(data_len); + + *data_len = ticket->ticket_data.size; + return S2N_SUCCESS; +} + +int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data) +{ + POSIX_ENSURE_REF(ticket); + POSIX_ENSURE_MUT(data); + + POSIX_ENSURE(ticket->ticket_data.size <= max_data_len, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); + POSIX_CHECKED_MEMCPY(data, ticket->ticket_data.data, ticket->ticket_data.size); + + return S2N_SUCCESS; +} + +int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime) +{ + POSIX_ENSURE_REF(ticket); + POSIX_ENSURE_REF(session_lifetime); + + *session_lifetime = ticket->session_lifetime; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_send.c b/tls/s2n_send.c index 1e5cfe3e8f0..bfbdf5b5824 100644 --- a/tls/s2n_send.c +++ b/tls/s2n_send.c @@ -1,275 +1,275 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "crypto/s2n_cipher.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_handshake.h" -#include "tls/s2n_internal.h" -#include "tls/s2n_ktls.h" -#include "tls/s2n_post_handshake.h" -#include "tls/s2n_record.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_io.h" -#include "utils/s2n_safety.h" - -/* - * Determine whether there is currently sufficient space in the send buffer to construct - * another record, or if we need to flush now. - * - * We only buffer multiple records when sending application data, NOT when - * sending handshake messages or alerts. If the next record is a post-handshake message - * or an alert, then the send buffer will be flushed regardless of the result of this method. - * Therefore we don't need to consider the size of any potential KeyUpdate messages, - * NewSessionTicket messages, or Alerts. - */ -bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size) -{ - /* Always flush if not buffering multiple records. */ - if (!conn->multirecord_send) { - return true; - } - - /* Flush if all data has been sent. */ - ssize_t remaining_payload_size = total_message_size - conn->current_user_data_consumed; - if (remaining_payload_size <= 0) { - return true; - } - - uint16_t max_payload_size = 0; - if (!s2n_result_is_ok(s2n_record_max_write_payload_size(conn, &max_payload_size))) { - /* When in doubt, flush */ - return true; - } - max_payload_size = S2N_MIN(max_payload_size, remaining_payload_size); - - uint16_t max_write_size = 0; - if (!s2n_result_is_ok(s2n_record_max_write_size(conn, max_payload_size, &max_write_size))) { - /* When in doubt, flush */ - return true; - } - - /* Flush if the stuffer can't store the max possible record size without growing. - * - * However, the stuffer is allocated when the record is sent, so if the stuffer - * hasn't been allocated, assume it will have enough space. - */ - uint32_t available_space = s2n_stuffer_space_remaining(&conn->out); - if (available_space < max_write_size && !s2n_stuffer_is_freed(&conn->out)) { - return true; - } - - return false; -} - -int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(blocked); - *blocked = S2N_BLOCKED_ON_WRITE; - - /* Write any data that's already pending */ - while (s2n_stuffer_data_available(&conn->out)) { - errno = 0; - int w = s2n_connection_send_stuffer(&conn->out, conn, s2n_stuffer_data_available(&conn->out)); - POSIX_GUARD_RESULT(s2n_io_check_write_result(w)); - conn->wire_bytes_out += w; - } - POSIX_GUARD(s2n_stuffer_rewrite(&conn->out)); - - if (conn->reader_warning_out) { - POSIX_GUARD_RESULT(s2n_alerts_write_warning(conn)); - conn->reader_warning_out = 0; - POSIX_GUARD(s2n_flush(conn, blocked)); - } - - *blocked = S2N_NOT_BLOCKED; - return 0; -} - -S2N_RESULT s2n_sendv_with_offset_total_size(const struct iovec *bufs, ssize_t count, - ssize_t offs, ssize_t *total_size_out) -{ - RESULT_ENSURE_REF(total_size_out); - if (count > 0) { - RESULT_ENSURE_REF(bufs); - } - - size_t total_size = 0; - for (ssize_t i = 0; i < count; i++) { - size_t iov_len = bufs[i].iov_len; - /* Account for any offset */ - if (offs > 0) { - size_t offs_consumed = S2N_MIN((size_t) offs, iov_len); - iov_len -= offs_consumed; - offs -= offs_consumed; - } - RESULT_ENSURE(S2N_ADD_IS_OVERFLOW_SAFE(total_size, iov_len, SIZE_MAX), - S2N_ERR_INVALID_ARGUMENT); - total_size += iov_len; - } - - /* We must have fully accounted for the offset, or else the offset is larger - * than the available data and our inputs are invalid. - */ - RESULT_ENSURE(offs == 0, S2N_ERR_INVALID_ARGUMENT); - - RESULT_ENSURE(total_size <= SSIZE_MAX, S2N_ERR_INVALID_ARGUMENT); - *total_size_out = total_size; - return S2N_RESULT_OK; -} - -ssize_t s2n_sendv_with_offset_impl(struct s2n_connection *conn, const struct iovec *bufs, - ssize_t count, ssize_t offs, s2n_blocked_status *blocked) -{ - ssize_t user_data_sent = 0, total_size = 0; - - POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE), S2N_ERR_CLOSED); - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); - - /* Flush any pending I/O */ - POSIX_GUARD(s2n_flush(conn, blocked)); - - if (conn->ktls_send_enabled) { - return s2n_ktls_sendv_with_offset(conn, bufs, count, offs, blocked); - } - - /* Acknowledge consumed and flushed user data as sent */ - user_data_sent = conn->current_user_data_consumed; - - *blocked = S2N_BLOCKED_ON_WRITE; - - uint16_t max_payload_size = 0; - POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_payload_size)); - - /* TLS 1.0 and SSLv3 are vulnerable to the so-called Beast attack. Work - * around this by splitting messages into one byte records, and then - * the remainder can follow as usual. - */ - int cbcHackUsed = 0; - - struct s2n_crypto_parameters *writer = conn->server; - if (conn->mode == S2N_CLIENT) { - writer = conn->client; - } - - POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count, offs, &total_size)); - /* Defensive check against an invalid retry */ - POSIX_ENSURE(conn->current_user_data_consumed <= total_size, S2N_ERR_SEND_SIZE); - POSIX_GUARD_RESULT(s2n_early_data_validate_send(conn, total_size)); - - if (conn->dynamic_record_timeout_threshold > 0) { - uint64_t elapsed = 0; - POSIX_GUARD_RESULT(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); - /* Reset record size back to a single segment after threshold seconds of inactivity */ - if (elapsed - conn->last_write_elapsed > (uint64_t) conn->dynamic_record_timeout_threshold * 1000000000) { - conn->active_application_bytes_consumed = 0; - } - conn->last_write_elapsed = elapsed; - } - - /* Now write the data we were asked to send this round */ - while (total_size - conn->current_user_data_consumed) { - ssize_t to_write = S2N_MIN(total_size - conn->current_user_data_consumed, max_payload_size); - - /* If dynamic record size is enabled, - * use small TLS records that fit into a single TCP segment for the threshold bytes of data - */ - if (conn->active_application_bytes_consumed < (uint64_t) conn->dynamic_record_resize_threshold) { - uint16_t min_payload_size = 0; - POSIX_GUARD_RESULT(s2n_record_min_write_payload_size(conn, &min_payload_size)); - to_write = S2N_MIN(min_payload_size, to_write); - } - - /* Don't split messages in server mode for interoperability with naive clients. - * Some clients may have expectations based on the amount of content in the first record. - */ - if (conn->actual_protocol_version < S2N_TLS11 - && writer->cipher_suite->record_alg->cipher->type == S2N_CBC && conn->mode != S2N_SERVER) { - if (to_write > 1 && cbcHackUsed == 0) { - to_write = 1; - cbcHackUsed = 1; - } - } - - POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); - - /* Write and encrypt the record */ - int written_to_record = s2n_record_writev(conn, TLS_APPLICATION_DATA, bufs, count, - conn->current_user_data_consumed + offs, to_write); - POSIX_GUARD(written_to_record); - conn->current_user_data_consumed += written_to_record; - conn->active_application_bytes_consumed += written_to_record; - - /* Send it, unless we're waiting for more records */ - if (s2n_should_flush(conn, total_size)) { - if (s2n_flush(conn, blocked) < 0) { - if (s2n_errno == S2N_ERR_IO_BLOCKED && user_data_sent > 0) { - /* We successfully sent >0 user bytes on the wire, but not the full requested payload - * because we became blocked on I/O. Acknowledge the data sent. */ - - conn->current_user_data_consumed -= user_data_sent; - return user_data_sent; - } else { - S2N_ERROR_PRESERVE_ERRNO(); - } - } - - /* Acknowledge consumed and flushed user data as sent */ - user_data_sent = conn->current_user_data_consumed; - } - } - - /* If everything has been written, then there's no user data pending */ - conn->current_user_data_consumed = 0; - - *blocked = S2N_NOT_BLOCKED; - return total_size; -} - -ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, - ssize_t offs, s2n_blocked_status *blocked) -{ - POSIX_ENSURE(!conn->send_in_use, S2N_ERR_REENTRANCY); - conn->send_in_use = true; - - ssize_t result = s2n_sendv_with_offset_impl(conn, bufs, count, offs, blocked); - POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); - - POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); - - conn->send_in_use = false; - return result; -} - -ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked) -{ - return s2n_sendv_with_offset(conn, bufs, count, 0, blocked); -} - -ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked) -{ - struct iovec iov; - iov.iov_base = (void *) (uintptr_t) buf; - iov.iov_len = size; - return s2n_sendv_with_offset(conn, &iov, 1, 0, blocked); -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "crypto/s2n_cipher.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_io.h" +#include "utils/s2n_safety.h" + +/* + * Determine whether there is currently sufficient space in the send buffer to construct + * another record, or if we need to flush now. + * + * We only buffer multiple records when sending application data, NOT when + * sending handshake messages or alerts. If the next record is a post-handshake message + * or an alert, then the send buffer will be flushed regardless of the result of this method. + * Therefore we don't need to consider the size of any potential KeyUpdate messages, + * NewSessionTicket messages, or Alerts. + */ +bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size) +{ + /* Always flush if not buffering multiple records. */ + if (!conn->multirecord_send) { + return true; + } + + /* Flush if all data has been sent. */ + ssize_t remaining_payload_size = total_message_size - conn->current_user_data_consumed; + if (remaining_payload_size <= 0) { + return true; + } + + uint16_t max_payload_size = 0; + if (!s2n_result_is_ok(s2n_record_max_write_payload_size(conn, &max_payload_size))) { + /* When in doubt, flush */ + return true; + } + max_payload_size = S2N_MIN(max_payload_size, remaining_payload_size); + + uint16_t max_write_size = 0; + if (!s2n_result_is_ok(s2n_record_max_write_size(conn, max_payload_size, &max_write_size))) { + /* When in doubt, flush */ + return true; + } + + /* Flush if the stuffer can't store the max possible record size without growing. + * + * However, the stuffer is allocated when the record is sent, so if the stuffer + * hasn't been allocated, assume it will have enough space. + */ + uint32_t available_space = s2n_stuffer_space_remaining(&conn->out); + if (available_space < max_write_size && !s2n_stuffer_is_freed(&conn->out)) { + return true; + } + + return false; +} + +int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(blocked); + *blocked = S2N_BLOCKED_ON_WRITE; + + /* Write any data that's already pending */ + while (s2n_stuffer_data_available(&conn->out)) { + errno = 0; + int w = s2n_connection_send_stuffer(&conn->out, conn, s2n_stuffer_data_available(&conn->out)); + POSIX_GUARD_RESULT(s2n_io_check_write_result(w)); + conn->wire_bytes_out += w; + } + POSIX_GUARD(s2n_stuffer_rewrite(&conn->out)); + + if (conn->reader_warning_out) { + POSIX_GUARD_RESULT(s2n_alerts_write_warning(conn)); + conn->reader_warning_out = 0; + POSIX_GUARD(s2n_flush(conn, blocked)); + } + + *blocked = S2N_NOT_BLOCKED; + return 0; +} + +S2N_RESULT s2n_sendv_with_offset_total_size(const struct iovec *bufs, ssize_t count, + ssize_t offs, ssize_t *total_size_out) +{ + RESULT_ENSURE_REF(total_size_out); + if (count > 0) { + RESULT_ENSURE_REF(bufs); + } + + size_t total_size = 0; + for (ssize_t i = 0; i < count; i++) { + size_t iov_len = bufs[i].iov_len; + /* Account for any offset */ + if (offs > 0) { + size_t offs_consumed = S2N_MIN((size_t) offs, iov_len); + iov_len -= offs_consumed; + offs -= offs_consumed; + } + RESULT_ENSURE(S2N_ADD_IS_OVERFLOW_SAFE(total_size, iov_len, SIZE_MAX), + S2N_ERR_INVALID_ARGUMENT); + total_size += iov_len; + } + + /* We must have fully accounted for the offset, or else the offset is larger + * than the available data and our inputs are invalid. + */ + RESULT_ENSURE(offs == 0, S2N_ERR_INVALID_ARGUMENT); + + RESULT_ENSURE(total_size <= SSIZE_MAX, S2N_ERR_INVALID_ARGUMENT); + *total_size_out = total_size; + return S2N_RESULT_OK; +} + +ssize_t s2n_sendv_with_offset_impl(struct s2n_connection *conn, const struct iovec *bufs, + ssize_t count, ssize_t offs, s2n_blocked_status *blocked) +{ + ssize_t user_data_sent = 0, total_size = 0; + + POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE), S2N_ERR_CLOSED); + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC); + + /* Flush any pending I/O */ + POSIX_GUARD(s2n_flush(conn, blocked)); + + if (conn->ktls_send_enabled) { + return s2n_ktls_sendv_with_offset(conn, bufs, count, offs, blocked); + } + + /* Acknowledge consumed and flushed user data as sent */ + user_data_sent = conn->current_user_data_consumed; + + *blocked = S2N_BLOCKED_ON_WRITE; + + uint16_t max_payload_size = 0; + POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_payload_size)); + + /* TLS 1.0 and SSLv3 are vulnerable to the so-called Beast attack. Work + * around this by splitting messages into one byte records, and then + * the remainder can follow as usual. + */ + int cbcHackUsed = 0; + + struct s2n_crypto_parameters *writer = conn->server; + if (conn->mode == S2N_CLIENT) { + writer = conn->client; + } + + POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count, offs, &total_size)); + /* Defensive check against an invalid retry */ + POSIX_ENSURE(conn->current_user_data_consumed <= total_size, S2N_ERR_SEND_SIZE); + POSIX_GUARD_RESULT(s2n_early_data_validate_send(conn, total_size)); + + if (conn->dynamic_record_timeout_threshold > 0) { + uint64_t elapsed = 0; + POSIX_GUARD_RESULT(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed)); + /* Reset record size back to a single segment after threshold seconds of inactivity */ + if (elapsed - conn->last_write_elapsed > (uint64_t) conn->dynamic_record_timeout_threshold * 1000000000) { + conn->active_application_bytes_consumed = 0; + } + conn->last_write_elapsed = elapsed; + } + + /* Now write the data we were asked to send this round */ + while (total_size - conn->current_user_data_consumed) { + ssize_t to_write = S2N_MIN(total_size - conn->current_user_data_consumed, max_payload_size); + + /* If dynamic record size is enabled, + * use small TLS records that fit into a single TCP segment for the threshold bytes of data + */ + if (conn->active_application_bytes_consumed < (uint64_t) conn->dynamic_record_resize_threshold) { + uint16_t min_payload_size = 0; + POSIX_GUARD_RESULT(s2n_record_min_write_payload_size(conn, &min_payload_size)); + to_write = S2N_MIN(min_payload_size, to_write); + } + + /* Don't split messages in server mode for interoperability with naive clients. + * Some clients may have expectations based on the amount of content in the first record. + */ + if (conn->actual_protocol_version < S2N_TLS11 + && writer->cipher_suite->record_alg->cipher->type == S2N_CBC && conn->mode != S2N_SERVER) { + if (to_write > 1 && cbcHackUsed == 0) { + to_write = 1; + cbcHackUsed = 1; + } + } + + POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); + + /* Write and encrypt the record */ + int written_to_record = s2n_record_writev(conn, TLS_APPLICATION_DATA, bufs, count, + conn->current_user_data_consumed + offs, to_write); + POSIX_GUARD(written_to_record); + conn->current_user_data_consumed += written_to_record; + conn->active_application_bytes_consumed += written_to_record; + + /* Send it, unless we're waiting for more records */ + if (s2n_should_flush(conn, total_size)) { + if (s2n_flush(conn, blocked) < 0) { + if (s2n_errno == S2N_ERR_IO_BLOCKED && user_data_sent > 0) { + /* We successfully sent >0 user bytes on the wire, but not the full requested payload + * because we became blocked on I/O. Acknowledge the data sent. */ + + conn->current_user_data_consumed -= user_data_sent; + return user_data_sent; + } else { + S2N_ERROR_PRESERVE_ERRNO(); + } + } + + /* Acknowledge consumed and flushed user data as sent */ + user_data_sent = conn->current_user_data_consumed; + } + } + + /* If everything has been written, then there's no user data pending */ + conn->current_user_data_consumed = 0; + + *blocked = S2N_NOT_BLOCKED; + return total_size; +} + +ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, + ssize_t offs, s2n_blocked_status *blocked) +{ + POSIX_ENSURE(!conn->send_in_use, S2N_ERR_REENTRANCY); + conn->send_in_use = true; + + ssize_t result = s2n_sendv_with_offset_impl(conn, bufs, count, offs, blocked); + POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result)); + + POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); + + conn->send_in_use = false; + return result; +} + +ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked) +{ + return s2n_sendv_with_offset(conn, bufs, count, 0, blocked); +} + +ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked) +{ + struct iovec iov; + iov.iov_base = (void *) (uintptr_t) buf; + iov.iov_len = size; + return s2n_sendv_with_offset(conn, &iov, 1, 0, blocked); +} diff --git a/tls/s2n_server_hello.c b/tls/s2n_server_hello.c index 221a10e714b..be45994cd82 100644 --- a/tls/s2n_server_hello.c +++ b/tls/s2n_server_hello.c @@ -1,344 +1,344 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_cipher_preferences.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_server_extensions.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13.h" -#include "tls/s2n_tls13_handshake.h" -#include "tls/s2n_tls13_key_schedule.h" -#include "utils/s2n_bitmap.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -/* From RFC5246 7.4.1.2. */ -#define S2N_TLS_COMPRESSION_METHOD_NULL 0 - -/* From RFC8446 4.1.3. */ -#define S2N_DOWNGRADE_PROTECTION_SIZE 8 -const uint8_t tls12_downgrade_protection_bytes[] = { - 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01 -}; - -const uint8_t tls11_downgrade_protection_bytes[] = { - 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x00 -}; - -static int s2n_random_value_is_hello_retry(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - POSIX_ENSURE(s2n_constant_time_equals(hello_retry_req_random, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN), - S2N_ERR_INVALID_HELLO_RETRY); - - return S2N_SUCCESS; -} - -static int s2n_client_detect_downgrade_mechanism(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; - - /* Detect downgrade attacks according to RFC 8446 section 4.1.3 */ - if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version == S2N_TLS12) { - if (s2n_constant_time_equals(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { - POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - } - } else if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version <= S2N_TLS11) { - if (s2n_constant_time_equals(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { - POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - } - } - - return 0; -} - -static int s2n_server_add_downgrade_mechanism(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; - - /* Protect against downgrade attacks according to RFC 8446 section 4.1.3 */ - if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version == S2N_TLS12) { - /* TLS1.3 servers MUST use a special random value when negotiating TLS1.2 */ - POSIX_CHECKED_MEMCPY(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); - } else if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version <= S2N_TLS11) { - /* TLS1.3 servers MUST, use a special random value when negotiating TLS1.1 or below */ - POSIX_CHECKED_MEMCPY(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); - } - - return 0; -} - -static int s2n_server_hello_parse(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - struct s2n_stuffer *in = &conn->handshake.io; - uint8_t compression_method = 0; - uint8_t session_id_len = 0; - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN]; - - POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - POSIX_GUARD(s2n_stuffer_read_bytes(in, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); - - uint8_t legacy_version = (uint8_t) (protocol_version[0] * 10) + protocol_version[1]; - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 - *# Upon receiving a message with type server_hello, implementations MUST - *# first examine the Random value and, if it matches this value, process - *# it as described in Section 4.1.4). - **/ - if (s2n_random_value_is_hello_retry(conn) == S2N_SUCCESS) { - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# If a client receives a second - *# HelloRetryRequest in the same connection (i.e., where the ClientHello - *# was itself in response to a HelloRetryRequest), it MUST abort the - *# handshake with an "unexpected_message" alert. - **/ - POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_INVALID_HELLO_RETRY); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version - **/ - POSIX_ENSURE(legacy_version == S2N_TLS12, S2N_ERR_INVALID_HELLO_RETRY); - - POSIX_GUARD(s2n_set_hello_retry_required(conn)); - } - - POSIX_GUARD(s2n_stuffer_read_uint8(in, &session_id_len)); - S2N_ERROR_IF(session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); - POSIX_GUARD(s2n_stuffer_read_bytes(in, session_id, session_id_len)); - - uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(in, S2N_TLS_CIPHER_SUITE_LEN); - POSIX_ENSURE_REF(cipher_suite_wire); - - POSIX_GUARD(s2n_stuffer_read_uint8(in, &compression_method)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 - *# legacy_compression_method: A single byte which MUST have the - *# value 0. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version, legacy_session_id_echo, cipher_suite, and - *# legacy_compression_method - **/ - S2N_ERROR_IF(compression_method != S2N_TLS_COMPRESSION_METHOD_NULL, S2N_ERR_BAD_MESSAGE); - - bool session_ids_match = session_id_len != 0 && session_id_len == conn->session_id_len - && s2n_constant_time_equals(session_id, conn->session_id, session_id_len); - if (!session_ids_match) { - conn->ems_negotiated = false; - } - - POSIX_GUARD(s2n_server_extensions_recv(conn, in)); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# The server's extensions MUST contain "supported_versions". - **/ - if (s2n_is_hello_retry_message(conn)) { - s2n_extension_type_id supported_versions_id = s2n_unsupported_extension; - POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); - POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_responses_received, supported_versions_id), - S2N_ERR_MISSING_EXTENSION); - } - - if (conn->server_protocol_version >= S2N_TLS13) { - POSIX_ENSURE(!conn->handshake.renegotiation, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.3 - *# A client which - *# receives a legacy_session_id_echo field that does not match what - *# it sent in the ClientHello MUST abort the handshake with an - *# "illegal_parameter" alert. - * - *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 - *# Upon receipt of a HelloRetryRequest, the client MUST check the - *# legacy_version, legacy_session_id_echo - **/ - POSIX_ENSURE(session_ids_match || (session_id_len == 0 && conn->session_id_len == 0), S2N_ERR_BAD_MESSAGE); - - conn->actual_protocol_version = conn->server_protocol_version; - POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); - - /* Erase TLS 1.2 client session ticket which might have been set for session resumption */ - POSIX_GUARD(s2n_free(&conn->client_ticket)); - } else { - conn->server_protocol_version = legacy_version; - - POSIX_ENSURE(!s2n_client_detect_downgrade_mechanism(conn), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); - POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - /* Hello retries are only supported in >=TLS1.3. */ - POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_BAD_MESSAGE); - - /* - *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.3 - *# A client that attempts to send 0-RTT data MUST fail a connection if - *# it receives a ServerHello with TLS 1.2 or older. - */ - POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - - const struct s2n_security_policy *security_policy = NULL; - POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); - - if (conn->server_protocol_version < security_policy->minimum_protocol_version - || conn->server_protocol_version > conn->client_protocol_version) { - POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); - POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); - } - - conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); - - /* - *= https://www.rfc-editor.org/rfc/rfc5077#section-3.4 - *# If the server accepts the ticket - *# and the Session ID is not empty, then it MUST respond with the same - *# Session ID present in the ClientHello. This allows the client to - *# easily differentiate when the server is resuming a session from when - *# it is falling back to a full handshake. - */ - if (session_ids_match) { - /* check if the resumed session state is valid */ - POSIX_ENSURE(conn->resume_protocol_version == conn->actual_protocol_version, S2N_ERR_BAD_MESSAGE); - POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite_wire, S2N_TLS_CIPHER_SUITE_LEN), - S2N_ERR_BAD_MESSAGE); - - /* Session is resumed */ - conn->client_session_resumed = 1; - } else { - conn->session_id_len = session_id_len; - POSIX_CHECKED_MEMCPY(conn->session_id, session_id, session_id_len); - POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); - /* Erase master secret which might have been set for session resumption */ - POSIX_CHECKED_MEMSET((uint8_t *) conn->secrets.version.tls12.master_secret, 0, S2N_TLS_SECRET_LEN); - - /* Erase client session ticket which might have been set for session resumption */ - POSIX_GUARD(s2n_free(&conn->client_ticket)); - } - } - - /* If it is not possible to accept early data on this connection - * (for example, because no PSK was negotiated) we need to reject early data now. - * Otherwise, early data logic may make certain invalid assumptions about the - * state of the connection (for example, that the prf is the early data prf). - */ - POSIX_GUARD_RESULT(s2n_early_data_accept_or_reject(conn)); - if (conn->early_data_state == S2N_EARLY_DATA_REJECTED) { - POSIX_GUARD_RESULT(s2n_tls13_key_schedule_reset(conn)); - } - - return 0; -} - -int s2n_server_hello_recv(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - /* Read the message off the wire */ - POSIX_GUARD(s2n_server_hello_parse(conn)); - - conn->actual_protocol_version_established = 1; - - POSIX_GUARD(s2n_conn_set_handshake_type(conn)); - - /* If this is a HelloRetryRequest, we don't process the ServerHello. - * Instead we proceed with retry logic. */ - if (s2n_is_hello_retry_message(conn)) { - POSIX_GUARD(s2n_server_hello_retry_recv(conn)); - return 0; - } - - if (conn->actual_protocol_version < S2N_TLS13 && s2n_connection_is_session_resumed(conn)) { - POSIX_GUARD(s2n_prf_key_expansion(conn)); - } - - /* Update the required hashes for this connection */ - POSIX_GUARD(s2n_conn_update_required_handshake_hashes(conn)); - - return 0; -} - -int s2n_server_hello_write_message(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - - /* The actual_protocol_version is set while processing the CLIENT_HELLO message, so - * it could be S2N_TLS13. SERVER_HELLO should always respond with the legacy version. - * https://tools.ietf.org/html/rfc8446#section-4.1.3 */ - const uint16_t legacy_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); - uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; - protocol_version[0] = (uint8_t) (legacy_protocol_version / 10); - protocol_version[1] = (uint8_t) (legacy_protocol_version % 10); - - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, conn->session_id_len)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->session_id, conn->session_id_len)); - POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); - POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, S2N_TLS_COMPRESSION_METHOD_NULL)); - - return 0; -} - -int s2n_server_hello_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - struct s2n_stuffer server_random = { 0 }; - struct s2n_blob b = { 0 }; - POSIX_GUARD(s2n_blob_init(&b, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); - - /* Create the server random data */ - POSIX_GUARD(s2n_stuffer_init(&server_random, &b)); - - struct s2n_blob rand_data = { 0 }; - POSIX_GUARD(s2n_blob_init(&rand_data, s2n_stuffer_raw_write(&server_random, S2N_TLS_RANDOM_DATA_LEN), S2N_TLS_RANDOM_DATA_LEN)); - POSIX_ENSURE_REF(rand_data.data); - POSIX_GUARD_RESULT(s2n_get_public_random_data(&rand_data)); - - /* Add a downgrade detection mechanism if required */ - POSIX_GUARD(s2n_server_add_downgrade_mechanism(conn)); - - POSIX_GUARD(s2n_server_hello_write_message(conn)); - - POSIX_GUARD(s2n_server_extensions_send(conn, &conn->handshake.io)); - - conn->actual_protocol_version_established = 1; - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_server_extensions.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "tls/s2n_tls13_key_schedule.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +/* From RFC5246 7.4.1.2. */ +#define S2N_TLS_COMPRESSION_METHOD_NULL 0 + +/* From RFC8446 4.1.3. */ +#define S2N_DOWNGRADE_PROTECTION_SIZE 8 +const uint8_t tls12_downgrade_protection_bytes[] = { + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01 +}; + +const uint8_t tls11_downgrade_protection_bytes[] = { + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x00 +}; + +static int s2n_random_value_is_hello_retry(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + POSIX_ENSURE(s2n_constant_time_equals(hello_retry_req_random, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN), + S2N_ERR_INVALID_HELLO_RETRY); + + return S2N_SUCCESS; +} + +static int s2n_client_detect_downgrade_mechanism(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; + + /* Detect downgrade attacks according to RFC 8446 section 4.1.3 */ + if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version == S2N_TLS12) { + if (s2n_constant_time_equals(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { + POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + } + } else if (conn->client_protocol_version == S2N_TLS13 && conn->server_protocol_version <= S2N_TLS11) { + if (s2n_constant_time_equals(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE)) { + POSIX_BAIL(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + } + } + + return 0; +} + +static int s2n_server_add_downgrade_mechanism(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + uint8_t *downgrade_bytes = &conn->handshake_params.server_random[S2N_TLS_RANDOM_DATA_LEN - S2N_DOWNGRADE_PROTECTION_SIZE]; + + /* Protect against downgrade attacks according to RFC 8446 section 4.1.3 */ + if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version == S2N_TLS12) { + /* TLS1.3 servers MUST use a special random value when negotiating TLS1.2 */ + POSIX_CHECKED_MEMCPY(downgrade_bytes, tls12_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); + } else if (conn->server_protocol_version >= S2N_TLS13 && conn->actual_protocol_version <= S2N_TLS11) { + /* TLS1.3 servers MUST, use a special random value when negotiating TLS1.1 or below */ + POSIX_CHECKED_MEMCPY(downgrade_bytes, tls11_downgrade_protection_bytes, S2N_DOWNGRADE_PROTECTION_SIZE); + } + + return 0; +} + +static int s2n_server_hello_parse(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + struct s2n_stuffer *in = &conn->handshake.io; + uint8_t compression_method = 0; + uint8_t session_id_len = 0; + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN]; + + POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + POSIX_GUARD(s2n_stuffer_read_bytes(in, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); + + uint8_t legacy_version = (uint8_t) (protocol_version[0] * 10) + protocol_version[1]; + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 + *# Upon receiving a message with type server_hello, implementations MUST + *# first examine the Random value and, if it matches this value, process + *# it as described in Section 4.1.4). + **/ + if (s2n_random_value_is_hello_retry(conn) == S2N_SUCCESS) { + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# If a client receives a second + *# HelloRetryRequest in the same connection (i.e., where the ClientHello + *# was itself in response to a HelloRetryRequest), it MUST abort the + *# handshake with an "unexpected_message" alert. + **/ + POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_INVALID_HELLO_RETRY); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version + **/ + POSIX_ENSURE(legacy_version == S2N_TLS12, S2N_ERR_INVALID_HELLO_RETRY); + + POSIX_GUARD(s2n_set_hello_retry_required(conn)); + } + + POSIX_GUARD(s2n_stuffer_read_uint8(in, &session_id_len)); + S2N_ERROR_IF(session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_BAD_MESSAGE); + POSIX_GUARD(s2n_stuffer_read_bytes(in, session_id, session_id_len)); + + uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(in, S2N_TLS_CIPHER_SUITE_LEN); + POSIX_ENSURE_REF(cipher_suite_wire); + + POSIX_GUARD(s2n_stuffer_read_uint8(in, &compression_method)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.3 + *# legacy_compression_method: A single byte which MUST have the + *# value 0. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo, cipher_suite, and + *# legacy_compression_method + **/ + S2N_ERROR_IF(compression_method != S2N_TLS_COMPRESSION_METHOD_NULL, S2N_ERR_BAD_MESSAGE); + + bool session_ids_match = session_id_len != 0 && session_id_len == conn->session_id_len + && s2n_constant_time_equals(session_id, conn->session_id, session_id_len); + if (!session_ids_match) { + conn->ems_negotiated = false; + } + + POSIX_GUARD(s2n_server_extensions_recv(conn, in)); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# The server's extensions MUST contain "supported_versions". + **/ + if (s2n_is_hello_retry_message(conn)) { + s2n_extension_type_id supported_versions_id = s2n_unsupported_extension; + POSIX_GUARD(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + POSIX_ENSURE(S2N_CBIT_TEST(conn->extension_responses_received, supported_versions_id), + S2N_ERR_MISSING_EXTENSION); + } + + if (conn->server_protocol_version >= S2N_TLS13) { + POSIX_ENSURE(!conn->handshake.renegotiation, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.3 + *# A client which + *# receives a legacy_session_id_echo field that does not match what + *# it sent in the ClientHello MUST abort the handshake with an + *# "illegal_parameter" alert. + * + *= https://www.rfc-editor.org/rfc/rfc8446#4.1.4 + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo + **/ + POSIX_ENSURE(session_ids_match || (session_id_len == 0 && conn->session_id_len == 0), S2N_ERR_BAD_MESSAGE); + + conn->actual_protocol_version = conn->server_protocol_version; + POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); + + /* Erase TLS 1.2 client session ticket which might have been set for session resumption */ + POSIX_GUARD(s2n_free(&conn->client_ticket)); + } else { + conn->server_protocol_version = legacy_version; + + POSIX_ENSURE(!s2n_client_detect_downgrade_mechanism(conn), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /* Hello retries are only supported in >=TLS1.3. */ + POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn), S2N_ERR_BAD_MESSAGE); + + /* + *= https://www.rfc-editor.org/rfc/rfc8446#appendix-D.3 + *# A client that attempts to send 0-RTT data MUST fail a connection if + *# it receives a ServerHello with TLS 1.2 or older. + */ + POSIX_ENSURE(conn->early_data_state != S2N_EARLY_DATA_REQUESTED, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + + if (conn->server_protocol_version < security_policy->minimum_protocol_version + || conn->server_protocol_version > conn->client_protocol_version) { + POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); + POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + } + + conn->actual_protocol_version = S2N_MIN(conn->server_protocol_version, conn->client_protocol_version); + + /* + *= https://www.rfc-editor.org/rfc/rfc5077#section-3.4 + *# If the server accepts the ticket + *# and the Session ID is not empty, then it MUST respond with the same + *# Session ID present in the ClientHello. This allows the client to + *# easily differentiate when the server is resuming a session from when + *# it is falling back to a full handshake. + */ + if (session_ids_match) { + /* check if the resumed session state is valid */ + POSIX_ENSURE(conn->resume_protocol_version == conn->actual_protocol_version, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite_wire, S2N_TLS_CIPHER_SUITE_LEN), + S2N_ERR_BAD_MESSAGE); + + /* Session is resumed */ + conn->client_session_resumed = 1; + } else { + conn->session_id_len = session_id_len; + POSIX_CHECKED_MEMCPY(conn->session_id, session_id, session_id_len); + POSIX_GUARD(s2n_set_cipher_as_client(conn, cipher_suite_wire)); + /* Erase master secret which might have been set for session resumption */ + POSIX_CHECKED_MEMSET((uint8_t *) conn->secrets.version.tls12.master_secret, 0, S2N_TLS_SECRET_LEN); + + /* Erase client session ticket which might have been set for session resumption */ + POSIX_GUARD(s2n_free(&conn->client_ticket)); + } + } + + /* If it is not possible to accept early data on this connection + * (for example, because no PSK was negotiated) we need to reject early data now. + * Otherwise, early data logic may make certain invalid assumptions about the + * state of the connection (for example, that the prf is the early data prf). + */ + POSIX_GUARD_RESULT(s2n_early_data_accept_or_reject(conn)); + if (conn->early_data_state == S2N_EARLY_DATA_REJECTED) { + POSIX_GUARD_RESULT(s2n_tls13_key_schedule_reset(conn)); + } + + return 0; +} + +int s2n_server_hello_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + /* Read the message off the wire */ + POSIX_GUARD(s2n_server_hello_parse(conn)); + + conn->actual_protocol_version_established = 1; + + POSIX_GUARD(s2n_conn_set_handshake_type(conn)); + + /* If this is a HelloRetryRequest, we don't process the ServerHello. + * Instead we proceed with retry logic. */ + if (s2n_is_hello_retry_message(conn)) { + POSIX_GUARD(s2n_server_hello_retry_recv(conn)); + return 0; + } + + if (conn->actual_protocol_version < S2N_TLS13 && s2n_connection_is_session_resumed(conn)) { + POSIX_GUARD(s2n_prf_key_expansion(conn)); + } + + /* Update the required hashes for this connection */ + POSIX_GUARD(s2n_conn_update_required_handshake_hashes(conn)); + + return 0; +} + +int s2n_server_hello_write_message(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + + /* The actual_protocol_version is set while processing the CLIENT_HELLO message, so + * it could be S2N_TLS13. SERVER_HELLO should always respond with the legacy version. + * https://tools.ietf.org/html/rfc8446#section-4.1.3 */ + const uint16_t legacy_protocol_version = S2N_MIN(conn->actual_protocol_version, S2N_TLS12); + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + protocol_version[0] = (uint8_t) (legacy_protocol_version / 10); + protocol_version[1] = (uint8_t) (legacy_protocol_version % 10); + + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, conn->session_id_len)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->session_id, conn->session_id_len)); + POSIX_GUARD(s2n_stuffer_write_bytes(&conn->handshake.io, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + POSIX_GUARD(s2n_stuffer_write_uint8(&conn->handshake.io, S2N_TLS_COMPRESSION_METHOD_NULL)); + + return 0; +} + +int s2n_server_hello_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + struct s2n_stuffer server_random = { 0 }; + struct s2n_blob b = { 0 }; + POSIX_GUARD(s2n_blob_init(&b, conn->handshake_params.server_random, S2N_TLS_RANDOM_DATA_LEN)); + + /* Create the server random data */ + POSIX_GUARD(s2n_stuffer_init(&server_random, &b)); + + struct s2n_blob rand_data = { 0 }; + POSIX_GUARD(s2n_blob_init(&rand_data, s2n_stuffer_raw_write(&server_random, S2N_TLS_RANDOM_DATA_LEN), S2N_TLS_RANDOM_DATA_LEN)); + POSIX_ENSURE_REF(rand_data.data); + POSIX_GUARD_RESULT(s2n_get_public_random_data(&rand_data)); + + /* Add a downgrade detection mechanism if required */ + POSIX_GUARD(s2n_server_add_downgrade_mechanism(conn)); + + POSIX_GUARD(s2n_server_hello_write_message(conn)); + + POSIX_GUARD(s2n_server_extensions_send(conn, &conn->handshake.io)); + + conn->actual_protocol_version_established = 1; + + return 0; +} diff --git a/tls/s2n_server_new_session_ticket.c b/tls/s2n_server_new_session_ticket.c index 7fa5379a4d1..e52a3b71c53 100644 --- a/tls/s2n_server_new_session_ticket.c +++ b/tls/s2n_server_new_session_ticket.c @@ -1,444 +1,444 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_alerts.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_record.h" -#include "tls/s2n_resume.h" -#include "tls/s2n_tls.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -/* - * The maximum size of the NewSessionTicket message, not taking into account the - * ticket itself. - * - * To get the actual maximum size required for the NewSessionTicket message, we'll need - * to add the size of the ticket, which is much less predictable. - * - * This constant is enforced via unit tests. - */ -#define S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE 112 - -int s2n_server_nst_recv(struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_stuffer_read_uint32(&conn->handshake.io, &conn->ticket_lifetime_hint)); - - uint16_t session_ticket_len = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(&conn->handshake.io, &session_ticket_len)); - - if (session_ticket_len > 0) { - POSIX_GUARD(s2n_realloc(&conn->client_ticket, session_ticket_len)); - - POSIX_GUARD(s2n_stuffer_read(&conn->handshake.io, &conn->client_ticket)); - - if (conn->config->session_ticket_cb != NULL) { - size_t session_len = s2n_connection_get_session_length(conn); - - /* Alloc some memory for the serialized session ticket */ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - POSIX_GUARD(s2n_alloc(&mem, - S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + S2N_TLS12_STATE_SIZE_IN_BYTES)); - - POSIX_GUARD(s2n_connection_get_session(conn, mem.data, session_len)); - uint32_t session_lifetime = s2n_connection_get_session_ticket_lifetime_hint(conn); - - struct s2n_session_ticket ticket = { .ticket_data = mem, .session_lifetime = session_lifetime }; - - POSIX_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, - S2N_ERR_CANCELLED); - } - } - - return S2N_SUCCESS; -} - -static S2N_RESULT s2n_generate_ticket_lifetime(struct s2n_connection *conn, uint64_t key_intro_time, - uint32_t *ticket_lifetime) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_MUT(ticket_lifetime); - - uint64_t now = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, &now)); - - /* Calculate ticket key age */ - RESULT_ENSURE_GTE(now, key_intro_time); - uint64_t ticket_key_age_in_nanos = now - key_intro_time; - - /* Calculate remaining key lifetime */ - uint64_t key_lifetime_in_nanos = conn->config->encrypt_decrypt_key_lifetime_in_nanos + conn->config->decrypt_key_lifetime_in_nanos; - RESULT_ENSURE_GTE(key_lifetime_in_nanos, ticket_key_age_in_nanos); - uint32_t remaining_key_lifetime = (key_lifetime_in_nanos - ticket_key_age_in_nanos) / ONE_SEC_IN_NANOS; - - uint32_t session_lifetime = conn->config->session_state_lifetime_in_nanos / ONE_SEC_IN_NANOS; - - /* Min of remaining key lifetime and session */ - uint32_t min_lifetime = S2N_MIN(remaining_key_lifetime, session_lifetime); - - /* In TLS1.3 we take into account keying material lifetime */ - if (conn->actual_protocol_version == S2N_TLS13) { - uint32_t key_material_lifetime = conn->server_keying_material_lifetime; - struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; - if (chosen_psk) { - RESULT_ENSURE_GTE(chosen_psk->keying_material_expiration, now); - uint32_t psk_key_material_lifetime = (chosen_psk->keying_material_expiration - now) / ONE_SEC_IN_NANOS; - key_material_lifetime = S2N_MIN(key_material_lifetime, psk_key_material_lifetime); - } - min_lifetime = S2N_MIN(min_lifetime, key_material_lifetime); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Servers MUST NOT use any value greater than - *# 604800 seconds (7 days). - **/ - *ticket_lifetime = S2N_MIN(min_lifetime, ONE_WEEK_IN_SEC); - - return S2N_RESULT_OK; -} - -int s2n_server_nst_send(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - - uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; - struct s2n_blob session_ticket = { 0 }; - POSIX_GUARD(s2n_blob_init(&session_ticket, data, sizeof(data))); - - uint32_t lifetime_hint_in_secs = 0; - - /* Send a zero-length ticket in the NewSessionTicket message if the server changes - * its mind mid-handshake or if there are no valid encrypt keys currently available. - * - *= https://www.rfc-editor.org/rfc/rfc5077#section-3.3 - *# If the server determines that it does not want to include a - *# ticket after it has included the SessionTicket extension in the - *# ServerHello, then it sends a zero-length ticket in the - *# NewSessionTicket handshake message. - **/ - if (s2n_result_is_error(s2n_server_nst_write(conn, &lifetime_hint_in_secs, &session_ticket))) { - POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, 0)); - POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, 0)); - return S2N_SUCCESS; - } - - POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, lifetime_hint_in_secs)); - POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, session_ticket.size)); - POSIX_GUARD(s2n_stuffer_write(&conn->handshake.io, &session_ticket)); - - /* For parity with TLS1.3, track the single ticket sent. - * This simplifies s2n_connection_get_tickets_sent. - */ - conn->tickets_sent++; - return S2N_SUCCESS; -} - -S2N_RESULT s2n_server_nst_write(struct s2n_connection *conn, uint32_t *lifetime_hint_in_secs, - struct s2n_blob *session_ticket) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE(s2n_server_sending_nst(conn), S2N_ERR_SENDING_NST); - - struct s2n_stuffer output = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&output, session_ticket)); - - struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); - RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); - - RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, lifetime_hint_in_secs)); - RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &output)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_server_nst_send(struct s2n_connection *conn, s2n_blocked_status *blocked) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); - - /* Usually tickets are sent immediately after the handshake. - * If possible, reuse the handshake IO stuffer before it's wiped. - * - * Note: handshake.io isn't explicitly dedicated to only reading or only writing, - * so we have to be careful using it outside of s2n_negotiate. - * If we use it for writing here, we CAN'T use it for reading any post-handshake messages. - */ - struct s2n_stuffer *nst_stuffer = &conn->handshake.io; - - if (conn->mode != S2N_SERVER || !conn->config->use_tickets) { - return S2N_RESULT_OK; - } - - /* Legacy behavior is that the s2n server sends a NST even if the client did not indicate support - * for resumption or does not support the psk_dhe_ke mode. This is potentially wasteful so we - * choose to not extend this behavior to QUIC. - */ - if (conn->quic_enabled && conn->psk_params.psk_ke_mode != S2N_PSK_DHE_KE) { - return S2N_RESULT_OK; - } - - /* No-op if all tickets already sent. - * Clean up the stuffer used for the ticket to conserve memory. */ - if (conn->tickets_to_send == conn->tickets_sent) { - RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, 0)); - return S2N_RESULT_OK; - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Note that in principle it is possible to continue issuing new tickets - *# which indefinitely extend the lifetime of the keying material - *# originally derived from an initial non-PSK handshake (which was most - *# likely tied to the peer's certificate). It is RECOMMENDED that - *# implementations place limits on the total lifetime of such keying - *# material; these limits should take into account the lifetime of the - *# peer's certificate, the likelihood of intervening revocation, and the - *# time since the peer's online CertificateVerify signature. - */ - if (s2n_result_is_error(s2n_psk_validate_keying_material(conn))) { - conn->tickets_to_send = conn->tickets_sent; - return S2N_RESULT_OK; - } - - RESULT_ENSURE(conn->tickets_sent <= conn->tickets_to_send, S2N_ERR_INTEGER_OVERFLOW); - - size_t session_state_size = 0; - RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); - const size_t maximum_nst_size = session_state_size + S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE; - if (s2n_stuffer_space_remaining(nst_stuffer) < maximum_nst_size) { - RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, maximum_nst_size)); - } - - while (conn->tickets_to_send - conn->tickets_sent > 0) { - if (s2n_result_is_error(s2n_tls13_server_nst_write(conn, nst_stuffer))) { - return S2N_RESULT_OK; - } - - RESULT_GUARD(s2n_post_handshake_write_records(conn, blocked)); - } - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# A per-ticket value that is unique across all tickets - *# issued on this connection. - **/ -static S2N_RESULT s2n_generate_ticket_nonce(uint16_t value, struct s2n_blob *output) -{ - RESULT_ENSURE_MUT(output); - - struct s2n_stuffer stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, output)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&stuffer, value)); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# A securely generated, random 32-bit value that is - *# used to obscure the age of the ticket that the client includes in - *# the "pre_shared_key" extension. - **/ -static S2N_RESULT s2n_generate_ticket_age_add(struct s2n_blob *random_data, uint32_t *ticket_age_add) -{ - RESULT_ENSURE_REF(random_data); - RESULT_ENSURE_REF(ticket_age_add); - - struct s2n_stuffer stuffer = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, random_data)); - RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&stuffer, random_data->size)); - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&stuffer, ticket_age_add)); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# The PSK associated with the ticket is computed as: - *# - *# HKDF-Expand-Label(resumption_master_secret, - *# "resumption", ticket_nonce, Hash.length) - **/ -static int s2n_generate_session_secret(struct s2n_connection *conn, struct s2n_blob *nonce, struct s2n_blob *output) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(nonce); - POSIX_ENSURE_REF(output); - - s2n_tls13_connection_keys(secrets, conn); - struct s2n_blob master_secret = { 0 }; - POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls13.resumption_master_secret, secrets.size)); - POSIX_GUARD(s2n_realloc(output, secrets.size)); - POSIX_GUARD_RESULT(s2n_tls13_derive_session_ticket_secret(&secrets, &master_secret, nonce, output)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_tls13_server_nst_write(struct s2n_connection *conn, struct s2n_stuffer *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); - RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); - - struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; - - /* Write message type because session resumption in TLS13 is a post-handshake message */ - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, TLS_SERVER_NEW_SESSION_TICKET)); - - struct s2n_stuffer_reservation message_size = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint24(output, &message_size)); - - uint32_t ticket_lifetime_in_secs = 0; - RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, &ticket_lifetime_in_secs)); - - RESULT_ENSURE(ticket_lifetime_in_secs > 0, S2N_ERR_ZERO_LIFETIME_TICKET); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_lifetime_in_secs)); - - /* Get random data to use as ticket_age_add value */ - uint8_t data[sizeof(uint32_t)] = { 0 }; - struct s2n_blob random_data = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&random_data, data, sizeof(data))); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# The server MUST generate a fresh value - *# for each ticket it sends. - **/ - RESULT_GUARD(s2n_get_private_random_data(&random_data)); - RESULT_GUARD(s2n_generate_ticket_age_add(&random_data, &ticket_fields->ticket_age_add)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_fields->ticket_age_add)); - - /* Write ticket nonce */ - uint8_t nonce_data[sizeof(uint16_t)] = { 0 }; - struct s2n_blob nonce = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, sizeof(nonce_data))); - RESULT_GUARD(s2n_generate_ticket_nonce(conn->tickets_sent, &nonce)); - RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, nonce.size)); - RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, nonce.data, nonce.size)); - - /* Derive individual session ticket secret */ - RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); - - /* Write ticket */ - struct s2n_stuffer_reservation ticket_size = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(output, &ticket_size)); - RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, output)); - RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&ticket_size)); - - RESULT_GUARD_POSIX(s2n_extension_list_send(S2N_EXTENSION_LIST_NST, conn, output)); - - RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&message_size)); - - RESULT_ENSURE(conn->tickets_sent < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); - conn->tickets_sent++; - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# struct { - *# uint32 ticket_lifetime; - *# uint32 ticket_age_add; - *# opaque ticket_nonce<0..255>; - *# opaque ticket<1..2^16-1>; - *# Extension extensions<0..2^16-2>; - *# } NewSessionTicket; -**/ -S2N_RESULT s2n_tls13_server_nst_recv(struct s2n_connection *conn, struct s2n_stuffer *input) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(input); - RESULT_ENSURE_REF(conn->config); - - RESULT_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_BAD_MESSAGE); - RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); - - if (!conn->config->use_tickets) { - return S2N_RESULT_OK; - } - struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; - - /* Handle `ticket_lifetime` field */ - uint32_t ticket_lifetime = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_lifetime)); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# Servers MUST NOT use any value greater than - *# 604800 seconds (7 days). - */ - RESULT_ENSURE(ticket_lifetime <= ONE_WEEK_IN_SEC, S2N_ERR_BAD_MESSAGE); - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 - *# The value of zero indicates that the - *# ticket should be discarded immediately. - */ - if (ticket_lifetime == 0) { - return S2N_RESULT_OK; - } - conn->ticket_lifetime_hint = ticket_lifetime; - - /* Handle `ticket_age_add` field */ - RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_fields->ticket_age_add)); - - /* Handle `ticket_nonce` field */ - uint8_t ticket_nonce_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(input, &ticket_nonce_len)); - uint8_t nonce_data[UINT8_MAX] = { 0 }; - struct s2n_blob nonce = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, ticket_nonce_len)); - RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, nonce.data, ticket_nonce_len)); - RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); - - /* Handle `ticket` field */ - uint16_t session_ticket_len = 0; - RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(input, &session_ticket_len)); - RESULT_ENSURE(session_ticket_len > 0, S2N_ERR_SAFETY); - RESULT_GUARD_POSIX(s2n_realloc(&conn->client_ticket, session_ticket_len)); - RESULT_GUARD_POSIX(s2n_stuffer_read(input, &conn->client_ticket)); - - /* Handle `extensions` field */ - RESULT_GUARD_POSIX(s2n_extension_list_recv(S2N_EXTENSION_LIST_NST, conn, input)); - - if (conn->config->session_ticket_cb != NULL) { - /* Retrieve serialized session data */ - const uint16_t session_state_size = s2n_connection_get_session_length(conn); - DEFER_CLEANUP(struct s2n_blob session_state = { 0 }, s2n_free); - RESULT_GUARD_POSIX(s2n_realloc(&session_state, session_state_size)); - RESULT_GUARD_POSIX(s2n_connection_get_session(conn, session_state.data, session_state.size)); - - struct s2n_session_ticket ticket = { - .ticket_data = session_state, - .session_lifetime = ticket_lifetime - }; - RESULT_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, - S2N_ERR_CANCELLED); - } - - return S2N_RESULT_OK; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_resume.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +/* + * The maximum size of the NewSessionTicket message, not taking into account the + * ticket itself. + * + * To get the actual maximum size required for the NewSessionTicket message, we'll need + * to add the size of the ticket, which is much less predictable. + * + * This constant is enforced via unit tests. + */ +#define S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE 112 + +int s2n_server_nst_recv(struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_stuffer_read_uint32(&conn->handshake.io, &conn->ticket_lifetime_hint)); + + uint16_t session_ticket_len = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(&conn->handshake.io, &session_ticket_len)); + + if (session_ticket_len > 0) { + POSIX_GUARD(s2n_realloc(&conn->client_ticket, session_ticket_len)); + + POSIX_GUARD(s2n_stuffer_read(&conn->handshake.io, &conn->client_ticket)); + + if (conn->config->session_ticket_cb != NULL) { + size_t session_len = s2n_connection_get_session_length(conn); + + /* Alloc some memory for the serialized session ticket */ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&mem, + S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + S2N_TLS12_STATE_SIZE_IN_BYTES)); + + POSIX_GUARD(s2n_connection_get_session(conn, mem.data, session_len)); + uint32_t session_lifetime = s2n_connection_get_session_ticket_lifetime_hint(conn); + + struct s2n_session_ticket ticket = { .ticket_data = mem, .session_lifetime = session_lifetime }; + + POSIX_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, + S2N_ERR_CANCELLED); + } + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_generate_ticket_lifetime(struct s2n_connection *conn, uint64_t key_intro_time, + uint32_t *ticket_lifetime) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_MUT(ticket_lifetime); + + uint64_t now = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, &now)); + + /* Calculate ticket key age */ + RESULT_ENSURE_GTE(now, key_intro_time); + uint64_t ticket_key_age_in_nanos = now - key_intro_time; + + /* Calculate remaining key lifetime */ + uint64_t key_lifetime_in_nanos = conn->config->encrypt_decrypt_key_lifetime_in_nanos + conn->config->decrypt_key_lifetime_in_nanos; + RESULT_ENSURE_GTE(key_lifetime_in_nanos, ticket_key_age_in_nanos); + uint32_t remaining_key_lifetime = (key_lifetime_in_nanos - ticket_key_age_in_nanos) / ONE_SEC_IN_NANOS; + + uint32_t session_lifetime = conn->config->session_state_lifetime_in_nanos / ONE_SEC_IN_NANOS; + + /* Min of remaining key lifetime and session */ + uint32_t min_lifetime = S2N_MIN(remaining_key_lifetime, session_lifetime); + + /* In TLS1.3 we take into account keying material lifetime */ + if (conn->actual_protocol_version == S2N_TLS13) { + uint32_t key_material_lifetime = conn->server_keying_material_lifetime; + struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk; + if (chosen_psk) { + RESULT_ENSURE_GTE(chosen_psk->keying_material_expiration, now); + uint32_t psk_key_material_lifetime = (chosen_psk->keying_material_expiration - now) / ONE_SEC_IN_NANOS; + key_material_lifetime = S2N_MIN(key_material_lifetime, psk_key_material_lifetime); + } + min_lifetime = S2N_MIN(min_lifetime, key_material_lifetime); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Servers MUST NOT use any value greater than + *# 604800 seconds (7 days). + **/ + *ticket_lifetime = S2N_MIN(min_lifetime, ONE_WEEK_IN_SEC); + + return S2N_RESULT_OK; +} + +int s2n_server_nst_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob session_ticket = { 0 }; + POSIX_GUARD(s2n_blob_init(&session_ticket, data, sizeof(data))); + + uint32_t lifetime_hint_in_secs = 0; + + /* Send a zero-length ticket in the NewSessionTicket message if the server changes + * its mind mid-handshake or if there are no valid encrypt keys currently available. + * + *= https://www.rfc-editor.org/rfc/rfc5077#section-3.3 + *# If the server determines that it does not want to include a + *# ticket after it has included the SessionTicket extension in the + *# ServerHello, then it sends a zero-length ticket in the + *# NewSessionTicket handshake message. + **/ + if (s2n_result_is_error(s2n_server_nst_write(conn, &lifetime_hint_in_secs, &session_ticket))) { + POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, 0)); + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, 0)); + return S2N_SUCCESS; + } + + POSIX_GUARD(s2n_stuffer_write_uint32(&conn->handshake.io, lifetime_hint_in_secs)); + POSIX_GUARD(s2n_stuffer_write_uint16(&conn->handshake.io, session_ticket.size)); + POSIX_GUARD(s2n_stuffer_write(&conn->handshake.io, &session_ticket)); + + /* For parity with TLS1.3, track the single ticket sent. + * This simplifies s2n_connection_get_tickets_sent. + */ + conn->tickets_sent++; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_server_nst_write(struct s2n_connection *conn, uint32_t *lifetime_hint_in_secs, + struct s2n_blob *session_ticket) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE(s2n_server_sending_nst(conn), S2N_ERR_SENDING_NST); + + struct s2n_stuffer output = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&output, session_ticket)); + + struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); + RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + + RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, lifetime_hint_in_secs)); + RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &output)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_server_nst_send(struct s2n_connection *conn, s2n_blocked_status *blocked) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); + + /* Usually tickets are sent immediately after the handshake. + * If possible, reuse the handshake IO stuffer before it's wiped. + * + * Note: handshake.io isn't explicitly dedicated to only reading or only writing, + * so we have to be careful using it outside of s2n_negotiate. + * If we use it for writing here, we CAN'T use it for reading any post-handshake messages. + */ + struct s2n_stuffer *nst_stuffer = &conn->handshake.io; + + if (conn->mode != S2N_SERVER || !conn->config->use_tickets) { + return S2N_RESULT_OK; + } + + /* Legacy behavior is that the s2n server sends a NST even if the client did not indicate support + * for resumption or does not support the psk_dhe_ke mode. This is potentially wasteful so we + * choose to not extend this behavior to QUIC. + */ + if (conn->quic_enabled && conn->psk_params.psk_ke_mode != S2N_PSK_DHE_KE) { + return S2N_RESULT_OK; + } + + /* No-op if all tickets already sent. + * Clean up the stuffer used for the ticket to conserve memory. */ + if (conn->tickets_to_send == conn->tickets_sent) { + RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, 0)); + return S2N_RESULT_OK; + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Note that in principle it is possible to continue issuing new tickets + *# which indefinitely extend the lifetime of the keying material + *# originally derived from an initial non-PSK handshake (which was most + *# likely tied to the peer's certificate). It is RECOMMENDED that + *# implementations place limits on the total lifetime of such keying + *# material; these limits should take into account the lifetime of the + *# peer's certificate, the likelihood of intervening revocation, and the + *# time since the peer's online CertificateVerify signature. + */ + if (s2n_result_is_error(s2n_psk_validate_keying_material(conn))) { + conn->tickets_to_send = conn->tickets_sent; + return S2N_RESULT_OK; + } + + RESULT_ENSURE(conn->tickets_sent <= conn->tickets_to_send, S2N_ERR_INTEGER_OVERFLOW); + + size_t session_state_size = 0; + RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size)); + const size_t maximum_nst_size = session_state_size + S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE; + if (s2n_stuffer_space_remaining(nst_stuffer) < maximum_nst_size) { + RESULT_GUARD_POSIX(s2n_stuffer_resize(nst_stuffer, maximum_nst_size)); + } + + while (conn->tickets_to_send - conn->tickets_sent > 0) { + if (s2n_result_is_error(s2n_tls13_server_nst_write(conn, nst_stuffer))) { + return S2N_RESULT_OK; + } + + RESULT_GUARD(s2n_post_handshake_write_records(conn, blocked)); + } + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# A per-ticket value that is unique across all tickets + *# issued on this connection. + **/ +static S2N_RESULT s2n_generate_ticket_nonce(uint16_t value, struct s2n_blob *output) +{ + RESULT_ENSURE_MUT(output); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, output)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&stuffer, value)); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# A securely generated, random 32-bit value that is + *# used to obscure the age of the ticket that the client includes in + *# the "pre_shared_key" extension. + **/ +static S2N_RESULT s2n_generate_ticket_age_add(struct s2n_blob *random_data, uint32_t *ticket_age_add) +{ + RESULT_ENSURE_REF(random_data); + RESULT_ENSURE_REF(ticket_age_add); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, random_data)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&stuffer, random_data->size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(&stuffer, ticket_age_add)); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# The PSK associated with the ticket is computed as: + *# + *# HKDF-Expand-Label(resumption_master_secret, + *# "resumption", ticket_nonce, Hash.length) + **/ +static int s2n_generate_session_secret(struct s2n_connection *conn, struct s2n_blob *nonce, struct s2n_blob *output) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(nonce); + POSIX_ENSURE_REF(output); + + s2n_tls13_connection_keys(secrets, conn); + struct s2n_blob master_secret = { 0 }; + POSIX_GUARD(s2n_blob_init(&master_secret, conn->secrets.version.tls13.resumption_master_secret, secrets.size)); + POSIX_GUARD(s2n_realloc(output, secrets.size)); + POSIX_GUARD_RESULT(s2n_tls13_derive_session_ticket_secret(&secrets, &master_secret, nonce, output)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_tls13_server_nst_write(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config); + RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY); + + struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; + + /* Write message type because session resumption in TLS13 is a post-handshake message */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, TLS_SERVER_NEW_SESSION_TICKET)); + + struct s2n_stuffer_reservation message_size = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint24(output, &message_size)); + + uint32_t ticket_lifetime_in_secs = 0; + RESULT_GUARD(s2n_generate_ticket_lifetime(conn, key->intro_timestamp, &ticket_lifetime_in_secs)); + + RESULT_ENSURE(ticket_lifetime_in_secs > 0, S2N_ERR_ZERO_LIFETIME_TICKET); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_lifetime_in_secs)); + + /* Get random data to use as ticket_age_add value */ + uint8_t data[sizeof(uint32_t)] = { 0 }; + struct s2n_blob random_data = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&random_data, data, sizeof(data))); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# The server MUST generate a fresh value + *# for each ticket it sends. + **/ + RESULT_GUARD(s2n_get_private_random_data(&random_data)); + RESULT_GUARD(s2n_generate_ticket_age_add(&random_data, &ticket_fields->ticket_age_add)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(output, ticket_fields->ticket_age_add)); + + /* Write ticket nonce */ + uint8_t nonce_data[sizeof(uint16_t)] = { 0 }; + struct s2n_blob nonce = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, sizeof(nonce_data))); + RESULT_GUARD(s2n_generate_ticket_nonce(conn->tickets_sent, &nonce)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(output, nonce.size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, nonce.data, nonce.size)); + + /* Derive individual session ticket secret */ + RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); + + /* Write ticket */ + struct s2n_stuffer_reservation ticket_size = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint16(output, &ticket_size)); + RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, output)); + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&ticket_size)); + + RESULT_GUARD_POSIX(s2n_extension_list_send(S2N_EXTENSION_LIST_NST, conn, output)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&message_size)); + + RESULT_ENSURE(conn->tickets_sent < UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW); + conn->tickets_sent++; + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# struct { + *# uint32 ticket_lifetime; + *# uint32 ticket_age_add; + *# opaque ticket_nonce<0..255>; + *# opaque ticket<1..2^16-1>; + *# Extension extensions<0..2^16-2>; + *# } NewSessionTicket; +**/ +S2N_RESULT s2n_tls13_server_nst_recv(struct s2n_connection *conn, struct s2n_stuffer *input) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(input); + RESULT_ENSURE_REF(conn->config); + + RESULT_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE); + + if (!conn->config->use_tickets) { + return S2N_RESULT_OK; + } + struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields; + + /* Handle `ticket_lifetime` field */ + uint32_t ticket_lifetime = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_lifetime)); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# Servers MUST NOT use any value greater than + *# 604800 seconds (7 days). + */ + RESULT_ENSURE(ticket_lifetime <= ONE_WEEK_IN_SEC, S2N_ERR_BAD_MESSAGE); + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1 + *# The value of zero indicates that the + *# ticket should be discarded immediately. + */ + if (ticket_lifetime == 0) { + return S2N_RESULT_OK; + } + conn->ticket_lifetime_hint = ticket_lifetime; + + /* Handle `ticket_age_add` field */ + RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(input, &ticket_fields->ticket_age_add)); + + /* Handle `ticket_nonce` field */ + uint8_t ticket_nonce_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(input, &ticket_nonce_len)); + uint8_t nonce_data[UINT8_MAX] = { 0 }; + struct s2n_blob nonce = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&nonce, nonce_data, ticket_nonce_len)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, nonce.data, ticket_nonce_len)); + RESULT_GUARD_POSIX(s2n_generate_session_secret(conn, &nonce, &ticket_fields->session_secret)); + + /* Handle `ticket` field */ + uint16_t session_ticket_len = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(input, &session_ticket_len)); + RESULT_ENSURE(session_ticket_len > 0, S2N_ERR_SAFETY); + RESULT_GUARD_POSIX(s2n_realloc(&conn->client_ticket, session_ticket_len)); + RESULT_GUARD_POSIX(s2n_stuffer_read(input, &conn->client_ticket)); + + /* Handle `extensions` field */ + RESULT_GUARD_POSIX(s2n_extension_list_recv(S2N_EXTENSION_LIST_NST, conn, input)); + + if (conn->config->session_ticket_cb != NULL) { + /* Retrieve serialized session data */ + const uint16_t session_state_size = s2n_connection_get_session_length(conn); + DEFER_CLEANUP(struct s2n_blob session_state = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_realloc(&session_state, session_state_size)); + RESULT_GUARD_POSIX(s2n_connection_get_session(conn, session_state.data, session_state.size)); + + struct s2n_session_ticket ticket = { + .ticket_data = session_state, + .session_lifetime = ticket_lifetime + }; + RESULT_ENSURE(conn->config->session_ticket_cb(conn, conn->config->session_ticket_ctx, &ticket) >= S2N_SUCCESS, + S2N_ERR_CANCELLED); + } + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_signature_scheme.h b/tls/s2n_signature_scheme.h index 38eb2a792f9..b6799e5010d 100644 --- a/tls/s2n_signature_scheme.h +++ b/tls/s2n_signature_scheme.h @@ -1,111 +1,111 @@ -/* - * 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 - -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "crypto/s2n_ecc_evp.h" -#include "crypto/s2n_hash.h" -#include "crypto/s2n_signature.h" - -struct s2n_signature_scheme { - uint16_t iana_value; - const char *name; - s2n_hash_algorithm hash_alg; - s2n_signature_algorithm sig_alg; - uint8_t minimum_protocol_version; - uint8_t maximum_protocol_version; - uint16_t libcrypto_nid; - - /* Curve is only defined for TLS1.3 ECDSA Signatures */ - struct s2n_ecc_named_curve const *signature_curve; - /* Because of the different curve behavior, we should refer - * to TLS1.3 and pre-TLS1.3 versions of this scheme differently. - * Where the version is unknown, use the standard "name" field. - */ - const char *legacy_name; - const char *tls13_name; -}; - -struct s2n_signature_preferences { - uint8_t count; - const struct s2n_signature_scheme *const *signature_schemes; -}; - -extern const struct s2n_signature_scheme s2n_null_sig_scheme; - -/* RSA PKCS1 */ -/* s2n_rsa_pkcs1_md5_sha1 is not in any preference list, but it is needed since it's the default for TLS 1.0 and 1.1 if - * no SignatureScheme is sent. */ -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_md5_sha1; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha1; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha224; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha256; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha384; -extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha512; - -extern const struct s2n_signature_scheme s2n_ecdsa_sha1; -extern const struct s2n_signature_scheme s2n_ecdsa_sha224; -extern const struct s2n_signature_scheme s2n_ecdsa_sha256; -extern const struct s2n_signature_scheme s2n_ecdsa_sha384; -extern const struct s2n_signature_scheme s2n_ecdsa_sha512; - -/* RSA PSS */ -/* - * Use RSA-PSS-RSAE instead of RSA-PSS-PSS in order to work with older certificates. - * For more info see: https://crypto.stackexchange.com/a/58708 - */ -extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha256; -extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha384; -extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha512; -extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha256; -extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha384; -extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha512; - -/* ML-DSA: post-quantum signature schemes */ -extern const struct s2n_signature_scheme s2n_mldsa44; -extern const struct s2n_signature_scheme s2n_mldsa65; -extern const struct s2n_signature_scheme s2n_mldsa87; - -extern const struct s2n_signature_preferences s2n_signature_preferences_20250512; -extern const struct s2n_signature_preferences s2n_signature_preferences_20240501; -extern const struct s2n_signature_preferences s2n_signature_preferences_20230317; -extern const struct s2n_signature_preferences s2n_signature_preferences_20140601; -extern const struct s2n_signature_preferences s2n_signature_preferences_20200207; -extern const struct s2n_signature_preferences s2n_signature_preferences_20200207_no_sha1; -extern const struct s2n_signature_preferences s2n_signature_preferences_20201021; -extern const struct s2n_signature_preferences s2n_signature_preferences_20210816; -extern const struct s2n_signature_preferences s2n_signature_preferences_20240521; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250429; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250820; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250821; -extern const struct s2n_signature_preferences s2n_signature_preferences_20251113; -extern const struct s2n_signature_preferences s2n_signature_preferences_20260219; -extern const struct s2n_signature_preferences s2n_signature_preferences_20260220; -extern const struct s2n_signature_preferences s2n_signature_preferences_default_fips; -extern const struct s2n_signature_preferences s2n_signature_preferences_null; -extern const struct s2n_signature_preferences s2n_signature_preferences_test_all_fips; -extern const struct s2n_signature_preferences s2n_signature_preferences_all; -extern const struct s2n_signature_preferences s2n_signature_preferences_20250813; - -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20260220; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20251113; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250512; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250429; -extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20201110; +/* + * 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 + +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_ecc_evp.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_signature.h" + +struct s2n_signature_scheme { + uint16_t iana_value; + const char *name; + s2n_hash_algorithm hash_alg; + s2n_signature_algorithm sig_alg; + uint8_t minimum_protocol_version; + uint8_t maximum_protocol_version; + uint16_t libcrypto_nid; + + /* Curve is only defined for TLS1.3 ECDSA Signatures */ + struct s2n_ecc_named_curve const *signature_curve; + /* Because of the different curve behavior, we should refer + * to TLS1.3 and pre-TLS1.3 versions of this scheme differently. + * Where the version is unknown, use the standard "name" field. + */ + const char *legacy_name; + const char *tls13_name; +}; + +struct s2n_signature_preferences { + uint8_t count; + const struct s2n_signature_scheme *const *signature_schemes; +}; + +extern const struct s2n_signature_scheme s2n_null_sig_scheme; + +/* RSA PKCS1 */ +/* s2n_rsa_pkcs1_md5_sha1 is not in any preference list, but it is needed since it's the default for TLS 1.0 and 1.1 if + * no SignatureScheme is sent. */ +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_md5_sha1; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha1; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha224; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha256; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha384; +extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha512; + +extern const struct s2n_signature_scheme s2n_ecdsa_sha1; +extern const struct s2n_signature_scheme s2n_ecdsa_sha224; +extern const struct s2n_signature_scheme s2n_ecdsa_sha256; +extern const struct s2n_signature_scheme s2n_ecdsa_sha384; +extern const struct s2n_signature_scheme s2n_ecdsa_sha512; + +/* RSA PSS */ +/* + * Use RSA-PSS-RSAE instead of RSA-PSS-PSS in order to work with older certificates. + * For more info see: https://crypto.stackexchange.com/a/58708 + */ +extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha256; +extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha384; +extern const struct s2n_signature_scheme s2n_rsa_pss_pss_sha512; +extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha256; +extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha384; +extern const struct s2n_signature_scheme s2n_rsa_pss_rsae_sha512; + +/* ML-DSA: post-quantum signature schemes */ +extern const struct s2n_signature_scheme s2n_mldsa44; +extern const struct s2n_signature_scheme s2n_mldsa65; +extern const struct s2n_signature_scheme s2n_mldsa87; + +extern const struct s2n_signature_preferences s2n_signature_preferences_20250512; +extern const struct s2n_signature_preferences s2n_signature_preferences_20240501; +extern const struct s2n_signature_preferences s2n_signature_preferences_20230317; +extern const struct s2n_signature_preferences s2n_signature_preferences_20140601; +extern const struct s2n_signature_preferences s2n_signature_preferences_20200207; +extern const struct s2n_signature_preferences s2n_signature_preferences_20200207_no_sha1; +extern const struct s2n_signature_preferences s2n_signature_preferences_20201021; +extern const struct s2n_signature_preferences s2n_signature_preferences_20210816; +extern const struct s2n_signature_preferences s2n_signature_preferences_20240521; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250429; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250820; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250821; +extern const struct s2n_signature_preferences s2n_signature_preferences_20251113; +extern const struct s2n_signature_preferences s2n_signature_preferences_20260219; +extern const struct s2n_signature_preferences s2n_signature_preferences_20260220; +extern const struct s2n_signature_preferences s2n_signature_preferences_default_fips; +extern const struct s2n_signature_preferences s2n_signature_preferences_null; +extern const struct s2n_signature_preferences s2n_signature_preferences_test_all_fips; +extern const struct s2n_signature_preferences s2n_signature_preferences_all; +extern const struct s2n_signature_preferences s2n_signature_preferences_20250813; + +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20260220; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20251113; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250512; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20250429; +extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20201110; diff --git a/tls/s2n_tls13.h b/tls/s2n_tls13.h index 766ccc5c3fa..4d04445d268 100644 --- a/tls/s2n_tls13.h +++ b/tls/s2n_tls13.h @@ -1,57 +1,57 @@ -/* - * 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 - -#include "api/s2n.h" -#include "tls/s2n_crypto.h" -#include "utils/s2n_compiler.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -#if defined(_MSC_VER) -S2N_API __declspec(deprecated) int s2n_enable_tls13(); -#elif S2N_GCC_VERSION_AT_LEAST(4, 5, 0) -S2N_API __attribute__((deprecated("The use of TLS1.3 is configured through security policies"))) int s2n_enable_tls13(); -#else -S2N_API __attribute__((deprecated)) int s2n_enable_tls13(); -#endif - - -#ifdef __cplusplus -} -#endif - -/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ -extern uint8_t hello_retry_req_random[S2N_TLS_RANDOM_DATA_LEN]; - -bool s2n_use_default_tls13_config(); -bool s2n_is_tls13_fully_supported(); -int s2n_get_highest_fully_supported_tls_version(); -int s2n_enable_tls13_in_test(); -int s2n_disable_tls13_in_test(); -int s2n_reset_tls13_in_test(); -bool s2n_is_valid_tls13_cipher(const uint8_t version[2]); -S2N_RESULT s2n_connection_validate_tls13_support(struct s2n_connection *conn); -bool s2n_connection_supports_tls13(struct s2n_connection *conn); - -bool s2n_is_middlebox_compat_enabled(struct s2n_connection *conn); - -bool s2n_is_hello_retry_handshake(struct s2n_connection *conn); -bool s2n_is_hello_retry_message(struct s2n_connection *conn); -int s2n_set_hello_retry_required(struct s2n_connection *conn); +/* + * 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 + +#include "api/s2n.h" +#include "tls/s2n_crypto.h" +#include "utils/s2n_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_MSC_VER) +S2N_API __declspec(deprecated) int s2n_enable_tls13(); +#elif S2N_GCC_VERSION_AT_LEAST(4, 5, 0) +S2N_API __attribute__((deprecated("The use of TLS1.3 is configured through security policies"))) int s2n_enable_tls13(); +#else +S2N_API __attribute__((deprecated)) int s2n_enable_tls13(); +#endif + + +#ifdef __cplusplus +} +#endif + +/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ +extern uint8_t hello_retry_req_random[S2N_TLS_RANDOM_DATA_LEN]; + +bool s2n_use_default_tls13_config(); +bool s2n_is_tls13_fully_supported(); +int s2n_get_highest_fully_supported_tls_version(); +int s2n_enable_tls13_in_test(); +int s2n_disable_tls13_in_test(); +int s2n_reset_tls13_in_test(); +bool s2n_is_valid_tls13_cipher(const uint8_t version[2]); +S2N_RESULT s2n_connection_validate_tls13_support(struct s2n_connection *conn); +bool s2n_connection_supports_tls13(struct s2n_connection *conn); + +bool s2n_is_middlebox_compat_enabled(struct s2n_connection *conn); + +bool s2n_is_hello_retry_handshake(struct s2n_connection *conn); +bool s2n_is_hello_retry_message(struct s2n_connection *conn); +int s2n_set_hello_retry_required(struct s2n_connection *conn); diff --git a/tls/s2n_tls13_certificate_verify.c b/tls/s2n_tls13_certificate_verify.c index 4f5516804d6..3d2a39c9113 100644 --- a/tls/s2n_tls13_certificate_verify.c +++ b/tls/s2n_tls13_certificate_verify.c @@ -1,212 +1,212 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_tls13_certificate_verify.h" - -#include - -#include "crypto/s2n_hash.h" -#include "error/s2n_errno.h" -#include "stuffer/s2n_stuffer.h" -#include "tls/s2n_async_pkey.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_safety.h" - -/** - * Specified in https://tools.ietf.org/html/rfc8446#section-4.4.3 - * - * Servers MUST send this message when authenticating via a certificate. - * Clients MUST send this message whenever authenticating via a certificate. - * When sent, this message MUST appear immediately after the Certificate - * message and immediately prior to the Finished message. - **/ - -/* 64 'space' characters (0x20) */ -const uint8_t S2N_CERT_VERIFY_PREFIX[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; -/* 'TLS 1.3, server CertificateVerify' with 0x00 separator */ -const uint8_t S2N_SERVER_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, - 0x2c, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; -/* 'TLS 1.3, client CertificateVerify' with 0x00 separator */ -const uint8_t S2N_CLIENT_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, - 0x2c, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; - -static int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme); -static int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature); -static int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, - struct s2n_stuffer *unsigned_content, s2n_mode mode); -static int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme); -static uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode); - -int s2n_tls13_cert_verify_send(struct s2n_connection *conn) -{ - S2N_ASYNC_PKEY_GUARD(conn); - - if (conn->mode == S2N_SERVER) { - /* Write digital signature */ - POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.server_cert_sig_scheme)); - } else { - /* Write digital signature */ - POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.client_cert_sig_scheme)); - } - - return 0; -} - -int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme) -{ - POSIX_ENSURE_REF(conn->handshake_params.our_chain_and_key); - - /* Write the SignatureScheme out */ - struct s2n_stuffer *out = &conn->handshake.io; - POSIX_GUARD(s2n_stuffer_write_uint16(out, chosen_sig_scheme->iana_value)); - - DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); - POSIX_GUARD(s2n_hash_new(&message_hash)); - POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); - - const struct s2n_pkey *pkey = conn->handshake_params.our_chain_and_key->private_key; - POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); - - DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); - POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, conn->mode)); - - POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, - s2n_stuffer_data_available(&unsigned_content))); - - S2N_ASYNC_PKEY_SIGN(conn, chosen_sig_scheme->sig_alg, &message_hash, s2n_tls13_write_signature); -} - -int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature) -{ - struct s2n_stuffer *out = &conn->handshake.io; - - POSIX_GUARD(s2n_stuffer_write_uint16(out, signature->size)); - POSIX_GUARD(s2n_stuffer_write_bytes(out, signature->data, signature->size)); - - return 0; -} - -int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, - struct s2n_stuffer *unsigned_content, s2n_mode mode) -{ - s2n_tls13_connection_keys(tls13_ctx, conn); - - uint8_t hash_digest_length = tls13_ctx.size; - uint8_t digest_out[S2N_MAX_DIGEST_LEN]; - - /* Get current handshake hash */ - POSIX_ENSURE_REF(conn->handshake.hashes); - struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; - POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, tls13_ctx.hash_algorithm, hash_state)); - POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, hash_digest_length)); - - /* Concatenate the content to be signed/verified */ - POSIX_GUARD(s2n_stuffer_alloc(unsigned_content, hash_digest_length + s2n_tls13_cert_verify_header_length(mode))); - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CERT_VERIFY_PREFIX, sizeof(S2N_CERT_VERIFY_PREFIX))); - - if (mode == S2N_CLIENT) { - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CLIENT_CERT_VERIFY_CONTEXT, - sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT))); - } else { - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_SERVER_CERT_VERIFY_CONTEXT, - sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT))); - } - - POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, digest_out, hash_digest_length)); - - return 0; -} - -uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode) -{ - if (mode == S2N_CLIENT) { - return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT); - } - return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT); -} - -int s2n_tls13_cert_verify_recv(struct s2n_connection *conn) -{ - S2N_ASYNC_OFFLOAD_POSIX_GUARD(conn, { - POSIX_GUARD_RESULT(s2n_signature_algorithm_recv(conn, &conn->handshake.io)); - /* Read the rest of the signature and verify */ - if (conn->mode == S2N_SERVER) { - POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, - conn->handshake_params.client_cert_sig_scheme)); - } else { - POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, - conn->handshake_params.server_cert_sig_scheme)); - } - }); - return 0; -} - -int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, - const struct s2n_signature_scheme *chosen_sig_scheme) -{ - struct s2n_stuffer *in = &conn->handshake.io; - DEFER_CLEANUP(struct s2n_blob signed_content = { 0 }, s2n_free); - DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); - POSIX_GUARD(s2n_hash_new(&message_hash)); - - /* Get signature size */ - uint16_t signature_size = 0; - POSIX_GUARD(s2n_stuffer_read_uint16(in, &signature_size)); - S2N_ERROR_IF(signature_size > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); - - /* Get wire signature */ - POSIX_GUARD(s2n_alloc(&signed_content, signature_size)); - signed_content.size = signature_size; - POSIX_GUARD(s2n_stuffer_read_bytes(in, signed_content.data, signature_size)); - - /* Verify signature. We send the opposite mode as we are trying to verify what was sent to us */ - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_SERVER)); - } else { - POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_CLIENT)); - } - - struct s2n_pkey *pkey = NULL; - if (conn->mode == S2N_CLIENT) { - pkey = &conn->handshake_params.server_public_key; - } else { - pkey = &conn->handshake_params.client_public_key; - } - - /* Assert that the public key params match the wire signature scheme */ - POSIX_ENSURE(s2n_result_is_ok(s2n_signature_scheme_params_match(conn, pkey, chosen_sig_scheme)), - S2N_ERR_INVALID_SIGNATURE_ALGORITHM); - - POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); - POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); - POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, - s2n_stuffer_data_available(&unsigned_content))); - - POSIX_GUARD(s2n_async_pkey_verify(conn, chosen_sig_scheme->sig_alg, - &message_hash, &signed_content)); - - return 0; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_tls13_certificate_verify.h" + +#include + +#include "crypto/s2n_hash.h" +#include "error/s2n_errno.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_safety.h" + +/** + * Specified in https://tools.ietf.org/html/rfc8446#section-4.4.3 + * + * Servers MUST send this message when authenticating via a certificate. + * Clients MUST send this message whenever authenticating via a certificate. + * When sent, this message MUST appear immediately after the Certificate + * message and immediately prior to the Finished message. + **/ + +/* 64 'space' characters (0x20) */ +const uint8_t S2N_CERT_VERIFY_PREFIX[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; +/* 'TLS 1.3, server CertificateVerify' with 0x00 separator */ +const uint8_t S2N_SERVER_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, + 0x2c, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; +/* 'TLS 1.3, client CertificateVerify' with 0x00 separator */ +const uint8_t S2N_CLIENT_CERT_VERIFY_CONTEXT[] = { 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, + 0x2c, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00 }; + +static int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme); +static int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature); +static int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, + struct s2n_stuffer *unsigned_content, s2n_mode mode); +static int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme); +static uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode); + +int s2n_tls13_cert_verify_send(struct s2n_connection *conn) +{ + S2N_ASYNC_PKEY_GUARD(conn); + + if (conn->mode == S2N_SERVER) { + /* Write digital signature */ + POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.server_cert_sig_scheme)); + } else { + /* Write digital signature */ + POSIX_GUARD(s2n_tls13_write_cert_verify_signature(conn, conn->handshake_params.client_cert_sig_scheme)); + } + + return 0; +} + +int s2n_tls13_write_cert_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme) +{ + POSIX_ENSURE_REF(conn->handshake_params.our_chain_and_key); + + /* Write the SignatureScheme out */ + struct s2n_stuffer *out = &conn->handshake.io; + POSIX_GUARD(s2n_stuffer_write_uint16(out, chosen_sig_scheme->iana_value)); + + DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&message_hash)); + POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); + + const struct s2n_pkey *pkey = conn->handshake_params.our_chain_and_key->private_key; + POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); + + DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, conn->mode)); + + POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, + s2n_stuffer_data_available(&unsigned_content))); + + S2N_ASYNC_PKEY_SIGN(conn, chosen_sig_scheme->sig_alg, &message_hash, s2n_tls13_write_signature); +} + +int s2n_tls13_write_signature(struct s2n_connection *conn, struct s2n_blob *signature) +{ + struct s2n_stuffer *out = &conn->handshake.io; + + POSIX_GUARD(s2n_stuffer_write_uint16(out, signature->size)); + POSIX_GUARD(s2n_stuffer_write_bytes(out, signature->data, signature->size)); + + return 0; +} + +int s2n_tls13_generate_unsigned_cert_verify_content(struct s2n_connection *conn, + struct s2n_stuffer *unsigned_content, s2n_mode mode) +{ + s2n_tls13_connection_keys(tls13_ctx, conn); + + uint8_t hash_digest_length = tls13_ctx.size; + uint8_t digest_out[S2N_MAX_DIGEST_LEN]; + + /* Get current handshake hash */ + POSIX_ENSURE_REF(conn->handshake.hashes); + struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, tls13_ctx.hash_algorithm, hash_state)); + POSIX_GUARD(s2n_hash_digest(hash_state, digest_out, hash_digest_length)); + + /* Concatenate the content to be signed/verified */ + POSIX_GUARD(s2n_stuffer_alloc(unsigned_content, hash_digest_length + s2n_tls13_cert_verify_header_length(mode))); + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CERT_VERIFY_PREFIX, sizeof(S2N_CERT_VERIFY_PREFIX))); + + if (mode == S2N_CLIENT) { + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_CLIENT_CERT_VERIFY_CONTEXT, + sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT))); + } else { + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, S2N_SERVER_CERT_VERIFY_CONTEXT, + sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT))); + } + + POSIX_GUARD(s2n_stuffer_write_bytes(unsigned_content, digest_out, hash_digest_length)); + + return 0; +} + +uint8_t s2n_tls13_cert_verify_header_length(s2n_mode mode) +{ + if (mode == S2N_CLIENT) { + return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_CLIENT_CERT_VERIFY_CONTEXT); + } + return sizeof(S2N_CERT_VERIFY_PREFIX) + sizeof(S2N_SERVER_CERT_VERIFY_CONTEXT); +} + +int s2n_tls13_cert_verify_recv(struct s2n_connection *conn) +{ + S2N_ASYNC_OFFLOAD_POSIX_GUARD(conn, { + POSIX_GUARD_RESULT(s2n_signature_algorithm_recv(conn, &conn->handshake.io)); + /* Read the rest of the signature and verify */ + if (conn->mode == S2N_SERVER) { + POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, + conn->handshake_params.client_cert_sig_scheme)); + } else { + POSIX_GUARD(s2n_tls13_cert_read_and_verify_signature(conn, + conn->handshake_params.server_cert_sig_scheme)); + } + }); + return 0; +} + +int s2n_tls13_cert_read_and_verify_signature(struct s2n_connection *conn, + const struct s2n_signature_scheme *chosen_sig_scheme) +{ + struct s2n_stuffer *in = &conn->handshake.io; + DEFER_CLEANUP(struct s2n_blob signed_content = { 0 }, s2n_free); + DEFER_CLEANUP(struct s2n_stuffer unsigned_content = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_hash_state message_hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&message_hash)); + + /* Get signature size */ + uint16_t signature_size = 0; + POSIX_GUARD(s2n_stuffer_read_uint16(in, &signature_size)); + S2N_ERROR_IF(signature_size > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); + + /* Get wire signature */ + POSIX_GUARD(s2n_alloc(&signed_content, signature_size)); + signed_content.size = signature_size; + POSIX_GUARD(s2n_stuffer_read_bytes(in, signed_content.data, signature_size)); + + /* Verify signature. We send the opposite mode as we are trying to verify what was sent to us */ + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_SERVER)); + } else { + POSIX_GUARD(s2n_tls13_generate_unsigned_cert_verify_content(conn, &unsigned_content, S2N_CLIENT)); + } + + struct s2n_pkey *pkey = NULL; + if (conn->mode == S2N_CLIENT) { + pkey = &conn->handshake_params.server_public_key; + } else { + pkey = &conn->handshake_params.client_public_key; + } + + /* Assert that the public key params match the wire signature scheme */ + POSIX_ENSURE(s2n_result_is_ok(s2n_signature_scheme_params_match(conn, pkey, chosen_sig_scheme)), + S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + + POSIX_GUARD(s2n_hash_init(&message_hash, chosen_sig_scheme->hash_alg)); + POSIX_GUARD_RESULT(s2n_pkey_init_hash(pkey, chosen_sig_scheme->sig_alg, &message_hash)); + POSIX_GUARD(s2n_hash_update(&message_hash, unsigned_content.blob.data, + s2n_stuffer_data_available(&unsigned_content))); + + POSIX_GUARD(s2n_async_pkey_verify(conn, chosen_sig_scheme->sig_alg, + &message_hash, &signed_content)); + + return 0; +} diff --git a/tls/s2n_tls13_handshake.c b/tls/s2n_tls13_handshake.c index dae0e5ca953..351a7e0edb1 100644 --- a/tls/s2n_tls13_handshake.c +++ b/tls/s2n_tls13_handshake.c @@ -1,223 +1,223 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_tls13_handshake.h" - -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_key_log.h" -#include "tls/s2n_security_policies.h" - -static int s2n_zero_sequence_number(struct s2n_connection *conn, s2n_mode mode) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - struct s2n_blob sequence_number = { 0 }; - POSIX_GUARD_RESULT(s2n_connection_get_sequence_number(conn, mode, &sequence_number)); - POSIX_GUARD(s2n_blob_zero(&sequence_number)); - return S2N_SUCCESS; -} - -int s2n_tls13_mac_verify(struct s2n_tls13_keys *keys, struct s2n_blob *finished_verify, struct s2n_blob *wire_verify) -{ - POSIX_ENSURE_REF(wire_verify->data); - POSIX_ENSURE_EQ(wire_verify->size, keys->size); - - S2N_ERROR_IF(!s2n_constant_time_equals(finished_verify->data, wire_verify->data, keys->size), S2N_ERR_BAD_MESSAGE); - - return S2N_SUCCESS; -} - -int s2n_tls13_keys_from_conn(struct s2n_tls13_keys *keys, struct s2n_connection *conn) -{ - POSIX_GUARD(s2n_tls13_keys_init(keys, conn->secure->cipher_suite->prf_alg)); - return S2N_SUCCESS; -} - -int s2n_tls13_compute_ecc_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) -{ - POSIX_ENSURE_REF(conn); - - const struct s2n_ecc_preferences *ecc_preferences = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); - POSIX_ENSURE_REF(ecc_preferences); - - struct s2n_ecc_evp_params *server_key = &conn->kex_params.server_ecc_evp_params; - POSIX_ENSURE_REF(server_key); - POSIX_ENSURE_REF(server_key->negotiated_curve); - - struct s2n_ecc_evp_params *client_key = &conn->kex_params.client_ecc_evp_params; - POSIX_ENSURE_REF(client_key); - POSIX_ENSURE_REF(client_key->negotiated_curve); - - POSIX_ENSURE_EQ(server_key->negotiated_curve, client_key->negotiated_curve); - - if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_key, server_key, shared_secret)); - } else { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_key, client_key, shared_secret)); - } - - return S2N_SUCCESS; -} - -/* Computes the ECDHE+PQKEM hybrid shared secret as defined in - * https://tools.ietf.org/html/draft-stebila-tls-hybrid-design - * Also supports "pure PQ" mode when kem_group->curve == &s2n_ecc_curve_none. - */ -int s2n_tls13_compute_pq_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(shared_secret); - - /* conn->kex_params.server_ecc_evp_params should be set only during a classic/non-hybrid handshake */ - POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.negotiated_curve); - POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.evp_pkey); - - struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params; - POSIX_ENSURE_REF(server_kem_group_params); - struct s2n_ecc_evp_params *server_ecc_params = &server_kem_group_params->ecc_params; - POSIX_ENSURE_REF(server_ecc_params); - - struct s2n_kem_group_params *client_kem_group_params = &conn->kex_params.client_kem_group_params; - POSIX_ENSURE_REF(client_kem_group_params); - struct s2n_ecc_evp_params *client_ecc_params = &client_kem_group_params->ecc_params; - POSIX_ENSURE_REF(client_ecc_params); - - struct s2n_blob *pq_shared_secret = &client_kem_group_params->kem_params.shared_secret; - POSIX_ENSURE_REF(pq_shared_secret); - POSIX_ENSURE_REF(pq_shared_secret->data); - - const struct s2n_kem_group *negotiated_kem_group = conn->kex_params.server_kem_group_params.kem_group; - POSIX_ENSURE_REF(negotiated_kem_group); - POSIX_ENSURE_REF(negotiated_kem_group->kem); - - DEFER_CLEANUP(struct s2n_blob ecdhe_shared_secret = { 0 }, s2n_free_or_wipe); - - if (negotiated_kem_group->curve == &s2n_ecc_curve_none) { - POSIX_ENSURE_EQ(ecdhe_shared_secret.size, 0); - } else if (conn->mode == S2N_CLIENT) { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_ecc_params, server_ecc_params, &ecdhe_shared_secret)); - } else { - POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_ecc_params, client_ecc_params, &ecdhe_shared_secret)); - } - - POSIX_ENSURE_EQ(pq_shared_secret->size, negotiated_kem_group->kem->shared_secret_key_length); - - /* Construct the concatenated/hybrid shared secret */ - uint32_t hybrid_shared_secret_size = ecdhe_shared_secret.size + negotiated_kem_group->kem->shared_secret_key_length; - POSIX_GUARD(s2n_alloc(shared_secret, hybrid_shared_secret_size)); - struct s2n_stuffer stuffer_combiner = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, shared_secret)); - - if (negotiated_kem_group->send_kem_first) { - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); - } else { - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); - POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); - } - - return S2N_SUCCESS; -} - -int s2n_tls13_pq_hybrid_supported(struct s2n_connection *conn) -{ - return conn->kex_params.server_kem_group_params.kem_group != NULL; -} - -int s2n_tls13_compute_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) -{ - POSIX_ENSURE_REF(conn); - - if (s2n_tls13_pq_hybrid_supported(conn)) { - POSIX_GUARD(s2n_tls13_compute_pq_shared_secret(conn, shared_secret)); - } else { - POSIX_GUARD(s2n_tls13_compute_ecc_shared_secret(conn, shared_secret)); - } - - POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); - - /* It would make more sense to wipe the PSK secrets in s2n_tls13_handle_early_secret, - * but at that point we don't know whether or not the server will request a HRR request - * and we'll have to use the secrets again. - * - * Instead, wipe them here when we wipe all the other connection secrets. */ - POSIX_GUARD_RESULT(s2n_psk_parameters_wipe_secrets(&conn->psk_params)); - - return S2N_SUCCESS; -} - -int s2n_update_application_traffic_keys(struct s2n_connection *conn, s2n_mode mode, keyupdate_status status) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); - - /* get tls13 key context */ - s2n_tls13_connection_keys(keys, conn); - - struct s2n_session_key *old_key = NULL; - struct s2n_blob old_app_secret = { 0 }; - struct s2n_blob app_iv = { 0 }; - - if (mode == S2N_CLIENT) { - old_key = &conn->secure->client_key; - POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.client_app_secret, keys.size)); - POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->client_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); - } else { - old_key = &conn->secure->server_key; - POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.server_app_secret, keys.size)); - POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->server_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); - } - - /* Produce new application secret */ - s2n_stack_blob(app_secret_update, keys.size, S2N_TLS13_SECRET_MAX_LEN); - - /* Derives next generation of traffic secret */ - POSIX_GUARD(s2n_tls13_update_application_traffic_secret(&keys, &old_app_secret, &app_secret_update)); - - s2n_tls13_key_blob(app_key, conn->secure->cipher_suite->record_alg->cipher->key_material_size); - - /* Derives next generation of traffic key */ - uint8_t *count = NULL; - POSIX_GUARD(s2n_tls13_derive_traffic_keys(&keys, &app_secret_update, &app_key, &app_iv)); - if (status == RECEIVING) { - POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(old_key, &app_key)); - count = &conn->recv_key_updated; - } else { - POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(old_key, &app_key)); - count = &conn->send_key_updated; - } - - /* Increment the count. - * Don't treat overflows as errors-- we only do best-effort reporting. - */ - *count = S2N_MIN(UINT8_MAX, *count + 1); - - /* According to https://tools.ietf.org/html/rfc8446#section-5.3: - * Each sequence number is set to zero at the beginning of a connection and - * whenever the key is changed; the first record transmitted under a particular traffic key - * MUST use sequence number 0. - */ - POSIX_GUARD(s2n_zero_sequence_number(conn, mode)); - - /* Save updated secret */ - struct s2n_stuffer old_secret_stuffer = { 0 }; - POSIX_GUARD(s2n_stuffer_init(&old_secret_stuffer, &old_app_secret)); - POSIX_GUARD(s2n_stuffer_write_bytes(&old_secret_stuffer, app_secret_update.data, keys.size)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_tls13_handshake.h" + +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_key_log.h" +#include "tls/s2n_security_policies.h" + +static int s2n_zero_sequence_number(struct s2n_connection *conn, s2n_mode mode) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + struct s2n_blob sequence_number = { 0 }; + POSIX_GUARD_RESULT(s2n_connection_get_sequence_number(conn, mode, &sequence_number)); + POSIX_GUARD(s2n_blob_zero(&sequence_number)); + return S2N_SUCCESS; +} + +int s2n_tls13_mac_verify(struct s2n_tls13_keys *keys, struct s2n_blob *finished_verify, struct s2n_blob *wire_verify) +{ + POSIX_ENSURE_REF(wire_verify->data); + POSIX_ENSURE_EQ(wire_verify->size, keys->size); + + S2N_ERROR_IF(!s2n_constant_time_equals(finished_verify->data, wire_verify->data, keys->size), S2N_ERR_BAD_MESSAGE); + + return S2N_SUCCESS; +} + +int s2n_tls13_keys_from_conn(struct s2n_tls13_keys *keys, struct s2n_connection *conn) +{ + POSIX_GUARD(s2n_tls13_keys_init(keys, conn->secure->cipher_suite->prf_alg)); + return S2N_SUCCESS; +} + +int s2n_tls13_compute_ecc_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) +{ + POSIX_ENSURE_REF(conn); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + POSIX_ENSURE_REF(ecc_preferences); + + struct s2n_ecc_evp_params *server_key = &conn->kex_params.server_ecc_evp_params; + POSIX_ENSURE_REF(server_key); + POSIX_ENSURE_REF(server_key->negotiated_curve); + + struct s2n_ecc_evp_params *client_key = &conn->kex_params.client_ecc_evp_params; + POSIX_ENSURE_REF(client_key); + POSIX_ENSURE_REF(client_key->negotiated_curve); + + POSIX_ENSURE_EQ(server_key->negotiated_curve, client_key->negotiated_curve); + + if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_key, server_key, shared_secret)); + } else { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_key, client_key, shared_secret)); + } + + return S2N_SUCCESS; +} + +/* Computes the ECDHE+PQKEM hybrid shared secret as defined in + * https://tools.ietf.org/html/draft-stebila-tls-hybrid-design + * Also supports "pure PQ" mode when kem_group->curve == &s2n_ecc_curve_none. + */ +int s2n_tls13_compute_pq_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(shared_secret); + + /* conn->kex_params.server_ecc_evp_params should be set only during a classic/non-hybrid handshake */ + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.negotiated_curve); + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_ecc_evp_params.evp_pkey); + + struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params; + POSIX_ENSURE_REF(server_kem_group_params); + struct s2n_ecc_evp_params *server_ecc_params = &server_kem_group_params->ecc_params; + POSIX_ENSURE_REF(server_ecc_params); + + struct s2n_kem_group_params *client_kem_group_params = &conn->kex_params.client_kem_group_params; + POSIX_ENSURE_REF(client_kem_group_params); + struct s2n_ecc_evp_params *client_ecc_params = &client_kem_group_params->ecc_params; + POSIX_ENSURE_REF(client_ecc_params); + + struct s2n_blob *pq_shared_secret = &client_kem_group_params->kem_params.shared_secret; + POSIX_ENSURE_REF(pq_shared_secret); + POSIX_ENSURE_REF(pq_shared_secret->data); + + const struct s2n_kem_group *negotiated_kem_group = conn->kex_params.server_kem_group_params.kem_group; + POSIX_ENSURE_REF(negotiated_kem_group); + POSIX_ENSURE_REF(negotiated_kem_group->kem); + + DEFER_CLEANUP(struct s2n_blob ecdhe_shared_secret = { 0 }, s2n_free_or_wipe); + + if (negotiated_kem_group->curve == &s2n_ecc_curve_none) { + POSIX_ENSURE_EQ(ecdhe_shared_secret.size, 0); + } else if (conn->mode == S2N_CLIENT) { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(client_ecc_params, server_ecc_params, &ecdhe_shared_secret)); + } else { + POSIX_GUARD(s2n_ecc_evp_compute_shared_secret_from_params(server_ecc_params, client_ecc_params, &ecdhe_shared_secret)); + } + + POSIX_ENSURE_EQ(pq_shared_secret->size, negotiated_kem_group->kem->shared_secret_key_length); + + /* Construct the concatenated/hybrid shared secret */ + uint32_t hybrid_shared_secret_size = ecdhe_shared_secret.size + negotiated_kem_group->kem->shared_secret_key_length; + POSIX_GUARD(s2n_alloc(shared_secret, hybrid_shared_secret_size)); + struct s2n_stuffer stuffer_combiner = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&stuffer_combiner, shared_secret)); + + if (negotiated_kem_group->send_kem_first) { + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); + } else { + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, &ecdhe_shared_secret)); + POSIX_GUARD(s2n_stuffer_write(&stuffer_combiner, pq_shared_secret)); + } + + return S2N_SUCCESS; +} + +int s2n_tls13_pq_hybrid_supported(struct s2n_connection *conn) +{ + return conn->kex_params.server_kem_group_params.kem_group != NULL; +} + +int s2n_tls13_compute_shared_secret(struct s2n_connection *conn, struct s2n_blob *shared_secret) +{ + POSIX_ENSURE_REF(conn); + + if (s2n_tls13_pq_hybrid_supported(conn)) { + POSIX_GUARD(s2n_tls13_compute_pq_shared_secret(conn, shared_secret)); + } else { + POSIX_GUARD(s2n_tls13_compute_ecc_shared_secret(conn, shared_secret)); + } + + POSIX_GUARD_RESULT(s2n_connection_wipe_all_keyshares(conn)); + + /* It would make more sense to wipe the PSK secrets in s2n_tls13_handle_early_secret, + * but at that point we don't know whether or not the server will request a HRR request + * and we'll have to use the secrets again. + * + * Instead, wipe them here when we wipe all the other connection secrets. */ + POSIX_GUARD_RESULT(s2n_psk_parameters_wipe_secrets(&conn->psk_params)); + + return S2N_SUCCESS; +} + +int s2n_update_application_traffic_keys(struct s2n_connection *conn, s2n_mode mode, keyupdate_status status) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); + + /* get tls13 key context */ + s2n_tls13_connection_keys(keys, conn); + + struct s2n_session_key *old_key = NULL; + struct s2n_blob old_app_secret = { 0 }; + struct s2n_blob app_iv = { 0 }; + + if (mode == S2N_CLIENT) { + old_key = &conn->secure->client_key; + POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.client_app_secret, keys.size)); + POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->client_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); + } else { + old_key = &conn->secure->server_key; + POSIX_GUARD(s2n_blob_init(&old_app_secret, conn->secrets.version.tls13.server_app_secret, keys.size)); + POSIX_GUARD(s2n_blob_init(&app_iv, conn->secure->server_implicit_iv, S2N_TLS13_FIXED_IV_LEN)); + } + + /* Produce new application secret */ + s2n_stack_blob(app_secret_update, keys.size, S2N_TLS13_SECRET_MAX_LEN); + + /* Derives next generation of traffic secret */ + POSIX_GUARD(s2n_tls13_update_application_traffic_secret(&keys, &old_app_secret, &app_secret_update)); + + s2n_tls13_key_blob(app_key, conn->secure->cipher_suite->record_alg->cipher->key_material_size); + + /* Derives next generation of traffic key */ + uint8_t *count = NULL; + POSIX_GUARD(s2n_tls13_derive_traffic_keys(&keys, &app_secret_update, &app_key, &app_iv)); + if (status == RECEIVING) { + POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(old_key, &app_key)); + count = &conn->recv_key_updated; + } else { + POSIX_GUARD_RESULT(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(old_key, &app_key)); + count = &conn->send_key_updated; + } + + /* Increment the count. + * Don't treat overflows as errors-- we only do best-effort reporting. + */ + *count = S2N_MIN(UINT8_MAX, *count + 1); + + /* According to https://tools.ietf.org/html/rfc8446#section-5.3: + * Each sequence number is set to zero at the beginning of a connection and + * whenever the key is changed; the first record transmitted under a particular traffic key + * MUST use sequence number 0. + */ + POSIX_GUARD(s2n_zero_sequence_number(conn, mode)); + + /* Save updated secret */ + struct s2n_stuffer old_secret_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&old_secret_stuffer, &old_app_secret)); + POSIX_GUARD(s2n_stuffer_write_bytes(&old_secret_stuffer, app_secret_update.data, keys.size)); + + return S2N_SUCCESS; +} diff --git a/tls/s2n_tls13_secrets.c b/tls/s2n_tls13_secrets.c index 54feefd8710..3b11b1cbb47 100644 --- a/tls/s2n_tls13_secrets.c +++ b/tls/s2n_tls13_secrets.c @@ -1,778 +1,778 @@ -#include "utils/s2n_prelude.h" -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "tls/s2n_tls13_secrets.h" - -#include "tls/s2n_connection.h" -#include "tls/s2n_key_log.h" -#include "tls/s2n_tls13_handshake.h" -#include "utils/s2n_bitmap.h" - -#define S2N_MAX_HASHLEN SHA384_DIGEST_LENGTH - -#define CONN_HMAC_ALG(conn) ((conn)->secure->cipher_suite->prf_alg) -#define CONN_SECRETS(conn) ((conn)->secrets.version.tls13) -#define CONN_HASHES(conn) ((conn)->handshake.hashes) - -#define CONN_SECRET(conn, secret) ( \ - (struct s2n_blob){ .data = CONN_SECRETS(conn).secret, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) -#define CONN_HASH(conn, hash) ( \ - (struct s2n_blob){ .data = CONN_HASHES(conn)->hash, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) -#define CONN_FINISHED(conn, mode) ( \ - (struct s2n_blob){ .data = (conn)->handshake.mode##_finished, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# If a given secret is not available, then the 0-value consisting of a - *# string of Hash.length bytes set to zeros is used. - */ -static uint8_t zero_value_bytes[S2N_MAX_HASHLEN] = { 0 }; -#define ZERO_VALUE(hmac_alg) ( \ - (const struct s2n_blob){ .data = zero_value_bytes, .size = s2n_get_hash_len(hmac_alg) }) - -/** - * When an operation doesn't need an actual transcript hash, - * it uses an empty transcript hash as an input instead. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# Note that in some cases a zero- - *# length Context (indicated by "") is passed to HKDF-Expand-Label - */ -#define EMPTY_CONTEXT(hmac_alg) ( \ - (const struct s2n_blob){ .data = s2n_get_empty_context(hmac_alg), .size = s2n_get_hash_len(hmac_alg) }) - -static uint8_t s2n_get_hash_len(s2n_hmac_algorithm hmac_alg) -{ - uint8_t hash_size = 0; - if (s2n_hmac_digest_size(hmac_alg, &hash_size) != S2N_SUCCESS) { - return 0; - } - return hash_size; -} - -static uint8_t *s2n_get_empty_context(s2n_hmac_algorithm hmac_alg) -{ - static uint8_t sha256_empty_digest[S2N_MAX_HASHLEN] = { 0 }; - static uint8_t sha384_empty_digest[S2N_MAX_HASHLEN] = { 0 }; - - switch (hmac_alg) { - case S2N_HMAC_SHA256: - return sha256_empty_digest; - case S2N_HMAC_SHA384: - return sha384_empty_digest; - default: - return NULL; - } -} - -static s2n_hmac_algorithm supported_hmacs[] = { - S2N_HMAC_SHA256, - S2N_HMAC_SHA384 -}; - -S2N_RESULT s2n_tls13_empty_transcripts_init() -{ - DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); - RESULT_GUARD_POSIX(s2n_hash_new(&hash)); - - s2n_hash_algorithm hash_alg = S2N_HASH_NONE; - for (size_t i = 0; i < s2n_array_len(supported_hmacs); i++) { - s2n_hmac_algorithm hmac_alg = supported_hmacs[i]; - struct s2n_blob digest = EMPTY_CONTEXT(hmac_alg); - - RESULT_GUARD_POSIX(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); - RESULT_GUARD_POSIX(s2n_hash_init(&hash, hash_alg)); - RESULT_GUARD_POSIX(s2n_hash_digest(&hash, digest.data, digest.size)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_calculate_transcript_digest(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->handshake.hashes); - - s2n_hash_algorithm hash_algorithm = S2N_HASH_NONE; - RESULT_GUARD_POSIX(s2n_hmac_hash_alg(CONN_HMAC_ALG(conn), &hash_algorithm)); - - uint8_t digest_size = 0; - RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_algorithm, &digest_size)); - - struct s2n_blob digest = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&digest, CONN_HASHES(conn)->transcript_hash_digest, digest_size)); - - struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; - RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_algorithm, hash_state)); - RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, digest.data, digest.size)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_extract_secret(s2n_hmac_algorithm hmac_alg, - const struct s2n_blob *previous_secret_material, const struct s2n_blob *new_secret_material, - struct s2n_blob *output) -{ - /* - * TODO: We should be able to reuse the prf_work_space rather - * than allocating a new HMAC every time. - * https://github.com/aws/s2n-tls/issues/3206 - */ - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); - - RESULT_GUARD_POSIX(s2n_hkdf_extract(&hmac_state, hmac_alg, - previous_secret_material, new_secret_material, output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# Derive-Secret(Secret, Label, Messages) = - *# HKDF-Expand-Label(Secret, Label, - *# Transcript-Hash(Messages), Hash.length) - */ -static S2N_RESULT s2n_derive_secret(s2n_hmac_algorithm hmac_alg, - const struct s2n_blob *previous_secret_material, const struct s2n_blob *label, const struct s2n_blob *context, - struct s2n_blob *output) -{ - /* - * TODO: We should be able to reuse the prf_work_space rather - * than allocating a new HMAC every time. - * https://github.com/aws/s2n-tls/issues/3206 - */ - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); - - output->size = s2n_get_hash_len(hmac_alg); - RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, hmac_alg, - previous_secret_material, label, context, output)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_derive_secret_with_context(struct s2n_connection *conn, - s2n_extract_secret_type_t input_secret_type, const struct s2n_blob *label, message_type_t transcript_end_msg, - struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(label); - RESULT_ENSURE_REF(output); - - RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); - RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == transcript_end_msg, S2N_ERR_SECRET_SCHEDULE_STATE); - RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), - label, &CONN_HASH(conn, transcript_hash_digest), output)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_derive_secret_without_context(struct s2n_connection *conn, - s2n_extract_secret_type_t input_secret_type, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); - RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), - &s2n_tls13_label_derived_secret, &EMPTY_CONTEXT(CONN_HMAC_ALG(conn)), output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 - *# The key used to compute the Finished message is computed from the - *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). - *# Specifically: - *# - *# finished_key = - *# HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) - **/ -static S2N_RESULT s2n_tls13_compute_finished_key(struct s2n_connection *conn, - const struct s2n_blob *base_key, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(base_key); - RESULT_ENSURE_REF(output); - - RESULT_GUARD(s2n_handshake_set_finished_len(conn, output->size)); - - /* - * TODO: We should be able to reuse the prf_work_space rather - * than allocating a new HMAC every time. - */ - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); - - RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, CONN_HMAC_ALG(conn), - base_key, &s2n_tls13_label_finished, &(struct s2n_blob){ 0 }, output)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_call_secret_callbacks(struct s2n_connection *conn, - const struct s2n_blob *secret, s2n_secret_type_t secret_type) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - - if (conn->secret_cb && (s2n_connection_is_quic_enabled(conn) || s2n_in_unit_test())) { - RESULT_GUARD_POSIX(conn->secret_cb(conn->secret_cb_context, conn, secret_type, - secret->data, secret->size)); - } - s2n_result_ignore(s2n_key_log_tls13_secret(conn, secret, secret_type)); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_trigger_secret_callbacks(struct s2n_connection *conn, - const struct s2n_blob *secret, s2n_extract_secret_type_t secret_type, s2n_mode mode) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - - static const s2n_secret_type_t conversions[][2] = { - [S2N_EARLY_SECRET] = { S2N_CLIENT_EARLY_TRAFFIC_SECRET, S2N_CLIENT_EARLY_TRAFFIC_SECRET }, - [S2N_HANDSHAKE_SECRET] = { S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET, S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET }, - [S2N_MASTER_SECRET] = { S2N_SERVER_APPLICATION_TRAFFIC_SECRET, S2N_CLIENT_APPLICATION_TRAFFIC_SECRET }, - }; - s2n_secret_type_t callback_secret_type = conversions[secret_type][mode]; - - RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, callback_secret_type)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# 0 - *# | - *# v - *# PSK -> HKDF-Extract = Early Secret - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# There are multiple potential Early Secret values, depending on which - *# PSK the server ultimately selects. The client will need to compute - *# one for each potential PSK - */ -S2N_RESULT s2n_extract_early_secret(struct s2n_psk *psk) -{ - RESULT_ENSURE_REF(psk); - RESULT_GUARD_POSIX(s2n_realloc(&psk->early_secret, s2n_get_hash_len(psk->hmac_alg))); - RESULT_GUARD(s2n_extract_secret(psk->hmac_alg, - &ZERO_VALUE(psk->hmac_alg), - &psk->secret, - &psk->early_secret)); - return S2N_RESULT_OK; -} - -/* - * When we require an early secret to derive other secrets, - * either retrieve the early secret stored on the chosen / early data PSK - * or calculate one using a "zero" PSK. - */ -static S2N_RESULT s2n_extract_early_secret_for_schedule(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_psk *psk = conn->psk_params.chosen_psk; - s2n_hmac_algorithm hmac_alg = CONN_HMAC_ALG(conn); - - /* - * If the client is sending early data, then the PSK is always assumed - * to be the first PSK offered. - */ - if (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { - RESULT_GUARD(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); - RESULT_ENSURE_REF(psk); - } - - /** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# if no PSK is selected, it will then need - *# to compute the Early Secret corresponding to the zero PSK. - */ - if (psk == NULL) { - RESULT_GUARD(s2n_extract_secret(hmac_alg, - &ZERO_VALUE(hmac_alg), - &ZERO_VALUE(hmac_alg), - &CONN_SECRET(conn, extract_secret))); - return S2N_RESULT_OK; - } - - /* - * The early secret is required to generate or verify a PSK's binder, - * so must have already been calculated if a valid PSK exists. - * Use the early secret stored on the PSK. - */ - RESULT_ENSURE_EQ(hmac_alg, psk->hmac_alg); - RESULT_CHECKED_MEMCPY(CONN_SECRETS(conn).extract_secret, psk->early_secret.data, psk->early_secret.size); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "ext binder" | "res binder", "") - *# | = binder_key - */ -S2N_RESULT s2n_derive_binder_key(struct s2n_psk *psk, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(psk); - RESULT_ENSURE_REF(output); - - const struct s2n_blob *label = &s2n_tls13_label_resumption_psk_binder_key; - if (psk->type == S2N_PSK_TYPE_EXTERNAL) { - label = &s2n_tls13_label_external_psk_binder_key; - } - RESULT_GUARD(s2n_extract_early_secret(psk)); - RESULT_GUARD(s2n_derive_secret(psk->hmac_alg, - &psk->early_secret, - label, - &EMPTY_CONTEXT(psk->hmac_alg), - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "c e traffic", ClientHello) - *# | = client_early_traffic_secret - */ -static S2N_RESULT s2n_derive_client_early_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_EARLY_SECRET, - &s2n_tls13_label_client_early_traffic_secret, - CLIENT_HELLO, - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# v - *# Derive-Secret(., "derived", "") - *# | - *# v - *# (EC)DHE -> HKDF-Extract = Handshake Secret - */ -static S2N_RESULT s2n_extract_handshake_secret(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_blob derived_secret = { 0 }; - uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); - RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_EARLY_SECRET, &derived_secret)); - - DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free_or_wipe); - RESULT_GUARD_POSIX(s2n_tls13_compute_shared_secret(conn, &shared_secret)); - - RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), - &derived_secret, - &shared_secret, - &CONN_SECRET(conn, extract_secret))); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "c hs traffic", - *# | ClientHello...ServerHello) - *# | = client_handshake_traffic_secret - */ -static S2N_RESULT s2n_derive_client_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_HANDSHAKE_SECRET, - &s2n_tls13_label_client_handshake_traffic_secret, - SERVER_HELLO, - output)); - - /* - * The client finished key needs to be calculated using the - * same connection state as the client handshake secret. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 - *# The key used to compute the Finished message is computed from the - *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). - */ - RESULT_GUARD(s2n_tls13_compute_finished_key(conn, - output, &CONN_FINISHED(conn, client))); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "s hs traffic", - *# | ClientHello...ServerHello) - *# | = server_handshake_traffic_secret - */ -static S2N_RESULT s2n_derive_server_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(output); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_HANDSHAKE_SECRET, - &s2n_tls13_label_server_handshake_traffic_secret, - SERVER_HELLO, - output)); - - /* - * The server finished key needs to be calculated using the - * same connection state as the server handshake secret. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 - *# The key used to compute the Finished message is computed from the - *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). - */ - RESULT_GUARD(s2n_tls13_compute_finished_key(conn, - output, &CONN_FINISHED(conn, server))); - - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# v - *# Derive-Secret(., "derived", "") - *# | - *# v - *# 0 -> HKDF-Extract = Master Secret - */ -static S2N_RESULT s2n_extract_master_secret(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - - struct s2n_blob derived_secret = { 0 }; - uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); - RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_HANDSHAKE_SECRET, &derived_secret)); - - RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), - &derived_secret, - &ZERO_VALUE(CONN_HMAC_ALG(conn)), - &CONN_SECRET(conn, extract_secret))); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "c ap traffic", - *# | ClientHello...server Finished) - *# | = client_application_traffic_secret_0 - */ -static S2N_RESULT s2n_derive_client_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_client_application_traffic_secret, - SERVER_FINISHED, - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "s ap traffic", - *# | ClientHello...server Finished) - *# | = server_application_traffic_secret_0 - */ -static S2N_RESULT s2n_derive_server_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) -{ - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_server_application_traffic_secret, - SERVER_FINISHED, - output)); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "res master", - *# ClientHello...client Finished) - *# = resumption_master_secret - */ -S2N_RESULT s2n_derive_resumption_master_secret(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - /* Secret derivation requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_resumption_master_secret, - CLIENT_FINISHED, - &CONN_SECRET(conn, resumption_master_secret))); - return S2N_RESULT_OK; -} - -/** - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 - *# | - *# +-----> Derive-Secret(., "exp master", - *# | ClientHello...server Finished) - *# | = exporter_master_secret - */ -S2N_RESULT s2n_derive_exporter_master_secret(struct s2n_connection *conn, struct s2n_blob *secret) -{ - RESULT_ENSURE_REF(conn); - /* Secret derivation requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - RESULT_GUARD(s2n_derive_secret_with_context(conn, - S2N_MASTER_SECRET, - &s2n_tls13_label_exporter_master_secret, - SERVER_FINISHED, - secret)); - - RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, S2N_EXPORTER_SECRET)); - - return S2N_RESULT_OK; -} - -static s2n_result (*extract_methods[])(struct s2n_connection *conn) = { - [S2N_EARLY_SECRET] = &s2n_extract_early_secret_for_schedule, - [S2N_HANDSHAKE_SECRET] = &s2n_extract_handshake_secret, - [S2N_MASTER_SECRET] = &s2n_extract_master_secret, -}; - -S2N_RESULT s2n_tls13_extract_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->handshake.hashes); - RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); - - RESULT_ENSURE_GTE(secret_type, 0); - RESULT_ENSURE_LT(secret_type, s2n_array_len(extract_methods)); - - s2n_extract_secret_type_t next_secret_type = conn->secrets.extract_secret_type + 1; - for (s2n_extract_secret_type_t i = next_secret_type; i <= secret_type; i++) { - RESULT_ENSURE_REF(extract_methods[i]); - RESULT_GUARD(extract_methods[i](conn)); - conn->secrets.extract_secret_type = i; - } - - return S2N_RESULT_OK; -} - -static s2n_result (*derive_methods[][2])(struct s2n_connection *conn, struct s2n_blob *secret) = { - [S2N_EARLY_SECRET] = { &s2n_derive_client_early_traffic_secret, &s2n_derive_client_early_traffic_secret }, - [S2N_HANDSHAKE_SECRET] = { &s2n_derive_server_handshake_traffic_secret, &s2n_derive_client_handshake_traffic_secret }, - [S2N_MASTER_SECRET] = { &s2n_derive_server_application_traffic_secret, &s2n_derive_client_application_traffic_secret }, -}; - -S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, - s2n_mode mode, struct s2n_blob *secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - RESULT_ENSURE_REF(conn->handshake.hashes); - RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); - - RESULT_GUARD(s2n_tls13_extract_secret(conn, secret_type)); - - RESULT_ENSURE_GTE(secret_type, 0); - RESULT_ENSURE_LT(secret_type, s2n_array_len(derive_methods)); - RESULT_ENSURE_REF(derive_methods[secret_type][mode]); - RESULT_GUARD(derive_methods[secret_type][mode](conn, secret)); - - RESULT_GUARD(s2n_trigger_secret_callbacks(conn, secret, secret_type, mode)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_secrets_clean(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - /* Secret clean requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - if (conn->actual_protocol_version < S2N_TLS13) { - return S2N_RESULT_OK; - } - - /* - * Wipe base secrets. - * Not strictly necessary, but probably safer than leaving them. - * A compromised secret additionally compromises all secrets derived from it, - * so these are the most sensitive secrets. - */ - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, extract_secret))); - conn->secrets.extract_secret_type = S2N_NONE_SECRET; - - /* Wipe other secrets no longer needed */ - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_early_secret))); - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_handshake_secret))); - RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, server_handshake_secret))); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_secrets_update(struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { - return S2N_RESULT_OK; - } - - /* Secret update requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - message_type_t message_type = s2n_conn_get_current_message_type(conn); - switch (message_type) { - case CLIENT_HELLO: - if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED - || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, - S2N_CLIENT, &CONN_SECRET(conn, client_early_secret))); - } - break; - case SERVER_HELLO: - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, - S2N_CLIENT, &CONN_SECRET(conn, client_handshake_secret))); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, - S2N_SERVER, &CONN_SECRET(conn, server_handshake_secret))); - break; - case SERVER_FINISHED: - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, - S2N_CLIENT, &CONN_SECRET(conn, client_app_secret))); - RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, - S2N_SERVER, &CONN_SECRET(conn, server_app_secret))); - RESULT_GUARD(s2n_derive_exporter_master_secret(conn, - &CONN_SECRET(conn, exporter_master_secret))); - break; - case CLIENT_FINISHED: - RESULT_GUARD(s2n_calculate_transcript_digest(conn)); - RESULT_GUARD(s2n_derive_resumption_master_secret(conn)); - break; - default: - break; - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, - s2n_mode mode, struct s2n_blob *secret) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(secret); - /* Getting secrets requires these fields to be non-null. */ - RESULT_ENSURE_REF(conn->secure); - RESULT_ENSURE_REF(conn->secure->cipher_suite); - - bool is_available = (secret_type <= conn->secrets.extract_secret_type) - /* Unlike the other secrets, we don't wipe the master / app secrets */ - || (secret_type == S2N_MASTER_SECRET && s2n_handshake_is_complete(conn)); - RESULT_ENSURE(is_available, S2N_ERR_SAFETY); - - uint8_t *secrets[][2] = { - [S2N_EARLY_SECRET] = { NULL, CONN_SECRETS(conn).client_early_secret }, - [S2N_HANDSHAKE_SECRET] = { CONN_SECRETS(conn).server_handshake_secret, CONN_SECRETS(conn).client_handshake_secret }, - [S2N_MASTER_SECRET] = { CONN_SECRETS(conn).server_app_secret, CONN_SECRETS(conn).client_app_secret }, - }; - RESULT_ENSURE_GT(secret_type, S2N_NONE_SECRET); - RESULT_ENSURE_LT(secret_type, s2n_array_len(secrets)); - RESULT_ENSURE_REF(secrets[secret_type][mode]); - - secret->size = s2n_get_hash_len(CONN_HMAC_ALG(conn)); - RESULT_CHECKED_MEMCPY(secret->data, secrets[secret_type][mode], secret->size); - RESULT_ENSURE_GT(secret->size, 0); - return S2N_RESULT_OK; -} - -/* - *= https://www.rfc-editor.org/rfc/rfc8446#section-7.5 - *# The exporter value is computed as: - *# - *# TLS-Exporter(label, context_value, key_length) = - *# HKDF-Expand-Label(Derive-Secret(Secret, label, ""), - *# "exporter", Hash(context_value), key_length) - */ -int s2n_connection_tls_exporter(struct s2n_connection *conn, - const uint8_t *label_in, uint32_t label_length, - const uint8_t *context, uint32_t context_length, - uint8_t *output_in, uint32_t output_length) -{ - POSIX_ENSURE_REF(conn); - POSIX_ENSURE_REF(output_in); - POSIX_ENSURE_REF(label_in); - POSIX_ENSURE_REF(context); - POSIX_ENSURE(s2n_connection_get_protocol_version(conn) == S2N_TLS13, S2N_ERR_INVALID_STATE); - - POSIX_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); - POSIX_ENSURE_REF(conn->secure); - POSIX_ENSURE_REF(conn->secure->cipher_suite); - s2n_hmac_algorithm hmac_alg = conn->secure->cipher_suite->prf_alg; - - uint8_t label_bytes[S2N_MAX_HKDF_EXPAND_LABEL_LENGTH] = { 0 }; - struct s2n_blob label = { 0 }; - POSIX_ENSURE_LTE(label_length, sizeof(label_bytes)); - POSIX_CHECKED_MEMCPY(label_bytes, label_in, label_length); - POSIX_GUARD(s2n_blob_init(&label, label_bytes, label_length)); - - uint8_t derived_secret_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob derived_secret = { 0 }; - POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); - POSIX_GUARD(s2n_blob_init(&derived_secret, - derived_secret_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); - POSIX_GUARD_RESULT(s2n_derive_secret(hmac_alg, &CONN_SECRET(conn, exporter_master_secret), - &label, &EMPTY_CONTEXT(hmac_alg), &derived_secret)); - - DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); - POSIX_GUARD(s2n_hmac_new(&hmac_state)); - - DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); - POSIX_GUARD(s2n_hash_new(&hash)); - - s2n_hash_algorithm hash_alg = { 0 }; - POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); - uint8_t digest_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; - struct s2n_blob digest = { 0 }; - POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); - POSIX_GUARD(s2n_blob_init(&digest, digest_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); - - POSIX_GUARD(s2n_hash_init(&hash, hash_alg)); - POSIX_GUARD(s2n_hash_update(&hash, context, context_length)); - POSIX_GUARD(s2n_hash_digest(&hash, digest.data, digest.size)); - - struct s2n_blob output = { 0 }; - POSIX_GUARD(s2n_blob_init(&output, output_in, output_length)); - POSIX_GUARD(s2n_hkdf_expand_label(&hmac_state, hmac_alg, - &derived_secret, &s2n_tls13_label_exporter, &digest, &output)); - - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_tls13_secrets.h" + +#include "tls/s2n_connection.h" +#include "tls/s2n_key_log.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_bitmap.h" + +#define S2N_MAX_HASHLEN SHA384_DIGEST_LENGTH + +#define CONN_HMAC_ALG(conn) ((conn)->secure->cipher_suite->prf_alg) +#define CONN_SECRETS(conn) ((conn)->secrets.version.tls13) +#define CONN_HASHES(conn) ((conn)->handshake.hashes) + +#define CONN_SECRET(conn, secret) ( \ + (struct s2n_blob){ .data = CONN_SECRETS(conn).secret, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) +#define CONN_HASH(conn, hash) ( \ + (struct s2n_blob){ .data = CONN_HASHES(conn)->hash, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) +#define CONN_FINISHED(conn, mode) ( \ + (struct s2n_blob){ .data = (conn)->handshake.mode##_finished, .size = s2n_get_hash_len(CONN_HMAC_ALG(conn)) }) + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# If a given secret is not available, then the 0-value consisting of a + *# string of Hash.length bytes set to zeros is used. + */ +static uint8_t zero_value_bytes[S2N_MAX_HASHLEN] = { 0 }; +#define ZERO_VALUE(hmac_alg) ( \ + (const struct s2n_blob){ .data = zero_value_bytes, .size = s2n_get_hash_len(hmac_alg) }) + +/** + * When an operation doesn't need an actual transcript hash, + * it uses an empty transcript hash as an input instead. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# Note that in some cases a zero- + *# length Context (indicated by "") is passed to HKDF-Expand-Label + */ +#define EMPTY_CONTEXT(hmac_alg) ( \ + (const struct s2n_blob){ .data = s2n_get_empty_context(hmac_alg), .size = s2n_get_hash_len(hmac_alg) }) + +static uint8_t s2n_get_hash_len(s2n_hmac_algorithm hmac_alg) +{ + uint8_t hash_size = 0; + if (s2n_hmac_digest_size(hmac_alg, &hash_size) != S2N_SUCCESS) { + return 0; + } + return hash_size; +} + +static uint8_t *s2n_get_empty_context(s2n_hmac_algorithm hmac_alg) +{ + static uint8_t sha256_empty_digest[S2N_MAX_HASHLEN] = { 0 }; + static uint8_t sha384_empty_digest[S2N_MAX_HASHLEN] = { 0 }; + + switch (hmac_alg) { + case S2N_HMAC_SHA256: + return sha256_empty_digest; + case S2N_HMAC_SHA384: + return sha384_empty_digest; + default: + return NULL; + } +} + +static s2n_hmac_algorithm supported_hmacs[] = { + S2N_HMAC_SHA256, + S2N_HMAC_SHA384 +}; + +S2N_RESULT s2n_tls13_empty_transcripts_init() +{ + DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); + RESULT_GUARD_POSIX(s2n_hash_new(&hash)); + + s2n_hash_algorithm hash_alg = S2N_HASH_NONE; + for (size_t i = 0; i < s2n_array_len(supported_hmacs); i++) { + s2n_hmac_algorithm hmac_alg = supported_hmacs[i]; + struct s2n_blob digest = EMPTY_CONTEXT(hmac_alg); + + RESULT_GUARD_POSIX(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); + RESULT_GUARD_POSIX(s2n_hash_init(&hash, hash_alg)); + RESULT_GUARD_POSIX(s2n_hash_digest(&hash, digest.data, digest.size)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_calculate_transcript_digest(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->handshake.hashes); + + s2n_hash_algorithm hash_algorithm = S2N_HASH_NONE; + RESULT_GUARD_POSIX(s2n_hmac_hash_alg(CONN_HMAC_ALG(conn), &hash_algorithm)); + + uint8_t digest_size = 0; + RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_algorithm, &digest_size)); + + struct s2n_blob digest = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&digest, CONN_HASHES(conn)->transcript_hash_digest, digest_size)); + + struct s2n_hash_state *hash_state = &conn->handshake.hashes->hash_workspace; + RESULT_GUARD(s2n_handshake_copy_hash_state(conn, hash_algorithm, hash_state)); + RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, digest.data, digest.size)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_extract_secret(s2n_hmac_algorithm hmac_alg, + const struct s2n_blob *previous_secret_material, const struct s2n_blob *new_secret_material, + struct s2n_blob *output) +{ + /* + * TODO: We should be able to reuse the prf_work_space rather + * than allocating a new HMAC every time. + * https://github.com/aws/s2n-tls/issues/3206 + */ + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); + + RESULT_GUARD_POSIX(s2n_hkdf_extract(&hmac_state, hmac_alg, + previous_secret_material, new_secret_material, output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# Derive-Secret(Secret, Label, Messages) = + *# HKDF-Expand-Label(Secret, Label, + *# Transcript-Hash(Messages), Hash.length) + */ +static S2N_RESULT s2n_derive_secret(s2n_hmac_algorithm hmac_alg, + const struct s2n_blob *previous_secret_material, const struct s2n_blob *label, const struct s2n_blob *context, + struct s2n_blob *output) +{ + /* + * TODO: We should be able to reuse the prf_work_space rather + * than allocating a new HMAC every time. + * https://github.com/aws/s2n-tls/issues/3206 + */ + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); + + output->size = s2n_get_hash_len(hmac_alg); + RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, hmac_alg, + previous_secret_material, label, context, output)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_derive_secret_with_context(struct s2n_connection *conn, + s2n_extract_secret_type_t input_secret_type, const struct s2n_blob *label, message_type_t transcript_end_msg, + struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(label); + RESULT_ENSURE_REF(output); + + RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); + RESULT_ENSURE(s2n_conn_get_current_message_type(conn) == transcript_end_msg, S2N_ERR_SECRET_SCHEDULE_STATE); + RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), + label, &CONN_HASH(conn, transcript_hash_digest), output)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_derive_secret_without_context(struct s2n_connection *conn, + s2n_extract_secret_type_t input_secret_type, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + RESULT_ENSURE(conn->secrets.extract_secret_type == input_secret_type, S2N_ERR_SECRET_SCHEDULE_STATE); + RESULT_GUARD(s2n_derive_secret(CONN_HMAC_ALG(conn), &CONN_SECRET(conn, extract_secret), + &s2n_tls13_label_derived_secret, &EMPTY_CONTEXT(CONN_HMAC_ALG(conn)), output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 + *# The key used to compute the Finished message is computed from the + *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). + *# Specifically: + *# + *# finished_key = + *# HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) + **/ +static S2N_RESULT s2n_tls13_compute_finished_key(struct s2n_connection *conn, + const struct s2n_blob *base_key, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(base_key); + RESULT_ENSURE_REF(output); + + RESULT_GUARD(s2n_handshake_set_finished_len(conn, output->size)); + + /* + * TODO: We should be able to reuse the prf_work_space rather + * than allocating a new HMAC every time. + */ + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + RESULT_GUARD_POSIX(s2n_hmac_new(&hmac_state)); + + RESULT_GUARD_POSIX(s2n_hkdf_expand_label(&hmac_state, CONN_HMAC_ALG(conn), + base_key, &s2n_tls13_label_finished, &(struct s2n_blob){ 0 }, output)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_call_secret_callbacks(struct s2n_connection *conn, + const struct s2n_blob *secret, s2n_secret_type_t secret_type) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + + if (conn->secret_cb && (s2n_connection_is_quic_enabled(conn) || s2n_in_unit_test())) { + RESULT_GUARD_POSIX(conn->secret_cb(conn->secret_cb_context, conn, secret_type, + secret->data, secret->size)); + } + s2n_result_ignore(s2n_key_log_tls13_secret(conn, secret, secret_type)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_trigger_secret_callbacks(struct s2n_connection *conn, + const struct s2n_blob *secret, s2n_extract_secret_type_t secret_type, s2n_mode mode) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + + static const s2n_secret_type_t conversions[][2] = { + [S2N_EARLY_SECRET] = { S2N_CLIENT_EARLY_TRAFFIC_SECRET, S2N_CLIENT_EARLY_TRAFFIC_SECRET }, + [S2N_HANDSHAKE_SECRET] = { S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET, S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET }, + [S2N_MASTER_SECRET] = { S2N_SERVER_APPLICATION_TRAFFIC_SECRET, S2N_CLIENT_APPLICATION_TRAFFIC_SECRET }, + }; + s2n_secret_type_t callback_secret_type = conversions[secret_type][mode]; + + RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, callback_secret_type)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# 0 + *# | + *# v + *# PSK -> HKDF-Extract = Early Secret + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# There are multiple potential Early Secret values, depending on which + *# PSK the server ultimately selects. The client will need to compute + *# one for each potential PSK + */ +S2N_RESULT s2n_extract_early_secret(struct s2n_psk *psk) +{ + RESULT_ENSURE_REF(psk); + RESULT_GUARD_POSIX(s2n_realloc(&psk->early_secret, s2n_get_hash_len(psk->hmac_alg))); + RESULT_GUARD(s2n_extract_secret(psk->hmac_alg, + &ZERO_VALUE(psk->hmac_alg), + &psk->secret, + &psk->early_secret)); + return S2N_RESULT_OK; +} + +/* + * When we require an early secret to derive other secrets, + * either retrieve the early secret stored on the chosen / early data PSK + * or calculate one using a "zero" PSK. + */ +static S2N_RESULT s2n_extract_early_secret_for_schedule(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_psk *psk = conn->psk_params.chosen_psk; + s2n_hmac_algorithm hmac_alg = CONN_HMAC_ALG(conn); + + /* + * If the client is sending early data, then the PSK is always assumed + * to be the first PSK offered. + */ + if (conn->mode == S2N_CLIENT && conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { + RESULT_GUARD(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + RESULT_ENSURE_REF(psk); + } + + /** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# if no PSK is selected, it will then need + *# to compute the Early Secret corresponding to the zero PSK. + */ + if (psk == NULL) { + RESULT_GUARD(s2n_extract_secret(hmac_alg, + &ZERO_VALUE(hmac_alg), + &ZERO_VALUE(hmac_alg), + &CONN_SECRET(conn, extract_secret))); + return S2N_RESULT_OK; + } + + /* + * The early secret is required to generate or verify a PSK's binder, + * so must have already been calculated if a valid PSK exists. + * Use the early secret stored on the PSK. + */ + RESULT_ENSURE_EQ(hmac_alg, psk->hmac_alg); + RESULT_CHECKED_MEMCPY(CONN_SECRETS(conn).extract_secret, psk->early_secret.data, psk->early_secret.size); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "ext binder" | "res binder", "") + *# | = binder_key + */ +S2N_RESULT s2n_derive_binder_key(struct s2n_psk *psk, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(psk); + RESULT_ENSURE_REF(output); + + const struct s2n_blob *label = &s2n_tls13_label_resumption_psk_binder_key; + if (psk->type == S2N_PSK_TYPE_EXTERNAL) { + label = &s2n_tls13_label_external_psk_binder_key; + } + RESULT_GUARD(s2n_extract_early_secret(psk)); + RESULT_GUARD(s2n_derive_secret(psk->hmac_alg, + &psk->early_secret, + label, + &EMPTY_CONTEXT(psk->hmac_alg), + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "c e traffic", ClientHello) + *# | = client_early_traffic_secret + */ +static S2N_RESULT s2n_derive_client_early_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_EARLY_SECRET, + &s2n_tls13_label_client_early_traffic_secret, + CLIENT_HELLO, + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# v + *# Derive-Secret(., "derived", "") + *# | + *# v + *# (EC)DHE -> HKDF-Extract = Handshake Secret + */ +static S2N_RESULT s2n_extract_handshake_secret(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_blob derived_secret = { 0 }; + uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); + RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_EARLY_SECRET, &derived_secret)); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free_or_wipe); + RESULT_GUARD_POSIX(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), + &derived_secret, + &shared_secret, + &CONN_SECRET(conn, extract_secret))); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "c hs traffic", + *# | ClientHello...ServerHello) + *# | = client_handshake_traffic_secret + */ +static S2N_RESULT s2n_derive_client_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_HANDSHAKE_SECRET, + &s2n_tls13_label_client_handshake_traffic_secret, + SERVER_HELLO, + output)); + + /* + * The client finished key needs to be calculated using the + * same connection state as the client handshake secret. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 + *# The key used to compute the Finished message is computed from the + *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). + */ + RESULT_GUARD(s2n_tls13_compute_finished_key(conn, + output, &CONN_FINISHED(conn, client))); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "s hs traffic", + *# | ClientHello...ServerHello) + *# | = server_handshake_traffic_secret + */ +static S2N_RESULT s2n_derive_server_handshake_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(output); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_HANDSHAKE_SECRET, + &s2n_tls13_label_server_handshake_traffic_secret, + SERVER_HELLO, + output)); + + /* + * The server finished key needs to be calculated using the + * same connection state as the server handshake secret. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.4 + *# The key used to compute the Finished message is computed from the + *# Base Key defined in Section 4.4 using HKDF (see Section 7.1). + */ + RESULT_GUARD(s2n_tls13_compute_finished_key(conn, + output, &CONN_FINISHED(conn, server))); + + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# v + *# Derive-Secret(., "derived", "") + *# | + *# v + *# 0 -> HKDF-Extract = Master Secret + */ +static S2N_RESULT s2n_extract_master_secret(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + struct s2n_blob derived_secret = { 0 }; + uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); + RESULT_GUARD(s2n_derive_secret_without_context(conn, S2N_HANDSHAKE_SECRET, &derived_secret)); + + RESULT_GUARD(s2n_extract_secret(CONN_HMAC_ALG(conn), + &derived_secret, + &ZERO_VALUE(CONN_HMAC_ALG(conn)), + &CONN_SECRET(conn, extract_secret))); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "c ap traffic", + *# | ClientHello...server Finished) + *# | = client_application_traffic_secret_0 + */ +static S2N_RESULT s2n_derive_client_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_client_application_traffic_secret, + SERVER_FINISHED, + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "s ap traffic", + *# | ClientHello...server Finished) + *# | = server_application_traffic_secret_0 + */ +static S2N_RESULT s2n_derive_server_application_traffic_secret(struct s2n_connection *conn, struct s2n_blob *output) +{ + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_server_application_traffic_secret, + SERVER_FINISHED, + output)); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "res master", + *# ClientHello...client Finished) + *# = resumption_master_secret + */ +S2N_RESULT s2n_derive_resumption_master_secret(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + /* Secret derivation requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_resumption_master_secret, + CLIENT_FINISHED, + &CONN_SECRET(conn, resumption_master_secret))); + return S2N_RESULT_OK; +} + +/** + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.1 + *# | + *# +-----> Derive-Secret(., "exp master", + *# | ClientHello...server Finished) + *# | = exporter_master_secret + */ +S2N_RESULT s2n_derive_exporter_master_secret(struct s2n_connection *conn, struct s2n_blob *secret) +{ + RESULT_ENSURE_REF(conn); + /* Secret derivation requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + RESULT_GUARD(s2n_derive_secret_with_context(conn, + S2N_MASTER_SECRET, + &s2n_tls13_label_exporter_master_secret, + SERVER_FINISHED, + secret)); + + RESULT_GUARD(s2n_call_secret_callbacks(conn, secret, S2N_EXPORTER_SECRET)); + + return S2N_RESULT_OK; +} + +static s2n_result (*extract_methods[])(struct s2n_connection *conn) = { + [S2N_EARLY_SECRET] = &s2n_extract_early_secret_for_schedule, + [S2N_HANDSHAKE_SECRET] = &s2n_extract_handshake_secret, + [S2N_MASTER_SECRET] = &s2n_extract_master_secret, +}; + +S2N_RESULT s2n_tls13_extract_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->handshake.hashes); + RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); + + RESULT_ENSURE_GTE(secret_type, 0); + RESULT_ENSURE_LT(secret_type, s2n_array_len(extract_methods)); + + s2n_extract_secret_type_t next_secret_type = conn->secrets.extract_secret_type + 1; + for (s2n_extract_secret_type_t i = next_secret_type; i <= secret_type; i++) { + RESULT_ENSURE_REF(extract_methods[i]); + RESULT_GUARD(extract_methods[i](conn)); + conn->secrets.extract_secret_type = i; + } + + return S2N_RESULT_OK; +} + +static s2n_result (*derive_methods[][2])(struct s2n_connection *conn, struct s2n_blob *secret) = { + [S2N_EARLY_SECRET] = { &s2n_derive_client_early_traffic_secret, &s2n_derive_client_early_traffic_secret }, + [S2N_HANDSHAKE_SECRET] = { &s2n_derive_server_handshake_traffic_secret, &s2n_derive_client_handshake_traffic_secret }, + [S2N_MASTER_SECRET] = { &s2n_derive_server_application_traffic_secret, &s2n_derive_client_application_traffic_secret }, +}; + +S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->handshake.hashes); + RESULT_ENSURE_NE(secret_type, S2N_NONE_SECRET); + + RESULT_GUARD(s2n_tls13_extract_secret(conn, secret_type)); + + RESULT_ENSURE_GTE(secret_type, 0); + RESULT_ENSURE_LT(secret_type, s2n_array_len(derive_methods)); + RESULT_ENSURE_REF(derive_methods[secret_type][mode]); + RESULT_GUARD(derive_methods[secret_type][mode](conn, secret)); + + RESULT_GUARD(s2n_trigger_secret_callbacks(conn, secret, secret_type, mode)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_secrets_clean(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + /* Secret clean requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + if (conn->actual_protocol_version < S2N_TLS13) { + return S2N_RESULT_OK; + } + + /* + * Wipe base secrets. + * Not strictly necessary, but probably safer than leaving them. + * A compromised secret additionally compromises all secrets derived from it, + * so these are the most sensitive secrets. + */ + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, extract_secret))); + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + + /* Wipe other secrets no longer needed */ + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_early_secret))); + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, client_handshake_secret))); + RESULT_GUARD_POSIX(s2n_blob_zero(&CONN_SECRET(conn, server_handshake_secret))); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_secrets_update(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { + return S2N_RESULT_OK; + } + + /* Secret update requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + message_type_t message_type = s2n_conn_get_current_message_type(conn); + switch (message_type) { + case CLIENT_HELLO: + if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED + || conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, + S2N_CLIENT, &CONN_SECRET(conn, client_early_secret))); + } + break; + case SERVER_HELLO: + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, + S2N_CLIENT, &CONN_SECRET(conn, client_handshake_secret))); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, + S2N_SERVER, &CONN_SECRET(conn, server_handshake_secret))); + break; + case SERVER_FINISHED: + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, + S2N_CLIENT, &CONN_SECRET(conn, client_app_secret))); + RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, + S2N_SERVER, &CONN_SECRET(conn, server_app_secret))); + RESULT_GUARD(s2n_derive_exporter_master_secret(conn, + &CONN_SECRET(conn, exporter_master_secret))); + break; + case CLIENT_FINISHED: + RESULT_GUARD(s2n_calculate_transcript_digest(conn)); + RESULT_GUARD(s2n_derive_resumption_master_secret(conn)); + break; + default: + break; + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret); + /* Getting secrets requires these fields to be non-null. */ + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + bool is_available = (secret_type <= conn->secrets.extract_secret_type) + /* Unlike the other secrets, we don't wipe the master / app secrets */ + || (secret_type == S2N_MASTER_SECRET && s2n_handshake_is_complete(conn)); + RESULT_ENSURE(is_available, S2N_ERR_SAFETY); + + uint8_t *secrets[][2] = { + [S2N_EARLY_SECRET] = { NULL, CONN_SECRETS(conn).client_early_secret }, + [S2N_HANDSHAKE_SECRET] = { CONN_SECRETS(conn).server_handshake_secret, CONN_SECRETS(conn).client_handshake_secret }, + [S2N_MASTER_SECRET] = { CONN_SECRETS(conn).server_app_secret, CONN_SECRETS(conn).client_app_secret }, + }; + RESULT_ENSURE_GT(secret_type, S2N_NONE_SECRET); + RESULT_ENSURE_LT(secret_type, s2n_array_len(secrets)); + RESULT_ENSURE_REF(secrets[secret_type][mode]); + + secret->size = s2n_get_hash_len(CONN_HMAC_ALG(conn)); + RESULT_CHECKED_MEMCPY(secret->data, secrets[secret_type][mode], secret->size); + RESULT_ENSURE_GT(secret->size, 0); + return S2N_RESULT_OK; +} + +/* + *= https://www.rfc-editor.org/rfc/rfc8446#section-7.5 + *# The exporter value is computed as: + *# + *# TLS-Exporter(label, context_value, key_length) = + *# HKDF-Expand-Label(Derive-Secret(Secret, label, ""), + *# "exporter", Hash(context_value), key_length) + */ +int s2n_connection_tls_exporter(struct s2n_connection *conn, + const uint8_t *label_in, uint32_t label_length, + const uint8_t *context, uint32_t context_length, + uint8_t *output_in, uint32_t output_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(output_in); + POSIX_ENSURE_REF(label_in); + POSIX_ENSURE_REF(context); + POSIX_ENSURE(s2n_connection_get_protocol_version(conn) == S2N_TLS13, S2N_ERR_INVALID_STATE); + + POSIX_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + s2n_hmac_algorithm hmac_alg = conn->secure->cipher_suite->prf_alg; + + uint8_t label_bytes[S2N_MAX_HKDF_EXPAND_LABEL_LENGTH] = { 0 }; + struct s2n_blob label = { 0 }; + POSIX_ENSURE_LTE(label_length, sizeof(label_bytes)); + POSIX_CHECKED_MEMCPY(label_bytes, label_in, label_length); + POSIX_GUARD(s2n_blob_init(&label, label_bytes, label_length)); + + uint8_t derived_secret_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob derived_secret = { 0 }; + POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); + POSIX_GUARD(s2n_blob_init(&derived_secret, + derived_secret_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); + POSIX_GUARD_RESULT(s2n_derive_secret(hmac_alg, &CONN_SECRET(conn, exporter_master_secret), + &label, &EMPTY_CONTEXT(hmac_alg), &derived_secret)); + + DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free); + POSIX_GUARD(s2n_hmac_new(&hmac_state)); + + DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free); + POSIX_GUARD(s2n_hash_new(&hash)); + + s2n_hash_algorithm hash_alg = { 0 }; + POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg)); + uint8_t digest_bytes[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest = { 0 }; + POSIX_ENSURE_LTE(s2n_get_hash_len(CONN_HMAC_ALG(conn)), S2N_MAX_DIGEST_LEN); + POSIX_GUARD(s2n_blob_init(&digest, digest_bytes, s2n_get_hash_len(CONN_HMAC_ALG(conn)))); + + POSIX_GUARD(s2n_hash_init(&hash, hash_alg)); + POSIX_GUARD(s2n_hash_update(&hash, context, context_length)); + POSIX_GUARD(s2n_hash_digest(&hash, digest.data, digest.size)); + + struct s2n_blob output = { 0 }; + POSIX_GUARD(s2n_blob_init(&output, output_in, output_length)); + POSIX_GUARD(s2n_hkdf_expand_label(&hmac_state, hmac_alg, + &derived_secret, &s2n_tls13_label_exporter, &digest, &output)); + + return S2N_SUCCESS; +} diff --git a/tls/s2n_x509_validator.c b/tls/s2n_x509_validator.c index ed74ccc7b4d..e68c441b810 100644 --- a/tls/s2n_x509_validator.c +++ b/tls/s2n_x509_validator.c @@ -1,1169 +1,1169 @@ -/* - * 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. - */ - -#if !defined(_MSC_VER) -#include -#else -#include -#include -#endif -#if !defined(_MSC_VER) -#include -#endif -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_openssl_x509.h" -#include "crypto/s2n_pkey.h" -#include "tls/extensions/s2n_extension_list.h" -#include "tls/s2n_config.h" -#include "tls/s2n_connection.h" -#include "tls/s2n_crl.h" -#include "tls/s2n_security_policies.h" -#include "utils/s2n_result.h" -#include "utils/s2n_rfc5952.h" -#include "utils/s2n_safety.h" - -#if S2N_OCSP_STAPLING_SUPPORTED - #include -DEFINE_POINTER_CLEANUP_FUNC(OCSP_RESPONSE *, OCSP_RESPONSE_free); -DEFINE_POINTER_CLEANUP_FUNC(OCSP_BASICRESP *, OCSP_BASICRESP_free); - -#endif - -#ifndef X509_V_FLAG_PARTIAL_CHAIN - #define X509_V_FLAG_PARTIAL_CHAIN 0x80000 -#endif - -#define DEFAULT_MAX_CHAIN_DEPTH 7 -/* Time used by default for nextUpdate if none provided in OCSP: 1 hour since thisUpdate. */ -#define DEFAULT_OCSP_NEXT_UPDATE_PERIOD 3600 - -/* s2n's internal clock measures epoch-nanoseconds stored with a uint64_t. The - * maximum representable timestamp is Sunday, July 21, 2554. time_t measures - * epoch-seconds in a int64_t or int32_t (platform dependent). If time_t is an - * int32_t, the maximum representable timestamp is January 19, 2038. - * - * This means that converting from the internal clock to a time_t is not safe, - * because the internal clock might hold a value that is too large to represent - * in a time_t. This constant represents the largest internal clock value that - * can be safely represented as a time_t. - */ -#define MAX_32_TIMESTAMP_NANOS 2147483647 * ONE_SEC_IN_NANOS - -#define OSSL_VERIFY_CALLBACK_IGNORE_ERROR 1 - -DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(X509_CRL) *, sk_X509_CRL_free); -DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(GENERAL_NAME) *, GENERAL_NAMES_free); - -uint8_t s2n_x509_ocsp_stapling_supported(void) -{ - return S2N_OCSP_STAPLING_SUPPORTED; -} - -void s2n_x509_trust_store_init_empty(struct s2n_x509_trust_store *store) -{ - store->trust_store = NULL; -} - -uint8_t s2n_x509_trust_store_has_certs(struct s2n_x509_trust_store *store) -{ - return store->trust_store ? (uint8_t) 1 : (uint8_t) 0; -} - -int s2n_x509_trust_store_add_pem(struct s2n_x509_trust_store *store, const char *pem) -{ - POSIX_ENSURE_REF(store); - POSIX_ENSURE_REF(pem); - - if (!store->trust_store) { - store->trust_store = X509_STORE_new(); - POSIX_ENSURE_REF(store->trust_store); - } - - DEFER_CLEANUP(struct s2n_stuffer pem_in_stuffer = { 0 }, s2n_stuffer_free); - DEFER_CLEANUP(struct s2n_stuffer der_out_stuffer = { 0 }, s2n_stuffer_free); - - POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&pem_in_stuffer, pem)); - POSIX_GUARD(s2n_stuffer_growable_alloc(&der_out_stuffer, 2048)); - - do { - DEFER_CLEANUP(struct s2n_blob next_cert = { 0 }, s2n_free); - - POSIX_GUARD(s2n_stuffer_certificate_from_pem(&pem_in_stuffer, &der_out_stuffer)); - POSIX_GUARD(s2n_alloc(&next_cert, s2n_stuffer_data_available(&der_out_stuffer))); - POSIX_GUARD(s2n_stuffer_read(&der_out_stuffer, &next_cert)); - - const uint8_t *data = next_cert.data; - DEFER_CLEANUP(X509 *ca_cert = d2i_X509(NULL, &data, next_cert.size), X509_free_pointer); - S2N_ERROR_IF(ca_cert == NULL, S2N_ERR_DECODE_CERTIFICATE); - - if (!X509_STORE_add_cert(store->trust_store, ca_cert)) { - unsigned long error = ERR_get_error(); - POSIX_ENSURE(ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE, S2N_ERR_DECODE_CERTIFICATE); - } - } while (s2n_stuffer_data_available(&pem_in_stuffer)); - - return 0; -} - -int s2n_x509_trust_store_from_ca_file(struct s2n_x509_trust_store *store, const char *ca_pem_filename, const char *ca_dir) -{ - if (!store->trust_store) { - store->trust_store = X509_STORE_new(); - POSIX_ENSURE_REF(store->trust_store); - } - - int err_code = X509_STORE_load_locations(store->trust_store, ca_pem_filename, ca_dir); - if (!err_code) { - s2n_x509_trust_store_wipe(store); - POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); - } - - return 0; -} - -void s2n_x509_trust_store_wipe(struct s2n_x509_trust_store *store) -{ - if (store->trust_store) { - X509_STORE_free(store->trust_store); - store->trust_store = NULL; - store->loaded_system_certs = false; - } -} - -int s2n_x509_validator_init_no_x509_validation(struct s2n_x509_validator *validator) -{ - POSIX_ENSURE_REF(validator); - validator->trust_store = NULL; - validator->store_ctx = NULL; - validator->skip_cert_validation = 1; - validator->check_stapled_ocsp = 0; - validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; - validator->state = INIT; - validator->cert_chain_from_wire = sk_X509_new_null(); - validator->crl_lookup_list = NULL; - validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; - validator->cert_validation_cb_invoked = false; - - return 0; -} - -int s2n_x509_validator_init(struct s2n_x509_validator *validator, struct s2n_x509_trust_store *trust_store, uint8_t check_ocsp) -{ - POSIX_ENSURE_REF(trust_store); - validator->trust_store = trust_store; - validator->skip_cert_validation = 0; - validator->check_stapled_ocsp = check_ocsp; - validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; - validator->store_ctx = NULL; - if (validator->trust_store->trust_store) { - validator->store_ctx = X509_STORE_CTX_new(); - POSIX_ENSURE_REF(validator->store_ctx); - } - validator->cert_chain_from_wire = sk_X509_new_null(); - validator->state = INIT; - validator->crl_lookup_list = NULL; - validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; - validator->cert_validation_cb_invoked = false; - - return 0; -} - -static inline void wipe_cert_chain(STACK_OF(X509) *cert_chain) -{ - if (cert_chain) { - sk_X509_pop_free(cert_chain, X509_free); - } -} - -int s2n_x509_validator_wipe(struct s2n_x509_validator *validator) -{ - if (validator->store_ctx) { - X509_STORE_CTX_free(validator->store_ctx); - validator->store_ctx = NULL; - } - wipe_cert_chain(validator->cert_chain_from_wire); - validator->cert_chain_from_wire = NULL; - validator->trust_store = NULL; - validator->skip_cert_validation = 0; - validator->state = UNINIT; - validator->max_chain_depth = 0; - if (validator->crl_lookup_list) { - POSIX_GUARD_RESULT(s2n_array_free(validator->crl_lookup_list)); - validator->crl_lookup_list = NULL; - } - - return S2N_SUCCESS; -} - -int s2n_x509_validator_set_max_chain_depth(struct s2n_x509_validator *validator, uint16_t max_depth) -{ - POSIX_ENSURE_REF(validator); - S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); - - validator->max_chain_depth = max_depth; - return 0; -} - -static S2N_RESULT s2n_verify_host_information_san_entry(struct s2n_connection *conn, GENERAL_NAME *current_name, bool *san_found) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(current_name); - RESULT_ENSURE_REF(san_found); - - if (current_name->type == GEN_DNS || current_name->type == GEN_URI) { - *san_found = true; - - const char *name = (const char *) ASN1_STRING_data(current_name->d.ia5); - RESULT_ENSURE_REF(name); - int name_len = ASN1_STRING_length(current_name->d.ia5); - RESULT_ENSURE_GT(name_len, 0); - - RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; - } - - if (current_name->type == GEN_IPADD) { - *san_found = true; - - /* try to validate an IP address if it's in the subject alt name. */ - const unsigned char *ip_addr = current_name->d.iPAddress->data; - RESULT_ENSURE_REF(ip_addr); - int ip_addr_len = current_name->d.iPAddress->length; - RESULT_ENSURE_GT(ip_addr_len, 0); - - RESULT_STACK_BLOB(address, INET6_ADDRSTRLEN + 1, INET6_ADDRSTRLEN + 1); - - if (ip_addr_len == 4) { - RESULT_GUARD(s2n_inet_ntop(AF_INET, ip_addr, &address)); - } else if (ip_addr_len == 16) { - RESULT_GUARD(s2n_inet_ntop(AF_INET6, ip_addr, &address)); - } else { - /* we aren't able to parse this value so skip it */ - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } - - /* strlen should be safe here since we made sure we were null terminated AND that inet_ntop succeeded */ - const char *name = (const char *) address.data; - size_t name_len = strlen(name); - - RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; - } - - /* we don't understand this entry type so skip it */ - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); -} - -static S2N_RESULT s2n_verify_host_information_san(struct s2n_connection *conn, X509 *public_cert, bool *san_found) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(public_cert); - RESULT_ENSURE_REF(san_found); - - *san_found = false; - - DEFER_CLEANUP(STACK_OF(GENERAL_NAME) *names_list = NULL, GENERAL_NAMES_free_pointer); - names_list = X509_get_ext_d2i(public_cert, NID_subject_alt_name, NULL, NULL); - RESULT_ENSURE(names_list, S2N_ERR_CERT_UNTRUSTED); - - int n = sk_GENERAL_NAME_num(names_list); - RESULT_ENSURE(n > 0, S2N_ERR_CERT_UNTRUSTED); - - s2n_result result = S2N_RESULT_OK; - for (int i = 0; i < n; i++) { - GENERAL_NAME *current_name = sk_GENERAL_NAME_value(names_list, i); - - /* return success on the first entry that passes verification */ - result = s2n_verify_host_information_san_entry(conn, current_name, san_found); - if (s2n_result_is_ok(result)) { - return S2N_RESULT_OK; - } - } - - /* if an error was set by one of the entries, then just propagate the error from the last SAN entry call */ - RESULT_GUARD(result); - - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); -} - -static S2N_RESULT s2n_verify_host_information_common_name(struct s2n_connection *conn, X509 *public_cert, bool *cn_found) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(public_cert); - RESULT_ENSURE_REF(cn_found); - - X509_NAME *subject_name = X509_get_subject_name(public_cert); - RESULT_ENSURE(subject_name, S2N_ERR_CERT_UNTRUSTED); - - int curr_idx = -1; - while (true) { - int next_idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, curr_idx); - if (next_idx >= 0) { - curr_idx = next_idx; - } else { - break; - } - } - - RESULT_ENSURE(curr_idx >= 0, S2N_ERR_CERT_UNTRUSTED); - - ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, curr_idx)); - RESULT_ENSURE(common_name, S2N_ERR_CERT_UNTRUSTED); - - /* X520CommonName allows the following ANSI string types per RFC 5280 Appendix A.1 */ - RESULT_ENSURE(ASN1_STRING_type(common_name) == V_ASN1_TELETEXSTRING - || ASN1_STRING_type(common_name) == V_ASN1_PRINTABLESTRING - || ASN1_STRING_type(common_name) == V_ASN1_UNIVERSALSTRING - || ASN1_STRING_type(common_name) == V_ASN1_UTF8STRING - || ASN1_STRING_type(common_name) == V_ASN1_BMPSTRING, - S2N_ERR_CERT_UNTRUSTED); - - /* at this point we have a valid CN value */ - *cn_found = true; - - char peer_cn[255] = { 0 }; - int cn_len = ASN1_STRING_length(common_name); - RESULT_ENSURE_GT(cn_len, 0); - uint32_t len = (uint32_t) cn_len; - RESULT_ENSURE_LTE(len, s2n_array_len(peer_cn) - 1); - RESULT_CHECKED_MEMCPY(peer_cn, ASN1_STRING_data(common_name), len); - - /* According to https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4, - * the CN fallback only applies to fully qualified DNS domain names. - * - * An IP address is not a fully qualified DNS domain name. Per RFC 6125 - * section 6.2.1, IP reference identities must only be matched against - * iPAddress SAN entries, never against CN values. Reject the CN if it - * parses as an IPv4 or IPv6 address. - */ - unsigned char ip_buf[sizeof(struct in6_addr)] = { 0 }; - if (inet_pton(AF_INET, peer_cn, ip_buf) == 1 || inet_pton(AF_INET6, peer_cn, ip_buf) == 1) { - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } - - RESULT_ENSURE(conn->verify_host_fn(peer_cn, len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; -} - -/* - * For each name in the cert. Iterate them. Call the callback. If one returns true, then consider it validated, - * if none of them return true, the cert is considered invalid. - */ -static S2N_RESULT s2n_verify_host_information(struct s2n_connection *conn, X509 *public_cert) -{ - bool entry_found = false; - - /* Check SubjectAltNames before CommonName as per RFC 6125 6.4.4 */ - s2n_result result = s2n_verify_host_information_san(conn, public_cert, &entry_found); - - /* - *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 - *# As noted, a client MUST NOT seek a match for a reference identifier - *# of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, - *# URI-ID, or any application-specific identifier types supported by the - *# client. - */ - if (entry_found) { - return result; - } - - /* - *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 - *# Therefore, if and only if the presented identifiers do not include a - *# DNS-ID, SRV-ID, URI-ID, or any application-specific identifier types - *# supported by the client, then the client MAY as a last resort check - *# for a string whose form matches that of a fully qualified DNS domain - *# name in a Common Name field of the subject field (i.e., a CN-ID). - */ - result = s2n_verify_host_information_common_name(conn, public_cert, &entry_found); - if (entry_found) { - return result; - } - - /* make a null-terminated string in case the callback tries to use strlen */ - const char *name = ""; - size_t name_len = 0; - - /* at this point, we don't have anything to identify the certificate with so pass an empty string to the callback */ - RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, - struct s2n_blob *asn1_cert) -{ - uint32_t certificate_size = 0; - - RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(cert_chain_in_stuffer, &certificate_size)); - RESULT_ENSURE(certificate_size > 0, S2N_ERR_CERT_INVALID); - RESULT_ENSURE(certificate_size <= s2n_stuffer_data_available(cert_chain_in_stuffer), S2N_ERR_CERT_INVALID); - - asn1_cert->size = certificate_size; - asn1_cert->data = s2n_stuffer_raw_read(cert_chain_in_stuffer, certificate_size); - RESULT_ENSURE_REF(asn1_cert->data); - - return S2N_RESULT_OK; -} - -/** -* Validates that each certificate in a peer's cert chain contains only signature algorithms in a security policy's -* certificate_signatures_preference list. -*/ -S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(cert); - - const struct s2n_security_policy *security_policy = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); - - /** - * We only restrict the signature algorithm on the certificates in the - * peer's certificate chain if the certificate_signature_preferences field - * is set in the security policy. This is contrary to the RFC, which - * specifies that the signatures in the "signature_algorithms" extension - * apply to signatures in the certificate chain in certain scenarios, so RFC - * compliance would imply validating that the certificate chain signature - * algorithm matches one of the algorithms specified in the - * "signature_algorithms" extension. - * - *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.2 - *= type=exception - *= reason=not implemented due to lack of utility - *# If the client provided a "signature_algorithms" extension, then all - *# certificates provided by the server MUST be signed by a - *# hash/signature algorithm pair that appears in that extension. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.3 - *= type=exception - *= reason=not implemented due to lack of utility - *# If no "signature_algorithms_cert" extension is present, then the - *# "signature_algorithms" extension also applies to signatures appearing in - *# certificates. - */ - struct s2n_cert_info info = { 0 }; - RESULT_GUARD(s2n_openssl_x509_get_cert_info(cert, &info)); - - bool certificate_preferences_defined = security_policy->certificate_signature_preferences != NULL - || security_policy->certificate_key_preferences != NULL; - if (certificate_preferences_defined && !info.self_signed && conn->actual_protocol_version == S2N_TLS13) { - /* Ensure that the certificate signature does not use SHA-1. While this check - * would ideally apply to all connections, we only enforce it when certificate - * preferences exist to stay backwards compatible. - */ - RESULT_ENSURE(info.signature_digest_nid != NID_sha1, S2N_ERR_CERT_UNTRUSTED); - } - - if (!info.self_signed) { - RESULT_GUARD(s2n_security_policy_validate_cert_signature(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); - } - RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_get_validated_cert_chain(const struct s2n_x509_validator *validator, - struct s2n_validated_cert_chain *validated_cert_chain) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validated_cert_chain); - - RESULT_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_INVALID_CERT_STATE); - RESULT_ENSURE_REF(validator->store_ctx); - -#if S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN - /* X509_STORE_CTX_get0_chain is used when available, since it returns a pointer to the - * validated cert chain in the X509_STORE_CTX, avoiding an allocation/copy. - */ - validated_cert_chain->stack = X509_STORE_CTX_get0_chain(validator->store_ctx); -#else - /* Otherwise, X509_STORE_CTX_get1_chain is used instead, which allocates a new cert chain. */ - validated_cert_chain->stack = X509_STORE_CTX_get1_chain(validator->store_ctx); -#endif - - RESULT_ENSURE_REF(validated_cert_chain->stack); - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_x509_validator_validated_cert_chain_free(struct s2n_validated_cert_chain *validated_cert_chain) -{ - RESULT_ENSURE_REF(validated_cert_chain); - -#if !S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN - /* When X509_STORE_CTX_get0_chain isn't supported, X509_STORE_CTX_get1_chain is used instead, - * which allocates a new cert chain that is owned by s2n-tls and MUST be freed. - * - * X509_STORE_CTX_get0_chain returns a pointer to the cert chain within the X509_STORE_CTX, - * which is NOT owned by s2n-tls and MUST NOT be manually freed. - */ - RESULT_GUARD(s2n_openssl_x509_stack_pop_free(&validated_cert_chain->stack)); -#endif - - /* Even though the cert chain reference is still valid in the case that get0_chain is used, set - * it to null for consistency with the get1_chain case. - */ - validated_cert_chain->stack = NULL; - - return S2N_RESULT_OK; -} - -/* Validates that the root certificate uses a key allowed by the security policy - * certificate preferences. - */ -static S2N_RESULT s2n_x509_validator_check_root_cert(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(conn); - - const struct s2n_security_policy *security_policy = NULL; - RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); - RESULT_ENSURE_REF(security_policy); - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - STACK_OF(X509) *cert_chain = validated_cert_chain.stack; - RESULT_ENSURE_REF(cert_chain); - - const int certs_in_chain = sk_X509_num(cert_chain); - RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_CERT_UNTRUSTED); - X509 *root = sk_X509_value(cert_chain, certs_in_chain - 1); - RESULT_ENSURE_REF(root); - - struct s2n_cert_info info = { 0 }; - RESULT_GUARD(s2n_openssl_x509_get_cert_info(root, &info)); - - RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, - S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_read_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len) -{ - RESULT_ENSURE(validator->skip_cert_validation || s2n_x509_trust_store_has_certs(validator->trust_store), S2N_ERR_CERT_UNTRUSTED); - RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); - - struct s2n_blob cert_chain_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); - DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); - - RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); - - while (s2n_stuffer_data_available(&cert_chain_in_stuffer) - && sk_X509_num(validator->cert_chain_from_wire) < validator->max_chain_depth) { - struct s2n_blob asn1_cert = { 0 }; - RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); - - /* We only do the trailing byte validation when parsing the leaf cert to - * match historical s2n-tls behavior. - */ - DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); - if (sk_X509_num(validator->cert_chain_from_wire) == 0) { - RESULT_GUARD(s2n_openssl_x509_parse(&asn1_cert, &cert)); - } else { - RESULT_GUARD(s2n_openssl_x509_parse_without_length_validation(&asn1_cert, &cert)); - } - - if (!validator->skip_cert_validation) { - RESULT_GUARD(s2n_x509_validator_check_cert_preferences(conn, cert)); - } - - /* add the cert to the chain */ - RESULT_ENSURE(sk_X509_push(validator->cert_chain_from_wire, cert) > 0, - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - /* After the cert is added to cert_chain_from_wire, it will be freed - * with the call to s2n_x509_validator_wipe. We disable the cleanup - * function since cleanup is no longer "owned" by cert. - */ - ZERO_TO_DISABLE_DEFER_CLEANUP(cert); - - /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ - if (conn->actual_protocol_version >= S2N_TLS13) { - s2n_parsed_extensions_list parsed_extensions_list = { 0 }; - RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); - } - } - - /* if this occurred we exceeded validator->max_chain_depth */ - RESULT_ENSURE(validator->skip_cert_validation || s2n_stuffer_data_available(&cert_chain_in_stuffer) == 0, - S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); - RESULT_ENSURE(sk_X509_num(validator->cert_chain_from_wire) > 0, S2N_ERR_NO_CERT_FOUND); - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_process_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len) -{ - RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); - - RESULT_GUARD(s2n_x509_validator_read_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); - - if (validator->skip_cert_validation) { - return S2N_RESULT_OK; - } - - X509 *leaf = sk_X509_value(validator->cert_chain_from_wire, 0); - RESULT_ENSURE_REF(leaf); - - if (conn->verify_host_fn) { - RESULT_GUARD(s2n_verify_host_information(conn, leaf)); - } - - RESULT_GUARD_OSSL(X509_STORE_CTX_init(validator->store_ctx, validator->trust_store->trust_store, leaf, - validator->cert_chain_from_wire), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - if (conn->config->crl_lookup_cb) { - RESULT_GUARD(s2n_crl_invoke_lookup_callbacks(conn, validator)); - RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); - } - - validator->state = READY_TO_VERIFY; - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_set_no_check_time_flag(struct s2n_x509_validator *validator) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validator->store_ctx); - - X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); - RESULT_ENSURE_REF(param); - -#ifdef S2N_LIBCRYPTO_SUPPORTS_FLAG_NO_CHECK_TIME - RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_NO_CHECK_TIME), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); -#else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -#endif - - return S2N_RESULT_OK; -} - -int s2n_disable_time_validation_ossl_verify_callback(int default_ossl_ret, X509_STORE_CTX *ctx) -{ - int err = X509_STORE_CTX_get_error(ctx); - switch (err) { - case X509_V_ERR_CERT_NOT_YET_VALID: - case X509_V_ERR_CERT_HAS_EXPIRED: - return OSSL_VERIFY_CALLBACK_IGNORE_ERROR; - default: - break; - } - - /* If CRL validation is enabled, setting the time validation verify callback will override the - * CRL verify callback. The CRL verify callback is manually triggered to work around this - * issue. - * - * The CRL verify callback ignores validation errors exclusively for CRL timestamp fields. So, - * if CRL validation isn't enabled, the CRL verify callback is a no-op. - */ - return s2n_crl_ossl_verify_callback(default_ossl_ret, ctx); -} - -static S2N_RESULT s2n_x509_validator_disable_time_validation(struct s2n_connection *conn, - struct s2n_x509_validator *validator) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validator->store_ctx); - - /* Setting an X509_STORE verify callback is not recommended with AWS-LC: - * https://github.com/aws/aws-lc/blob/aa90e509f2e940916fbe9fdd469a4c90c51824f6/include/openssl/x509.h#L2980-L2990 - * - * If the libcrypto supports the ability to disable time validation with an X509_VERIFY_PARAM - * NO_CHECK_TIME flag, this method is preferred. - * - * However, older versions of AWS-LC and OpenSSL 1.0.2 do not support this flag. In this case, - * an X509_STORE verify callback is used. This is acceptable in older versions of AWS-LC - * because the versions are fixed, and updates to AWS-LC will not break the callback - * implementation. - */ - if (s2n_libcrypto_supports_flag_no_check_time()) { - RESULT_GUARD(s2n_x509_validator_set_no_check_time_flag(validator)); - } else { - X509_STORE_CTX_set_verify_cb(validator->store_ctx, - s2n_disable_time_validation_ossl_verify_callback); - } - - return S2N_RESULT_OK; -} - -int s2n_no_op_verify_custom_crit_oids_cb(X509_STORE_CTX *ctx, X509 *x509, STACK_OF(ASN1_OBJECT) *oids) -{ - return 1; -} - -static S2N_RESULT s2n_x509_validator_add_custom_extensions(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(validator); - RESULT_ENSURE_REF(validator->store_ctx); - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - if (conn->config->custom_x509_extension_oids) { -#if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_OID - size_t custom_oid_count = sk_ASN1_OBJECT_num(conn->config->custom_x509_extension_oids); - for (size_t i = 0; i < custom_oid_count; i++) { - ASN1_OBJECT *critical_oid = sk_ASN1_OBJECT_value(conn->config->custom_x509_extension_oids, i); - RESULT_ENSURE_REF(critical_oid); - RESULT_GUARD_OSSL(X509_STORE_CTX_add_custom_crit_oid(validator->store_ctx, critical_oid), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - } - /* To enable AWS-LC accepting custom extensions, an X509_STORE_CTX_verify_crit_oids_cb must be set. - * See https://github.com/aws/aws-lc/blob/f0b4afedd7d45fc2517643d890b654856c57f994/include/openssl/x509.h#L2913-L2918. - * - * The `X509_STORE_CTX_verify_crit_oids_cb` callback can be used to implement the validation for the - * custom certificate extensions. However, s2n-tls consumers are expected to implement this validation - * in the `s2n_cert_validation_callback` instead. So, a no-op callback is provided to AWS-LC. - */ - X509_STORE_CTX_set_verify_crit_oids(validator->store_ctx, s2n_no_op_verify_custom_crit_oids_cb); -#else - RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); -#endif - } - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_verify_intent_for_cert(struct s2n_connection *conn, X509 *cert, bool is_leaf) -{ - RESULT_ENSURE_REF(cert); - - /* The X509_PURPOSE values indicate the purpose that certificates must specify. For servers, - * received client certificates MUST have a TLS client purpose. For clients, received server - * certificates MUST have a TLS server purpose. - */ - int purpose = X509_PURPOSE_SSL_CLIENT; - if (conn->mode == S2N_CLIENT) { - purpose = X509_PURPOSE_SSL_SERVER; - } - - RESULT_GUARD_OSSL(X509_check_purpose(cert, purpose, !is_leaf), S2N_ERR_CERT_INTENT_INVALID); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_verify_intent(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - if (conn->config->disable_x509_intent_verification) { - return S2N_RESULT_OK; - } - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - - int cert_count = sk_X509_num(validated_cert_chain.stack); - RESULT_ENSURE_GT(cert_count, 0); - - /* The validated cert chain returned from the libcrypto includes the trust anchor. The trust - * anchor is omitted from intent verification since its TLS intent is implicitly indicated by - * its presence in the s2n-tls trust store. - */ - cert_count -= 1; - - for (int i = 0; i < cert_count; i++) { - X509 *cert = sk_X509_value(validated_cert_chain.stack, i); - RESULT_ENSURE_REF(cert); - - bool is_leaf = (i == 0); - RESULT_GUARD(s2n_x509_validator_verify_intent_for_cert(conn, cert, is_leaf)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_verify_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn) -{ - RESULT_ENSURE(validator->state == READY_TO_VERIFY, S2N_ERR_INVALID_CERT_STATE); - - X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); - X509_VERIFY_PARAM_set_depth(param, validator->max_chain_depth); - - DEFER_CLEANUP(STACK_OF(X509_CRL) *crl_stack = NULL, sk_X509_CRL_free_pointer); - - if (conn->config->crl_lookup_cb) { - X509_STORE_CTX_set_verify_cb(validator->store_ctx, s2n_crl_ossl_verify_callback); - - crl_stack = sk_X509_CRL_new_null(); - RESULT_GUARD(s2n_crl_get_crls_from_lookup_list(validator, crl_stack)); - - /* Set the CRL list that the libcrypto will use to validate certificates with */ - X509_STORE_CTX_set0_crls(validator->store_ctx, crl_stack); - - /* Enable CRL validation for certificates in X509_verify_cert */ - RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - - /* Enable CRL validation for all certificates, not just the leaf */ - RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK_ALL), - S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); - } - - /* Disabling time validation may set a NO_CHECK_TIME flag on the X509_STORE_CTX. Calling - * X509_STORE_CTX_set_time will override this flag. To prevent this, X509_STORE_CTX_set_time is - * only called if time validation is enabled. - */ - if (conn->config->disable_x509_time_validation) { - RESULT_GUARD(s2n_x509_validator_disable_time_validation(conn, validator)); - } else { - uint64_t current_sys_time = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time)); - if (sizeof(time_t) == 4) { - /* cast value to uint64_t to prevent overflow errors */ - RESULT_ENSURE_LTE(current_sys_time, (uint64_t) MAX_32_TIMESTAMP_NANOS); - } - - /* this wants seconds not nanoseconds */ - time_t current_time = (time_t) (current_sys_time / ONE_SEC_IN_NANOS); - X509_STORE_CTX_set_time(validator->store_ctx, 0, current_time); - } - - /* It's assumed that if a valid certificate chain is received with an issuer that's present in - * the trust store, the certificate chain should be trusted. This should be the case even if - * the issuer in the trust store isn't a root certificate. Setting the PARTIAL_CHAIN flag - * allows the libcrypto to trust certificates in the trust store that aren't root certificates. - */ - X509_STORE_CTX_set_flags(validator->store_ctx, X509_V_FLAG_PARTIAL_CHAIN); - - RESULT_GUARD(s2n_x509_validator_add_custom_extensions(validator, conn)); - - int verify_ret = X509_verify_cert(validator->store_ctx); - if (verify_ret <= 0) { - int ossl_error = X509_STORE_CTX_get_error(validator->store_ctx); - switch (ossl_error) { - case X509_V_ERR_CERT_NOT_YET_VALID: - RESULT_BAIL(S2N_ERR_CERT_NOT_YET_VALID); - case X509_V_ERR_CERT_HAS_EXPIRED: - RESULT_BAIL(S2N_ERR_CERT_EXPIRED); - case X509_V_ERR_CERT_REVOKED: - RESULT_BAIL(S2N_ERR_CERT_REVOKED); - case X509_V_ERR_UNABLE_TO_GET_CRL: - case X509_V_ERR_DIFFERENT_CRL_SCOPE: - RESULT_BAIL(S2N_ERR_CRL_LOOKUP_FAILED); - case X509_V_ERR_CRL_SIGNATURE_FAILURE: - RESULT_BAIL(S2N_ERR_CRL_SIGNATURE); - case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: - RESULT_BAIL(S2N_ERR_CRL_ISSUER); - case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: - RESULT_BAIL(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); - case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: - RESULT_BAIL(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION); - default: - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } - } - - validator->state = VALIDATED; - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_parse_leaf_certificate_extensions(struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len, - s2n_parsed_extensions_list *first_certificate_extensions) -{ - /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ - RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); - - struct s2n_blob cert_chain_blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); - DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); - - RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); - RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); - - struct s2n_blob asn1_cert = { 0 }; - RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); - - s2n_parsed_extensions_list parsed_extensions_list = { 0 }; - RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); - *first_certificate_extensions = parsed_extensions_list; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_validate_cert_chain_pre_cb(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len) -{ - RESULT_ENSURE_REF(conn); - RESULT_ENSURE_REF(conn->config); - - switch (validator->state) { - case INIT: - break; - case AWAITING_CRL_CALLBACK: - RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); - break; - default: - RESULT_BAIL(S2N_ERR_INVALID_CERT_STATE); - } - - if (validator->state == INIT) { - RESULT_GUARD(s2n_x509_validator_process_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); - } - - if (validator->state == READY_TO_VERIFY) { - RESULT_GUARD(s2n_x509_validator_verify_cert_chain(validator, conn)); - RESULT_GUARD(s2n_x509_validator_verify_intent(validator, conn)); - RESULT_GUARD(s2n_x509_validator_check_root_cert(validator, conn)); - } - - if (conn->actual_protocol_version >= S2N_TLS13) { - /* Only process certificate extensions received in the first certificate. Extensions received in all other - * certificates are ignored. - * - *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.2 - *# If an extension applies to the entire chain, it SHOULD be included in - *# the first CertificateEntry. - */ - s2n_parsed_extensions_list first_certificate_extensions = { 0 }; - RESULT_GUARD(s2n_x509_validator_parse_leaf_certificate_extensions(conn, cert_chain_in, cert_chain_len, &first_certificate_extensions)); - RESULT_GUARD_POSIX(s2n_extension_list_process(S2N_EXTENSION_LIST_CERTIFICATE, conn, &first_certificate_extensions)); - } - - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_x509_validator_handle_cert_validation_callback_result(struct s2n_x509_validator *validator) -{ - RESULT_ENSURE_REF(validator); - - if (!validator->cert_validation_info.finished) { - RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); - } - - RESULT_ENSURE(validator->cert_validation_info.accepted, S2N_ERR_CERT_REJECTED); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, - uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_pkey_type *pkey_type, struct s2n_pkey *public_key_out) -{ - RESULT_ENSURE_REF(validator); - - if (validator->cert_validation_cb_invoked) { - RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); - } else { - RESULT_GUARD(s2n_x509_validator_validate_cert_chain_pre_cb(validator, conn, cert_chain_in, cert_chain_len)); - - if (conn->config->cert_validation_cb) { - RESULT_ENSURE(conn->config->cert_validation_cb(conn, &(validator->cert_validation_info), conn->config->cert_validation_ctx) == S2N_SUCCESS, - S2N_ERR_CANCELLED); - validator->cert_validation_cb_invoked = true; - RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); - } - } - - /* retrieve information from leaf cert */ - RESULT_ENSURE_GT(sk_X509_num(validator->cert_chain_from_wire), 0); - X509 *leaf_cert = sk_X509_value(validator->cert_chain_from_wire, 0); - RESULT_ENSURE_REF(leaf_cert); - DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); - RESULT_GUARD(s2n_pkey_from_x509(leaf_cert, &public_key, pkey_type)); - - *public_key_out = public_key; - - /* Reset the old struct, so we don't clean up public_key_out */ - ZERO_TO_DISABLE_DEFER_CLEANUP(public_key); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_x509_validator_validate_cert_stapled_ocsp_response(struct s2n_x509_validator *validator, - struct s2n_connection *conn, const uint8_t *ocsp_response_raw, uint32_t ocsp_response_length) -{ - if (validator->skip_cert_validation || !validator->check_stapled_ocsp) { - validator->state = OCSP_VALIDATED; - return S2N_RESULT_OK; - } - - RESULT_ENSURE(validator->state == VALIDATED, S2N_ERR_INVALID_CERT_STATE); - -#if !S2N_OCSP_STAPLING_SUPPORTED - /* Default to safety */ - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); -#else - - RESULT_ENSURE_REF(ocsp_response_raw); - - DEFER_CLEANUP(OCSP_RESPONSE *ocsp_response = d2i_OCSP_RESPONSE(NULL, &ocsp_response_raw, ocsp_response_length), - OCSP_RESPONSE_free_pointer); - RESULT_ENSURE(ocsp_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); - - int ocsp_status = OCSP_response_status(ocsp_response); - RESULT_ENSURE(ocsp_status == OCSP_RESPONSE_STATUS_SUCCESSFUL, S2N_ERR_CERT_UNTRUSTED); - - DEFER_CLEANUP(OCSP_BASICRESP *basic_response = OCSP_response_get1_basic(ocsp_response), OCSP_BASICRESP_free_pointer); - RESULT_ENSURE(basic_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); - - DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); - RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); - STACK_OF(X509) *cert_chain = validated_cert_chain.stack; - RESULT_ENSURE_REF(cert_chain); - - const int certs_in_chain = sk_X509_num(cert_chain); - RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_NO_CERT_FOUND); - - /* leaf is the top: not the bottom. */ - X509 *subject = sk_X509_value(cert_chain, 0); - X509 *issuer = NULL; - /* find the issuer in the chain. If it's not there. Fail everything. */ - for (int i = 0; i < certs_in_chain; ++i) { - X509 *issuer_candidate = sk_X509_value(cert_chain, i); - const int issuer_value = X509_check_issued(issuer_candidate, subject); - - if (issuer_value == X509_V_OK) { - issuer = issuer_candidate; - break; - } - } - RESULT_ENSURE(issuer != NULL, S2N_ERR_CERT_UNTRUSTED); - - /* Important: this checks that the stapled ocsp response CAN be verified, not that it has been verified. */ - const int ocsp_verify_res = OCSP_basic_verify(basic_response, cert_chain, validator->trust_store->trust_store, 0); - RESULT_GUARD_OSSL(ocsp_verify_res, S2N_ERR_CERT_UNTRUSTED); - - /* do the crypto checks on the response.*/ - int status = 0; - int reason = 0; - - /* SHA-1 is the only supported hash algorithm for the CertID due to its established use in - * OCSP responders. - */ - OCSP_CERTID *cert_id = OCSP_cert_to_id(EVP_sha1(), subject, issuer); - RESULT_ENSURE_REF(cert_id); - - /** - *= https://www.rfc-editor.org/rfc/rfc6960#section-2.4 - *# - *# thisUpdate The most recent time at which the status being - *# indicated is known by the responder to have been - *# correct. - *# - *# nextUpdate The time at or before which newer information will be - *# available about the status of the certificate. - **/ - ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; - /* Actual verification of the response */ - const int ocsp_resp_find_status_res = OCSP_resp_find_status(basic_response, cert_id, &status, &reason, &revtime, &thisupd, &nextupd); - OCSP_CERTID_free(cert_id); - RESULT_GUARD_OSSL(ocsp_resp_find_status_res, S2N_ERR_CERT_UNTRUSTED); - - uint64_t current_sys_time_nanoseconds = 0; - RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time_nanoseconds)); - if (sizeof(time_t) == 4) { - /* cast value to uint64_t to prevent overflow errors */ - RESULT_ENSURE_LTE(current_sys_time_nanoseconds, (uint64_t) MAX_32_TIMESTAMP_NANOS); - } - /* convert the current_sys_time (which is in nanoseconds) to seconds */ - time_t current_sys_time_seconds = (time_t) (current_sys_time_nanoseconds / ONE_SEC_IN_NANOS); - - DEFER_CLEANUP(ASN1_GENERALIZEDTIME *current_sys_time = ASN1_GENERALIZEDTIME_set(NULL, current_sys_time_seconds), s2n_openssl_asn1_time_free_pointer); - RESULT_ENSURE_REF(current_sys_time); - - /** - * It is fine to use ASN1_TIME functions with ASN1_GENERALIZEDTIME structures - * From openssl documentation: - * It is recommended that functions starting with ASN1_TIME be used instead - * of those starting with ASN1_UTCTIME or ASN1_GENERALIZEDTIME. The - * functions starting with ASN1_UTCTIME and ASN1_GENERALIZEDTIME act only on - * that specific time format. The functions starting with ASN1_TIME will - * operate on either format. - * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_generalizedtime.html - * - * ASN1_TIME_compare has a much nicer API, but is not available in Openssl - * 1.0.1, so we use ASN1_TIME_diff. - */ - int pday = 0; - int psec = 0; - RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, thisupd, current_sys_time), S2N_ERR_CERT_UNTRUSTED); - /* ensure that current_time is after or the same as "this update" */ - RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_INVALID); - - /* ensure that current_time is before or the same as "next update" */ - if (nextupd) { - RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, current_sys_time, nextupd), S2N_ERR_CERT_UNTRUSTED); - RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_EXPIRED); - } else { - /** - * if nextupd isn't present, assume that nextupd is - * DEFAULT_OCSP_NEXT_UPDATE_PERIOD after thisupd. This means that if the - * current time is more than DEFAULT_OCSP_NEXT_UPDATE_PERIOD - * seconds ahead of thisupd, we consider it invalid. We already compared - * current_sys_time to thisupd, so reuse those values - */ - uint64_t seconds_after_thisupd = pday * (3600 * 24) + psec; - RESULT_ENSURE(seconds_after_thisupd < DEFAULT_OCSP_NEXT_UPDATE_PERIOD, S2N_ERR_CERT_EXPIRED); - } - - switch (status) { - case V_OCSP_CERTSTATUS_GOOD: - validator->state = OCSP_VALIDATED; - return S2N_RESULT_OK; - case V_OCSP_CERTSTATUS_REVOKED: - RESULT_BAIL(S2N_ERR_CERT_REVOKED); - default: - RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); - } -#endif /* S2N_OCSP_STAPLING_SUPPORTED */ -} - -bool s2n_x509_validator_is_cert_chain_validated(const struct s2n_x509_validator *validator) -{ - return validator && (validator->state == VALIDATED || validator->state == OCSP_VALIDATED); -} - -int s2n_cert_validation_accept(struct s2n_cert_validation_info *info) -{ - POSIX_ENSURE_REF(info); - POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); - - info->finished = true; - info->accepted = true; - - return S2N_SUCCESS; -} - -int s2n_cert_validation_reject(struct s2n_cert_validation_info *info) -{ - POSIX_ENSURE_REF(info); - POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); - - info->finished = true; - info->accepted = false; - - return S2N_SUCCESS; -} +/* + * 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. + */ + +#if !defined(_MSC_VER) +#include +#else +#include +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_openssl_x509.h" +#include "crypto/s2n_pkey.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crl.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_result.h" +#include "utils/s2n_rfc5952.h" +#include "utils/s2n_safety.h" + +#if S2N_OCSP_STAPLING_SUPPORTED + #include +DEFINE_POINTER_CLEANUP_FUNC(OCSP_RESPONSE *, OCSP_RESPONSE_free); +DEFINE_POINTER_CLEANUP_FUNC(OCSP_BASICRESP *, OCSP_BASICRESP_free); + +#endif + +#ifndef X509_V_FLAG_PARTIAL_CHAIN + #define X509_V_FLAG_PARTIAL_CHAIN 0x80000 +#endif + +#define DEFAULT_MAX_CHAIN_DEPTH 7 +/* Time used by default for nextUpdate if none provided in OCSP: 1 hour since thisUpdate. */ +#define DEFAULT_OCSP_NEXT_UPDATE_PERIOD 3600 + +/* s2n's internal clock measures epoch-nanoseconds stored with a uint64_t. The + * maximum representable timestamp is Sunday, July 21, 2554. time_t measures + * epoch-seconds in a int64_t or int32_t (platform dependent). If time_t is an + * int32_t, the maximum representable timestamp is January 19, 2038. + * + * This means that converting from the internal clock to a time_t is not safe, + * because the internal clock might hold a value that is too large to represent + * in a time_t. This constant represents the largest internal clock value that + * can be safely represented as a time_t. + */ +#define MAX_32_TIMESTAMP_NANOS 2147483647 * ONE_SEC_IN_NANOS + +#define OSSL_VERIFY_CALLBACK_IGNORE_ERROR 1 + +DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(X509_CRL) *, sk_X509_CRL_free); +DEFINE_POINTER_CLEANUP_FUNC(STACK_OF(GENERAL_NAME) *, GENERAL_NAMES_free); + +uint8_t s2n_x509_ocsp_stapling_supported(void) +{ + return S2N_OCSP_STAPLING_SUPPORTED; +} + +void s2n_x509_trust_store_init_empty(struct s2n_x509_trust_store *store) +{ + store->trust_store = NULL; +} + +uint8_t s2n_x509_trust_store_has_certs(struct s2n_x509_trust_store *store) +{ + return store->trust_store ? (uint8_t) 1 : (uint8_t) 0; +} + +int s2n_x509_trust_store_add_pem(struct s2n_x509_trust_store *store, const char *pem) +{ + POSIX_ENSURE_REF(store); + POSIX_ENSURE_REF(pem); + + if (!store->trust_store) { + store->trust_store = X509_STORE_new(); + POSIX_ENSURE_REF(store->trust_store); + } + + DEFER_CLEANUP(struct s2n_stuffer pem_in_stuffer = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer der_out_stuffer = { 0 }, s2n_stuffer_free); + + POSIX_GUARD(s2n_stuffer_alloc_ro_from_string(&pem_in_stuffer, pem)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&der_out_stuffer, 2048)); + + do { + DEFER_CLEANUP(struct s2n_blob next_cert = { 0 }, s2n_free); + + POSIX_GUARD(s2n_stuffer_certificate_from_pem(&pem_in_stuffer, &der_out_stuffer)); + POSIX_GUARD(s2n_alloc(&next_cert, s2n_stuffer_data_available(&der_out_stuffer))); + POSIX_GUARD(s2n_stuffer_read(&der_out_stuffer, &next_cert)); + + const uint8_t *data = next_cert.data; + DEFER_CLEANUP(X509 *ca_cert = d2i_X509(NULL, &data, next_cert.size), X509_free_pointer); + S2N_ERROR_IF(ca_cert == NULL, S2N_ERR_DECODE_CERTIFICATE); + + if (!X509_STORE_add_cert(store->trust_store, ca_cert)) { + unsigned long error = ERR_get_error(); + POSIX_ENSURE(ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE, S2N_ERR_DECODE_CERTIFICATE); + } + } while (s2n_stuffer_data_available(&pem_in_stuffer)); + + return 0; +} + +int s2n_x509_trust_store_from_ca_file(struct s2n_x509_trust_store *store, const char *ca_pem_filename, const char *ca_dir) +{ + if (!store->trust_store) { + store->trust_store = X509_STORE_new(); + POSIX_ENSURE_REF(store->trust_store); + } + + int err_code = X509_STORE_load_locations(store->trust_store, ca_pem_filename, ca_dir); + if (!err_code) { + s2n_x509_trust_store_wipe(store); + POSIX_BAIL(S2N_ERR_X509_TRUST_STORE); + } + + return 0; +} + +void s2n_x509_trust_store_wipe(struct s2n_x509_trust_store *store) +{ + if (store->trust_store) { + X509_STORE_free(store->trust_store); + store->trust_store = NULL; + store->loaded_system_certs = false; + } +} + +int s2n_x509_validator_init_no_x509_validation(struct s2n_x509_validator *validator) +{ + POSIX_ENSURE_REF(validator); + validator->trust_store = NULL; + validator->store_ctx = NULL; + validator->skip_cert_validation = 1; + validator->check_stapled_ocsp = 0; + validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; + validator->state = INIT; + validator->cert_chain_from_wire = sk_X509_new_null(); + validator->crl_lookup_list = NULL; + validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; + validator->cert_validation_cb_invoked = false; + + return 0; +} + +int s2n_x509_validator_init(struct s2n_x509_validator *validator, struct s2n_x509_trust_store *trust_store, uint8_t check_ocsp) +{ + POSIX_ENSURE_REF(trust_store); + validator->trust_store = trust_store; + validator->skip_cert_validation = 0; + validator->check_stapled_ocsp = check_ocsp; + validator->max_chain_depth = DEFAULT_MAX_CHAIN_DEPTH; + validator->store_ctx = NULL; + if (validator->trust_store->trust_store) { + validator->store_ctx = X509_STORE_CTX_new(); + POSIX_ENSURE_REF(validator->store_ctx); + } + validator->cert_chain_from_wire = sk_X509_new_null(); + validator->state = INIT; + validator->crl_lookup_list = NULL; + validator->cert_validation_info = (struct s2n_cert_validation_info){ 0 }; + validator->cert_validation_cb_invoked = false; + + return 0; +} + +static inline void wipe_cert_chain(STACK_OF(X509) *cert_chain) +{ + if (cert_chain) { + sk_X509_pop_free(cert_chain, X509_free); + } +} + +int s2n_x509_validator_wipe(struct s2n_x509_validator *validator) +{ + if (validator->store_ctx) { + X509_STORE_CTX_free(validator->store_ctx); + validator->store_ctx = NULL; + } + wipe_cert_chain(validator->cert_chain_from_wire); + validator->cert_chain_from_wire = NULL; + validator->trust_store = NULL; + validator->skip_cert_validation = 0; + validator->state = UNINIT; + validator->max_chain_depth = 0; + if (validator->crl_lookup_list) { + POSIX_GUARD_RESULT(s2n_array_free(validator->crl_lookup_list)); + validator->crl_lookup_list = NULL; + } + + return S2N_SUCCESS; +} + +int s2n_x509_validator_set_max_chain_depth(struct s2n_x509_validator *validator, uint16_t max_depth) +{ + POSIX_ENSURE_REF(validator); + S2N_ERROR_IF(max_depth == 0, S2N_ERR_INVALID_ARGUMENT); + + validator->max_chain_depth = max_depth; + return 0; +} + +static S2N_RESULT s2n_verify_host_information_san_entry(struct s2n_connection *conn, GENERAL_NAME *current_name, bool *san_found) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(current_name); + RESULT_ENSURE_REF(san_found); + + if (current_name->type == GEN_DNS || current_name->type == GEN_URI) { + *san_found = true; + + const char *name = (const char *) ASN1_STRING_data(current_name->d.ia5); + RESULT_ENSURE_REF(name); + int name_len = ASN1_STRING_length(current_name->d.ia5); + RESULT_ENSURE_GT(name_len, 0); + + RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; + } + + if (current_name->type == GEN_IPADD) { + *san_found = true; + + /* try to validate an IP address if it's in the subject alt name. */ + const unsigned char *ip_addr = current_name->d.iPAddress->data; + RESULT_ENSURE_REF(ip_addr); + int ip_addr_len = current_name->d.iPAddress->length; + RESULT_ENSURE_GT(ip_addr_len, 0); + + RESULT_STACK_BLOB(address, INET6_ADDRSTRLEN + 1, INET6_ADDRSTRLEN + 1); + + if (ip_addr_len == 4) { + RESULT_GUARD(s2n_inet_ntop(AF_INET, ip_addr, &address)); + } else if (ip_addr_len == 16) { + RESULT_GUARD(s2n_inet_ntop(AF_INET6, ip_addr, &address)); + } else { + /* we aren't able to parse this value so skip it */ + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } + + /* strlen should be safe here since we made sure we were null terminated AND that inet_ntop succeeded */ + const char *name = (const char *) address.data; + size_t name_len = strlen(name); + + RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; + } + + /* we don't understand this entry type so skip it */ + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); +} + +static S2N_RESULT s2n_verify_host_information_san(struct s2n_connection *conn, X509 *public_cert, bool *san_found) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(public_cert); + RESULT_ENSURE_REF(san_found); + + *san_found = false; + + DEFER_CLEANUP(STACK_OF(GENERAL_NAME) *names_list = NULL, GENERAL_NAMES_free_pointer); + names_list = X509_get_ext_d2i(public_cert, NID_subject_alt_name, NULL, NULL); + RESULT_ENSURE(names_list, S2N_ERR_CERT_UNTRUSTED); + + int n = sk_GENERAL_NAME_num(names_list); + RESULT_ENSURE(n > 0, S2N_ERR_CERT_UNTRUSTED); + + s2n_result result = S2N_RESULT_OK; + for (int i = 0; i < n; i++) { + GENERAL_NAME *current_name = sk_GENERAL_NAME_value(names_list, i); + + /* return success on the first entry that passes verification */ + result = s2n_verify_host_information_san_entry(conn, current_name, san_found); + if (s2n_result_is_ok(result)) { + return S2N_RESULT_OK; + } + } + + /* if an error was set by one of the entries, then just propagate the error from the last SAN entry call */ + RESULT_GUARD(result); + + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); +} + +static S2N_RESULT s2n_verify_host_information_common_name(struct s2n_connection *conn, X509 *public_cert, bool *cn_found) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(public_cert); + RESULT_ENSURE_REF(cn_found); + + X509_NAME *subject_name = X509_get_subject_name(public_cert); + RESULT_ENSURE(subject_name, S2N_ERR_CERT_UNTRUSTED); + + int curr_idx = -1; + while (true) { + int next_idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, curr_idx); + if (next_idx >= 0) { + curr_idx = next_idx; + } else { + break; + } + } + + RESULT_ENSURE(curr_idx >= 0, S2N_ERR_CERT_UNTRUSTED); + + ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, curr_idx)); + RESULT_ENSURE(common_name, S2N_ERR_CERT_UNTRUSTED); + + /* X520CommonName allows the following ANSI string types per RFC 5280 Appendix A.1 */ + RESULT_ENSURE(ASN1_STRING_type(common_name) == V_ASN1_TELETEXSTRING + || ASN1_STRING_type(common_name) == V_ASN1_PRINTABLESTRING + || ASN1_STRING_type(common_name) == V_ASN1_UNIVERSALSTRING + || ASN1_STRING_type(common_name) == V_ASN1_UTF8STRING + || ASN1_STRING_type(common_name) == V_ASN1_BMPSTRING, + S2N_ERR_CERT_UNTRUSTED); + + /* at this point we have a valid CN value */ + *cn_found = true; + + char peer_cn[255] = { 0 }; + int cn_len = ASN1_STRING_length(common_name); + RESULT_ENSURE_GT(cn_len, 0); + uint32_t len = (uint32_t) cn_len; + RESULT_ENSURE_LTE(len, s2n_array_len(peer_cn) - 1); + RESULT_CHECKED_MEMCPY(peer_cn, ASN1_STRING_data(common_name), len); + + /* According to https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4, + * the CN fallback only applies to fully qualified DNS domain names. + * + * An IP address is not a fully qualified DNS domain name. Per RFC 6125 + * section 6.2.1, IP reference identities must only be matched against + * iPAddress SAN entries, never against CN values. Reject the CN if it + * parses as an IPv4 or IPv6 address. + */ + unsigned char ip_buf[sizeof(struct in6_addr)] = { 0 }; + if (inet_pton(AF_INET, peer_cn, ip_buf) == 1 || inet_pton(AF_INET6, peer_cn, ip_buf) == 1) { + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } + + RESULT_ENSURE(conn->verify_host_fn(peer_cn, len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; +} + +/* + * For each name in the cert. Iterate them. Call the callback. If one returns true, then consider it validated, + * if none of them return true, the cert is considered invalid. + */ +static S2N_RESULT s2n_verify_host_information(struct s2n_connection *conn, X509 *public_cert) +{ + bool entry_found = false; + + /* Check SubjectAltNames before CommonName as per RFC 6125 6.4.4 */ + s2n_result result = s2n_verify_host_information_san(conn, public_cert, &entry_found); + + /* + *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 + *# As noted, a client MUST NOT seek a match for a reference identifier + *# of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, + *# URI-ID, or any application-specific identifier types supported by the + *# client. + */ + if (entry_found) { + return result; + } + + /* + *= https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 + *# Therefore, if and only if the presented identifiers do not include a + *# DNS-ID, SRV-ID, URI-ID, or any application-specific identifier types + *# supported by the client, then the client MAY as a last resort check + *# for a string whose form matches that of a fully qualified DNS domain + *# name in a Common Name field of the subject field (i.e., a CN-ID). + */ + result = s2n_verify_host_information_common_name(conn, public_cert, &entry_found); + if (entry_found) { + return result; + } + + /* make a null-terminated string in case the callback tries to use strlen */ + const char *name = ""; + size_t name_len = 0; + + /* at this point, we don't have anything to identify the certificate with so pass an empty string to the callback */ + RESULT_ENSURE(conn->verify_host_fn(name, name_len, conn->data_for_verify_host), S2N_ERR_CERT_INVALID_HOSTNAME); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_read_asn1_cert(struct s2n_stuffer *cert_chain_in_stuffer, + struct s2n_blob *asn1_cert) +{ + uint32_t certificate_size = 0; + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint24(cert_chain_in_stuffer, &certificate_size)); + RESULT_ENSURE(certificate_size > 0, S2N_ERR_CERT_INVALID); + RESULT_ENSURE(certificate_size <= s2n_stuffer_data_available(cert_chain_in_stuffer), S2N_ERR_CERT_INVALID); + + asn1_cert->size = certificate_size; + asn1_cert->data = s2n_stuffer_raw_read(cert_chain_in_stuffer, certificate_size); + RESULT_ENSURE_REF(asn1_cert->data); + + return S2N_RESULT_OK; +} + +/** +* Validates that each certificate in a peer's cert chain contains only signature algorithms in a security policy's +* certificate_signatures_preference list. +*/ +S2N_RESULT s2n_x509_validator_check_cert_preferences(struct s2n_connection *conn, X509 *cert) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(cert); + + const struct s2n_security_policy *security_policy = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); + + /** + * We only restrict the signature algorithm on the certificates in the + * peer's certificate chain if the certificate_signature_preferences field + * is set in the security policy. This is contrary to the RFC, which + * specifies that the signatures in the "signature_algorithms" extension + * apply to signatures in the certificate chain in certain scenarios, so RFC + * compliance would imply validating that the certificate chain signature + * algorithm matches one of the algorithms specified in the + * "signature_algorithms" extension. + * + *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.2 + *= type=exception + *= reason=not implemented due to lack of utility + *# If the client provided a "signature_algorithms" extension, then all + *# certificates provided by the server MUST be signed by a + *# hash/signature algorithm pair that appears in that extension. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.3 + *= type=exception + *= reason=not implemented due to lack of utility + *# If no "signature_algorithms_cert" extension is present, then the + *# "signature_algorithms" extension also applies to signatures appearing in + *# certificates. + */ + struct s2n_cert_info info = { 0 }; + RESULT_GUARD(s2n_openssl_x509_get_cert_info(cert, &info)); + + bool certificate_preferences_defined = security_policy->certificate_signature_preferences != NULL + || security_policy->certificate_key_preferences != NULL; + if (certificate_preferences_defined && !info.self_signed && conn->actual_protocol_version == S2N_TLS13) { + /* Ensure that the certificate signature does not use SHA-1. While this check + * would ideally apply to all connections, we only enforce it when certificate + * preferences exist to stay backwards compatible. + */ + RESULT_ENSURE(info.signature_digest_nid != NID_sha1, S2N_ERR_CERT_UNTRUSTED); + } + + if (!info.self_signed) { + RESULT_GUARD(s2n_security_policy_validate_cert_signature(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); + } + RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_get_validated_cert_chain(const struct s2n_x509_validator *validator, + struct s2n_validated_cert_chain *validated_cert_chain) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validated_cert_chain); + + RESULT_ENSURE(s2n_x509_validator_is_cert_chain_validated(validator), S2N_ERR_INVALID_CERT_STATE); + RESULT_ENSURE_REF(validator->store_ctx); + +#if S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN + /* X509_STORE_CTX_get0_chain is used when available, since it returns a pointer to the + * validated cert chain in the X509_STORE_CTX, avoiding an allocation/copy. + */ + validated_cert_chain->stack = X509_STORE_CTX_get0_chain(validator->store_ctx); +#else + /* Otherwise, X509_STORE_CTX_get1_chain is used instead, which allocates a new cert chain. */ + validated_cert_chain->stack = X509_STORE_CTX_get1_chain(validator->store_ctx); +#endif + + RESULT_ENSURE_REF(validated_cert_chain->stack); + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_x509_validator_validated_cert_chain_free(struct s2n_validated_cert_chain *validated_cert_chain) +{ + RESULT_ENSURE_REF(validated_cert_chain); + +#if !S2N_LIBCRYPTO_SUPPORTS_GET0_CHAIN + /* When X509_STORE_CTX_get0_chain isn't supported, X509_STORE_CTX_get1_chain is used instead, + * which allocates a new cert chain that is owned by s2n-tls and MUST be freed. + * + * X509_STORE_CTX_get0_chain returns a pointer to the cert chain within the X509_STORE_CTX, + * which is NOT owned by s2n-tls and MUST NOT be manually freed. + */ + RESULT_GUARD(s2n_openssl_x509_stack_pop_free(&validated_cert_chain->stack)); +#endif + + /* Even though the cert chain reference is still valid in the case that get0_chain is used, set + * it to null for consistency with the get1_chain case. + */ + validated_cert_chain->stack = NULL; + + return S2N_RESULT_OK; +} + +/* Validates that the root certificate uses a key allowed by the security policy + * certificate preferences. + */ +static S2N_RESULT s2n_x509_validator_check_root_cert(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(conn); + + const struct s2n_security_policy *security_policy = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_security_policy(conn, &security_policy)); + RESULT_ENSURE_REF(security_policy); + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + STACK_OF(X509) *cert_chain = validated_cert_chain.stack; + RESULT_ENSURE_REF(cert_chain); + + const int certs_in_chain = sk_X509_num(cert_chain); + RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_CERT_UNTRUSTED); + X509 *root = sk_X509_value(cert_chain, certs_in_chain - 1); + RESULT_ENSURE_REF(root); + + struct s2n_cert_info info = { 0 }; + RESULT_GUARD(s2n_openssl_x509_get_cert_info(root, &info)); + + RESULT_GUARD(s2n_security_policy_validate_cert_key(security_policy, &info, + S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_read_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len) +{ + RESULT_ENSURE(validator->skip_cert_validation || s2n_x509_trust_store_has_certs(validator->trust_store), S2N_ERR_CERT_UNTRUSTED); + RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); + + struct s2n_blob cert_chain_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); + DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); + + RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); + + while (s2n_stuffer_data_available(&cert_chain_in_stuffer) + && sk_X509_num(validator->cert_chain_from_wire) < validator->max_chain_depth) { + struct s2n_blob asn1_cert = { 0 }; + RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); + + /* We only do the trailing byte validation when parsing the leaf cert to + * match historical s2n-tls behavior. + */ + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + if (sk_X509_num(validator->cert_chain_from_wire) == 0) { + RESULT_GUARD(s2n_openssl_x509_parse(&asn1_cert, &cert)); + } else { + RESULT_GUARD(s2n_openssl_x509_parse_without_length_validation(&asn1_cert, &cert)); + } + + if (!validator->skip_cert_validation) { + RESULT_GUARD(s2n_x509_validator_check_cert_preferences(conn, cert)); + } + + /* add the cert to the chain */ + RESULT_ENSURE(sk_X509_push(validator->cert_chain_from_wire, cert) > 0, + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + /* After the cert is added to cert_chain_from_wire, it will be freed + * with the call to s2n_x509_validator_wipe. We disable the cleanup + * function since cleanup is no longer "owned" by cert. + */ + ZERO_TO_DISABLE_DEFER_CLEANUP(cert); + + /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ + if (conn->actual_protocol_version >= S2N_TLS13) { + s2n_parsed_extensions_list parsed_extensions_list = { 0 }; + RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); + } + } + + /* if this occurred we exceeded validator->max_chain_depth */ + RESULT_ENSURE(validator->skip_cert_validation || s2n_stuffer_data_available(&cert_chain_in_stuffer) == 0, + S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); + RESULT_ENSURE(sk_X509_num(validator->cert_chain_from_wire) > 0, S2N_ERR_NO_CERT_FOUND); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_process_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len) +{ + RESULT_ENSURE(validator->state == INIT, S2N_ERR_INVALID_CERT_STATE); + + RESULT_GUARD(s2n_x509_validator_read_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); + + if (validator->skip_cert_validation) { + return S2N_RESULT_OK; + } + + X509 *leaf = sk_X509_value(validator->cert_chain_from_wire, 0); + RESULT_ENSURE_REF(leaf); + + if (conn->verify_host_fn) { + RESULT_GUARD(s2n_verify_host_information(conn, leaf)); + } + + RESULT_GUARD_OSSL(X509_STORE_CTX_init(validator->store_ctx, validator->trust_store->trust_store, leaf, + validator->cert_chain_from_wire), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + if (conn->config->crl_lookup_cb) { + RESULT_GUARD(s2n_crl_invoke_lookup_callbacks(conn, validator)); + RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); + } + + validator->state = READY_TO_VERIFY; + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_set_no_check_time_flag(struct s2n_x509_validator *validator) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validator->store_ctx); + + X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); + RESULT_ENSURE_REF(param); + +#ifdef S2N_LIBCRYPTO_SUPPORTS_FLAG_NO_CHECK_TIME + RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_NO_CHECK_TIME), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); +#else + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +#endif + + return S2N_RESULT_OK; +} + +int s2n_disable_time_validation_ossl_verify_callback(int default_ossl_ret, X509_STORE_CTX *ctx) +{ + int err = X509_STORE_CTX_get_error(ctx); + switch (err) { + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + return OSSL_VERIFY_CALLBACK_IGNORE_ERROR; + default: + break; + } + + /* If CRL validation is enabled, setting the time validation verify callback will override the + * CRL verify callback. The CRL verify callback is manually triggered to work around this + * issue. + * + * The CRL verify callback ignores validation errors exclusively for CRL timestamp fields. So, + * if CRL validation isn't enabled, the CRL verify callback is a no-op. + */ + return s2n_crl_ossl_verify_callback(default_ossl_ret, ctx); +} + +static S2N_RESULT s2n_x509_validator_disable_time_validation(struct s2n_connection *conn, + struct s2n_x509_validator *validator) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validator->store_ctx); + + /* Setting an X509_STORE verify callback is not recommended with AWS-LC: + * https://github.com/aws/aws-lc/blob/aa90e509f2e940916fbe9fdd469a4c90c51824f6/include/openssl/x509.h#L2980-L2990 + * + * If the libcrypto supports the ability to disable time validation with an X509_VERIFY_PARAM + * NO_CHECK_TIME flag, this method is preferred. + * + * However, older versions of AWS-LC and OpenSSL 1.0.2 do not support this flag. In this case, + * an X509_STORE verify callback is used. This is acceptable in older versions of AWS-LC + * because the versions are fixed, and updates to AWS-LC will not break the callback + * implementation. + */ + if (s2n_libcrypto_supports_flag_no_check_time()) { + RESULT_GUARD(s2n_x509_validator_set_no_check_time_flag(validator)); + } else { + X509_STORE_CTX_set_verify_cb(validator->store_ctx, + s2n_disable_time_validation_ossl_verify_callback); + } + + return S2N_RESULT_OK; +} + +int s2n_no_op_verify_custom_crit_oids_cb(X509_STORE_CTX *ctx, X509 *x509, STACK_OF(ASN1_OBJECT) *oids) +{ + return 1; +} + +static S2N_RESULT s2n_x509_validator_add_custom_extensions(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(validator); + RESULT_ENSURE_REF(validator->store_ctx); + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + if (conn->config->custom_x509_extension_oids) { +#if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_OID + size_t custom_oid_count = sk_ASN1_OBJECT_num(conn->config->custom_x509_extension_oids); + for (size_t i = 0; i < custom_oid_count; i++) { + ASN1_OBJECT *critical_oid = sk_ASN1_OBJECT_value(conn->config->custom_x509_extension_oids, i); + RESULT_ENSURE_REF(critical_oid); + RESULT_GUARD_OSSL(X509_STORE_CTX_add_custom_crit_oid(validator->store_ctx, critical_oid), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + } + /* To enable AWS-LC accepting custom extensions, an X509_STORE_CTX_verify_crit_oids_cb must be set. + * See https://github.com/aws/aws-lc/blob/f0b4afedd7d45fc2517643d890b654856c57f994/include/openssl/x509.h#L2913-L2918. + * + * The `X509_STORE_CTX_verify_crit_oids_cb` callback can be used to implement the validation for the + * custom certificate extensions. However, s2n-tls consumers are expected to implement this validation + * in the `s2n_cert_validation_callback` instead. So, a no-op callback is provided to AWS-LC. + */ + X509_STORE_CTX_set_verify_crit_oids(validator->store_ctx, s2n_no_op_verify_custom_crit_oids_cb); +#else + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); +#endif + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_verify_intent_for_cert(struct s2n_connection *conn, X509 *cert, bool is_leaf) +{ + RESULT_ENSURE_REF(cert); + + /* The X509_PURPOSE values indicate the purpose that certificates must specify. For servers, + * received client certificates MUST have a TLS client purpose. For clients, received server + * certificates MUST have a TLS server purpose. + */ + int purpose = X509_PURPOSE_SSL_CLIENT; + if (conn->mode == S2N_CLIENT) { + purpose = X509_PURPOSE_SSL_SERVER; + } + + RESULT_GUARD_OSSL(X509_check_purpose(cert, purpose, !is_leaf), S2N_ERR_CERT_INTENT_INVALID); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_verify_intent(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + if (conn->config->disable_x509_intent_verification) { + return S2N_RESULT_OK; + } + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + + int cert_count = sk_X509_num(validated_cert_chain.stack); + RESULT_ENSURE_GT(cert_count, 0); + + /* The validated cert chain returned from the libcrypto includes the trust anchor. The trust + * anchor is omitted from intent verification since its TLS intent is implicitly indicated by + * its presence in the s2n-tls trust store. + */ + cert_count -= 1; + + for (int i = 0; i < cert_count; i++) { + X509 *cert = sk_X509_value(validated_cert_chain.stack, i); + RESULT_ENSURE_REF(cert); + + bool is_leaf = (i == 0); + RESULT_GUARD(s2n_x509_validator_verify_intent_for_cert(conn, cert, is_leaf)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_verify_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn) +{ + RESULT_ENSURE(validator->state == READY_TO_VERIFY, S2N_ERR_INVALID_CERT_STATE); + + X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(validator->store_ctx); + X509_VERIFY_PARAM_set_depth(param, validator->max_chain_depth); + + DEFER_CLEANUP(STACK_OF(X509_CRL) *crl_stack = NULL, sk_X509_CRL_free_pointer); + + if (conn->config->crl_lookup_cb) { + X509_STORE_CTX_set_verify_cb(validator->store_ctx, s2n_crl_ossl_verify_callback); + + crl_stack = sk_X509_CRL_new_null(); + RESULT_GUARD(s2n_crl_get_crls_from_lookup_list(validator, crl_stack)); + + /* Set the CRL list that the libcrypto will use to validate certificates with */ + X509_STORE_CTX_set0_crls(validator->store_ctx, crl_stack); + + /* Enable CRL validation for certificates in X509_verify_cert */ + RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + + /* Enable CRL validation for all certificates, not just the leaf */ + RESULT_GUARD_OSSL(X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK_ALL), + S2N_ERR_INTERNAL_LIBCRYPTO_ERROR); + } + + /* Disabling time validation may set a NO_CHECK_TIME flag on the X509_STORE_CTX. Calling + * X509_STORE_CTX_set_time will override this flag. To prevent this, X509_STORE_CTX_set_time is + * only called if time validation is enabled. + */ + if (conn->config->disable_x509_time_validation) { + RESULT_GUARD(s2n_x509_validator_disable_time_validation(conn, validator)); + } else { + uint64_t current_sys_time = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time)); + if (sizeof(time_t) == 4) { + /* cast value to uint64_t to prevent overflow errors */ + RESULT_ENSURE_LTE(current_sys_time, (uint64_t) MAX_32_TIMESTAMP_NANOS); + } + + /* this wants seconds not nanoseconds */ + time_t current_time = (time_t) (current_sys_time / ONE_SEC_IN_NANOS); + X509_STORE_CTX_set_time(validator->store_ctx, 0, current_time); + } + + /* It's assumed that if a valid certificate chain is received with an issuer that's present in + * the trust store, the certificate chain should be trusted. This should be the case even if + * the issuer in the trust store isn't a root certificate. Setting the PARTIAL_CHAIN flag + * allows the libcrypto to trust certificates in the trust store that aren't root certificates. + */ + X509_STORE_CTX_set_flags(validator->store_ctx, X509_V_FLAG_PARTIAL_CHAIN); + + RESULT_GUARD(s2n_x509_validator_add_custom_extensions(validator, conn)); + + int verify_ret = X509_verify_cert(validator->store_ctx); + if (verify_ret <= 0) { + int ossl_error = X509_STORE_CTX_get_error(validator->store_ctx); + switch (ossl_error) { + case X509_V_ERR_CERT_NOT_YET_VALID: + RESULT_BAIL(S2N_ERR_CERT_NOT_YET_VALID); + case X509_V_ERR_CERT_HAS_EXPIRED: + RESULT_BAIL(S2N_ERR_CERT_EXPIRED); + case X509_V_ERR_CERT_REVOKED: + RESULT_BAIL(S2N_ERR_CERT_REVOKED); + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_DIFFERENT_CRL_SCOPE: + RESULT_BAIL(S2N_ERR_CRL_LOOKUP_FAILED); + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + RESULT_BAIL(S2N_ERR_CRL_SIGNATURE); + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + RESULT_BAIL(S2N_ERR_CRL_ISSUER); + case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: + RESULT_BAIL(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION); + case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: + RESULT_BAIL(S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION); + default: + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } + } + + validator->state = VALIDATED; + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_parse_leaf_certificate_extensions(struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len, + s2n_parsed_extensions_list *first_certificate_extensions) +{ + /* certificate extensions is a field in TLS 1.3 - https://tools.ietf.org/html/rfc8446#section-4.4.2 */ + RESULT_ENSURE_GTE(conn->actual_protocol_version, S2N_TLS13); + + struct s2n_blob cert_chain_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&cert_chain_blob, cert_chain_in, cert_chain_len)); + DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = { 0 }, s2n_stuffer_free); + + RESULT_GUARD_POSIX(s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob)); + + struct s2n_blob asn1_cert = { 0 }; + RESULT_GUARD(s2n_x509_validator_read_asn1_cert(&cert_chain_in_stuffer, &asn1_cert)); + + s2n_parsed_extensions_list parsed_extensions_list = { 0 }; + RESULT_GUARD_POSIX(s2n_extension_list_parse(&cert_chain_in_stuffer, &parsed_extensions_list)); + *first_certificate_extensions = parsed_extensions_list; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_validate_cert_chain_pre_cb(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->config); + + switch (validator->state) { + case INIT: + break; + case AWAITING_CRL_CALLBACK: + RESULT_GUARD(s2n_crl_handle_lookup_callback_result(validator)); + break; + default: + RESULT_BAIL(S2N_ERR_INVALID_CERT_STATE); + } + + if (validator->state == INIT) { + RESULT_GUARD(s2n_x509_validator_process_cert_chain(validator, conn, cert_chain_in, cert_chain_len)); + } + + if (validator->state == READY_TO_VERIFY) { + RESULT_GUARD(s2n_x509_validator_verify_cert_chain(validator, conn)); + RESULT_GUARD(s2n_x509_validator_verify_intent(validator, conn)); + RESULT_GUARD(s2n_x509_validator_check_root_cert(validator, conn)); + } + + if (conn->actual_protocol_version >= S2N_TLS13) { + /* Only process certificate extensions received in the first certificate. Extensions received in all other + * certificates are ignored. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.2 + *# If an extension applies to the entire chain, it SHOULD be included in + *# the first CertificateEntry. + */ + s2n_parsed_extensions_list first_certificate_extensions = { 0 }; + RESULT_GUARD(s2n_x509_validator_parse_leaf_certificate_extensions(conn, cert_chain_in, cert_chain_len, &first_certificate_extensions)); + RESULT_GUARD_POSIX(s2n_extension_list_process(S2N_EXTENSION_LIST_CERTIFICATE, conn, &first_certificate_extensions)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_x509_validator_handle_cert_validation_callback_result(struct s2n_x509_validator *validator) +{ + RESULT_ENSURE_REF(validator); + + if (!validator->cert_validation_info.finished) { + RESULT_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + + RESULT_ENSURE(validator->cert_validation_info.accepted, S2N_ERR_CERT_REJECTED); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, + uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_pkey_type *pkey_type, struct s2n_pkey *public_key_out) +{ + RESULT_ENSURE_REF(validator); + + if (validator->cert_validation_cb_invoked) { + RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); + } else { + RESULT_GUARD(s2n_x509_validator_validate_cert_chain_pre_cb(validator, conn, cert_chain_in, cert_chain_len)); + + if (conn->config->cert_validation_cb) { + RESULT_ENSURE(conn->config->cert_validation_cb(conn, &(validator->cert_validation_info), conn->config->cert_validation_ctx) == S2N_SUCCESS, + S2N_ERR_CANCELLED); + validator->cert_validation_cb_invoked = true; + RESULT_GUARD(s2n_x509_validator_handle_cert_validation_callback_result(validator)); + } + } + + /* retrieve information from leaf cert */ + RESULT_ENSURE_GT(sk_X509_num(validator->cert_chain_from_wire), 0); + X509 *leaf_cert = sk_X509_value(validator->cert_chain_from_wire, 0); + RESULT_ENSURE_REF(leaf_cert); + DEFER_CLEANUP(struct s2n_pkey public_key = { 0 }, s2n_pkey_free); + RESULT_GUARD(s2n_pkey_from_x509(leaf_cert, &public_key, pkey_type)); + + *public_key_out = public_key; + + /* Reset the old struct, so we don't clean up public_key_out */ + ZERO_TO_DISABLE_DEFER_CLEANUP(public_key); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_x509_validator_validate_cert_stapled_ocsp_response(struct s2n_x509_validator *validator, + struct s2n_connection *conn, const uint8_t *ocsp_response_raw, uint32_t ocsp_response_length) +{ + if (validator->skip_cert_validation || !validator->check_stapled_ocsp) { + validator->state = OCSP_VALIDATED; + return S2N_RESULT_OK; + } + + RESULT_ENSURE(validator->state == VALIDATED, S2N_ERR_INVALID_CERT_STATE); + +#if !S2N_OCSP_STAPLING_SUPPORTED + /* Default to safety */ + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); +#else + + RESULT_ENSURE_REF(ocsp_response_raw); + + DEFER_CLEANUP(OCSP_RESPONSE *ocsp_response = d2i_OCSP_RESPONSE(NULL, &ocsp_response_raw, ocsp_response_length), + OCSP_RESPONSE_free_pointer); + RESULT_ENSURE(ocsp_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); + + int ocsp_status = OCSP_response_status(ocsp_response); + RESULT_ENSURE(ocsp_status == OCSP_RESPONSE_STATUS_SUCCESSFUL, S2N_ERR_CERT_UNTRUSTED); + + DEFER_CLEANUP(OCSP_BASICRESP *basic_response = OCSP_response_get1_basic(ocsp_response), OCSP_BASICRESP_free_pointer); + RESULT_ENSURE(basic_response != NULL, S2N_ERR_INVALID_OCSP_RESPONSE); + + DEFER_CLEANUP(struct s2n_validated_cert_chain validated_cert_chain = { 0 }, s2n_x509_validator_validated_cert_chain_free); + RESULT_GUARD(s2n_x509_validator_get_validated_cert_chain(validator, &validated_cert_chain)); + STACK_OF(X509) *cert_chain = validated_cert_chain.stack; + RESULT_ENSURE_REF(cert_chain); + + const int certs_in_chain = sk_X509_num(cert_chain); + RESULT_ENSURE(certs_in_chain > 0, S2N_ERR_NO_CERT_FOUND); + + /* leaf is the top: not the bottom. */ + X509 *subject = sk_X509_value(cert_chain, 0); + X509 *issuer = NULL; + /* find the issuer in the chain. If it's not there. Fail everything. */ + for (int i = 0; i < certs_in_chain; ++i) { + X509 *issuer_candidate = sk_X509_value(cert_chain, i); + const int issuer_value = X509_check_issued(issuer_candidate, subject); + + if (issuer_value == X509_V_OK) { + issuer = issuer_candidate; + break; + } + } + RESULT_ENSURE(issuer != NULL, S2N_ERR_CERT_UNTRUSTED); + + /* Important: this checks that the stapled ocsp response CAN be verified, not that it has been verified. */ + const int ocsp_verify_res = OCSP_basic_verify(basic_response, cert_chain, validator->trust_store->trust_store, 0); + RESULT_GUARD_OSSL(ocsp_verify_res, S2N_ERR_CERT_UNTRUSTED); + + /* do the crypto checks on the response.*/ + int status = 0; + int reason = 0; + + /* SHA-1 is the only supported hash algorithm for the CertID due to its established use in + * OCSP responders. + */ + OCSP_CERTID *cert_id = OCSP_cert_to_id(EVP_sha1(), subject, issuer); + RESULT_ENSURE_REF(cert_id); + + /** + *= https://www.rfc-editor.org/rfc/rfc6960#section-2.4 + *# + *# thisUpdate The most recent time at which the status being + *# indicated is known by the responder to have been + *# correct. + *# + *# nextUpdate The time at or before which newer information will be + *# available about the status of the certificate. + **/ + ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; + /* Actual verification of the response */ + const int ocsp_resp_find_status_res = OCSP_resp_find_status(basic_response, cert_id, &status, &reason, &revtime, &thisupd, &nextupd); + OCSP_CERTID_free(cert_id); + RESULT_GUARD_OSSL(ocsp_resp_find_status_res, S2N_ERR_CERT_UNTRUSTED); + + uint64_t current_sys_time_nanoseconds = 0; + RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_sys_time_nanoseconds)); + if (sizeof(time_t) == 4) { + /* cast value to uint64_t to prevent overflow errors */ + RESULT_ENSURE_LTE(current_sys_time_nanoseconds, (uint64_t) MAX_32_TIMESTAMP_NANOS); + } + /* convert the current_sys_time (which is in nanoseconds) to seconds */ + time_t current_sys_time_seconds = (time_t) (current_sys_time_nanoseconds / ONE_SEC_IN_NANOS); + + DEFER_CLEANUP(ASN1_GENERALIZEDTIME *current_sys_time = ASN1_GENERALIZEDTIME_set(NULL, current_sys_time_seconds), s2n_openssl_asn1_time_free_pointer); + RESULT_ENSURE_REF(current_sys_time); + + /** + * It is fine to use ASN1_TIME functions with ASN1_GENERALIZEDTIME structures + * From openssl documentation: + * It is recommended that functions starting with ASN1_TIME be used instead + * of those starting with ASN1_UTCTIME or ASN1_GENERALIZEDTIME. The + * functions starting with ASN1_UTCTIME and ASN1_GENERALIZEDTIME act only on + * that specific time format. The functions starting with ASN1_TIME will + * operate on either format. + * https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_generalizedtime.html + * + * ASN1_TIME_compare has a much nicer API, but is not available in Openssl + * 1.0.1, so we use ASN1_TIME_diff. + */ + int pday = 0; + int psec = 0; + RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, thisupd, current_sys_time), S2N_ERR_CERT_UNTRUSTED); + /* ensure that current_time is after or the same as "this update" */ + RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_INVALID); + + /* ensure that current_time is before or the same as "next update" */ + if (nextupd) { + RESULT_GUARD_OSSL(ASN1_TIME_diff(&pday, &psec, current_sys_time, nextupd), S2N_ERR_CERT_UNTRUSTED); + RESULT_ENSURE(pday >= 0 && psec >= 0, S2N_ERR_CERT_EXPIRED); + } else { + /** + * if nextupd isn't present, assume that nextupd is + * DEFAULT_OCSP_NEXT_UPDATE_PERIOD after thisupd. This means that if the + * current time is more than DEFAULT_OCSP_NEXT_UPDATE_PERIOD + * seconds ahead of thisupd, we consider it invalid. We already compared + * current_sys_time to thisupd, so reuse those values + */ + uint64_t seconds_after_thisupd = pday * (3600 * 24) + psec; + RESULT_ENSURE(seconds_after_thisupd < DEFAULT_OCSP_NEXT_UPDATE_PERIOD, S2N_ERR_CERT_EXPIRED); + } + + switch (status) { + case V_OCSP_CERTSTATUS_GOOD: + validator->state = OCSP_VALIDATED; + return S2N_RESULT_OK; + case V_OCSP_CERTSTATUS_REVOKED: + RESULT_BAIL(S2N_ERR_CERT_REVOKED); + default: + RESULT_BAIL(S2N_ERR_CERT_UNTRUSTED); + } +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ +} + +bool s2n_x509_validator_is_cert_chain_validated(const struct s2n_x509_validator *validator) +{ + return validator && (validator->state == VALIDATED || validator->state == OCSP_VALIDATED); +} + +int s2n_cert_validation_accept(struct s2n_cert_validation_info *info) +{ + POSIX_ENSURE_REF(info); + POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); + + info->finished = true; + info->accepted = true; + + return S2N_SUCCESS; +} + +int s2n_cert_validation_reject(struct s2n_cert_validation_info *info) +{ + POSIX_ENSURE_REF(info); + POSIX_ENSURE(!info->finished, S2N_ERR_INVALID_STATE); + + info->finished = true; + info->accepted = false; + + return S2N_SUCCESS; +} diff --git a/utils/s2n_array.c b/utils/s2n_array.c index f43d60fb652..57dc38e2637 100644 --- a/utils/s2n_array.c +++ b/utils/s2n_array.c @@ -1,222 +1,222 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "utils/s2n_array.h" - -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_array_validate(const struct s2n_array *array) -{ - uint32_t mem_size = 0; - RESULT_ENSURE_REF(array); - RESULT_GUARD(s2n_blob_validate(&array->mem)); - RESULT_ENSURE_NE(array->element_size, 0); - RESULT_GUARD_POSIX(s2n_mul_overflow(array->len, array->element_size, &mem_size)); - RESULT_ENSURE_GTE(array->mem.size, mem_size); - RESULT_ENSURE(S2N_IMPLIES(array->mem.size, array->mem.growable), S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_array_enlarge(struct s2n_array *array, uint32_t capacity) -{ - RESULT_ENSURE_REF(array); - - /* Acquire the memory */ - uint32_t mem_needed = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, capacity, &mem_needed)); - RESULT_GUARD_POSIX(s2n_realloc(&array->mem, mem_needed)); - - /* Zero the extened part */ - uint32_t array_elements_size = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, array->len, &array_elements_size)); - RESULT_CHECKED_MEMSET(array->mem.data + array_elements_size, 0, array->mem.size - array_elements_size); - RESULT_POSTCONDITION(s2n_array_validate(array)); - return S2N_RESULT_OK; -} - -struct s2n_array *s2n_array_new(uint32_t element_size) -{ - struct s2n_array *array = s2n_array_new_with_capacity(element_size, S2N_INITIAL_ARRAY_SIZE); - PTR_ENSURE_REF(array); - - return array; -} - -struct s2n_array *s2n_array_new_with_capacity(uint32_t element_size, uint32_t capacity) -{ - DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); - PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_array))); - - DEFER_CLEANUP(struct s2n_array *array = (void *) mem.data, s2n_array_free_p); - ZERO_TO_DISABLE_DEFER_CLEANUP(mem); - - PTR_GUARD_RESULT(s2n_array_init_with_capacity(array, element_size, capacity)); - - struct s2n_array *array_ret = array; - ZERO_TO_DISABLE_DEFER_CLEANUP(array); - - return array_ret; -} - -S2N_RESULT s2n_array_init(struct s2n_array *array, uint32_t element_size) -{ - RESULT_ENSURE_REF(array); - - RESULT_GUARD(s2n_array_init_with_capacity(array, element_size, 0)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_init_with_capacity(struct s2n_array *array, uint32_t element_size, uint32_t capacity) -{ - RESULT_ENSURE_REF(array); - - *array = (struct s2n_array){ .element_size = element_size }; - - RESULT_GUARD(s2n_array_enlarge(array, capacity)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_pushback(struct s2n_array *array, void **element) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_REF(element); - return s2n_array_insert(array, array->len, element); -} - -S2N_RESULT s2n_array_get(struct s2n_array *array, uint32_t idx, void **element) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_REF(element); - RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); - *element = array->mem.data + (array->element_size * idx); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_insert_and_copy(struct s2n_array *array, uint32_t idx, void *element) -{ - void *insert_location = NULL; - RESULT_GUARD(s2n_array_insert(array, idx, &insert_location)); - RESULT_CHECKED_MEMCPY(insert_location, element, array->element_size); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_insert(struct s2n_array *array, uint32_t idx, void **element) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_REF(element); - /* index == len is ok since we're about to add one element */ - RESULT_ENSURE(idx <= array->len, S2N_ERR_ARRAY_INDEX_OOB); - - /* We are about to add one more element to the array. Add capacity if necessary */ - uint32_t current_capacity = 0; - RESULT_GUARD(s2n_array_capacity(array, ¤t_capacity)); - - if (array->len >= current_capacity) { - /* Enlarge the array */ - uint32_t new_capacity = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(current_capacity, 2, &new_capacity)); - new_capacity = S2N_MAX(new_capacity, S2N_INITIAL_ARRAY_SIZE); - RESULT_GUARD(s2n_array_enlarge(array, new_capacity)); - } - - /* If we are adding at an existing index, slide everything down. */ - if (idx < array->len) { - uint32_t size = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx, array->element_size, &size)); - memmove(array->mem.data + array->element_size * (idx + 1), - array->mem.data + array->element_size * idx, - size); - } - - *element = array->mem.data + array->element_size * idx; - array->len++; - - RESULT_POSTCONDITION(s2n_array_validate(array)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_remove(struct s2n_array *array, uint32_t idx) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); - - /* If the removed element is the last one, no need to move anything. - * Otherwise, shift everything down */ - if (idx != array->len - 1) { - uint32_t size = 0; - RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx - 1, array->element_size, &size)); - memmove(array->mem.data + array->element_size * idx, - array->mem.data + array->element_size * (idx + 1), - size); - } - array->len--; - - /* After shifting, zero the last element */ - RESULT_CHECKED_MEMSET(array->mem.data + array->element_size * array->len, - 0, - array->element_size); - - RESULT_POSTCONDITION(s2n_array_validate(array)); - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_num_elements(struct s2n_array *array, uint32_t *len) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_MUT(len); - - *len = array->len; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_capacity(struct s2n_array *array, uint32_t *capacity) -{ - RESULT_PRECONDITION(s2n_array_validate(array)); - RESULT_ENSURE_MUT(capacity); - - *capacity = array->mem.size / array->element_size; - - return S2N_RESULT_OK; -} - -S2N_CLEANUP_RESULT s2n_array_free_p(struct s2n_array **parray) -{ - RESULT_ENSURE_REF(parray); - - struct s2n_array *array = *parray; - if (array == NULL) { - return S2N_RESULT_OK; - } - - /* Free the elements */ - RESULT_GUARD_POSIX(s2n_free(&array->mem)); - - /* And finally the array */ - RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) parray, sizeof(struct s2n_array))); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_array_free(struct s2n_array *array) -{ - RESULT_ENSURE_REF(array); - return s2n_array_free_p(&array); -} +#include "utils/s2n_prelude.h" +/* + * 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 "utils/s2n_array.h" + +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_array_validate(const struct s2n_array *array) +{ + uint32_t mem_size = 0; + RESULT_ENSURE_REF(array); + RESULT_GUARD(s2n_blob_validate(&array->mem)); + RESULT_ENSURE_NE(array->element_size, 0); + RESULT_GUARD_POSIX(s2n_mul_overflow(array->len, array->element_size, &mem_size)); + RESULT_ENSURE_GTE(array->mem.size, mem_size); + RESULT_ENSURE(S2N_IMPLIES(array->mem.size, array->mem.growable), S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_array_enlarge(struct s2n_array *array, uint32_t capacity) +{ + RESULT_ENSURE_REF(array); + + /* Acquire the memory */ + uint32_t mem_needed = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, capacity, &mem_needed)); + RESULT_GUARD_POSIX(s2n_realloc(&array->mem, mem_needed)); + + /* Zero the extened part */ + uint32_t array_elements_size = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->element_size, array->len, &array_elements_size)); + RESULT_CHECKED_MEMSET(array->mem.data + array_elements_size, 0, array->mem.size - array_elements_size); + RESULT_POSTCONDITION(s2n_array_validate(array)); + return S2N_RESULT_OK; +} + +struct s2n_array *s2n_array_new(uint32_t element_size) +{ + struct s2n_array *array = s2n_array_new_with_capacity(element_size, S2N_INITIAL_ARRAY_SIZE); + PTR_ENSURE_REF(array); + + return array; +} + +struct s2n_array *s2n_array_new_with_capacity(uint32_t element_size, uint32_t capacity) +{ + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + PTR_GUARD_POSIX(s2n_alloc(&mem, sizeof(struct s2n_array))); + + DEFER_CLEANUP(struct s2n_array *array = (void *) mem.data, s2n_array_free_p); + ZERO_TO_DISABLE_DEFER_CLEANUP(mem); + + PTR_GUARD_RESULT(s2n_array_init_with_capacity(array, element_size, capacity)); + + struct s2n_array *array_ret = array; + ZERO_TO_DISABLE_DEFER_CLEANUP(array); + + return array_ret; +} + +S2N_RESULT s2n_array_init(struct s2n_array *array, uint32_t element_size) +{ + RESULT_ENSURE_REF(array); + + RESULT_GUARD(s2n_array_init_with_capacity(array, element_size, 0)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_init_with_capacity(struct s2n_array *array, uint32_t element_size, uint32_t capacity) +{ + RESULT_ENSURE_REF(array); + + *array = (struct s2n_array){ .element_size = element_size }; + + RESULT_GUARD(s2n_array_enlarge(array, capacity)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_pushback(struct s2n_array *array, void **element) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_REF(element); + return s2n_array_insert(array, array->len, element); +} + +S2N_RESULT s2n_array_get(struct s2n_array *array, uint32_t idx, void **element) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_REF(element); + RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); + *element = array->mem.data + (array->element_size * idx); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_insert_and_copy(struct s2n_array *array, uint32_t idx, void *element) +{ + void *insert_location = NULL; + RESULT_GUARD(s2n_array_insert(array, idx, &insert_location)); + RESULT_CHECKED_MEMCPY(insert_location, element, array->element_size); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_insert(struct s2n_array *array, uint32_t idx, void **element) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_REF(element); + /* index == len is ok since we're about to add one element */ + RESULT_ENSURE(idx <= array->len, S2N_ERR_ARRAY_INDEX_OOB); + + /* We are about to add one more element to the array. Add capacity if necessary */ + uint32_t current_capacity = 0; + RESULT_GUARD(s2n_array_capacity(array, ¤t_capacity)); + + if (array->len >= current_capacity) { + /* Enlarge the array */ + uint32_t new_capacity = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(current_capacity, 2, &new_capacity)); + new_capacity = S2N_MAX(new_capacity, S2N_INITIAL_ARRAY_SIZE); + RESULT_GUARD(s2n_array_enlarge(array, new_capacity)); + } + + /* If we are adding at an existing index, slide everything down. */ + if (idx < array->len) { + uint32_t size = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx, array->element_size, &size)); + memmove(array->mem.data + array->element_size * (idx + 1), + array->mem.data + array->element_size * idx, + size); + } + + *element = array->mem.data + array->element_size * idx; + array->len++; + + RESULT_POSTCONDITION(s2n_array_validate(array)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_remove(struct s2n_array *array, uint32_t idx) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE(idx < array->len, S2N_ERR_ARRAY_INDEX_OOB); + + /* If the removed element is the last one, no need to move anything. + * Otherwise, shift everything down */ + if (idx != array->len - 1) { + uint32_t size = 0; + RESULT_GUARD_POSIX(s2n_mul_overflow(array->len - idx - 1, array->element_size, &size)); + memmove(array->mem.data + array->element_size * idx, + array->mem.data + array->element_size * (idx + 1), + size); + } + array->len--; + + /* After shifting, zero the last element */ + RESULT_CHECKED_MEMSET(array->mem.data + array->element_size * array->len, + 0, + array->element_size); + + RESULT_POSTCONDITION(s2n_array_validate(array)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_num_elements(struct s2n_array *array, uint32_t *len) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_MUT(len); + + *len = array->len; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_capacity(struct s2n_array *array, uint32_t *capacity) +{ + RESULT_PRECONDITION(s2n_array_validate(array)); + RESULT_ENSURE_MUT(capacity); + + *capacity = array->mem.size / array->element_size; + + return S2N_RESULT_OK; +} + +S2N_CLEANUP_RESULT s2n_array_free_p(struct s2n_array **parray) +{ + RESULT_ENSURE_REF(parray); + + struct s2n_array *array = *parray; + if (array == NULL) { + return S2N_RESULT_OK; + } + + /* Free the elements */ + RESULT_GUARD_POSIX(s2n_free(&array->mem)); + + /* And finally the array */ + RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) parray, sizeof(struct s2n_array))); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_array_free(struct s2n_array *array) +{ + RESULT_ENSURE_REF(array); + return s2n_array_free_p(&array); +} diff --git a/utils/s2n_blob.c b/utils/s2n_blob.c index 648bf8085ba..6b5e9b8e433 100644 --- a/utils/s2n_blob.c +++ b/utils/s2n_blob.c @@ -1,93 +1,93 @@ -#include "utils/s2n_prelude.h" -/* - * 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 "utils/s2n_blob.h" - -#include -#include - -#include "api/s2n.h" -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" - -S2N_RESULT s2n_blob_validate(const struct s2n_blob *b) -{ - RESULT_ENSURE_REF(b); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->size == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->allocated == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 0, b->allocated == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 1, b->allocated > 0 || b->size == 0), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable != 0, b->size <= b->allocated), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->allocated), S2N_ERR_SAFETY); - RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->size), S2N_ERR_SAFETY); - return S2N_RESULT_OK; -} - -/** - * Initialize a blob to reference some data. - * - * `b` will not free `data`. The caller is responsible for making sure that - * `data` outlives `b`. - */ -int s2n_blob_init(struct s2n_blob *b, uint8_t *data, uint32_t size) -{ - POSIX_ENSURE_REF(b); - POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); - *b = (struct s2n_blob){ .data = data, .size = size, .allocated = 0, .growable = 0 }; - POSIX_POSTCONDITION(s2n_blob_validate(b)); - return S2N_SUCCESS; -} - -int s2n_blob_zero(struct s2n_blob *b) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - POSIX_CHECKED_MEMSET(b->data, 0, S2N_MAX(b->allocated, b->size)); - POSIX_POSTCONDITION(s2n_blob_validate(b)); - return S2N_SUCCESS; -} - -/** - * Set `slice` to reference some portion of `b`. - * - * The caller is responsible for ensuring that the data pointed to by `b` outlives - * `slice`. - */ -int s2n_blob_slice(const struct s2n_blob *b, struct s2n_blob *slice, uint32_t offset, uint32_t size) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - POSIX_PRECONDITION(s2n_blob_validate(slice)); - - uint32_t slice_size = 0; - POSIX_GUARD(s2n_add_overflow(offset, size, &slice_size)); - POSIX_ENSURE(b->size >= slice_size, S2N_ERR_SIZE_MISMATCH); - slice->data = (b->data) ? (b->data + offset) : NULL; - slice->size = size; - slice->growable = 0; - slice->allocated = 0; - - POSIX_POSTCONDITION(s2n_blob_validate(slice)); - return S2N_SUCCESS; -} - -int s2n_blob_char_to_lower(struct s2n_blob *b) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - for (size_t i = 0; i < b->size; i++) { - b->data[i] = tolower(b->data[i]); - } - POSIX_POSTCONDITION(s2n_blob_validate(b)); - return S2N_SUCCESS; -} +#include "utils/s2n_prelude.h" +/* + * 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 "utils/s2n_blob.h" + +#include +#include + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_blob_validate(const struct s2n_blob *b) +{ + RESULT_ENSURE_REF(b); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->size == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->data == NULL, b->allocated == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 0, b->allocated == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable == 1, b->allocated > 0 || b->size == 0), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_IMPLIES(b->growable != 0, b->size <= b->allocated), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->allocated), S2N_ERR_SAFETY); + RESULT_DEBUG_ENSURE(S2N_MEM_IS_READABLE(b->data, b->size), S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +/** + * Initialize a blob to reference some data. + * + * `b` will not free `data`. The caller is responsible for making sure that + * `data` outlives `b`. + */ +int s2n_blob_init(struct s2n_blob *b, uint8_t *data, uint32_t size) +{ + POSIX_ENSURE_REF(b); + POSIX_ENSURE(S2N_MEM_IS_READABLE(data, size), S2N_ERR_SAFETY); + *b = (struct s2n_blob){ .data = data, .size = size, .allocated = 0, .growable = 0 }; + POSIX_POSTCONDITION(s2n_blob_validate(b)); + return S2N_SUCCESS; +} + +int s2n_blob_zero(struct s2n_blob *b) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + POSIX_CHECKED_MEMSET(b->data, 0, S2N_MAX(b->allocated, b->size)); + POSIX_POSTCONDITION(s2n_blob_validate(b)); + return S2N_SUCCESS; +} + +/** + * Set `slice` to reference some portion of `b`. + * + * The caller is responsible for ensuring that the data pointed to by `b` outlives + * `slice`. + */ +int s2n_blob_slice(const struct s2n_blob *b, struct s2n_blob *slice, uint32_t offset, uint32_t size) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + POSIX_PRECONDITION(s2n_blob_validate(slice)); + + uint32_t slice_size = 0; + POSIX_GUARD(s2n_add_overflow(offset, size, &slice_size)); + POSIX_ENSURE(b->size >= slice_size, S2N_ERR_SIZE_MISMATCH); + slice->data = (b->data) ? (b->data + offset) : NULL; + slice->size = size; + slice->growable = 0; + slice->allocated = 0; + + POSIX_POSTCONDITION(s2n_blob_validate(slice)); + return S2N_SUCCESS; +} + +int s2n_blob_char_to_lower(struct s2n_blob *b) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + for (size_t i = 0; i < b->size; i++) { + b->data[i] = tolower(b->data[i]); + } + POSIX_POSTCONDITION(s2n_blob_validate(b)); + return S2N_SUCCESS; +} diff --git a/utils/s2n_compiler.h b/utils/s2n_compiler.h index 17b27aaace8..ac3314889dd 100644 --- a/utils/s2n_compiler.h +++ b/utils/s2n_compiler.h @@ -1,34 +1,34 @@ -/* - * 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 - - -#if defined(_MSC_VER) -#define S2N_GCC_VERSION 0 -#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) 0 -#else -#define S2N_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) - -#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) \ - ((S2N_GCC_VERSION) >= ((major) * 10000 + (minor) * 100 + (patch_level))) -#endif - - -#if defined(_MSC_VER) -#ifndef __builtin_expect -#define __builtin_expect(x, y) (x) -#endif -#endif +/* + * 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 + + +#if defined(_MSC_VER) +#define S2N_GCC_VERSION 0 +#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) 0 +#else +#define S2N_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +#define S2N_GCC_VERSION_AT_LEAST(major, minor, patch_level) \ + ((S2N_GCC_VERSION) >= ((major) * 10000 + (minor) * 100 + (patch_level))) +#endif + + +#if defined(_MSC_VER) +#ifndef __builtin_expect +#define __builtin_expect(x, y) (x) +#endif +#endif diff --git a/utils/s2n_ensure.h b/utils/s2n_ensure.h index 86c8aa14725..9595e71dfe1 100644 --- a/utils/s2n_ensure.h +++ b/utils/s2n_ensure.h @@ -1,198 +1,198 @@ -/* - * 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 - -#define s2n_likely(x) __builtin_expect(!!(x), 1) -#define s2n_unlikely(x) __builtin_expect(!!(x), 0) - -/** - * s2n_ensure provides low-level safety check functionality - * - * This should only consumed directly by s2n_safety. - * - * Note: This module can be replaced by static analyzer implementation - * to insert additional safety checks. - */ - -/** - * Ensures `cond` is true, otherwise `action` will be performed - */ -#define __S2N_ENSURE(cond, action) \ - do { \ - if (!(cond)) { \ - action; \ - } \ - } while (0) - -#define __S2N_ENSURE_LIKELY(cond, action) \ - do { \ - if (s2n_unlikely(!(cond))) { \ - action; \ - } \ - } while (0) - -#ifdef NDEBUG - #define __S2N_ENSURE_DEBUG(cond, action) \ - do { \ - } while (0) -#else - #define __S2N_ENSURE_DEBUG(cond, action) __S2N_ENSURE_LIKELY((cond), action) -#endif - -#define __S2N_ENSURE_PRECONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) - -#ifdef NDEBUG - #define __S2N_ENSURE_POSTCONDITION(result) (S2N_RESULT_OK) -#else - #define __S2N_ENSURE_POSTCONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) -#endif - -#if defined(_MSC_VER) -#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ - do { \ - if (s2n_likely((n))) { \ - void *r = s2n_ensure_memmove_trace((d), (s), (n)); \ - guard(r); \ - } \ - } while (0) -#else -#if defined(_MSC_VER) -#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ - do { \ - if (s2n_likely((n))) { \ - void *r = s2n_ensure_memmove_trace((d), (s), (n)); \ - guard(r); \ - } \ - } while (0) -#else -#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ - do { \ - __typeof(n) __tmp_n = (n); \ - if (s2n_likely(__tmp_n)) { \ - void *r = s2n_ensure_memmove_trace((d), (s), (__tmp_n)); \ - guard(r); \ - } \ - } while (0) -#endif -#endif - -#if defined(_MSC_VER) -#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ - do { \ - if (s2n_likely((n))) { \ - guard((d)); \ - memset((d), (c), (n)); \ - } \ - } while (0) -#else -#if defined(_MSC_VER) -#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ - do { \ - if (s2n_likely((n))) { \ - guard((d)); \ - memset((d), (c), (n)); \ - } \ - } while (0) -#else -#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ - do { \ - __typeof(n) __tmp_n = (n); \ - if (s2n_likely(__tmp_n)) { \ - __typeof(d) __tmp_d = (d); \ - guard(__tmp_d); \ - memset(__tmp_d, (c), __tmp_n); \ - } \ - } while (0) -#endif -#endif - -#if defined(S2N_DIAGNOSTICS_PUSH_SUPPORTED) && defined(S2N_DIAGNOSTICS_POP_SUPPORTED) - #define __S2N_ENSURE_CHECKED_RETURN(v) \ - do { \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic error \"-Wconversion\"") return v; \ - _Pragma("GCC diagnostic pop") \ - } while (0) -#else - #define __S2N_ENSURE_CHECKED_RETURN(v) return v -#endif - -void *s2n_ensure_memmove_trace(void *to, const void *from, size_t size); - -/** - * These macros should not be used in validate functions. - * All validate functions are also used in assumptions for CBMC proofs, - * which should not contain __CPROVER_*_ok primitives. The use of these primitives - * in assumptions may lead to spurious results. - * When the code is being verified using CBMC, these properties are formally verified; - * When the code is built in debug mode, they are checked as much as possible using assertions. - * When the code is built in production mode, non-fatal properties are not checked. - * Violations of these properties are undefined behaviour. - */ -#ifdef CBMC - #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || __CPROVER_r_ok((base), (len))) - #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || __CPROVER_w_ok((base), (len))) -#else - /* the C runtime does not give a way to check these properties, - * but we can at least check for nullness. */ - #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) - #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) -#endif /* CBMC */ - -/** - * These macros can safely be used in validate functions. - */ -#define S2N_MEM_IS_READABLE(base, len) (((len) == 0) || (base) != NULL) -#define S2N_MEM_IS_WRITABLE(base, len) (((len) == 0) || (base) != NULL) -#define S2N_OBJECT_PTR_IS_READABLE(ptr) ((ptr) != NULL) -#define S2N_OBJECT_PTR_IS_WRITABLE(ptr) ((ptr) != NULL) - -/** - * If `a` is true, then `b` must be true. - */ -#define S2N_IMPLIES(a, b) (!(a) || (b)) -/** - * If and only if (iff) is a biconditional logical connective between statements a and b. - * Equivalent to (S2N_IMPLIES(a, b) && S2N_IMPLIES(b, a)). - */ -#define S2N_IFF(a, b) (!!(a) == !!(b)) - -/** - * These macros are used to specify code contracts in CBMC proofs. - * Define function contracts. - * When the code is being verified using CBMC, these contracts are formally verified; - * When the code is built in production mode, contracts are not checked. - * 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(...) - #define CONTRACT_RETURN_VALUE -#endif +/* + * 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 + +#define s2n_likely(x) __builtin_expect(!!(x), 1) +#define s2n_unlikely(x) __builtin_expect(!!(x), 0) + +/** + * s2n_ensure provides low-level safety check functionality + * + * This should only consumed directly by s2n_safety. + * + * Note: This module can be replaced by static analyzer implementation + * to insert additional safety checks. + */ + +/** + * Ensures `cond` is true, otherwise `action` will be performed + */ +#define __S2N_ENSURE(cond, action) \ + do { \ + if (!(cond)) { \ + action; \ + } \ + } while (0) + +#define __S2N_ENSURE_LIKELY(cond, action) \ + do { \ + if (s2n_unlikely(!(cond))) { \ + action; \ + } \ + } while (0) + +#ifdef NDEBUG + #define __S2N_ENSURE_DEBUG(cond, action) \ + do { \ + } while (0) +#else + #define __S2N_ENSURE_DEBUG(cond, action) __S2N_ENSURE_LIKELY((cond), action) +#endif + +#define __S2N_ENSURE_PRECONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) + +#ifdef NDEBUG + #define __S2N_ENSURE_POSTCONDITION(result) (S2N_RESULT_OK) +#else + #define __S2N_ENSURE_POSTCONDITION(result) (s2n_likely(s2n_result_is_ok(result)) ? S2N_RESULT_OK : S2N_RESULT_ERROR) +#endif + +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + void *r = s2n_ensure_memmove_trace((d), (s), (n)); \ + guard(r); \ + } \ + } while (0) +#else +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + void *r = s2n_ensure_memmove_trace((d), (s), (n)); \ + guard(r); \ + } \ + } while (0) +#else +#define __S2N_ENSURE_SAFE_MEMMOVE(d, s, n, guard) \ + do { \ + __typeof(n) __tmp_n = (n); \ + if (s2n_likely(__tmp_n)) { \ + void *r = s2n_ensure_memmove_trace((d), (s), (__tmp_n)); \ + guard(r); \ + } \ + } while (0) +#endif +#endif + +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + guard((d)); \ + memset((d), (c), (n)); \ + } \ + } while (0) +#else +#if defined(_MSC_VER) +#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ + do { \ + if (s2n_likely((n))) { \ + guard((d)); \ + memset((d), (c), (n)); \ + } \ + } while (0) +#else +#define __S2N_ENSURE_SAFE_MEMSET(d, c, n, guard) \ + do { \ + __typeof(n) __tmp_n = (n); \ + if (s2n_likely(__tmp_n)) { \ + __typeof(d) __tmp_d = (d); \ + guard(__tmp_d); \ + memset(__tmp_d, (c), __tmp_n); \ + } \ + } while (0) +#endif +#endif + +#if defined(S2N_DIAGNOSTICS_PUSH_SUPPORTED) && defined(S2N_DIAGNOSTICS_POP_SUPPORTED) + #define __S2N_ENSURE_CHECKED_RETURN(v) \ + do { \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic error \"-Wconversion\"") return v; \ + _Pragma("GCC diagnostic pop") \ + } while (0) +#else + #define __S2N_ENSURE_CHECKED_RETURN(v) return v +#endif + +void *s2n_ensure_memmove_trace(void *to, const void *from, size_t size); + +/** + * These macros should not be used in validate functions. + * All validate functions are also used in assumptions for CBMC proofs, + * which should not contain __CPROVER_*_ok primitives. The use of these primitives + * in assumptions may lead to spurious results. + * When the code is being verified using CBMC, these properties are formally verified; + * When the code is built in debug mode, they are checked as much as possible using assertions. + * When the code is built in production mode, non-fatal properties are not checked. + * Violations of these properties are undefined behaviour. + */ +#ifdef CBMC + #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || __CPROVER_r_ok((base), (len))) + #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || __CPROVER_w_ok((base), (len))) +#else + /* the C runtime does not give a way to check these properties, + * but we can at least check for nullness. */ + #define S2N_MEM_IS_READABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) + #define S2N_MEM_IS_WRITABLE_CHECK(base, len) (((len) == 0) || (base) != NULL) +#endif /* CBMC */ + +/** + * These macros can safely be used in validate functions. + */ +#define S2N_MEM_IS_READABLE(base, len) (((len) == 0) || (base) != NULL) +#define S2N_MEM_IS_WRITABLE(base, len) (((len) == 0) || (base) != NULL) +#define S2N_OBJECT_PTR_IS_READABLE(ptr) ((ptr) != NULL) +#define S2N_OBJECT_PTR_IS_WRITABLE(ptr) ((ptr) != NULL) + +/** + * If `a` is true, then `b` must be true. + */ +#define S2N_IMPLIES(a, b) (!(a) || (b)) +/** + * If and only if (iff) is a biconditional logical connective between statements a and b. + * Equivalent to (S2N_IMPLIES(a, b) && S2N_IMPLIES(b, a)). + */ +#define S2N_IFF(a, b) (!!(a) == !!(b)) + +/** + * These macros are used to specify code contracts in CBMC proofs. + * Define function contracts. + * When the code is being verified using CBMC, these contracts are formally verified; + * When the code is built in production mode, contracts are not checked. + * 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(...) + #define CONTRACT_RETURN_VALUE +#endif diff --git a/utils/s2n_init.c b/utils/s2n_init.c index 8ad853ecc1f..504fd04f2a2 100644 --- a/utils/s2n_init.c +++ b/utils/s2n_init.c @@ -1,167 +1,167 @@ -/* - * 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 "utils/s2n_init.h" - -#if !defined(_MSC_VER) -#include -#else -#include -#endif - -#include "api/unstable/cleanup.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_libcrypto.h" -#include "crypto/s2n_locking.h" -#include "error/s2n_errno.h" -#include "openssl/opensslv.h" -#include "tls/extensions/s2n_client_key_share.h" -#include "tls/extensions/s2n_extension_type.h" -#include "tls/s2n_cipher_suites.h" -#include "tls/s2n_security_policies.h" -#include "tls/s2n_tls13_secrets.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" -#include "utils/s2n_safety_macros.h" - -static void s2n_cleanup_atexit(void); - -#if !defined(_MSC_VER) -static pthread_t main_thread = 0; -#else -static DWORD main_thread_id = 0; -#endif -static bool initialized = false; -static bool atexit_cleanup = false; -int s2n_disable_atexit(void) -{ - POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); - atexit_cleanup = false; - return S2N_SUCCESS; -} - -int s2n_enable_atexit(void) -{ - atexit_cleanup = true; - return S2N_SUCCESS; -} - -int s2n_init(void) -{ - /* USAGE-GUIDE says s2n_init MUST NOT be called more than once - * Public documentation for API states s2n_init should only be called once - * https://github.com/aws/s2n-tls/issues/3446 is a result of not enforcing this - */ - POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); - - #if !defined(_MSC_VER) - main_thread = pthread_self(); -#else - main_thread_id = GetCurrentThreadId(); -#endif - - if (getenv("S2N_INTEG_TEST")) { - POSIX_GUARD(s2n_in_integ_test_set(true)); - } - - /* Should run before any init method that calls libcrypto methods - * to ensure we don't try to call methods that don't exist. - * It doesn't require any locks since it only deals with values that - * should be constant, so can run before s2n_locking_init. */ - POSIX_GUARD_RESULT(s2n_libcrypto_validate_runtime()); - /* Must run before any init method that allocates memory. */ - POSIX_GUARD(s2n_mem_init()); - /* Must run before any init method that calls libcrypto methods. */ - POSIX_GUARD_RESULT(s2n_locking_init()); - POSIX_GUARD(s2n_fips_init()); - POSIX_GUARD_RESULT(s2n_rand_init()); - POSIX_GUARD_RESULT(s2n_hash_algorithms_init()); - POSIX_GUARD(s2n_cipher_suites_init()); - POSIX_GUARD(s2n_security_policies_init()); - POSIX_GUARD(s2n_config_defaults_init()); - POSIX_GUARD(s2n_extension_type_init()); - POSIX_GUARD_RESULT(s2n_tls13_empty_transcripts_init()); - POSIX_GUARD_RESULT(s2n_atomic_init()); - - if (atexit_cleanup) { - POSIX_ENSURE_OK(atexit(s2n_cleanup_atexit), S2N_ERR_ATEXIT); - } - - if (getenv("S2N_PRINT_STACKTRACE")) { - s2n_stack_traces_enabled_set(true); - } - -#if defined(OPENSSL_IS_AWSLC) - CRYPTO_pre_sandbox_init(); -#endif - - initialized = true; - - return S2N_SUCCESS; -} - -static bool s2n_cleanup_atexit_impl(void) -{ - /* all of these should run, regardless of result, but the - * values to need to be consumed to prevent warnings */ - - /* the configs need to be wiped before resetting the memory callbacks */ - s2n_wipe_static_configs(); - - bool cleaned_up = s2n_result_is_ok(s2n_cipher_suites_cleanup()) - && s2n_result_is_ok(s2n_hash_algorithms_cleanup()) - && s2n_result_is_ok(s2n_rand_cleanup()) - && s2n_result_is_ok(s2n_locking_cleanup()) - && (s2n_mem_cleanup() == S2N_SUCCESS); - - initialized = !cleaned_up; - return cleaned_up; -} - -int s2n_cleanup_final(void) -{ - /* some cleanups are not idempotent (rand_cleanup, mem_cleanup) so protect */ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE(s2n_cleanup_atexit_impl(), S2N_ERR_ATEXIT); - - return S2N_SUCCESS; -} - -int s2n_cleanup(void) -{ - /* Previously cleaned up thread-local DRBG state. The custom DRBG has - * been removed, so this is now a no-op kept for API compatibility. - */ - return S2N_SUCCESS; -} - -int s2n_cleanup_thread(void) -{ - /* Thread-local DRBG state has been removed. This is now a no-op kept - * for backwards compatibility with callers of the public API. - */ - return S2N_SUCCESS; -} - -static void s2n_cleanup_atexit(void) -{ - (void) s2n_cleanup_atexit_impl(); -} - -bool s2n_is_initialized(void) -{ - return initialized; -} +/* + * 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 "utils/s2n_init.h" + +#if !defined(_MSC_VER) +#include +#else +#include +#endif + +#include "api/unstable/cleanup.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_libcrypto.h" +#include "crypto/s2n_locking.h" +#include "error/s2n_errno.h" +#include "openssl/opensslv.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13_secrets.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_safety_macros.h" + +static void s2n_cleanup_atexit(void); + +#if !defined(_MSC_VER) +static pthread_t main_thread = 0; +#else +static DWORD main_thread_id = 0; +#endif +static bool initialized = false; +static bool atexit_cleanup = false; +int s2n_disable_atexit(void) +{ + POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); + atexit_cleanup = false; + return S2N_SUCCESS; +} + +int s2n_enable_atexit(void) +{ + atexit_cleanup = true; + return S2N_SUCCESS; +} + +int s2n_init(void) +{ + /* USAGE-GUIDE says s2n_init MUST NOT be called more than once + * Public documentation for API states s2n_init should only be called once + * https://github.com/aws/s2n-tls/issues/3446 is a result of not enforcing this + */ + POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); + + #if !defined(_MSC_VER) + main_thread = pthread_self(); +#else + main_thread_id = GetCurrentThreadId(); +#endif + + if (getenv("S2N_INTEG_TEST")) { + POSIX_GUARD(s2n_in_integ_test_set(true)); + } + + /* Should run before any init method that calls libcrypto methods + * to ensure we don't try to call methods that don't exist. + * It doesn't require any locks since it only deals with values that + * should be constant, so can run before s2n_locking_init. */ + POSIX_GUARD_RESULT(s2n_libcrypto_validate_runtime()); + /* Must run before any init method that allocates memory. */ + POSIX_GUARD(s2n_mem_init()); + /* Must run before any init method that calls libcrypto methods. */ + POSIX_GUARD_RESULT(s2n_locking_init()); + POSIX_GUARD(s2n_fips_init()); + POSIX_GUARD_RESULT(s2n_rand_init()); + POSIX_GUARD_RESULT(s2n_hash_algorithms_init()); + POSIX_GUARD(s2n_cipher_suites_init()); + POSIX_GUARD(s2n_security_policies_init()); + POSIX_GUARD(s2n_config_defaults_init()); + POSIX_GUARD(s2n_extension_type_init()); + POSIX_GUARD_RESULT(s2n_tls13_empty_transcripts_init()); + POSIX_GUARD_RESULT(s2n_atomic_init()); + + if (atexit_cleanup) { + POSIX_ENSURE_OK(atexit(s2n_cleanup_atexit), S2N_ERR_ATEXIT); + } + + if (getenv("S2N_PRINT_STACKTRACE")) { + s2n_stack_traces_enabled_set(true); + } + +#if defined(OPENSSL_IS_AWSLC) + CRYPTO_pre_sandbox_init(); +#endif + + initialized = true; + + return S2N_SUCCESS; +} + +static bool s2n_cleanup_atexit_impl(void) +{ + /* all of these should run, regardless of result, but the + * values to need to be consumed to prevent warnings */ + + /* the configs need to be wiped before resetting the memory callbacks */ + s2n_wipe_static_configs(); + + bool cleaned_up = s2n_result_is_ok(s2n_cipher_suites_cleanup()) + && s2n_result_is_ok(s2n_hash_algorithms_cleanup()) + && s2n_result_is_ok(s2n_rand_cleanup()) + && s2n_result_is_ok(s2n_locking_cleanup()) + && (s2n_mem_cleanup() == S2N_SUCCESS); + + initialized = !cleaned_up; + return cleaned_up; +} + +int s2n_cleanup_final(void) +{ + /* some cleanups are not idempotent (rand_cleanup, mem_cleanup) so protect */ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE(s2n_cleanup_atexit_impl(), S2N_ERR_ATEXIT); + + return S2N_SUCCESS; +} + +int s2n_cleanup(void) +{ + /* Previously cleaned up thread-local DRBG state. The custom DRBG has + * been removed, so this is now a no-op kept for API compatibility. + */ + return S2N_SUCCESS; +} + +int s2n_cleanup_thread(void) +{ + /* Thread-local DRBG state has been removed. This is now a no-op kept + * for backwards compatibility with callers of the public API. + */ + return S2N_SUCCESS; +} + +static void s2n_cleanup_atexit(void) +{ + (void) s2n_cleanup_atexit_impl(); +} + +bool s2n_is_initialized(void) +{ + return initialized; +} diff --git a/utils/s2n_mem.c b/utils/s2n_mem.c index c3df6ad7d40..e092bf092bf 100644 --- a/utils/s2n_mem.c +++ b/utils/s2n_mem.c @@ -1,399 +1,399 @@ -#include "utils/s2n_prelude.h" -/* - * 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. - */ - -#define _DEFAULT_SOURCE 1 -#if defined(S2N_FEATURES_AVAILABLE) - #include -#endif - -#include -#include -#include -#ifndef _WIN32 - #include - #include -#if !defined(_MSC_VER) - #include -#endif -#endif - -#include "error/s2n_errno.h" -#include "utils/s2n_blob.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_safety.h" - -static uint32_t page_size = 4096; -static bool initialized = false; - -static int s2n_mem_init_impl(void); -static int s2n_mem_cleanup_impl(void); - -#ifdef _WIN32 -static int s2n_windows_mem_free_impl(void *ptr, uint32_t size); -static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated); -#else -static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size); -static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); -static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size); -static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); -#endif - -static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl; -static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl; -#ifdef _WIN32 -static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; -static s2n_mem_free_callback s2n_mem_free_cb = s2n_windows_mem_free_impl; -#else -static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl; -static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl; -#endif - -static int s2n_mem_init_impl(void) -{ -#ifndef _WIN32 - long sysconf_rc = sysconf(_SC_PAGESIZE); - - /* sysconf must not error, and page_size cannot be 0 */ - POSIX_ENSURE_GT(sysconf_rc, 0); - - /* page_size must be a valid uint32 */ - long max_page_size = S2N_MIN(UINT32_MAX, LONG_MAX); - POSIX_ENSURE_LTE(sysconf_rc, max_page_size); - page_size = (uint32_t) sysconf_rc; - - if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) { - s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; - s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; - } -#endif - return S2N_SUCCESS; -} - -static int s2n_mem_cleanup_impl(void) -{ - page_size = 4096; -#ifdef _WIN32 - s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; - s2n_mem_free_cb = s2n_windows_mem_free_impl; -#else - s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; - s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; -#endif - return S2N_SUCCESS; -} - -#ifdef _WIN32 -/* mlock is not supported on Windows, so these use plain malloc/free without memory locking. */ - -static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated) -{ - *ptr = malloc(requested); - POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); - *allocated = requested; - - return S2N_SUCCESS; -} - -static int s2n_windows_mem_free_impl(void *ptr, uint32_t size) -{ - free(ptr); - return S2N_SUCCESS; -} - -#else - -static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size) -{ - /* Perform a best-effort `munlock`: ignore any errors during unlocking. */ - munlock(ptr, size); - free(ptr); - return S2N_SUCCESS; -} - -static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size) -{ - free(ptr); - - return S2N_SUCCESS; -} - -static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) -{ - POSIX_ENSURE_REF(ptr); - - /* Page aligned allocation required for mlock */ - uint32_t allocate = 0; - - POSIX_GUARD(s2n_align_to(requested, page_size, &allocate)); - - *ptr = NULL; - POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC); - *allocated = allocate; - - /* - ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because - ** both need to be able to dump pages to function. It's how they map heap output. - */ - #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING) - if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) { - POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); - POSIX_BAIL(S2N_ERR_MADVISE); - } - #endif - - if (mlock(*ptr, *allocated) != 0) { - /* When mlock fails, no memory will be locked, so we don't use munlock on free */ - POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); - POSIX_BAIL(S2N_ERR_MLOCK); - } - - POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); - - return S2N_SUCCESS; -} - -static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) -{ - *ptr = malloc(requested); - POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); - *allocated = requested; - - return S2N_SUCCESS; -} - -#endif - -int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, - s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) -{ - POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); - POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback, - mem_malloc_callback, mem_free_callback)); - return S2N_SUCCESS; -} - -S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, - s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) -{ - RESULT_ENSURE_REF(mem_init_callback); - RESULT_ENSURE_REF(mem_cleanup_callback); - RESULT_ENSURE_REF(mem_malloc_callback); - RESULT_ENSURE_REF(mem_free_callback); - - s2n_mem_init_cb = mem_init_callback; - s2n_mem_cleanup_cb = mem_cleanup_callback; - s2n_mem_malloc_cb = mem_malloc_callback; - s2n_mem_free_cb = mem_free_callback; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback, - s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback) -{ - RESULT_ENSURE_REF(mem_init_callback); - RESULT_ENSURE_REF(mem_cleanup_callback); - RESULT_ENSURE_REF(mem_malloc_callback); - RESULT_ENSURE_REF(mem_free_callback); - - *mem_init_callback = s2n_mem_init_cb; - *mem_cleanup_callback = s2n_mem_cleanup_cb; - *mem_malloc_callback = s2n_mem_malloc_cb; - *mem_free_callback = s2n_mem_free_cb; - - return S2N_RESULT_OK; -} - -/** - * Allocate a new blob on the heap. - * - * The blob will be _growable_. - * - * This blob owns the underlying memory, which will be freed when `s2n_free` is - * called on `b`. - */ -int s2n_alloc(struct s2n_blob *b, uint32_t size) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE_REF(b); - const struct s2n_blob temp = { 0 }; - *b = temp; - POSIX_GUARD(s2n_realloc(b, size)); - return S2N_SUCCESS; -} - -/* A blob is growable if it is either explicitly marked as such, or if it contains no data */ -bool s2n_blob_is_growable(const struct s2n_blob *b) -{ - return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0)); -} - -/** - * Resize a blob to `size`. The blob must be allocated or empty. - * - * This will allocate more memory if necessary, or reuse the existing allocation - * if the requested size is smaller than the current size. - * - * If failed, *b remains unchanged. - */ -int s2n_realloc(struct s2n_blob *b, uint32_t size) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE_REF(b); - POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB); - if (size == 0) { - return s2n_free(b); - } - - /* blob already has space for the request */ - if (size <= b->allocated) { - if (size < b->size) { - /* Zero the existing blob memory before the we release it */ - struct s2n_blob slice = { 0 }; - POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size)); - POSIX_GUARD(s2n_blob_zero(&slice)); - } - - b->size = size; - return S2N_SUCCESS; - } - - struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 }; - if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) { - S2N_ERROR_PRESERVE_ERRNO(); - } - - POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC); - POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC); - - if (b->size) { - POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size); - } - - if (b->allocated) { - POSIX_GUARD(s2n_free(b)); - } - - *b = new_memory; - return S2N_SUCCESS; -} - -int s2n_free_object(uint8_t **p_data, uint32_t size) -{ - POSIX_ENSURE_REF(p_data); - - if (*p_data == NULL) { - return S2N_SUCCESS; - } - - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 }; - - /* s2n_free() will call free() even if it returns error (for a growable blob). - ** This makes sure *p_data is not used after free() */ - *p_data = NULL; - - return s2n_free(&b); -} - -/** - * Allocate enough memory for `to` to contain all the data in `from`, then copy - * the data in `from` to `to`. - */ -int s2n_dup(struct s2n_blob *from, struct s2n_blob *to) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE_REF(to); - POSIX_ENSURE_REF(from); - POSIX_ENSURE_EQ(to->size, 0); - POSIX_ENSURE_EQ(to->data, NULL); - POSIX_ENSURE_NE(from->size, 0); - POSIX_ENSURE_NE(from->data, NULL); - - POSIX_GUARD(s2n_alloc(to, from->size)); - - POSIX_CHECKED_MEMCPY(to->data, from->data, to->size); - - return S2N_SUCCESS; -} - -int s2n_mem_init(void) -{ - POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - - initialized = true; - - return S2N_SUCCESS; -} - -bool s2n_mem_is_init(void) -{ - return initialized; -} - -uint32_t s2n_mem_get_page_size(void) -{ - return page_size; -} - -int s2n_mem_cleanup(void) -{ - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - - initialized = false; - - return S2N_SUCCESS; -} - -int s2n_free(struct s2n_blob *b) -{ - /* To avoid memory leaks, don't exit the function until the memory - has been freed */ - int zero_rc = s2n_blob_zero(b); - POSIX_GUARD(s2n_free_without_wipe(b)); - return zero_rc; -} - -int s2n_free_without_wipe(struct s2n_blob *b) -{ - POSIX_PRECONDITION(s2n_blob_validate(b)); - - POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); - POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB); - - if (b->data) { - void *data = b->data; - uint32_t allocated = b->allocated; - /* Set data point to NULL first to prevent potential double-free on s2n_mem_free_cb error path */ - *b = (struct s2n_blob){ 0 }; - POSIX_ENSURE(s2n_mem_free_cb(data, allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED); - } - - *b = (struct s2n_blob){ 0 }; - - return S2N_SUCCESS; -} - -int s2n_free_or_wipe(struct s2n_blob *b) -{ - POSIX_ENSURE_REF(b); - int zero_rc = s2n_blob_zero(b); - if (b->allocated) { - POSIX_GUARD(s2n_free_without_wipe(b)); - } - return zero_rc; -} +#include "utils/s2n_prelude.h" +/* + * 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. + */ + +#define _DEFAULT_SOURCE 1 +#if defined(S2N_FEATURES_AVAILABLE) + #include +#endif + +#include +#include +#include +#ifndef _WIN32 + #include + #include +#if !defined(_MSC_VER) + #include +#endif +#endif + +#include "error/s2n_errno.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +static uint32_t page_size = 4096; +static bool initialized = false; + +static int s2n_mem_init_impl(void); +static int s2n_mem_cleanup_impl(void); + +#ifdef _WIN32 +static int s2n_windows_mem_free_impl(void *ptr, uint32_t size); +static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated); +#else +static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size); +static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); +static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size); +static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated); +#endif + +static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl; +static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl; +#ifdef _WIN32 +static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; +static s2n_mem_free_callback s2n_mem_free_cb = s2n_windows_mem_free_impl; +#else +static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl; +static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl; +#endif + +static int s2n_mem_init_impl(void) +{ +#ifndef _WIN32 + long sysconf_rc = sysconf(_SC_PAGESIZE); + + /* sysconf must not error, and page_size cannot be 0 */ + POSIX_ENSURE_GT(sysconf_rc, 0); + + /* page_size must be a valid uint32 */ + long max_page_size = S2N_MIN(UINT32_MAX, LONG_MAX); + POSIX_ENSURE_LTE(sysconf_rc, max_page_size); + page_size = (uint32_t) sysconf_rc; + + if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) { + s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; + s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; + } +#endif + return S2N_SUCCESS; +} + +static int s2n_mem_cleanup_impl(void) +{ + page_size = 4096; +#ifdef _WIN32 + s2n_mem_malloc_cb = s2n_windows_mem_malloc_impl; + s2n_mem_free_cb = s2n_windows_mem_free_impl; +#else + s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl; + s2n_mem_free_cb = s2n_mem_free_no_mlock_impl; +#endif + return S2N_SUCCESS; +} + +#ifdef _WIN32 +/* mlock is not supported on Windows, so these use plain malloc/free without memory locking. */ + +static int s2n_windows_mem_malloc_impl(void **ptr, uint32_t requested, uint32_t *allocated) +{ + *ptr = malloc(requested); + POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); + *allocated = requested; + + return S2N_SUCCESS; +} + +static int s2n_windows_mem_free_impl(void *ptr, uint32_t size) +{ + free(ptr); + return S2N_SUCCESS; +} + +#else + +static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size) +{ + /* Perform a best-effort `munlock`: ignore any errors during unlocking. */ + munlock(ptr, size); + free(ptr); + return S2N_SUCCESS; +} + +static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size) +{ + free(ptr); + + return S2N_SUCCESS; +} + +static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) +{ + POSIX_ENSURE_REF(ptr); + + /* Page aligned allocation required for mlock */ + uint32_t allocate = 0; + + POSIX_GUARD(s2n_align_to(requested, page_size, &allocate)); + + *ptr = NULL; + POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC); + *allocated = allocate; + + /* + ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because + ** both need to be able to dump pages to function. It's how they map heap output. + */ + #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING) + if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) { + POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); + POSIX_BAIL(S2N_ERR_MADVISE); + } + #endif + + if (mlock(*ptr, *allocated) != 0) { + /* When mlock fails, no memory will be locked, so we don't use munlock on free */ + POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated)); + POSIX_BAIL(S2N_ERR_MLOCK); + } + + POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); + + return S2N_SUCCESS; +} + +static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated) +{ + *ptr = malloc(requested); + POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC); + *allocated = requested; + + return S2N_SUCCESS; +} + +#endif + +int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, + s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) +{ + POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED); + POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback, + mem_malloc_callback, mem_free_callback)); + return S2N_SUCCESS; +} + +S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback, + s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback) +{ + RESULT_ENSURE_REF(mem_init_callback); + RESULT_ENSURE_REF(mem_cleanup_callback); + RESULT_ENSURE_REF(mem_malloc_callback); + RESULT_ENSURE_REF(mem_free_callback); + + s2n_mem_init_cb = mem_init_callback; + s2n_mem_cleanup_cb = mem_cleanup_callback; + s2n_mem_malloc_cb = mem_malloc_callback; + s2n_mem_free_cb = mem_free_callback; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback, + s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback) +{ + RESULT_ENSURE_REF(mem_init_callback); + RESULT_ENSURE_REF(mem_cleanup_callback); + RESULT_ENSURE_REF(mem_malloc_callback); + RESULT_ENSURE_REF(mem_free_callback); + + *mem_init_callback = s2n_mem_init_cb; + *mem_cleanup_callback = s2n_mem_cleanup_cb; + *mem_malloc_callback = s2n_mem_malloc_cb; + *mem_free_callback = s2n_mem_free_cb; + + return S2N_RESULT_OK; +} + +/** + * Allocate a new blob on the heap. + * + * The blob will be _growable_. + * + * This blob owns the underlying memory, which will be freed when `s2n_free` is + * called on `b`. + */ +int s2n_alloc(struct s2n_blob *b, uint32_t size) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE_REF(b); + const struct s2n_blob temp = { 0 }; + *b = temp; + POSIX_GUARD(s2n_realloc(b, size)); + return S2N_SUCCESS; +} + +/* A blob is growable if it is either explicitly marked as such, or if it contains no data */ +bool s2n_blob_is_growable(const struct s2n_blob *b) +{ + return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0)); +} + +/** + * Resize a blob to `size`. The blob must be allocated or empty. + * + * This will allocate more memory if necessary, or reuse the existing allocation + * if the requested size is smaller than the current size. + * + * If failed, *b remains unchanged. + */ +int s2n_realloc(struct s2n_blob *b, uint32_t size) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE_REF(b); + POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB); + if (size == 0) { + return s2n_free(b); + } + + /* blob already has space for the request */ + if (size <= b->allocated) { + if (size < b->size) { + /* Zero the existing blob memory before the we release it */ + struct s2n_blob slice = { 0 }; + POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size)); + POSIX_GUARD(s2n_blob_zero(&slice)); + } + + b->size = size; + return S2N_SUCCESS; + } + + struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 }; + if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) { + S2N_ERROR_PRESERVE_ERRNO(); + } + + POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC); + POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC); + + if (b->size) { + POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size); + } + + if (b->allocated) { + POSIX_GUARD(s2n_free(b)); + } + + *b = new_memory; + return S2N_SUCCESS; +} + +int s2n_free_object(uint8_t **p_data, uint32_t size) +{ + POSIX_ENSURE_REF(p_data); + + if (*p_data == NULL) { + return S2N_SUCCESS; + } + + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 }; + + /* s2n_free() will call free() even if it returns error (for a growable blob). + ** This makes sure *p_data is not used after free() */ + *p_data = NULL; + + return s2n_free(&b); +} + +/** + * Allocate enough memory for `to` to contain all the data in `from`, then copy + * the data in `from` to `to`. + */ +int s2n_dup(struct s2n_blob *from, struct s2n_blob *to) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE_REF(to); + POSIX_ENSURE_REF(from); + POSIX_ENSURE_EQ(to->size, 0); + POSIX_ENSURE_EQ(to->data, NULL); + POSIX_ENSURE_NE(from->size, 0); + POSIX_ENSURE_NE(from->data, NULL); + + POSIX_GUARD(s2n_alloc(to, from->size)); + + POSIX_CHECKED_MEMCPY(to->data, from->data, to->size); + + return S2N_SUCCESS; +} + +int s2n_mem_init(void) +{ + POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + + initialized = true; + + return S2N_SUCCESS; +} + +bool s2n_mem_is_init(void) +{ + return initialized; +} + +uint32_t s2n_mem_get_page_size(void) +{ + return page_size; +} + +int s2n_mem_cleanup(void) +{ + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + + initialized = false; + + return S2N_SUCCESS; +} + +int s2n_free(struct s2n_blob *b) +{ + /* To avoid memory leaks, don't exit the function until the memory + has been freed */ + int zero_rc = s2n_blob_zero(b); + POSIX_GUARD(s2n_free_without_wipe(b)); + return zero_rc; +} + +int s2n_free_without_wipe(struct s2n_blob *b) +{ + POSIX_PRECONDITION(s2n_blob_validate(b)); + + POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED); + POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB); + + if (b->data) { + void *data = b->data; + uint32_t allocated = b->allocated; + /* Set data point to NULL first to prevent potential double-free on s2n_mem_free_cb error path */ + *b = (struct s2n_blob){ 0 }; + POSIX_ENSURE(s2n_mem_free_cb(data, allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED); + } + + *b = (struct s2n_blob){ 0 }; + + return S2N_SUCCESS; +} + +int s2n_free_or_wipe(struct s2n_blob *b) +{ + POSIX_ENSURE_REF(b); + int zero_rc = s2n_blob_zero(b); + if (b->allocated) { + POSIX_GUARD(s2n_free_without_wipe(b)); + } + return zero_rc; +} diff --git a/utils/s2n_prelude.h b/utils/s2n_prelude.h index 8333ad1af43..1a7d53bc44a 100644 --- a/utils/s2n_prelude.h +++ b/utils/s2n_prelude.h @@ -1,54 +1,54 @@ -/* - * 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 - -/* Let modules know that the prelude was included */ -#define _S2N_PRELUDE_INCLUDED - -/* Define the POSIX API we are targeting */ -#ifndef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 200809L -#endif - -/** - * If we're building in release mode make sure _FORTIFY_SOURCE is set - * See: https://www.gnu.org/software/libc/manual/html_node/Source-Fortification.html - * https://man7.org/linux/man-pages/man7/feature_test_macros.7.html - * - * NOTE: _FORTIFY_SOURCE can only be set when optimizations are enabled. - * https://sourceware.org/git/?p=glibc.git;a=commit;f=include/features.h;h=05c2c9618f583ea4acd69b3fe5ae2a2922dd2ddc - */ -#if !defined(_FORTIFY_SOURCE) && defined(S2N_BUILD_RELEASE) - #define _FORTIFY_SOURCE 2 -#endif - -#ifndef S2N_API -#if ((__GNUC__ >= 4) || defined(__clang__)) && defined(S2N_EXPORTS) - #define S2N_API __attribute__((visibility("default"))) -#elif defined(_MSC_VER) - #if defined(S2N_EXPORTS) - #define S2N_API __declspec(dllexport) - #else - #define S2N_API __declspec(dllimport) - #endif -#else - #define S2N_API -#endif -#endif - -/* These replace the use of MIN/MAX from , which is not available on Windows. */ -#define S2N_MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define S2N_MAX(a, b) (((a) > (b)) ? (a) : (b)) +/* + * 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 + +/* Let modules know that the prelude was included */ +#define _S2N_PRELUDE_INCLUDED + +/* Define the POSIX API we are targeting */ +#ifndef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 200809L +#endif + +/** + * If we're building in release mode make sure _FORTIFY_SOURCE is set + * See: https://www.gnu.org/software/libc/manual/html_node/Source-Fortification.html + * https://man7.org/linux/man-pages/man7/feature_test_macros.7.html + * + * NOTE: _FORTIFY_SOURCE can only be set when optimizations are enabled. + * https://sourceware.org/git/?p=glibc.git;a=commit;f=include/features.h;h=05c2c9618f583ea4acd69b3fe5ae2a2922dd2ddc + */ +#if !defined(_FORTIFY_SOURCE) && defined(S2N_BUILD_RELEASE) + #define _FORTIFY_SOURCE 2 +#endif + +#ifndef S2N_API +#if ((__GNUC__ >= 4) || defined(__clang__)) && defined(S2N_EXPORTS) + #define S2N_API __attribute__((visibility("default"))) +#elif defined(_MSC_VER) + #if defined(S2N_EXPORTS) + #define S2N_API __declspec(dllexport) + #else + #define S2N_API __declspec(dllimport) + #endif +#else + #define S2N_API +#endif +#endif + +/* These replace the use of MIN/MAX from , which is not available on Windows. */ +#define S2N_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define S2N_MAX(a, b) (((a) > (b)) ? (a) : (b)) diff --git a/utils/s2n_random.c b/utils/s2n_random.c index 16b4fdac7bf..e9192403573 100644 --- a/utils/s2n_random.c +++ b/utils/s2n_random.c @@ -1,416 +1,416 @@ -#include "utils/s2n_prelude.h" -#if defined(_MSC_VER) -#pragma comment(lib, "bcrypt.lib") -#include -#include -#include "error/s2n_errno.h" -#include "utils/s2n_random.h" -#include "utils/s2n_safety.h" - -static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) -{ - POSIX_ENSURE_REF(ptr); - if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, (PUCHAR)ptr, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG))) { - POSIX_BAIL(S2N_ERR_RANDOM_UNINITIALIZED); - } - return S2N_SUCCESS; -} - -static int s2n_rand_init_cb_impl(void) { return S2N_SUCCESS; } -static int s2n_rand_cleanup_cb_impl(void) { return S2N_SUCCESS; } - -#else -/* - * 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. - */ - -/* - * _XOPEN_SOURCE is needed for resolving the constant O_CLOEXEC in some - * environments. We use _XOPEN_SOURCE instead of _GNU_SOURCE because - * _GNU_SOURCE is not portable and breaks when attempting to build rust - * bindings on MacOS. - * - * https://man7.org/linux/man-pages/man2/open.2.html - * The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not - * specified in POSIX.1-2001, but are specified in POSIX.1-2008. - * Since glibc 2.12, one can obtain their definitions by defining - * either _POSIX_C_SOURCE with a value greater than or equal to - * 200809L or _XOPEN_SOURCE with a value greater than or equal to - * 700. In glibc 2.11 and earlier, one obtains the definitions by - * defining _GNU_SOURCE. - * - * We use two feature probes to detect the need to perform this workaround. - * It is only applied if we can't get CLOEXEC without it and the build doesn't - * fail with _XOPEN_SOURCE being defined. - * - * # Relevent Links - * - * - POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799 - * - https://stackoverflow.com/a/5724485 - * - https://stackoverflow.com/a/5583764 - */ -#if !defined(S2N_CLOEXEC_SUPPORTED) && defined(S2N_CLOEXEC_XOPEN_SUPPORTED) && !defined(_XOPEN_SOURCE) - #define _XOPEN_SOURCE 700 - #include - #undef _XOPEN_SOURCE -#else - #include -#endif -#include -#include -/* LibreSSL requires include. - * https://github.com/aws/s2n-tls/issues/153#issuecomment-129651643 - */ -#include -#include -#include -#include -#include -#include -#if !defined(_MSC_VER) -#include -#endif - -#include "api/s2n.h" -#include "crypto/s2n_fips.h" -#include "crypto/s2n_libcrypto.h" -#include "error/s2n_errno.h" -#include "s2n_io.h" -#include "utils/s2n_init.h" -#include "utils/s2n_mem.h" -#include "utils/s2n_random.h" -#include "utils/s2n_result.h" -#include "utils/s2n_safety.h" - -#if defined(O_CLOEXEC) - #define ENTROPY_FLAGS O_RDONLY | O_CLOEXEC -#else - #define ENTROPY_FLAGS O_RDONLY -#endif - -/* One second in nanoseconds */ -#define ONE_S INT64_C(1000000000) - -/* Placeholder value for an uninitialized entropy file descriptor */ -#define UNINITIALIZED_ENTROPY_FD -1 - -static struct s2n_rand_device s2n_dev_urandom = { - .source = "/dev/urandom", - .fd = UNINITIALIZED_ENTROPY_FD, -}; - -static int s2n_rand_init_cb_impl(void); -static int s2n_rand_cleanup_cb_impl(void); -static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size); - -static int s2n_rand_entropy_fd_close_ptr(int *fd) -{ - if (fd && *fd != UNINITIALIZED_ENTROPY_FD) { - close(*fd); - } - return S2N_SUCCESS; -} - -/* - * Use libcrypto for randomness when the linked libcrypto supports at least - * one of RAND_priv_bytes or RAND_public_bytes, or when the libcrypto is - * AWS-LC (whose RAND_bytes is trusted even in older FIPS builds that lack - * the distinct pub/priv APIs). For older libcryptos that lack both and are - * not AWS-LC (e.g. OpenSSL 1.0.2), fall back to system random (/dev/urandom) - * to avoid depending on the weaker single-stream PRNG. - */ -bool s2n_use_libcrypto_rand(void) -{ -#if defined(S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND) || defined(S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND) - return true; -#elif defined(OPENSSL_IS_AWSLC) - /* Older AWS-LC FIPS builds (e.g. aws-lc-fips-2022) may lack - * RAND_priv_bytes/RAND_public_bytes but still provide a trusted - * RAND_bytes implementation that we can defer to. - */ - return true; -#else - return false; -#endif -} - -static S2N_RESULT s2n_get_libcrypto_private_random_data(struct s2n_blob *out_blob) -{ - RESULT_GUARD_PTR(out_blob); - RESULT_ENSURE_REF(out_blob->data); -#if S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND - RESULT_GUARD_OSSL(RAND_priv_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#else - /* Libcryptos that support RAND_public_bytes but not RAND_priv_bytes still - * provide RAND_bytes. OpenSSL 1.0.2 (which lacks both) is handled by - * s2n_use_libcrypto_rand() returning false, so this path is only reached - * by libcryptos that have at least RAND_public_bytes. - */ - RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#endif - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_get_libcrypto_public_random_data(struct s2n_blob *out_blob) -{ - RESULT_GUARD_PTR(out_blob); - RESULT_ENSURE_REF(out_blob->data); -#if S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND - RESULT_GUARD_OSSL(RAND_public_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#else - RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); -#endif - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_get_system_random_data(struct s2n_blob *blob) -{ - RESULT_GUARD_PTR(blob); - RESULT_GUARD_PTR(blob->data); - - /* This function sets s2n_errno on failure */ - RESULT_GUARD_POSIX(s2n_rand_get_entropy_from_urandom(blob->data, blob->size)); - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob) -{ - if (s2n_use_libcrypto_rand()) { - RESULT_GUARD(s2n_get_libcrypto_public_random_data(blob)); - } else { - RESULT_GUARD(s2n_get_system_random_data(blob)); - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob) -{ - if (s2n_use_libcrypto_rand()) { - RESULT_GUARD(s2n_get_libcrypto_private_random_data(blob)); - } else { - RESULT_GUARD(s2n_get_system_random_data(blob)); - } - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_rand_get_urandom_for_test(struct s2n_rand_device **device) -{ - RESULT_ENSURE_REF(device); - RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST); - *device = &s2n_dev_urandom; - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_rand_device_open(struct s2n_rand_device *device) -{ - RESULT_ENSURE_REF(device); - RESULT_ENSURE_REF(device->source); - - DEFER_CLEANUP(int fd = -1, s2n_rand_entropy_fd_close_ptr); - S2N_IO_RETRY_EINTR(fd, open(device->source, ENTROPY_FLAGS)); - RESULT_ENSURE(fd >= 0, S2N_ERR_OPEN_RANDOM); - - struct stat st = { 0 }; - RESULT_ENSURE(fstat(fd, &st) == 0, S2N_ERR_OPEN_RANDOM); - device->dev = st.st_dev; - device->ino = st.st_ino; - device->mode = st.st_mode; - device->rdev = st.st_rdev; - - device->fd = fd; - - /* Disable closing the file descriptor with defer cleanup */ - fd = UNINITIALIZED_ENTROPY_FD; - - return S2N_RESULT_OK; -} - -S2N_RESULT s2n_rand_device_validate(struct s2n_rand_device *device) -{ - RESULT_ENSURE_REF(device); - RESULT_ENSURE_NE(device->fd, UNINITIALIZED_ENTROPY_FD); - - /* Ensure that the random device is still valid by comparing it to the current file descriptor - * status. From: - * https://github.com/openssl/openssl/blob/260d97229c467d17934ca3e2e0455b1b5c0994a6/providers/implementations/rands/seeding/rand_unix.c#L513 - */ - struct stat st = { 0 }; - RESULT_ENSURE(fstat(device->fd, &st) == 0, S2N_ERR_OPEN_RANDOM); - RESULT_ENSURE_EQ(device->dev, st.st_dev); - RESULT_ENSURE_EQ(device->ino, st.st_ino); - RESULT_ENSURE_EQ(device->rdev, st.st_rdev); - - /* Ensure that the mode is the same (equal to 0 when xor'd), but don't check the permission bits. */ - mode_t permission_mask = ~(S_IRWXU | S_IRWXG | S_IRWXO); - RESULT_ENSURE_EQ((device->mode ^ st.st_mode) & permission_mask, 0); - - return S2N_RESULT_OK; -} - -static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) -{ - POSIX_ENSURE_REF(ptr); - POSIX_ENSURE(s2n_dev_urandom.fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED); - - /* It's possible that the file descriptor pointing to /dev/urandom was closed or changed from - * when it was last opened. Ensure that the file descriptor is still valid, and if it isn't, - * re-open it before getting entropy. - * - * If the file descriptor is invalid and the process doesn't have access to /dev/urandom (as is - * the case within a chroot tree), an error is raised here before attempting to indefinitely - * read. - */ - if (s2n_result_is_error(s2n_rand_device_validate(&s2n_dev_urandom))) { - POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); - } - - uint8_t *data = ptr; - uint32_t n = size; - struct timespec sleep_time = { .tv_sec = 0, .tv_nsec = 0 }; - long backoff = 1; - - while (n) { - errno = 0; - int r = read(s2n_dev_urandom.fd, data, n); - if (r <= 0) { - /* - * A non-blocking read() on /dev/urandom should "never" fail, - * except for EINTR. If it does, briefly pause and use - * exponential backoff to avoid creating a tight spinning loop. - * - * iteration delay - * --------- ----------------- - * 1 10 nsec - * 2 100 nsec - * 3 1,000 nsec - * 4 10,000 nsec - * 5 100,000 nsec - * 6 1,000,000 nsec - * 7 10,000,000 nsec - * 8 99,999,999 nsec - * 9 99,999,999 nsec - * ... - */ - if (errno != EINTR) { - backoff = S2N_MIN(backoff * 10, ONE_S - 1); - sleep_time.tv_nsec = backoff; - do { - r = nanosleep(&sleep_time, &sleep_time); - } while (r != 0); - } - - continue; - } - - data += r; - n -= r; - } - - return S2N_SUCCESS; -} - -/* - * Return a random number in the range [0, bound) - */ -S2N_RESULT s2n_public_random(int64_t bound, uint64_t *output) -{ - uint64_t r = 0; - - RESULT_ENSURE_GT(bound, 0); - - while (1) { - struct s2n_blob blob = { 0 }; - RESULT_GUARD_POSIX(s2n_blob_init(&blob, (void *) &r, sizeof(r))); - RESULT_GUARD(s2n_get_public_random_data(&blob)); - - /* Imagine an int was one byte and UINT_MAX was 256. If the - * caller asked for s2n_random(129, ...) we'd end up in - * trouble. Each number in the range 0...127 would be twice - * as likely as 128. That's because r == 0 % 129 -> 0, and - * r == 129 % 129 -> 0, but only r == 128 returns 128, - * r == 257 is out of range. - * - * To de-bias the dice, we discard values of r that are higher - * that the highest multiple of 'bound' an int can support. If - * bound is a uint, then in the worst case we discard 50% - 1 r's. - * But since 'bound' is an int and INT_MAX is <= UINT_MAX / 2, - * in the worst case we discard 25% - 1 r's. - */ - if (r < (UINT64_MAX - (UINT64_MAX % bound))) { - *output = r % bound; - return S2N_RESULT_OK; - } - } -} - -static int s2n_rand_init_cb_impl(void) -{ - POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_rand_init(void) -{ - /* Only open /dev/urandom when we actually need it for randomness. - * When libcrypto handles randomness, avoid the extra syscall and fd. - */ - if (!s2n_use_libcrypto_rand()) { - RESULT_ENSURE(s2n_rand_init_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - } - - return S2N_RESULT_OK; -} - -static int s2n_rand_cleanup_cb_impl(void) -{ - if (s2n_dev_urandom.fd == UNINITIALIZED_ENTROPY_FD) { - return S2N_SUCCESS; - } - - if (s2n_result_is_ok(s2n_rand_device_validate(&s2n_dev_urandom))) { - POSIX_GUARD(close(s2n_dev_urandom.fd)); - } - s2n_dev_urandom.fd = UNINITIALIZED_ENTROPY_FD; - - return S2N_SUCCESS; -} - -S2N_RESULT s2n_rand_cleanup(void) -{ - if (!s2n_use_libcrypto_rand()) { - RESULT_ENSURE(s2n_rand_cleanup_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); - } - - return S2N_RESULT_OK; -} - -int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback, - s2n_rand_cleanup_callback rand_cleanup_callback, - s2n_rand_seed_callback rand_seed_callback, - s2n_rand_mix_callback rand_mix_callback) -{ - /* Custom random callbacks are no longer supported. Randomness is now - * delegated directly to libcrypto or /dev/urandom. This stub is kept - * for backwards compatibility. - */ - (void) rand_init_callback; - (void) rand_cleanup_callback; - (void) rand_seed_callback; - (void) rand_mix_callback; - return S2N_SUCCESS; -} - +#include "utils/s2n_prelude.h" +#if defined(_MSC_VER) +#pragma comment(lib, "bcrypt.lib") +#include +#include +#include "error/s2n_errno.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) +{ + POSIX_ENSURE_REF(ptr); + if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, (PUCHAR)ptr, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG))) { + POSIX_BAIL(S2N_ERR_RANDOM_UNINITIALIZED); + } + return S2N_SUCCESS; +} + +static int s2n_rand_init_cb_impl(void) { return S2N_SUCCESS; } +static int s2n_rand_cleanup_cb_impl(void) { return S2N_SUCCESS; } + +#else +/* + * 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. + */ + +/* + * _XOPEN_SOURCE is needed for resolving the constant O_CLOEXEC in some + * environments. We use _XOPEN_SOURCE instead of _GNU_SOURCE because + * _GNU_SOURCE is not portable and breaks when attempting to build rust + * bindings on MacOS. + * + * https://man7.org/linux/man-pages/man2/open.2.html + * The O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW flags are not + * specified in POSIX.1-2001, but are specified in POSIX.1-2008. + * Since glibc 2.12, one can obtain their definitions by defining + * either _POSIX_C_SOURCE with a value greater than or equal to + * 200809L or _XOPEN_SOURCE with a value greater than or equal to + * 700. In glibc 2.11 and earlier, one obtains the definitions by + * defining _GNU_SOURCE. + * + * We use two feature probes to detect the need to perform this workaround. + * It is only applied if we can't get CLOEXEC without it and the build doesn't + * fail with _XOPEN_SOURCE being defined. + * + * # Relevent Links + * + * - POSIX.1-2017: https://pubs.opengroup.org/onlinepubs/9699919799 + * - https://stackoverflow.com/a/5724485 + * - https://stackoverflow.com/a/5583764 + */ +#if !defined(S2N_CLOEXEC_SUPPORTED) && defined(S2N_CLOEXEC_XOPEN_SUPPORTED) && !defined(_XOPEN_SOURCE) + #define _XOPEN_SOURCE 700 + #include + #undef _XOPEN_SOURCE +#else + #include +#endif +#include +#include +/* LibreSSL requires include. + * https://github.com/aws/s2n-tls/issues/153#issuecomment-129651643 + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_libcrypto.h" +#include "error/s2n_errno.h" +#include "s2n_io.h" +#include "utils/s2n_init.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_random.h" +#include "utils/s2n_result.h" +#include "utils/s2n_safety.h" + +#if defined(O_CLOEXEC) + #define ENTROPY_FLAGS O_RDONLY | O_CLOEXEC +#else + #define ENTROPY_FLAGS O_RDONLY +#endif + +/* One second in nanoseconds */ +#define ONE_S INT64_C(1000000000) + +/* Placeholder value for an uninitialized entropy file descriptor */ +#define UNINITIALIZED_ENTROPY_FD -1 + +static struct s2n_rand_device s2n_dev_urandom = { + .source = "/dev/urandom", + .fd = UNINITIALIZED_ENTROPY_FD, +}; + +static int s2n_rand_init_cb_impl(void); +static int s2n_rand_cleanup_cb_impl(void); +static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size); + +static int s2n_rand_entropy_fd_close_ptr(int *fd) +{ + if (fd && *fd != UNINITIALIZED_ENTROPY_FD) { + close(*fd); + } + return S2N_SUCCESS; +} + +/* + * Use libcrypto for randomness when the linked libcrypto supports at least + * one of RAND_priv_bytes or RAND_public_bytes, or when the libcrypto is + * AWS-LC (whose RAND_bytes is trusted even in older FIPS builds that lack + * the distinct pub/priv APIs). For older libcryptos that lack both and are + * not AWS-LC (e.g. OpenSSL 1.0.2), fall back to system random (/dev/urandom) + * to avoid depending on the weaker single-stream PRNG. + */ +bool s2n_use_libcrypto_rand(void) +{ +#if defined(S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND) || defined(S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND) + return true; +#elif defined(OPENSSL_IS_AWSLC) + /* Older AWS-LC FIPS builds (e.g. aws-lc-fips-2022) may lack + * RAND_priv_bytes/RAND_public_bytes but still provide a trusted + * RAND_bytes implementation that we can defer to. + */ + return true; +#else + return false; +#endif +} + +static S2N_RESULT s2n_get_libcrypto_private_random_data(struct s2n_blob *out_blob) +{ + RESULT_GUARD_PTR(out_blob); + RESULT_ENSURE_REF(out_blob->data); +#if S2N_LIBCRYPTO_SUPPORTS_PRIVATE_RAND + RESULT_GUARD_OSSL(RAND_priv_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#else + /* Libcryptos that support RAND_public_bytes but not RAND_priv_bytes still + * provide RAND_bytes. OpenSSL 1.0.2 (which lacks both) is handled by + * s2n_use_libcrypto_rand() returning false, so this path is only reached + * by libcryptos that have at least RAND_public_bytes. + */ + RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#endif + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_get_libcrypto_public_random_data(struct s2n_blob *out_blob) +{ + RESULT_GUARD_PTR(out_blob); + RESULT_ENSURE_REF(out_blob->data); +#if S2N_LIBCRYPTO_SUPPORTS_PUBLIC_RAND + RESULT_GUARD_OSSL(RAND_public_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#else + RESULT_GUARD_OSSL(RAND_bytes(out_blob->data, out_blob->size), S2N_ERR_RANDOM); +#endif + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_get_system_random_data(struct s2n_blob *blob) +{ + RESULT_GUARD_PTR(blob); + RESULT_GUARD_PTR(blob->data); + + /* This function sets s2n_errno on failure */ + RESULT_GUARD_POSIX(s2n_rand_get_entropy_from_urandom(blob->data, blob->size)); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob) +{ + if (s2n_use_libcrypto_rand()) { + RESULT_GUARD(s2n_get_libcrypto_public_random_data(blob)); + } else { + RESULT_GUARD(s2n_get_system_random_data(blob)); + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob) +{ + if (s2n_use_libcrypto_rand()) { + RESULT_GUARD(s2n_get_libcrypto_private_random_data(blob)); + } else { + RESULT_GUARD(s2n_get_system_random_data(blob)); + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_rand_get_urandom_for_test(struct s2n_rand_device **device) +{ + RESULT_ENSURE_REF(device); + RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST); + *device = &s2n_dev_urandom; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_rand_device_open(struct s2n_rand_device *device) +{ + RESULT_ENSURE_REF(device); + RESULT_ENSURE_REF(device->source); + + DEFER_CLEANUP(int fd = -1, s2n_rand_entropy_fd_close_ptr); + S2N_IO_RETRY_EINTR(fd, open(device->source, ENTROPY_FLAGS)); + RESULT_ENSURE(fd >= 0, S2N_ERR_OPEN_RANDOM); + + struct stat st = { 0 }; + RESULT_ENSURE(fstat(fd, &st) == 0, S2N_ERR_OPEN_RANDOM); + device->dev = st.st_dev; + device->ino = st.st_ino; + device->mode = st.st_mode; + device->rdev = st.st_rdev; + + device->fd = fd; + + /* Disable closing the file descriptor with defer cleanup */ + fd = UNINITIALIZED_ENTROPY_FD; + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_rand_device_validate(struct s2n_rand_device *device) +{ + RESULT_ENSURE_REF(device); + RESULT_ENSURE_NE(device->fd, UNINITIALIZED_ENTROPY_FD); + + /* Ensure that the random device is still valid by comparing it to the current file descriptor + * status. From: + * https://github.com/openssl/openssl/blob/260d97229c467d17934ca3e2e0455b1b5c0994a6/providers/implementations/rands/seeding/rand_unix.c#L513 + */ + struct stat st = { 0 }; + RESULT_ENSURE(fstat(device->fd, &st) == 0, S2N_ERR_OPEN_RANDOM); + RESULT_ENSURE_EQ(device->dev, st.st_dev); + RESULT_ENSURE_EQ(device->ino, st.st_ino); + RESULT_ENSURE_EQ(device->rdev, st.st_rdev); + + /* Ensure that the mode is the same (equal to 0 when xor'd), but don't check the permission bits. */ + mode_t permission_mask = ~(S_IRWXU | S_IRWXG | S_IRWXO); + RESULT_ENSURE_EQ((device->mode ^ st.st_mode) & permission_mask, 0); + + return S2N_RESULT_OK; +} + +static int s2n_rand_get_entropy_from_urandom(void *ptr, uint32_t size) +{ + POSIX_ENSURE_REF(ptr); + POSIX_ENSURE(s2n_dev_urandom.fd != UNINITIALIZED_ENTROPY_FD, S2N_ERR_NOT_INITIALIZED); + + /* It's possible that the file descriptor pointing to /dev/urandom was closed or changed from + * when it was last opened. Ensure that the file descriptor is still valid, and if it isn't, + * re-open it before getting entropy. + * + * If the file descriptor is invalid and the process doesn't have access to /dev/urandom (as is + * the case within a chroot tree), an error is raised here before attempting to indefinitely + * read. + */ + if (s2n_result_is_error(s2n_rand_device_validate(&s2n_dev_urandom))) { + POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); + } + + uint8_t *data = ptr; + uint32_t n = size; + struct timespec sleep_time = { .tv_sec = 0, .tv_nsec = 0 }; + long backoff = 1; + + while (n) { + errno = 0; + int r = read(s2n_dev_urandom.fd, data, n); + if (r <= 0) { + /* + * A non-blocking read() on /dev/urandom should "never" fail, + * except for EINTR. If it does, briefly pause and use + * exponential backoff to avoid creating a tight spinning loop. + * + * iteration delay + * --------- ----------------- + * 1 10 nsec + * 2 100 nsec + * 3 1,000 nsec + * 4 10,000 nsec + * 5 100,000 nsec + * 6 1,000,000 nsec + * 7 10,000,000 nsec + * 8 99,999,999 nsec + * 9 99,999,999 nsec + * ... + */ + if (errno != EINTR) { + backoff = S2N_MIN(backoff * 10, ONE_S - 1); + sleep_time.tv_nsec = backoff; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + } + + continue; + } + + data += r; + n -= r; + } + + return S2N_SUCCESS; +} + +/* + * Return a random number in the range [0, bound) + */ +S2N_RESULT s2n_public_random(int64_t bound, uint64_t *output) +{ + uint64_t r = 0; + + RESULT_ENSURE_GT(bound, 0); + + while (1) { + struct s2n_blob blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&blob, (void *) &r, sizeof(r))); + RESULT_GUARD(s2n_get_public_random_data(&blob)); + + /* Imagine an int was one byte and UINT_MAX was 256. If the + * caller asked for s2n_random(129, ...) we'd end up in + * trouble. Each number in the range 0...127 would be twice + * as likely as 128. That's because r == 0 % 129 -> 0, and + * r == 129 % 129 -> 0, but only r == 128 returns 128, + * r == 257 is out of range. + * + * To de-bias the dice, we discard values of r that are higher + * that the highest multiple of 'bound' an int can support. If + * bound is a uint, then in the worst case we discard 50% - 1 r's. + * But since 'bound' is an int and INT_MAX is <= UINT_MAX / 2, + * in the worst case we discard 25% - 1 r's. + */ + if (r < (UINT64_MAX - (UINT64_MAX % bound))) { + *output = r % bound; + return S2N_RESULT_OK; + } + } +} + +static int s2n_rand_init_cb_impl(void) +{ + POSIX_GUARD_RESULT(s2n_rand_device_open(&s2n_dev_urandom)); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_rand_init(void) +{ + /* Only open /dev/urandom when we actually need it for randomness. + * When libcrypto handles randomness, avoid the extra syscall and fd. + */ + if (!s2n_use_libcrypto_rand()) { + RESULT_ENSURE(s2n_rand_init_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + } + + return S2N_RESULT_OK; +} + +static int s2n_rand_cleanup_cb_impl(void) +{ + if (s2n_dev_urandom.fd == UNINITIALIZED_ENTROPY_FD) { + return S2N_SUCCESS; + } + + if (s2n_result_is_ok(s2n_rand_device_validate(&s2n_dev_urandom))) { + POSIX_GUARD(close(s2n_dev_urandom.fd)); + } + s2n_dev_urandom.fd = UNINITIALIZED_ENTROPY_FD; + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_rand_cleanup(void) +{ + if (!s2n_use_libcrypto_rand()) { + RESULT_ENSURE(s2n_rand_cleanup_cb_impl() >= S2N_SUCCESS, S2N_ERR_CANCELLED); + } + + return S2N_RESULT_OK; +} + +int s2n_rand_set_callbacks(s2n_rand_init_callback rand_init_callback, + s2n_rand_cleanup_callback rand_cleanup_callback, + s2n_rand_seed_callback rand_seed_callback, + s2n_rand_mix_callback rand_mix_callback) +{ + /* Custom random callbacks are no longer supported. Randomness is now + * delegated directly to libcrypto or /dev/urandom. This stub is kept + * for backwards compatibility. + */ + (void) rand_init_callback; + (void) rand_cleanup_callback; + (void) rand_seed_callback; + (void) rand_mix_callback; + return S2N_SUCCESS; +} + #endif \ No newline at end of file diff --git a/utils/s2n_random.h b/utils/s2n_random.h index 4aa6cc6ff39..eece58b40a2 100644 --- a/utils/s2n_random.h +++ b/utils/s2n_random.h @@ -1,37 +1,37 @@ -/* - * 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 - -#include "utils/s2n_blob.h" -#include "utils/s2n_result.h" - -#if !defined(_MSC_VER) -struct s2n_rand_device { - const char *source; - int fd; - dev_t dev; - ino_t ino; - mode_t mode; - dev_t rdev; -}; -#endif - -S2N_RESULT s2n_rand_init(void); -S2N_RESULT s2n_rand_cleanup(void); -S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob); -S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob); -S2N_RESULT s2n_public_random(int64_t max, uint64_t *output); -bool s2n_use_libcrypto_rand(void); +/* + * 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 + +#include "utils/s2n_blob.h" +#include "utils/s2n_result.h" + +#if !defined(_MSC_VER) +struct s2n_rand_device { + const char *source; + int fd; + dev_t dev; + ino_t ino; + mode_t mode; + dev_t rdev; +}; +#endif + +S2N_RESULT s2n_rand_init(void); +S2N_RESULT s2n_rand_cleanup(void); +S2N_RESULT s2n_get_public_random_data(struct s2n_blob *blob); +S2N_RESULT s2n_get_private_random_data(struct s2n_blob *blob); +S2N_RESULT s2n_public_random(int64_t max, uint64_t *output); +bool s2n_use_libcrypto_rand(void); diff --git a/utils/s2n_rfc5952.c b/utils/s2n_rfc5952.c index d3a56b94775..6a5fad9bcbe 100644 --- a/utils/s2n_rfc5952.c +++ b/utils/s2n_rfc5952.c @@ -1,138 +1,138 @@ -/* - * 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 "utils/s2n_rfc5952.h" - -#if defined(_MSC_VER) -#include -#include -#endif - - -#include -#if !defined(_MSC_VER) -#include -#endif -#include - -#include "error/s2n_errno.h" -#include "utils/s2n_safety.h" - -static uint8_t dec[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; -static uint8_t hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - -S2N_RESULT s2n_inet_ntop(int af, const void *addr, struct s2n_blob *dst) -{ - const uint8_t *bytes = addr; - uint8_t *cursor = dst->data; - - if (af == AF_INET) { - RESULT_ENSURE(dst->size >= sizeof("111.222.333.444"), S2N_ERR_SIZE_MISMATCH); - - for (int i = 0; i < 4; i++) { - if (bytes[i] / 100) { - *cursor++ = dec[bytes[i] / 100]; - } - if (bytes[i] >= 10) { - *cursor++ = dec[(bytes[i] % 100) / 10]; - } - *cursor++ = dec[(bytes[i] % 10)]; - *cursor++ = '.'; - } - - *--cursor = '\0'; - - return S2N_RESULT_OK; - } - - if (af == AF_INET6) { - RESULT_ENSURE(dst->size >= sizeof("1111:2222:3333:4444:5555:6666:7777:8888"), S2N_ERR_SIZE_MISMATCH); - - /* See Section 4 of RFC5952 for the rules we are going to follow here - * - * Here's the general algorithm: - * - * 1/ Treat the bytes as 8 16-bit fields - * 2/ Find the longest run of 16-bit fields. - * 3/ or if there are two or more equal length longest runs, go with the left-most run - * 4/ Make that run :: - * 5/ Print the remaining 16-bit fields in lowercase hex, no leading zeroes - */ - - uint16_t octets[8] = { 0 }; - - int longest_run_start = 0; - int longest_run_length = 0; - int current_run_length = 0; - - /* 2001:db8::1:0:0:1 */ - - /* Find the longest run of zeroes */ - for (int i = 0; i < 8; i++) { - octets[i] = (bytes[i * 2] << 8) + bytes[(i * 2) + 1]; - - if (octets[i]) { - current_run_length = 0; - } else { - current_run_length++; - } - - if (current_run_length > longest_run_length) { - longest_run_length = current_run_length; - longest_run_start = (i - current_run_length) + 1; - } - } - - for (int i = 0; i < 8; i++) { - if (i == longest_run_start && longest_run_length > 1) { - if (i == 0) { - *cursor++ = ':'; - } - - if (longest_run_length == 8) { - *cursor++ = ':'; - } - - i += longest_run_length - 1; - - } else { - uint8_t nibbles[4] = { (octets[i] & 0xF000) >> 12, - (octets[i] & 0x0F00) >> 8, - (octets[i] & 0x00F0) >> 4, - (octets[i] & 0x000F) }; - - /* Skip up to three leading zeroes */ - int j = 0; - for (j = 0; j < 3; j++) { - if (nibbles[j]) { - break; - } - } - - for (; j < 4; j++) { - *cursor++ = hex[nibbles[j]]; - } - } - - *cursor++ = ':'; - } - - *--cursor = '\0'; - - return S2N_RESULT_OK; - } - - RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); -} +/* + * 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 "utils/s2n_rfc5952.h" + +#if defined(_MSC_VER) +#include +#include +#endif + + +#include +#if !defined(_MSC_VER) +#include +#endif +#include + +#include "error/s2n_errno.h" +#include "utils/s2n_safety.h" + +static uint8_t dec[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; +static uint8_t hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +S2N_RESULT s2n_inet_ntop(int af, const void *addr, struct s2n_blob *dst) +{ + const uint8_t *bytes = addr; + uint8_t *cursor = dst->data; + + if (af == AF_INET) { + RESULT_ENSURE(dst->size >= sizeof("111.222.333.444"), S2N_ERR_SIZE_MISMATCH); + + for (int i = 0; i < 4; i++) { + if (bytes[i] / 100) { + *cursor++ = dec[bytes[i] / 100]; + } + if (bytes[i] >= 10) { + *cursor++ = dec[(bytes[i] % 100) / 10]; + } + *cursor++ = dec[(bytes[i] % 10)]; + *cursor++ = '.'; + } + + *--cursor = '\0'; + + return S2N_RESULT_OK; + } + + if (af == AF_INET6) { + RESULT_ENSURE(dst->size >= sizeof("1111:2222:3333:4444:5555:6666:7777:8888"), S2N_ERR_SIZE_MISMATCH); + + /* See Section 4 of RFC5952 for the rules we are going to follow here + * + * Here's the general algorithm: + * + * 1/ Treat the bytes as 8 16-bit fields + * 2/ Find the longest run of 16-bit fields. + * 3/ or if there are two or more equal length longest runs, go with the left-most run + * 4/ Make that run :: + * 5/ Print the remaining 16-bit fields in lowercase hex, no leading zeroes + */ + + uint16_t octets[8] = { 0 }; + + int longest_run_start = 0; + int longest_run_length = 0; + int current_run_length = 0; + + /* 2001:db8::1:0:0:1 */ + + /* Find the longest run of zeroes */ + for (int i = 0; i < 8; i++) { + octets[i] = (bytes[i * 2] << 8) + bytes[(i * 2) + 1]; + + if (octets[i]) { + current_run_length = 0; + } else { + current_run_length++; + } + + if (current_run_length > longest_run_length) { + longest_run_length = current_run_length; + longest_run_start = (i - current_run_length) + 1; + } + } + + for (int i = 0; i < 8; i++) { + if (i == longest_run_start && longest_run_length > 1) { + if (i == 0) { + *cursor++ = ':'; + } + + if (longest_run_length == 8) { + *cursor++ = ':'; + } + + i += longest_run_length - 1; + + } else { + uint8_t nibbles[4] = { (octets[i] & 0xF000) >> 12, + (octets[i] & 0x0F00) >> 8, + (octets[i] & 0x00F0) >> 4, + (octets[i] & 0x000F) }; + + /* Skip up to three leading zeroes */ + int j = 0; + for (j = 0; j < 3; j++) { + if (nibbles[j]) { + break; + } + } + + for (; j < 4; j++) { + *cursor++ = hex[nibbles[j]]; + } + } + + *cursor++ = ':'; + } + + *--cursor = '\0'; + + return S2N_RESULT_OK; + } + + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); +} diff --git a/utils/s2n_safety.h b/utils/s2n_safety.h index 5d2cbb14422..6ae5e8b1e29 100644 --- a/utils/s2n_safety.h +++ b/utils/s2n_safety.h @@ -1,123 +1,123 @@ -/* - * 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 - -#include -#include -#include -#include - -#include "error/s2n_errno.h" -#include "utils/s2n_ensure.h" -#include "utils/s2n_result.h" -#include "utils/s2n_safety_macros.h" - -/** - * The goal of s2n_safety is to provide helpers to perform common - * checks, which help with code readability. - */ - -/** - * Marks a case of a switch statement as able to fall through to the next case - */ -#if defined(S2N_FALL_THROUGH_SUPPORTED) - #define FALL_THROUGH __attribute__((fallthrough)) -#else - #define FALL_THROUGH ((void) 0) -#endif - -int s2n_in_unit_test_set(bool is_unit); -int s2n_in_integ_test_set(bool is_integ); -bool s2n_in_unit_test(); -bool s2n_in_test(); - -/* Returns true if a and b are equal, in constant time */ -bool s2n_constant_time_equals(const uint8_t* a, const uint8_t* b, const uint32_t len); - -/* Copy src to dst, or don't copy it, in constant time */ -int s2n_constant_time_copy_or_dont(uint8_t* dst, const uint8_t* src, uint32_t len, uint8_t dont); - -/* If src contains valid PKCS#1 v1.5 padding of exactly expectlen bytes, decode - * it into dst, otherwise leave dst alone, in constant time. - * Always returns zero. */ -int s2n_constant_time_pkcs1_unpad_or_dont(uint8_t* dst, const uint8_t* src, uint32_t srclen, uint32_t expectlen); - -/** - * Runs _thecleanup function on _thealloc once _thealloc went out of scope - */ -#if defined(_MSC_VER) -#define DEFER_CLEANUP(_thealloc, _thecleanup) _thealloc -#else -#define DEFER_CLEANUP(_thealloc, _thecleanup) \ - __attribute__((cleanup(_thecleanup))) _thealloc -#endif -/** - * Often we want to free memory on an error, but not on a success. - * We do this by declaring a variable with DEFER_CLEANUP, then zeroing - * that variable after success to prevent DEFER_CLEANUP from accessing - * and freeing any memory it allocated. - * - * This pattern is not intuitive, so a named macro makes it more readable. - */ -#define ZERO_TO_DISABLE_DEFER_CLEANUP(_thealloc) memset(&_thealloc, 0, sizeof(_thealloc)) - -/* We want to apply blinding whenever `action` fails. - * Unfortunately, because functions in S2N do not have a consistent return type, determining failure is difficult. - * Instead, let's rely on the consistent error handling behavior of returning from a method early on error - * and apply blinding if our tracking variable goes out of scope early. - */ -S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection** conn); -#define WITH_ERROR_BLINDING(conn, action) \ - do { \ - DEFER_CLEANUP(struct s2n_connection* _conn_to_blind = conn, s2n_connection_apply_error_blinding); \ - action; \ - /* The `if` here is to avoid a redundantInitialization warning from cppcheck */ \ - if (_conn_to_blind) { \ - _conn_to_blind = NULL; \ - } \ - } while (0) - -/* Creates cleanup function for pointers from function func which accepts a pointer. - * This is useful for DEFER_CLEANUP as it passes &_thealloc into _thecleanup function, - * so if _thealloc is a pointer _thecleanup will receive a pointer to a pointer.*/ -#define DEFINE_POINTER_CLEANUP_FUNC(type, func) \ - static inline void func##_pointer(type* p) \ - { \ - if (p && *p) \ - func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -/* This method works for ARRAYS, not for POINTERS. - * Calling sizeof on an array declared in the current function correctly returns - * the total size of the array. But once the array is passed to another function, - * it behaves like a pointer. Calling sizeof on a pointer only returns the size - * of the pointer address itself (so usually 8). - * Newer compilers (gcc >= 8.1, clang >= 8.0) will warn if the argument is a pointer. - */ -#define s2n_array_len(array) (sizeof(array) / sizeof(array[0])) - -int s2n_mul_overflow(uint32_t a, uint32_t b, uint32_t* out); - -/** - * Rounds "initial" up to a multiple of "alignment", and stores the result in "out". - * Raises an error if overflow would occur. - * NOT CONSTANT TIME. - */ -int s2n_align_to(uint32_t initial, uint32_t alignment, uint32_t* out); -int s2n_add_overflow(uint32_t a, uint32_t b, uint32_t* out); -int s2n_sub_overflow(uint32_t a, uint32_t b, uint32_t* out); -#define S2N_ADD_IS_OVERFLOW_SAFE(a, b, max) (((max) >= (a)) && ((max) - (a) >= (b))) +/* + * 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 + +#include +#include +#include +#include + +#include "error/s2n_errno.h" +#include "utils/s2n_ensure.h" +#include "utils/s2n_result.h" +#include "utils/s2n_safety_macros.h" + +/** + * The goal of s2n_safety is to provide helpers to perform common + * checks, which help with code readability. + */ + +/** + * Marks a case of a switch statement as able to fall through to the next case + */ +#if defined(S2N_FALL_THROUGH_SUPPORTED) + #define FALL_THROUGH __attribute__((fallthrough)) +#else + #define FALL_THROUGH ((void) 0) +#endif + +int s2n_in_unit_test_set(bool is_unit); +int s2n_in_integ_test_set(bool is_integ); +bool s2n_in_unit_test(); +bool s2n_in_test(); + +/* Returns true if a and b are equal, in constant time */ +bool s2n_constant_time_equals(const uint8_t* a, const uint8_t* b, const uint32_t len); + +/* Copy src to dst, or don't copy it, in constant time */ +int s2n_constant_time_copy_or_dont(uint8_t* dst, const uint8_t* src, uint32_t len, uint8_t dont); + +/* If src contains valid PKCS#1 v1.5 padding of exactly expectlen bytes, decode + * it into dst, otherwise leave dst alone, in constant time. + * Always returns zero. */ +int s2n_constant_time_pkcs1_unpad_or_dont(uint8_t* dst, const uint8_t* src, uint32_t srclen, uint32_t expectlen); + +/** + * Runs _thecleanup function on _thealloc once _thealloc went out of scope + */ +#if defined(_MSC_VER) +#define DEFER_CLEANUP(_thealloc, _thecleanup) _thealloc +#else +#define DEFER_CLEANUP(_thealloc, _thecleanup) \ + __attribute__((cleanup(_thecleanup))) _thealloc +#endif +/** + * Often we want to free memory on an error, but not on a success. + * We do this by declaring a variable with DEFER_CLEANUP, then zeroing + * that variable after success to prevent DEFER_CLEANUP from accessing + * and freeing any memory it allocated. + * + * This pattern is not intuitive, so a named macro makes it more readable. + */ +#define ZERO_TO_DISABLE_DEFER_CLEANUP(_thealloc) memset(&_thealloc, 0, sizeof(_thealloc)) + +/* We want to apply blinding whenever `action` fails. + * Unfortunately, because functions in S2N do not have a consistent return type, determining failure is difficult. + * Instead, let's rely on the consistent error handling behavior of returning from a method early on error + * and apply blinding if our tracking variable goes out of scope early. + */ +S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection** conn); +#define WITH_ERROR_BLINDING(conn, action) \ + do { \ + DEFER_CLEANUP(struct s2n_connection* _conn_to_blind = conn, s2n_connection_apply_error_blinding); \ + action; \ + /* The `if` here is to avoid a redundantInitialization warning from cppcheck */ \ + if (_conn_to_blind) { \ + _conn_to_blind = NULL; \ + } \ + } while (0) + +/* Creates cleanup function for pointers from function func which accepts a pointer. + * This is useful for DEFER_CLEANUP as it passes &_thealloc into _thecleanup function, + * so if _thealloc is a pointer _thecleanup will receive a pointer to a pointer.*/ +#define DEFINE_POINTER_CLEANUP_FUNC(type, func) \ + static inline void func##_pointer(type* p) \ + { \ + if (p && *p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +/* This method works for ARRAYS, not for POINTERS. + * Calling sizeof on an array declared in the current function correctly returns + * the total size of the array. But once the array is passed to another function, + * it behaves like a pointer. Calling sizeof on a pointer only returns the size + * of the pointer address itself (so usually 8). + * Newer compilers (gcc >= 8.1, clang >= 8.0) will warn if the argument is a pointer. + */ +#define s2n_array_len(array) (sizeof(array) / sizeof(array[0])) + +int s2n_mul_overflow(uint32_t a, uint32_t b, uint32_t* out); + +/** + * Rounds "initial" up to a multiple of "alignment", and stores the result in "out". + * Raises an error if overflow would occur. + * NOT CONSTANT TIME. + */ +int s2n_align_to(uint32_t initial, uint32_t alignment, uint32_t* out); +int s2n_add_overflow(uint32_t a, uint32_t b, uint32_t* out); +int s2n_sub_overflow(uint32_t a, uint32_t b, uint32_t* out); +#define S2N_ADD_IS_OVERFLOW_SAFE(a, b, max) (((max) >= (a)) && ((max) - (a) >= (b))) diff --git a/utils/s2n_safety_macros.h b/utils/s2n_safety_macros.h index 1fed9b7e8bb..d628dd6714a 100644 --- a/utils/s2n_safety_macros.h +++ b/utils/s2n_safety_macros.h @@ -1,659 +1,659 @@ - -/* - * 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 - -/** - * DO NOT DIRECTLY MODIFY THIS FILE: - * - * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications - * should be in there. - */ - -/* clang-format off */ - -#include "error/s2n_errno.h" -#include "utils/s2n_ensure.h" -#include "utils/s2n_result.h" - -/** - * The goal of s2n_safety is to provide helpers to perform common - * checks, which help with code readability. - */ - -/* Success signal value for OpenSSL functions */ -#define _OSSL_SUCCESS 1 - -/** - * Sets the global `s2n_errno` to `error` and returns with an `S2N_RESULT_ERROR` - */ -#define RESULT_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR); } while (0) - -/** - * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` - */ -#define RESULT_ENSURE(condition, error) __S2N_ENSURE((condition), RESULT_BAIL(error)) - -/** - * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - */ -#define RESULT_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), RESULT_BAIL(error)) - -/** - * Ensures `s2n_result_is_ok(result)`, otherwise the function will `RESULT_BAIL` with `error` - * - * This can be useful for overriding the global `s2n_errno` - */ -#define RESULT_ENSURE_OK(result, error) __S2N_ENSURE(s2n_result_is_ok(result), RESULT_BAIL(error)) - -/** - * Ensures `a` is greater than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is less than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is greater than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is less than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `a` is not equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define RESULT_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), RESULT_BAIL(S2N_ERR_SAFETY)) - -/** - * Ensures `min <= n <= max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` - */ -#if defined(_MSC_VER) -#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - RESULT_ENSURE_GTE((n), (min)); \ - RESULT_ENSURE_LTE((n), (max)); \ - } while(0) -#else -#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - RESULT_ENSURE_GTE(__tmp_n, __tmp_min); \ - RESULT_ENSURE_LTE(__tmp_n, __tmp_max); \ - } while(0) -#endif - -/** - * Ensures `min < n < max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` - */ -#if defined(_MSC_VER) -#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - RESULT_ENSURE_GT((n), (min)); \ - RESULT_ENSURE_LT((n), (max)); \ - } while(0) -#else -#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - RESULT_ENSURE_GT(__tmp_n, __tmp_min); \ - RESULT_ENSURE_LT(__tmp_n, __tmp_max); \ - } while(0) -#endif - -/** - * Ensures `x` is a readable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` - */ -#define RESULT_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), RESULT_BAIL(S2N_ERR_NULL)) - -/** - * Ensures `x` is a mutable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` - */ -#define RESULT_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), RESULT_BAIL(S2N_ERR_NULL)) - -/** - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * `RESULT_PRECONDITION` should be used at the beginning of a function to make assertions about - * the provided arguments. By default, it is functionally equivalent to `RESULT_GUARD(result)` - * but can be altered by a testing environment to provide additional guarantees. - */ -#define RESULT_PRECONDITION(result) RESULT_GUARD(__S2N_ENSURE_PRECONDITION((result))) - -/** - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - * - * `RESULT_POSTCONDITION` should be used at the end of a function to make assertions about - * the resulting state. In debug mode, it is functionally equivalent to `RESULT_GUARD(result)`. - * In production builds, it becomes a no-op. This can also be altered by a testing environment - * to provide additional guarantees. - */ -#define RESULT_POSTCONDITION(result) RESULT_GUARD(__S2N_ENSURE_POSTCONDITION((result))) - -/** - * Performs a safer memcpy. - * - * The following checks are performed: - * - * * `destination` is non-null - * * `source` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by both the `destination` and `source` parameters, - * shall be at least `len` bytes. - */ -#define RESULT_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), RESULT_ENSURE_REF) - -/** - * Performs a safer memset - * - * The following checks are performed: - * - * * `destination` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by the `destination` parameter shall be at least - * `len` bytes. - */ -#define RESULT_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), RESULT_ENSURE_REF) - -/** - * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` - */ -#define RESULT_GUARD(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) - -/** - * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `RESULT_BAIL` with `error` - */ -#define RESULT_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, RESULT_BAIL(error)) - -/** - * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_RESULT_ERROR` - */ -#define RESULT_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) - -/** - * Ensures `(result) != NULL`, otherwise the function will return `S2N_RESULT_ERROR` - * - * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for RESULT_ENSURE_REF. - */ -#define RESULT_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Sets the global `s2n_errno` to `error` and returns with an `S2N_FAILURE` - */ -#define POSIX_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE); } while (0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` - */ -#define POSIX_ENSURE(condition, error) __S2N_ENSURE((condition), POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - */ -#define POSIX_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) > S2N_FAILURE`, otherwise the function will `POSIX_BAIL` with `error` - * - * This can be useful for overriding the global `s2n_errno` - */ -#define POSIX_ENSURE_OK(result, error) __S2N_ENSURE((result) > S2N_FAILURE, POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is not equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define POSIX_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), POSIX_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min <= n <= max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` - */ -#if defined(_MSC_VER) -#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - POSIX_ENSURE_GTE((n), (min)); \ - POSIX_ENSURE_LTE((n), (max)); \ - } while(0) -#else -#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - POSIX_ENSURE_GTE(__tmp_n, __tmp_min); \ - POSIX_ENSURE_LTE(__tmp_n, __tmp_max); \ - } while(0) -#endif - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min < n < max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` - */ -#if defined(_MSC_VER) -#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - POSIX_ENSURE_GT((n), (min)); \ - POSIX_ENSURE_LT((n), (max)); \ - } while(0) -#else -#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - POSIX_ENSURE_GT(__tmp_n, __tmp_min); \ - POSIX_ENSURE_LT(__tmp_n, __tmp_max); \ - } while(0) -#endif - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a readable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` - */ -#define POSIX_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), POSIX_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a mutable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` - */ -#define POSIX_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), POSIX_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * `POSIX_PRECONDITION` should be used at the beginning of a function to make assertions about - * the provided arguments. By default, it is functionally equivalent to `POSIX_GUARD_RESULT(result)` - * but can be altered by a testing environment to provide additional guarantees. - */ -#define POSIX_PRECONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - * - * `POSIX_POSTCONDITION` should be used at the end of a function to make assertions about - * the resulting state. In debug mode, it is functionally equivalent to `POSIX_GUARD_RESULT(result)`. - * In production builds, it becomes a no-op. This can also be altered by a testing environment - * to provide additional guarantees. - */ -#define POSIX_POSTCONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memcpy. - * - * The following checks are performed: - * - * * `destination` is non-null - * * `source` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by both the `destination` and `source` parameters, - * shall be at least `len` bytes. - */ -#define POSIX_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), POSIX_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memset - * - * The following checks are performed: - * - * * `destination` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by the `destination` parameter shall be at least - * `len` bytes. - */ -#define POSIX_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), POSIX_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_FAILURE` - */ -#define POSIX_GUARD(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` - */ -#define POSIX_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, POSIX_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_FAILURE` - */ -#define POSIX_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) != NULL`, otherwise the function will return `S2N_FAILURE` - * - * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for POSIX_ENSURE_REF. - */ -#define POSIX_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Sets the global `s2n_errno` to `error` and returns with an `NULL` - */ -#define PTR_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(NULL); } while (0) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` - */ -#define PTR_ENSURE(condition, error) __S2N_ENSURE((condition), PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - */ -#define PTR_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) != NULL`, otherwise the function will `PTR_BAIL` with `error` - * - * This can be useful for overriding the global `s2n_errno` - */ -#define PTR_ENSURE_OK(result, error) __S2N_ENSURE((result) != NULL, PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is greater than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is less than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `a` is not equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error - */ -#define PTR_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), PTR_BAIL(S2N_ERR_SAFETY)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min <= n <= max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` - */ -#if defined(_MSC_VER) -#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - PTR_ENSURE_GTE((n), (min)); \ - PTR_ENSURE_LTE((n), (max)); \ - } while(0) -#else -#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - PTR_ENSURE_GTE(__tmp_n, __tmp_min); \ - PTR_ENSURE_LTE(__tmp_n, __tmp_max); \ - } while(0) -#endif - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `min < n < max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` - */ -#if defined(_MSC_VER) -#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - PTR_ENSURE_GT((n), (min)); \ - PTR_ENSURE_LT((n), (max)); \ - } while(0) -#else -#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ - do { \ - __typeof(n) __tmp_n = ( n ); \ - __typeof(n) __tmp_min = ( min ); \ - __typeof(n) __tmp_max = ( max ); \ - PTR_ENSURE_GT(__tmp_n, __tmp_min); \ - PTR_ENSURE_LT(__tmp_n, __tmp_max); \ - } while(0) -#endif - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a readable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` - */ -#define PTR_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), PTR_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `x` is a mutable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` - */ -#define PTR_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), PTR_BAIL(S2N_ERR_NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * `PTR_PRECONDITION` should be used at the beginning of a function to make assertions about - * the provided arguments. By default, it is functionally equivalent to `PTR_GUARD_RESULT(result)` - * but can be altered by a testing environment to provide additional guarantees. - */ -#define PTR_PRECONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal - * - * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. - * In release mode, the check is removed. - * - * `PTR_POSTCONDITION` should be used at the end of a function to make assertions about - * the resulting state. In debug mode, it is functionally equivalent to `PTR_GUARD_RESULT(result)`. - * In production builds, it becomes a no-op. This can also be altered by a testing environment - * to provide additional guarantees. - */ -#define PTR_POSTCONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memcpy. - * - * The following checks are performed: - * - * * `destination` is non-null - * * `source` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by both the `destination` and `source` parameters, - * shall be at least `len` bytes. - */ -#define PTR_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), PTR_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Performs a safer memset - * - * The following checks are performed: - * - * * `destination` is non-null - * - * Callers will still need to ensure the following: - * - * * The size of the data pointed to by the `destination` parameter shall be at least - * `len` bytes. - */ -#define PTR_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), PTR_ENSURE_REF) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) != NULL`, otherwise the function will return `NULL` - */ -#define PTR_GUARD(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `PTR_BAIL` with `error` - */ -#define PTR_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, PTR_BAIL(error)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `NULL` - */ -#define PTR_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(NULL)) - -/** - * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. - * - * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `NULL` - */ -#define PTR_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(NULL)) - + +/* + * 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 + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +/* clang-format off */ + +#include "error/s2n_errno.h" +#include "utils/s2n_ensure.h" +#include "utils/s2n_result.h" + +/** + * The goal of s2n_safety is to provide helpers to perform common + * checks, which help with code readability. + */ + +/* Success signal value for OpenSSL functions */ +#define _OSSL_SUCCESS 1 + +/** + * Sets the global `s2n_errno` to `error` and returns with an `S2N_RESULT_ERROR` + */ +#define RESULT_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR); } while (0) + +/** + * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + */ +#define RESULT_ENSURE(condition, error) __S2N_ENSURE((condition), RESULT_BAIL(error)) + +/** + * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define RESULT_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), RESULT_BAIL(error)) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will `RESULT_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define RESULT_ENSURE_OK(result, error) __S2N_ENSURE(s2n_result_is_ok(result), RESULT_BAIL(error)) + +/** + * Ensures `a` is greater than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is greater than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is not equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `min <= n <= max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + RESULT_ENSURE_GTE((n), (min)); \ + RESULT_ENSURE_LTE((n), (max)); \ + } while(0) +#else +#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + RESULT_ENSURE_GTE(__tmp_n, __tmp_min); \ + RESULT_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * Ensures `min < n < max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + RESULT_ENSURE_GT((n), (min)); \ + RESULT_ENSURE_LT((n), (max)); \ + } while(0) +#else +#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + RESULT_ENSURE_GT(__tmp_n, __tmp_min); \ + RESULT_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * Ensures `x` is a readable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + */ +#define RESULT_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), RESULT_BAIL(S2N_ERR_NULL)) + +/** + * Ensures `x` is a mutable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + */ +#define RESULT_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), RESULT_BAIL(S2N_ERR_NULL)) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `RESULT_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `RESULT_GUARD(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define RESULT_PRECONDITION(result) RESULT_GUARD(__S2N_ENSURE_PRECONDITION((result))) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `RESULT_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `RESULT_GUARD(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define RESULT_POSTCONDITION(result) RESULT_GUARD(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define RESULT_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), RESULT_ENSURE_REF) + +/** + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define RESULT_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), RESULT_ENSURE_REF) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) + +/** + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `RESULT_BAIL` with `error` + */ +#define RESULT_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, RESULT_BAIL(error)) + +/** + * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) + +/** + * Ensures `(result) != NULL`, otherwise the function will return `S2N_RESULT_ERROR` + * + * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for RESULT_ENSURE_REF. + */ +#define RESULT_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_RESULT_ERROR)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Sets the global `s2n_errno` to `error` and returns with an `S2N_FAILURE` + */ +#define POSIX_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE); } while (0) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + */ +#define POSIX_ENSURE(condition, error) __S2N_ENSURE((condition), POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define POSIX_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) > S2N_FAILURE`, otherwise the function will `POSIX_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define POSIX_ENSURE_OK(result, error) __S2N_ENSURE((result) > S2N_FAILURE, POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is not equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min <= n <= max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + POSIX_ENSURE_GTE((n), (min)); \ + POSIX_ENSURE_LTE((n), (max)); \ + } while(0) +#else +#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + POSIX_ENSURE_GTE(__tmp_n, __tmp_min); \ + POSIX_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min < n < max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + POSIX_ENSURE_GT((n), (min)); \ + POSIX_ENSURE_LT((n), (max)); \ + } while(0) +#else +#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + POSIX_ENSURE_GT(__tmp_n, __tmp_min); \ + POSIX_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a readable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + */ +#define POSIX_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), POSIX_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a mutable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + */ +#define POSIX_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), POSIX_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `POSIX_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `POSIX_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define POSIX_PRECONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `POSIX_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `POSIX_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define POSIX_POSTCONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define POSIX_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), POSIX_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define POSIX_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), POSIX_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` + */ +#define POSIX_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, POSIX_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) != NULL`, otherwise the function will return `S2N_FAILURE` + * + * Does not set s2n_errno to S2N_ERR_NULL, so is NOT a direct replacement for POSIX_ENSURE_REF. + */ +#define POSIX_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(S2N_FAILURE)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Sets the global `s2n_errno` to `error` and returns with an `NULL` + */ +#define PTR_BAIL(error) do { _S2N_ERROR((error)); __S2N_ENSURE_CHECKED_RETURN(NULL); } while (0) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + */ +#define PTR_ENSURE(condition, error) __S2N_ENSURE((condition), PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define PTR_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) != NULL`, otherwise the function will `PTR_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define PTR_ENSURE_OK(result, error) __S2N_ENSURE((result) != NULL, PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is greater than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is less than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `a` is not equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min <= n <= max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + PTR_ENSURE_GTE((n), (min)); \ + PTR_ENSURE_LTE((n), (max)); \ + } while(0) +#else +#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + PTR_ENSURE_GTE(__tmp_n, __tmp_min); \ + PTR_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `min < n < max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + */ +#if defined(_MSC_VER) +#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + PTR_ENSURE_GT((n), (min)); \ + PTR_ENSURE_LT((n), (max)); \ + } while(0) +#else +#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + PTR_ENSURE_GT(__tmp_n, __tmp_min); \ + PTR_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) +#endif + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a readable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + */ +#define PTR_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), PTR_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `x` is a mutable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + */ +#define PTR_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), PTR_BAIL(S2N_ERR_NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `PTR_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `PTR_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define PTR_PRECONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `PTR_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `PTR_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define PTR_POSTCONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define PTR_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMMOVE((destination), (source), (len), PTR_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define PTR_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), PTR_ENSURE_REF) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) != NULL`, otherwise the function will return `NULL` + */ +#define PTR_GUARD(result) __S2N_ENSURE((result) != NULL, __S2N_ENSURE_CHECKED_RETURN(NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `PTR_BAIL` with `error` + */ +#define PTR_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, PTR_BAIL(error)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), __S2N_ENSURE_CHECKED_RETURN(NULL)) + +/** + * DEPRECATED: all methods (except those in s2n.h) should return s2n_result. + * + * Ensures `(result) > S2N_FAILURE`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_POSIX(result) __S2N_ENSURE((result) > S2N_FAILURE, __S2N_ENSURE_CHECKED_RETURN(NULL)) + diff --git a/utils/s2n_socket.c b/utils/s2n_socket.c index 2c7e0e8e462..b6f942d76ce 100644 --- a/utils/s2n_socket.c +++ b/utils/s2n_socket.c @@ -1,264 +1,264 @@ -/* - * 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 "utils/s2n_socket.h" - -#if defined(_MSC_VER) -#include -#include -#endif - - -#if !defined(_MSC_VER) -#include -#endif -#if !defined(_MSC_VER) -#include -#endif -#if !defined(_MSC_VER) -#include -#endif -#if !defined(_MSC_VER) -#include -#endif - -#include "tls/s2n_connection.h" -#include "utils/s2n_safety.h" - -#if TCP_CORK - #define S2N_CORK TCP_CORK - #define S2N_CORK_ON 1 - #define S2N_CORK_OFF 0 -#elif TCP_NOPUSH - #define S2N_CORK TCP_NOPUSH - #define S2N_CORK_ON 1 - #define S2N_CORK_OFF 0 -#elif TCP_NODELAY - #define S2N_CORK TCP_NODELAY - #define S2N_CORK_ON 0 - #define S2N_CORK_OFF 1 -#endif - -int s2n_socket_quickack(struct s2n_connection *conn) -{ -#ifdef TCP_QUICKACK - POSIX_ENSURE_REF(conn); - if (!conn->managed_recv_io) { - return 0; - } - - struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; - POSIX_ENSURE_REF(r_io_ctx); - if (r_io_ctx->tcp_quickack_set) { - return 0; - } - - /* Ignore the return value, if it fails it fails */ - int optval = 1; - if (setsockopt(r_io_ctx->fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == 0) { - r_io_ctx->tcp_quickack_set = 1; - } -#endif - - return 0; -} - -int s2n_socket_write_snapshot(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - socklen_t corklen = sizeof(int); - POSIX_ENSURE_REF(conn); - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - getsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, &corklen); - POSIX_ENSURE_EQ(corklen, sizeof(int)); - w_io_ctx->original_cork_is_set = 1; -#endif - - return 0; -} - -int s2n_socket_read_snapshot(struct s2n_connection *conn) -{ -#ifdef SO_RCVLOWAT - socklen_t watlen = sizeof(int); - POSIX_ENSURE_REF(conn); - struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; - POSIX_ENSURE_REF(r_io_ctx); - - getsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, &watlen); - POSIX_ENSURE_EQ(watlen, sizeof(int)); - r_io_ctx->original_rcvlowat_is_set = 1; -#endif - - return 0; -} - -int s2n_socket_write_restore(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - POSIX_ENSURE_REF(conn); - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - if (!w_io_ctx->original_cork_is_set) { - return 0; - } - setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, sizeof(w_io_ctx->original_cork_val)); - w_io_ctx->original_cork_is_set = 0; -#endif - - return 0; -} - -int s2n_socket_read_restore(struct s2n_connection *conn) -{ -#ifdef SO_RCVLOWAT - POSIX_ENSURE_REF(conn); - struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; - POSIX_ENSURE_REF(r_io_ctx); - - if (!r_io_ctx->original_rcvlowat_is_set) { - return 0; - } - setsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, sizeof(r_io_ctx->original_rcvlowat_val)); - r_io_ctx->original_rcvlowat_is_set = 0; -#endif - - return 0; -} - -int s2n_socket_was_corked(struct s2n_connection *conn) -{ - POSIX_ENSURE_REF(conn); - /* If we're not using custom I/O and a send fd has not been set yet, return false*/ - if (!conn->managed_send_io || !conn->send) { - return 0; - } - - struct s2n_socket_write_io_context *io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(io_ctx); - - return io_ctx->original_cork_val; -} - -int s2n_socket_write_cork(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - POSIX_ENSURE_REF(conn); - int optval = S2N_CORK_ON; - - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - /* Ignore the return value, if it fails it fails */ - setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); -#endif - - return 0; -} - -int s2n_socket_write_uncork(struct s2n_connection *conn) -{ -#ifdef S2N_CORK - POSIX_ENSURE_REF(conn); - int optval = S2N_CORK_OFF; - - struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; - POSIX_ENSURE_REF(w_io_ctx); - - /* Ignore the return value, if it fails it fails */ - setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); -#endif - - return 0; -} - -int s2n_socket_read(void *io_context, uint8_t *buf, uint32_t len) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(buf); - int rfd = ((struct s2n_socket_read_io_context *) io_context)->fd; - if (rfd < 0) { - errno = EBADF; - POSIX_BAIL(S2N_ERR_BAD_FD); - } - - /* Clear the quickack flag so we know to reset it */ - ((struct s2n_socket_read_io_context *) io_context)->tcp_quickack_set = 0; - - /* On success, the number of bytes read is returned. On failure, -1 is - * returned and errno is set appropriately. */ - #if defined(_MSC_VER) - ssize_t result = recv(rfd, (char *)buf, (int)len, 0); - if (result < 0) { - int wsa_err = WSAGetLastError(); - if (wsa_err == WSAEWOULDBLOCK) { errno = EWOULDBLOCK; } - else if (wsa_err == WSAECONNRESET) { errno = ECONNRESET; } - else if (wsa_err == WSAEINTR) { errno = EINTR; } - else { errno = EIO; } - } -#else - ssize_t result = read(rfd, buf, len); -#endif - POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); - return result; -} - -int s2n_socket_write(void *io_context, const uint8_t *buf, uint32_t len) -{ - POSIX_ENSURE_REF(io_context); - POSIX_ENSURE_REF(buf); - int wfd = ((struct s2n_socket_write_io_context *) io_context)->fd; - if (wfd < 0) { - errno = EBADF; - POSIX_BAIL(S2N_ERR_BAD_FD); - } - - /* On success, the number of bytes written is returned. On failure, -1 is - * returned and errno is set appropriately. */ - #if defined(_MSC_VER) - ssize_t result = send(wfd, (const char *)buf, (int)len, 0); - if (result < 0) { - int wsa_err = WSAGetLastError(); - if (wsa_err == WSAEWOULDBLOCK) { errno = EWOULDBLOCK; } - else if (wsa_err == WSAECONNRESET) { errno = ECONNRESET; } - else if (wsa_err == WSAEINTR) { errno = EINTR; } - else { errno = EIO; } - } -#else - ssize_t result = write(wfd, buf, len); -#endif - POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); - return result; -} - -int s2n_socket_is_ipv6(int fd, uint8_t *ipv6) -{ - POSIX_ENSURE_REF(ipv6); - - socklen_t len = 0; - struct sockaddr_storage addr; - len = sizeof(addr); - POSIX_GUARD(getpeername(fd, (struct sockaddr *) &addr, &len)); - - *ipv6 = 0; - if (AF_INET6 == addr.ss_family) { - *ipv6 = 1; - } - - return 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 "utils/s2n_socket.h" + +#if defined(_MSC_VER) +#include +#include +#endif + + +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +#if !defined(_MSC_VER) +#include +#endif + +#include "tls/s2n_connection.h" +#include "utils/s2n_safety.h" + +#if TCP_CORK + #define S2N_CORK TCP_CORK + #define S2N_CORK_ON 1 + #define S2N_CORK_OFF 0 +#elif TCP_NOPUSH + #define S2N_CORK TCP_NOPUSH + #define S2N_CORK_ON 1 + #define S2N_CORK_OFF 0 +#elif TCP_NODELAY + #define S2N_CORK TCP_NODELAY + #define S2N_CORK_ON 0 + #define S2N_CORK_OFF 1 +#endif + +int s2n_socket_quickack(struct s2n_connection *conn) +{ +#ifdef TCP_QUICKACK + POSIX_ENSURE_REF(conn); + if (!conn->managed_recv_io) { + return 0; + } + + struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; + POSIX_ENSURE_REF(r_io_ctx); + if (r_io_ctx->tcp_quickack_set) { + return 0; + } + + /* Ignore the return value, if it fails it fails */ + int optval = 1; + if (setsockopt(r_io_ctx->fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == 0) { + r_io_ctx->tcp_quickack_set = 1; + } +#endif + + return 0; +} + +int s2n_socket_write_snapshot(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + socklen_t corklen = sizeof(int); + POSIX_ENSURE_REF(conn); + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + getsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, &corklen); + POSIX_ENSURE_EQ(corklen, sizeof(int)); + w_io_ctx->original_cork_is_set = 1; +#endif + + return 0; +} + +int s2n_socket_read_snapshot(struct s2n_connection *conn) +{ +#ifdef SO_RCVLOWAT + socklen_t watlen = sizeof(int); + POSIX_ENSURE_REF(conn); + struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; + POSIX_ENSURE_REF(r_io_ctx); + + getsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, &watlen); + POSIX_ENSURE_EQ(watlen, sizeof(int)); + r_io_ctx->original_rcvlowat_is_set = 1; +#endif + + return 0; +} + +int s2n_socket_write_restore(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + POSIX_ENSURE_REF(conn); + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + if (!w_io_ctx->original_cork_is_set) { + return 0; + } + setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &w_io_ctx->original_cork_val, sizeof(w_io_ctx->original_cork_val)); + w_io_ctx->original_cork_is_set = 0; +#endif + + return 0; +} + +int s2n_socket_read_restore(struct s2n_connection *conn) +{ +#ifdef SO_RCVLOWAT + POSIX_ENSURE_REF(conn); + struct s2n_socket_read_io_context *r_io_ctx = (struct s2n_socket_read_io_context *) conn->recv_io_context; + POSIX_ENSURE_REF(r_io_ctx); + + if (!r_io_ctx->original_rcvlowat_is_set) { + return 0; + } + setsockopt(r_io_ctx->fd, SOL_SOCKET, SO_RCVLOWAT, &r_io_ctx->original_rcvlowat_val, sizeof(r_io_ctx->original_rcvlowat_val)); + r_io_ctx->original_rcvlowat_is_set = 0; +#endif + + return 0; +} + +int s2n_socket_was_corked(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + /* If we're not using custom I/O and a send fd has not been set yet, return false*/ + if (!conn->managed_send_io || !conn->send) { + return 0; + } + + struct s2n_socket_write_io_context *io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(io_ctx); + + return io_ctx->original_cork_val; +} + +int s2n_socket_write_cork(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + POSIX_ENSURE_REF(conn); + int optval = S2N_CORK_ON; + + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + /* Ignore the return value, if it fails it fails */ + setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); +#endif + + return 0; +} + +int s2n_socket_write_uncork(struct s2n_connection *conn) +{ +#ifdef S2N_CORK + POSIX_ENSURE_REF(conn); + int optval = S2N_CORK_OFF; + + struct s2n_socket_write_io_context *w_io_ctx = (struct s2n_socket_write_io_context *) conn->send_io_context; + POSIX_ENSURE_REF(w_io_ctx); + + /* Ignore the return value, if it fails it fails */ + setsockopt(w_io_ctx->fd, IPPROTO_TCP, S2N_CORK, &optval, sizeof(optval)); +#endif + + return 0; +} + +int s2n_socket_read(void *io_context, uint8_t *buf, uint32_t len) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(buf); + int rfd = ((struct s2n_socket_read_io_context *) io_context)->fd; + if (rfd < 0) { + errno = EBADF; + POSIX_BAIL(S2N_ERR_BAD_FD); + } + + /* Clear the quickack flag so we know to reset it */ + ((struct s2n_socket_read_io_context *) io_context)->tcp_quickack_set = 0; + + /* On success, the number of bytes read is returned. On failure, -1 is + * returned and errno is set appropriately. */ + #if defined(_MSC_VER) + ssize_t result = recv(rfd, (char *)buf, (int)len, 0); + if (result < 0) { + int wsa_err = WSAGetLastError(); + if (wsa_err == WSAEWOULDBLOCK) { errno = EWOULDBLOCK; } + else if (wsa_err == WSAECONNRESET) { errno = ECONNRESET; } + else if (wsa_err == WSAEINTR) { errno = EINTR; } + else { errno = EIO; } + } +#else + ssize_t result = read(rfd, buf, len); +#endif + POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); + return result; +} + +int s2n_socket_write(void *io_context, const uint8_t *buf, uint32_t len) +{ + POSIX_ENSURE_REF(io_context); + POSIX_ENSURE_REF(buf); + int wfd = ((struct s2n_socket_write_io_context *) io_context)->fd; + if (wfd < 0) { + errno = EBADF; + POSIX_BAIL(S2N_ERR_BAD_FD); + } + + /* On success, the number of bytes written is returned. On failure, -1 is + * returned and errno is set appropriately. */ + #if defined(_MSC_VER) + ssize_t result = send(wfd, (const char *)buf, (int)len, 0); + if (result < 0) { + int wsa_err = WSAGetLastError(); + if (wsa_err == WSAEWOULDBLOCK) { errno = EWOULDBLOCK; } + else if (wsa_err == WSAECONNRESET) { errno = ECONNRESET; } + else if (wsa_err == WSAEINTR) { errno = EINTR; } + else { errno = EIO; } + } +#else + ssize_t result = write(wfd, buf, len); +#endif + POSIX_ENSURE_INCLUSIVE_RANGE(INT_MIN, result, INT_MAX); + return result; +} + +int s2n_socket_is_ipv6(int fd, uint8_t *ipv6) +{ + POSIX_ENSURE_REF(ipv6); + + socklen_t len = 0; + struct sockaddr_storage addr; + len = sizeof(addr); + POSIX_GUARD(getpeername(fd, (struct sockaddr *) &addr, &len)); + + *ipv6 = 0; + if (AF_INET6 == addr.ss_family) { + *ipv6 = 1; + } + + return 0; +} diff --git a/win_shim/mmap-windows.c b/win_shim/mmap-windows.c index 226868e9488..a1684a1388b 100644 --- a/win_shim/mmap-windows.c +++ b/win_shim/mmap-windows.c @@ -1,78 +1,78 @@ -/* mmap() replacement for Windows - * - * Author: Mike Frysinger - * Placed into the public domain - */ - -/* References: - * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx - * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx - * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx - * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx - */ - -#include -#include -#include - -#include "win_shim.h" - -void *mmap(void *start, size_t length, int prot, int flags, int fd, size_t offset) -{ - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) - return MAP_FAILED; - if (fd == -1) { - if (!(flags & MAP_ANON) || offset) - return MAP_FAILED; - } else if (flags & MAP_ANON) - return MAP_FAILED; - - DWORD flProtect; - if (prot & PROT_WRITE) { - if (prot & PROT_EXEC) - flProtect = PAGE_EXECUTE_READWRITE; - else - flProtect = PAGE_READWRITE; - } else if (prot & PROT_EXEC) { - if (prot & PROT_READ) - flProtect = PAGE_EXECUTE_READ; - else if (prot & PROT_EXEC) - flProtect = PAGE_EXECUTE; - } else - flProtect = PAGE_READONLY; - - off_t end = (off_t)length + offset; - HANDLE mmap_fd, h; - if (fd == -1) - mmap_fd = INVALID_HANDLE_VALUE; - else - mmap_fd = (HANDLE)_get_osfhandle(fd); - h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); - if (h == NULL) - return MAP_FAILED; - - DWORD dwDesiredAccess; - if (prot & PROT_WRITE) - dwDesiredAccess = FILE_MAP_WRITE; - else - dwDesiredAccess = FILE_MAP_READ; - if (prot & PROT_EXEC) - dwDesiredAccess |= FILE_MAP_EXECUTE; - if (flags & MAP_PRIVATE) - dwDesiredAccess |= FILE_MAP_COPY; - void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); - if (ret == NULL) { - CloseHandle(h); - ret = MAP_FAILED; - } - return ret; -} - -void munmap(void *addr, size_t length) -{ - UnmapViewOfFile(addr); - /* ruh-ro, we leaked handle from CreateFileMapping() ... */ -} - -#undef DWORD_HI -#undef DWORD_LO +/* mmap() replacement for Windows + * + * Author: Mike Frysinger + * Placed into the public domain + */ + +/* References: + * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx + * CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx + * MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx + * UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx + */ + +#include +#include +#include + +#include "win_shim.h" + +void *mmap(void *start, size_t length, int prot, int flags, int fd, size_t offset) +{ + if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) + return MAP_FAILED; + if (fd == -1) { + if (!(flags & MAP_ANON) || offset) + return MAP_FAILED; + } else if (flags & MAP_ANON) + return MAP_FAILED; + + DWORD flProtect; + if (prot & PROT_WRITE) { + if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_READWRITE; + } else if (prot & PROT_EXEC) { + if (prot & PROT_READ) + flProtect = PAGE_EXECUTE_READ; + else if (prot & PROT_EXEC) + flProtect = PAGE_EXECUTE; + } else + flProtect = PAGE_READONLY; + + off_t end = (off_t)length + offset; + HANDLE mmap_fd, h; + if (fd == -1) + mmap_fd = INVALID_HANDLE_VALUE; + else + mmap_fd = (HANDLE)_get_osfhandle(fd); + h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL); + if (h == NULL) + return MAP_FAILED; + + DWORD dwDesiredAccess; + if (prot & PROT_WRITE) + dwDesiredAccess = FILE_MAP_WRITE; + else + dwDesiredAccess = FILE_MAP_READ; + if (prot & PROT_EXEC) + dwDesiredAccess |= FILE_MAP_EXECUTE; + if (flags & MAP_PRIVATE) + dwDesiredAccess |= FILE_MAP_COPY; + void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length); + if (ret == NULL) { + CloseHandle(h); + ret = MAP_FAILED; + } + return ret; +} + +void munmap(void *addr, size_t length) +{ + UnmapViewOfFile(addr); + /* ruh-ro, we leaked handle from CreateFileMapping() ... */ +} + +#undef DWORD_HI +#undef DWORD_LO diff --git a/win_shim/win_shim.h b/win_shim/win_shim.h index dab5087a0bf..bac42a28261 100644 --- a/win_shim/win_shim.h +++ b/win_shim/win_shim.h @@ -1,87 +1,87 @@ -#ifndef S2N_WIN_SHIM_H -#define S2N_WIN_SHIM_H - -#ifdef WIN32 - -#include - -#include -#include -typedef SSIZE_T ssize_t; - -#ifndef SSIZE_MAX -#ifdef _WIN64 -#define SSIZE_MAX _I64_MAX -#else -#define SSIZE_MAX LONG_MAX -#endif -#endif - - -#ifndef __thread -#define __thread __declspec(thread) -#endif - - -struct iovec { - size_t iov_len; - void *iov_base; -}; - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif /* !MIN */ - -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif /* !MAX */ - -/* */ - -#define PROT_READ 0x1 -#define PROT_WRITE 0x2 -/* This flag is only available in WinXP+ */ -#ifdef FILE_MAP_EXECUTE - #define PROT_EXEC 0x4 -#else - #define PROT_EXEC 0x0 - #define FILE_MAP_EXECUTE 0 -#endif - -#define MAP_SHARED 0x01 -#define MAP_PRIVATE 0x02 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS -#define MAP_FAILED ((void *) -1) - -#ifdef __USE_FILE_OFFSET64 - # define DWORD_HI(x) (x >> 32) - # define DWORD_LO(x) ((x) & 0xffffffff) -#else - # define DWORD_HI(x) (0) - # define DWORD_LO(x) (x) -#endif - -void *mmap(void *, size_t, int, int, int, size_t); -void munmap(void *, size_t); - -/* */ - - - -#ifndef strncasecmp -#define strncasecmp _strnicmp -#endif - -#ifndef __builtin_expect -#define __builtin_expect(x, y) (x) -#endif - -#ifndef strcasecmp -#define strcasecmp _stricmp -#endif - -#endif /* WIN32 */ - -#endif /* !S2N_WIN_SHIM_H */ - +#ifndef S2N_WIN_SHIM_H +#define S2N_WIN_SHIM_H + +#ifdef WIN32 + +#include + +#include +#include +typedef SSIZE_T ssize_t; + +#ifndef SSIZE_MAX +#ifdef _WIN64 +#define SSIZE_MAX _I64_MAX +#else +#define SSIZE_MAX LONG_MAX +#endif +#endif + + +#ifndef __thread +#define __thread __declspec(thread) +#endif + + +struct iovec { + size_t iov_len; + void *iov_base; +}; + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* !MIN */ + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif /* !MAX */ + +/* */ + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +/* This flag is only available in WinXP+ */ +#ifdef FILE_MAP_EXECUTE + #define PROT_EXEC 0x4 +#else + #define PROT_EXEC 0x0 + #define FILE_MAP_EXECUTE 0 +#endif + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FAILED ((void *) -1) + +#ifdef __USE_FILE_OFFSET64 + # define DWORD_HI(x) (x >> 32) + # define DWORD_LO(x) ((x) & 0xffffffff) +#else + # define DWORD_HI(x) (0) + # define DWORD_LO(x) (x) +#endif + +void *mmap(void *, size_t, int, int, int, size_t); +void munmap(void *, size_t); + +/* */ + + + +#ifndef strncasecmp +#define strncasecmp _strnicmp +#endif + +#ifndef __builtin_expect +#define __builtin_expect(x, y) (x) +#endif + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif + +#endif /* WIN32 */ + +#endif /* !S2N_WIN_SHIM_H */ + From 5cab8b94dc446d0c9f79725193aeadd5e22cb21c Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Wed, 27 May 2026 13:05:48 +1200 Subject: [PATCH 5/6] Implement MSVC SEH to support DEFER_CLEANUP Disabling DEFER_CLEANUP via zeroes on MSVC removes critical failsafe memory management that handles early returns across the codebase. Instead of forcing upstream macros to compile as C++ RAII or stripping them entirely, we implement a python AST-rewriting pass. fix_defer_cleanup.py intercepts DEFER_CLEANUP calls during build setup. It copies the source files, parses the variable initialization and cleanup function, and dynamically wraps the remainder of the scope in a native MSVC \__try { ... } __finally { cleanup_fn(&var); }\ block. CMakeLists.txt is then patched to source these rewritten C files, ensuring perfect runtime safety without modifying any original code. Includes fix for conditionally compiled DEFER_CLEANUP scopes and vcpkg.json baseline requirement. --- CMakeLists.txt | 36 +++++------ fix_defer_cleanup.py | 139 +++++++++++++++++++++++++++++++++++++++++++ vcpkg.json | 5 +- 3 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 fix_defer_cleanup.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 91722dec136..3eb59e18669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,20 +51,20 @@ option(SECCOMP "Link with seccomp and run seccomp tests" OFF) file(GLOB API_HEADERS "api/*.h") file(GLOB API_UNSTABLE_HEADERS "api/unstable/*.h") -file(GLOB CRYPTO_HEADERS "crypto/*.h") -file(GLOB CRYPTO_SRC "crypto/*.c") +file(GLOB CRYPTO_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/crypto/*.h") +file(GLOB CRYPTO_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/crypto/*.c") -file(GLOB ERROR_HEADERS "error/*.h") -file(GLOB ERROR_SRC "error/*.c") +file(GLOB ERROR_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/error/*.h") +file(GLOB ERROR_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/error/*.c") -file(GLOB STUFFER_HEADERS "stuffer/*.h") -file(GLOB STUFFER_SRC "stuffer/*.c") +file(GLOB STUFFER_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/stuffer/*.h") +file(GLOB STUFFER_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/stuffer/*.c") -file(GLOB_RECURSE TLS_HEADERS "tls/*.h") -file(GLOB_RECURSE TLS_SRC "tls/*.c") +file(GLOB_RECURSE TLS_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tls/*.h") +file(GLOB_RECURSE TLS_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tls/*.c") -file(GLOB UTILS_HEADERS "utils/*.h") -file(GLOB UTILS_SRC "utils/*.c") +file(GLOB UTILS_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/utils/*.h") +file(GLOB UTILS_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/utils/*.c") if (WINDOWS) list(APPEND UTILS_HEADERS "win_shim/win_shim.h") list(APPEND UTILS_SRC "win_shim/mmap-windows.c") @@ -502,7 +502,7 @@ endif() target_link_libraries(${PROJECT_NAME} PUBLIC ${OS_LIBS} m) -target_include_directories(${PROJECT_NAME} PUBLIC $) +target_include_directories(${PROJECT_NAME} PUBLIC $ $) target_include_directories(${PROJECT_NAME} PUBLIC $ $) if (BUILD_TESTING AND NOT MSVC) @@ -512,7 +512,7 @@ if (BUILD_TESTING AND NOT MSVC) ################### build testlib (utility library) ######################## ############################################################################ - file(GLOB TESTLIB_SRC "tests/testlib/*.c") + file(GLOB TESTLIB_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tests/testlib/*.c") file(GLOB EXAMPLES_SRC "docs/examples/*.c") add_library(testss2n STATIC ${TESTLIB_SRC} ${EXAMPLES_SRC}) @@ -599,7 +599,7 @@ if (BUILD_TESTING AND NOT MSVC) ############################ build unit tests ############################## ############################################################################ - file(GLOB UNITTESTS_SRC "tests/unit/*.c") + file(GLOB UNITTESTS_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tests/unit/*.c") foreach(test_case ${UNITTESTS_SRC}) # NAME_WE: name without extension get_filename_component(test_case_name ${test_case} NAME_WE) @@ -646,15 +646,15 @@ if (BUILD_TESTING AND NOT MSVC) ############################################################################ if (NOT MSVC) - add_executable(s2nc "bin/s2nc.c" "bin/echo.c" "bin/https.c" "bin/common.c") + add_executable(s2nc "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/s2nc.c" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/echo.c" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/https.c" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/common.c") target_link_libraries(s2nc ${PROJECT_NAME}) target_compile_options(s2nc PRIVATE -std=gnu99) - add_executable(s2nd "bin/s2nd.c" "bin/echo.c" "bin/https.c" "bin/common.c") + add_executable(s2nd "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/s2nd.c" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/echo.c" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/https.c" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/common.c") target_link_libraries(s2nd ${PROJECT_NAME}) target_compile_options(s2nd PRIVATE -std=gnu99) - add_executable(policy "bin/policy.c") + add_executable(policy "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/bin/policy.c") target_link_libraries(policy ${PROJECT_NAME}) target_compile_options(policy PRIVATE -std=gnu99) @@ -719,8 +719,8 @@ if (NOT MSVC) set(SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/runFuzzTest.sh") file(GLOB FUZZ_TEST_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/tests/fuzz/*.c") - file(GLOB TESTLIB_SRC "tests/testlib/*.c") - file(GLOB TESTLIB_HEADERS "tests/testlib/*.h" "tests/s2n_test.h") + file(GLOB TESTLIB_SRC "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tests/testlib/*.c") + file(GLOB TESTLIB_HEADERS "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tests/testlib/*.h" "${CMAKE_CURRENT_LIST_DIR}/../auto-win-msvc/rewritten_src/tests/s2n_test.h") # This must be a shared object so that symbols can be overridden by the # fuzz test specific LD_PRELOAD libraries. diff --git a/fix_defer_cleanup.py b/fix_defer_cleanup.py new file mode 100644 index 00000000000..00a6c613757 --- /dev/null +++ b/fix_defer_cleanup.py @@ -0,0 +1,139 @@ +import os +import shutil +import re + +def process_content(path, content): + if "s2n_cert_authorities.c" in path: + content = content.replace('#if S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST\n DEFER_CLEANUP', '#if S2N_LIBCRYPTO_SUPPORTS_X509_STORE_LIST\n{\n DEFER_CLEANUP') + content = content.replace('return S2N_RESULT_OK;\n#else', 'return S2N_RESULT_OK;\n}\n#else') + + # Remove #else for EVP_APIS_SUPPORTED since MSVC build is OpenSSL 3.0 + if "s2n_ecc_evp.c" in path: + lines = content.split('\n') + out_lines = [] + in_evp = False + in_else_evp = False + for line in lines: + if line.startswith("#if EVP_APIS_SUPPORTED"): + in_evp = True + out_lines.append(line) + elif line.startswith("#else") and in_evp: + in_else_evp = True + elif line.startswith("#endif") and in_evp: + in_else_evp = False + in_evp = False + out_lines.append(line) + else: + if not in_else_evp: + out_lines.append(line) + content = '\n'.join(out_lines) + + while True: + match = re.search(r'DEFER_CLEANUP\s*\(', content) + if not match: + break + + start_idx = match.start() + + p_count = 0 + arg_start = match.end() + end_idx = -1 + for i in range(arg_start, len(content)): + if content[i] == '(': + p_count += 1 + elif content[i] == ')': + if p_count == 0: + end_idx = i + break + p_count -= 1 + + if end_idx == -1: + break + + args_str = content[arg_start:end_idx] + + last_comma = args_str.rfind(',') + if last_comma == -1: + break + + arg1 = args_str[:last_comma].strip() + arg2 = args_str[last_comma+1:].strip() + + var_decl_part = arg1.split('=')[0].strip() + var_name_match = re.search(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*$', var_decl_part.replace('*', ' ')) + if not var_name_match: + break + var_name = var_name_match.group(1) + + semi_idx = content.find(';', end_idx) + if semi_idx == -1: + break + + b_count = 0 + block_end_idx = -1 + for i in range(semi_idx + 1, len(content)): + if content[i] == '{': + b_count += 1 + elif content[i] == '}': + if b_count == 0: + block_end_idx = i + break + b_count -= 1 + + if block_end_idx == -1: + break + + new_content = content[:start_idx] + arg1 + ";\n__try {" + content[semi_idx+1:block_end_idx] + f"}} __finally {{ {arg2}(&{var_name}); }}\n" + content[block_end_idx:] + content = new_content + + return content + +def main(): + src_dirs = ["crypto", "error", "stuffer", "tls", "utils", "bin", "tests"] + out_base = "../auto-win-msvc/rewritten_src" + + if not os.path.exists(out_base): + os.makedirs(out_base) + + for src_dir in src_dirs: + if not os.path.exists(src_dir): + continue + out_dir = os.path.join(out_base, src_dir) + if os.path.exists(out_dir): + shutil.rmtree(out_dir) + shutil.copytree(src_dir, out_dir) + + for root, dirs, files in os.walk(out_dir): + for file in files: + if file.endswith(".c") or file.endswith(".h"): + path = os.path.join(root, file) + with open(path, "r", encoding="utf-8") as f: + content = f.read() + + # Pre-process content, whether it has DEFER_CLEANUP or not, to fix braces + new_content = process_content(path, content) + if new_content != content: + with open(path, "w", encoding="utf-8") as f: + f.write(new_content) + + with open("CMakeLists.txt", "r", encoding="utf-8") as f: + cmake_content = f.read() + + for src_dir in src_dirs: + cmake_content = re.sub( + fr'(?)', + 'target_include_directories(${PROJECT_NAME} PUBLIC $ $)' + ) + + with open("CMakeLists.txt", "w", encoding="utf-8") as f: + f.write(cmake_content) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index ac28683f597..b936450c0e5 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,5 +4,6 @@ "dependencies": [ "openssl", "pthreads" - ] -} \ No newline at end of file + ], + "builtin-baseline": "9b965a116838c6cdcd36bca60d1b81b030c8ab8d" +} From 344c17c7427f05e1761fd3d3d742a610995d8e3c Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Fri, 29 May 2026 10:56:31 +1200 Subject: [PATCH 6/6] ci: remove legacy and unused codebuild scripts --- .../fuzz/fuzz_targets/psk_client_hello.rs | 12 - .../fuzz/fuzz_targets/fuzz_cert_parse.rs | 10 - codebuild/README.md | 41 -- codebuild/bin/KWStyle.xml | 16 - codebuild/bin/apache2/apache2.conf | 218 ---------- codebuild/bin/apache2/ports.conf | 7 - .../apache2/sites-enabled/renegotiate.conf | 54 --- .../www/change_cipher_suite/index.html | 8 - codebuild/bin/apache2/www/html/index.html | 18 - .../bin/apache2/www/mutual_auth/index.html | 8 - codebuild/bin/build_aws_crt_cpp.sh | 56 --- codebuild/bin/clang_format_changed_files.sh | 27 -- codebuild/bin/coverage_report.sh | 34 -- codebuild/bin/cpp_style_comment_linter.sh | 36 -- codebuild/bin/cppcheck_suppressions.txt | 17 - codebuild/bin/format.sh | 27 -- codebuild/bin/fuzz_corpus_download.sh | 33 -- codebuild/bin/fuzz_corpus_upload.sh | 25 -- codebuild/bin/fuzz_coverage_report.sh | 75 ---- codebuild/bin/grep_simple_mistakes.sh | 302 -------------- codebuild/bin/header_mistake_scanner.sh | 58 --- codebuild/bin/install_al_dependencies.sh | 108 ----- codebuild/bin/install_apache2.sh | 82 ---- codebuild/bin/install_awslc.sh | 67 --- codebuild/bin/install_awslc_fips.sh | 85 ---- codebuild/bin/install_awslc_fips_2022.sh | 20 - codebuild/bin/install_awslc_fips_2024.sh | 18 - codebuild/bin/install_boringssl.sh | 63 --- codebuild/bin/install_clang.sh | 66 --- codebuild/bin/install_cppcheck.sh | 47 --- codebuild/bin/install_ctverif.sh | 45 -- codebuild/bin/install_default_dependencies.sh | 146 ------- codebuild/bin/install_gnutls37.sh | 76 ---- codebuild/bin/install_libressl.sh | 37 -- codebuild/bin/install_openssl_1_0_2.sh | 61 --- codebuild/bin/install_openssl_1_0_2_fips.sh | 82 ---- codebuild/bin/install_openssl_1_1_1.sh | 64 --- codebuild/bin/install_openssl_3_0.sh | 94 ----- codebuild/bin/install_prlimit.sh | 56 --- codebuild/bin/install_python.sh | 36 -- codebuild/bin/install_s2n_head.sh | 79 ---- codebuild/bin/install_saw.sh | 45 -- codebuild/bin/install_shellcheck.sh | 47 --- codebuild/bin/install_sidetrail.sh | 49 --- .../bin/install_sidetrail_dependencies.sh | 67 --- codebuild/bin/install_sslyze.sh | 37 -- codebuild/bin/install_ubuntu_dependencies.sh | 68 ---- codebuild/bin/install_z3_yices.sh | 47 --- codebuild/bin/jobs.sh | 23 -- codebuild/bin/run_cppcheck.sh | 40 -- codebuild/bin/run_ctverif.sh | 60 --- codebuild/bin/run_kwstyle.sh | 40 -- codebuild/bin/run_sidetrail.sh | 71 ---- codebuild/bin/s2n_apache2.sh | 50 --- codebuild/bin/s2n_codebuild.sh | 113 ------ codebuild/bin/s2n_codebuild_al.sh | 49 --- codebuild/bin/s2n_dynamic_load_test.c | 112 ----- codebuild/bin/s2n_fips_openssl.cnf | 383 ------------------ .../bin/s2n_install_test_dependencies.sh | 44 -- codebuild/bin/s2n_open_fds_test.py | 65 --- codebuild/bin/s2n_override_paths.sh | 21 - codebuild/bin/s2n_set_build_preset.sh | 83 ---- codebuild/bin/s2n_setup_env.sh | 223 ---------- codebuild/bin/setup_ec2.sh | 67 --- codebuild/bin/start_codebuild.sh | 67 --- codebuild/bin/test_dynamic_load.sh | 56 --- codebuild/bin/test_exec_leak.sh | 99 ----- .../bin/test_install_shared_and_static.sh | 133 ------ codebuild/bin/test_libcrypto_interning.sh | 176 -------- codebuild/bin/utils.sh | 53 --- .../spec/buildspec_32bit_cross_compile.yml | 31 -- codebuild/spec/buildspec_amazonlinux.yml | 35 -- .../spec/buildspec_disable_rand_override.yml | 57 --- codebuild/spec/buildspec_fuzz.yml | 36 -- codebuild/spec/buildspec_fuzz_batch.yml | 81 ---- codebuild/spec/buildspec_fuzz_scheduled.yml | 53 --- codebuild/spec/buildspec_generalbatch.yml | 316 --------------- codebuild/spec/buildspec_integ_rust.yml | 168 -------- codebuild/spec/buildspec_integv2_nix.yml | 139 ------- codebuild/spec/buildspec_ktls.yml | 55 --- codebuild/spec/buildspec_ktls_keyupdate.yml | 42 -- codebuild/spec/buildspec_mem.yml | 46 --- codebuild/spec/buildspec_musl.yml | 46 --- codebuild/spec/buildspec_s2n_tls_bench.yml | 53 --- codebuild/spec/buildspec_sanitizer.yml | 150 ------- codebuild/spec/buildspec_sidetrail.yml | 34 -- codebuild/spec/buildspec_timing.yml | 36 -- codebuild/spec/buildspec_tsan.yml | 31 -- codebuild/spec/buildspec_ubuntu.yml | 33 -- codebuild/spec/buildspec_ubuntu_cmake.yml | 35 -- .../spec/buildspec_ubuntu_integrationv2.yml | 68 ---- codebuild/spec/buildspec_unit_coverage.yml | 45 -- codebuild/spec/buildspec_unit_nix.yml | 181 --------- codebuild/spec/buildspec_valgrind.yml | 95 ----- libcrypto-build/README.md | 4 - 95 files changed, 6672 deletions(-) delete mode 100644 bindings/rust/aws-kms-tls-auth/fuzz/fuzz_targets/psk_client_hello.rs delete mode 100644 bindings/rust/standard/s2n-tls-metrics-subscriber/fuzz/fuzz_targets/fuzz_cert_parse.rs delete mode 100644 codebuild/README.md delete mode 100644 codebuild/bin/KWStyle.xml delete mode 100644 codebuild/bin/apache2/apache2.conf delete mode 100644 codebuild/bin/apache2/ports.conf delete mode 100644 codebuild/bin/apache2/sites-enabled/renegotiate.conf delete mode 100644 codebuild/bin/apache2/www/change_cipher_suite/index.html delete mode 100755 codebuild/bin/apache2/www/html/index.html delete mode 100644 codebuild/bin/apache2/www/mutual_auth/index.html delete mode 100755 codebuild/bin/build_aws_crt_cpp.sh delete mode 100755 codebuild/bin/clang_format_changed_files.sh delete mode 100755 codebuild/bin/coverage_report.sh delete mode 100755 codebuild/bin/cpp_style_comment_linter.sh delete mode 100644 codebuild/bin/cppcheck_suppressions.txt delete mode 100755 codebuild/bin/format.sh delete mode 100755 codebuild/bin/fuzz_corpus_download.sh delete mode 100755 codebuild/bin/fuzz_corpus_upload.sh delete mode 100755 codebuild/bin/fuzz_coverage_report.sh delete mode 100755 codebuild/bin/grep_simple_mistakes.sh delete mode 100755 codebuild/bin/header_mistake_scanner.sh delete mode 100755 codebuild/bin/install_al_dependencies.sh delete mode 100755 codebuild/bin/install_apache2.sh delete mode 100755 codebuild/bin/install_awslc.sh delete mode 100755 codebuild/bin/install_awslc_fips.sh delete mode 100755 codebuild/bin/install_awslc_fips_2022.sh delete mode 100755 codebuild/bin/install_awslc_fips_2024.sh delete mode 100755 codebuild/bin/install_boringssl.sh delete mode 100755 codebuild/bin/install_clang.sh delete mode 100755 codebuild/bin/install_cppcheck.sh delete mode 100755 codebuild/bin/install_ctverif.sh delete mode 100755 codebuild/bin/install_default_dependencies.sh delete mode 100755 codebuild/bin/install_gnutls37.sh delete mode 100755 codebuild/bin/install_libressl.sh delete mode 100755 codebuild/bin/install_openssl_1_0_2.sh delete mode 100755 codebuild/bin/install_openssl_1_0_2_fips.sh delete mode 100755 codebuild/bin/install_openssl_1_1_1.sh delete mode 100755 codebuild/bin/install_openssl_3_0.sh delete mode 100755 codebuild/bin/install_prlimit.sh delete mode 100755 codebuild/bin/install_python.sh delete mode 100755 codebuild/bin/install_s2n_head.sh delete mode 100755 codebuild/bin/install_saw.sh delete mode 100755 codebuild/bin/install_shellcheck.sh delete mode 100755 codebuild/bin/install_sidetrail.sh delete mode 100755 codebuild/bin/install_sidetrail_dependencies.sh delete mode 100755 codebuild/bin/install_sslyze.sh delete mode 100755 codebuild/bin/install_ubuntu_dependencies.sh delete mode 100755 codebuild/bin/install_z3_yices.sh delete mode 100644 codebuild/bin/jobs.sh delete mode 100755 codebuild/bin/run_cppcheck.sh delete mode 100755 codebuild/bin/run_ctverif.sh delete mode 100755 codebuild/bin/run_kwstyle.sh delete mode 100755 codebuild/bin/run_sidetrail.sh delete mode 100644 codebuild/bin/s2n_apache2.sh delete mode 100755 codebuild/bin/s2n_codebuild.sh delete mode 100755 codebuild/bin/s2n_codebuild_al.sh delete mode 100644 codebuild/bin/s2n_dynamic_load_test.c delete mode 100644 codebuild/bin/s2n_fips_openssl.cnf delete mode 100755 codebuild/bin/s2n_install_test_dependencies.sh delete mode 100644 codebuild/bin/s2n_open_fds_test.py delete mode 100755 codebuild/bin/s2n_override_paths.sh delete mode 100755 codebuild/bin/s2n_set_build_preset.sh delete mode 100755 codebuild/bin/s2n_setup_env.sh delete mode 100755 codebuild/bin/setup_ec2.sh delete mode 100755 codebuild/bin/start_codebuild.sh delete mode 100755 codebuild/bin/test_dynamic_load.sh delete mode 100755 codebuild/bin/test_exec_leak.sh delete mode 100755 codebuild/bin/test_install_shared_and_static.sh delete mode 100755 codebuild/bin/test_libcrypto_interning.sh delete mode 100755 codebuild/bin/utils.sh delete mode 100644 codebuild/spec/buildspec_32bit_cross_compile.yml delete mode 100644 codebuild/spec/buildspec_amazonlinux.yml delete mode 100644 codebuild/spec/buildspec_disable_rand_override.yml delete mode 100644 codebuild/spec/buildspec_fuzz.yml delete mode 100644 codebuild/spec/buildspec_fuzz_batch.yml delete mode 100644 codebuild/spec/buildspec_fuzz_scheduled.yml delete mode 100644 codebuild/spec/buildspec_generalbatch.yml delete mode 100644 codebuild/spec/buildspec_integ_rust.yml delete mode 100644 codebuild/spec/buildspec_integv2_nix.yml delete mode 100644 codebuild/spec/buildspec_ktls.yml delete mode 100644 codebuild/spec/buildspec_ktls_keyupdate.yml delete mode 100644 codebuild/spec/buildspec_mem.yml delete mode 100644 codebuild/spec/buildspec_musl.yml delete mode 100644 codebuild/spec/buildspec_s2n_tls_bench.yml delete mode 100644 codebuild/spec/buildspec_sanitizer.yml delete mode 100644 codebuild/spec/buildspec_sidetrail.yml delete mode 100644 codebuild/spec/buildspec_timing.yml delete mode 100644 codebuild/spec/buildspec_tsan.yml delete mode 100644 codebuild/spec/buildspec_ubuntu.yml delete mode 100644 codebuild/spec/buildspec_ubuntu_cmake.yml delete mode 100644 codebuild/spec/buildspec_ubuntu_integrationv2.yml delete mode 100644 codebuild/spec/buildspec_unit_coverage.yml delete mode 100644 codebuild/spec/buildspec_unit_nix.yml delete mode 100644 codebuild/spec/buildspec_valgrind.yml delete mode 100644 libcrypto-build/README.md diff --git a/bindings/rust/aws-kms-tls-auth/fuzz/fuzz_targets/psk_client_hello.rs b/bindings/rust/aws-kms-tls-auth/fuzz/fuzz_targets/psk_client_hello.rs deleted file mode 100644 index 1757b767266..00000000000 --- a/bindings/rust/aws-kms-tls-auth/fuzz/fuzz_targets/psk_client_hello.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#![no_main] - -use aws_kms_tls_auth::DecodeValue; -use aws_kms_tls_auth::PresharedKeyClientHello; -use libfuzzer_sys::fuzz_target; - -fuzz_target!(|data: &[u8]| { - let _ = PresharedKeyClientHello::decode_from(data); -}); diff --git a/bindings/rust/standard/s2n-tls-metrics-subscriber/fuzz/fuzz_targets/fuzz_cert_parse.rs b/bindings/rust/standard/s2n-tls-metrics-subscriber/fuzz/fuzz_targets/fuzz_cert_parse.rs deleted file mode 100644 index cf1b5ba77e9..00000000000 --- a/bindings/rust/standard/s2n-tls-metrics-subscriber/fuzz/fuzz_targets/fuzz_cert_parse.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#![no_main] -use libfuzzer_sys::fuzz_target; - -fuzz_target!(|data: &[u8]| { - // parse must never panic, regardless of input - let _ = s2n_tls_metrics_subscriber::parsing::cert::parse(data); -}); diff --git a/codebuild/README.md b/codebuild/README.md deleted file mode 100644 index 313cfdaa4d8..00000000000 --- a/codebuild/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Docker Image Structure -The codebuild specifications are run on a custom docker images that have the test dependencies installed. The docker image structure is described below. - -### libcrypto -Various libcryptos are installed to `/usr/local/$LIBCRYPTO` directories. For example -``` -# non-exhaustive list -/usr/local/openssl-1.0.2/lib/libcrypto.a -/usr/local/openssl-1.0.2/lib/libcrypto.so -/usr/local/openssl-1.0.2/lib/libcrypto.so.1.0.0 -/usr/local/openssl-1.0.2/lib/pkgconfig/libcrypto.pc -/usr/local/openssl-3.0/lib64/libcrypto.a -/usr/local/openssl-3.0/lib64/libcrypto.so.3 -/usr/local/openssl-3.0/lib64/libcrypto.so -/usr/local/openssl-3.0/lib64/pkgconfig/libcrypto.pc -/usr/local/boringssl/lib/libcrypto.so -/usr/local/awslc/lib/libcrypto.a -/usr/local/awslc/lib/libcrypto.so -``` - -Packages installed from the `apt` package manager can generally be found in `/usr/lib`. For example, our 32 bit build uses the 32 bit `i386` libcrypto, and it's artifacts are located at -``` -/usr/lib/i386-linux-gnu/libcrypto.a -/usr/lib/i386-linux-gnu/libcrypto.so.3 -/usr/lib/i386-linux-gnu/libcrypto.so -/usr/lib/i386-linux-gnu/pkgconfig/libcrypto.pc -``` - -When the docker image is available locally, the structure can be easily examined by attaching an interactive terminal to the container with the following command -``` -docker run --entrypoint /bin/bash -it --privileged -``` - -Then the `find` command can be used to look at the various artifacts that are available. -``` -sudo find / -name libcrypto* # list all libcrypto artifacts -``` -or -``` -sudo find / -name clang* # find all clang binaries -``` \ No newline at end of file diff --git a/codebuild/bin/KWStyle.xml b/codebuild/bin/KWStyle.xml deleted file mode 100644 index d4fc9c75874..00000000000 --- a/codebuild/bin/KWStyle.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - 256 - - - [A-Za-z=_+] - 512 - - diff --git a/codebuild/bin/apache2/apache2.conf b/codebuild/bin/apache2/apache2.conf deleted file mode 100644 index 09ff4d6240d..00000000000 --- a/codebuild/bin/apache2/apache2.conf +++ /dev/null @@ -1,218 +0,0 @@ -# This is the main Apache server configuration file. It contains the -# configuration directives that give the server its instructions. -# See http://httpd.apache.org/docs/2.4/ for detailed information about -# the directives and /usr/share/doc/apache2/README.Debian about Debian specific -# hints. -# -# -# Summary of how the Apache 2 configuration works in Debian: -# The Apache 2 web server configuration in Debian is quite different to -# upstream's suggested way to configure the web server. This is because Debian's -# default Apache2 installation attempts to make adding and removing modules, -# virtual hosts, and extra configuration directives as flexible as possible, in -# order to make automating the changes and administering the server as easy as -# possible. - -# It is split into several files forming the configuration hierarchy outlined -# below, all located in the /etc/apache2/ directory: -# -# /etc/apache2/ -# |-- apache2.conf -# | `-- ports.conf -# |-- mods-enabled -# | |-- *.load -# | `-- *.conf -# |-- conf-enabled -# | `-- *.conf -# `-- sites-enabled -# `-- *.conf -# -# -# * apache2.conf is the main configuration file (this file). It puts the pieces -# together by including all remaining configuration files when starting up the -# web server. -# -# * ports.conf is always included from the main configuration file. It is -# supposed to determine listening ports for incoming connections which can be -# customized anytime. -# -# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/ -# directories contain particular configuration snippets which manage modules, -# global configuration fragments, or virtual host configurations, -# respectively. -# -# They are activated by symlinking available configuration files from their -# respective *-available/ counterparts. These should be managed by using our -# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See -# their respective man pages for detailed information. -# -# * The binary is called apache2. Due to the use of environment variables, in -# the default configuration, apache2 needs to be started/stopped with -# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not -# work with the default configuration. - - -# Global configuration -# - -# -# ServerRoot: The top of the directory tree under which the server's -# configuration, error, and log files are kept. -# -# NOTE! If you intend to place this on an NFS (or otherwise network) -# mounted filesystem then please read the Mutex documentation (available -# at ); -# you will save yourself a lot of trouble. -# -# Do NOT add a slash at the end of the directory path. -# -ServerRoot ${APACHE_SERVER_ROOT} - -# -# The accept serialization lock file MUST BE STORED ON A LOCAL DISK. -# -#Mutex file:${APACHE_LOCK_DIR} default - -# -# The directory where shm and other runtime files will be stored. -# -DefaultRuntimeDir ${APACHE_RUN_DIR} - -# -# PidFile: The file in which the server should record its process -# identification number when it starts. -# This needs to be set in /etc/apache2/envvars -# -PidFile ${APACHE_PID_FILE} - -# -# Timeout: The number of seconds before receives and sends time out. -# -Timeout 60 - -# -# KeepAlive: Whether or not to allow persistent connections (more than -# one request per connection). Set to "Off" to deactivate. -# -KeepAlive On - -# -# MaxKeepAliveRequests: The maximum number of requests to allow -# during a persistent connection. Set to 0 to allow an unlimited amount. -# We recommend you leave this number high, for maximum performance. -# -MaxKeepAliveRequests 100 - -# -# KeepAliveTimeout: Number of seconds to wait for the next request from the -# same client on the same connection. -# -KeepAliveTimeout 5 - - -# These need to be set in /etc/apache2/envvars -User ${APACHE_RUN_USER} -Group ${APACHE_RUN_GROUP} - -# -# HostnameLookups: Log the names of clients or just their IP addresses -# e.g., www.apache.org (on) or 204.62.129.132 (off). -# The default is off because it'd be overall better for the net if people -# had to knowingly turn this feature on, since enabling it means that -# each client request will result in AT LEAST one lookup request to the -# nameserver. -# -HostnameLookups Off - -# ErrorLog: The location of the error log file. -# If you do not specify an ErrorLog directive within a -# container, error messages relating to that virtual host will be -# logged here. If you *do* define an error logfile for a -# container, that host's errors will be logged there and not here. -# -ErrorLog ${APACHE_LOG_DIR}/error.log - -# -# LogLevel: Control the severity of messages logged to the error_log. -# Available values: trace8, ..., trace1, debug, info, notice, warn, -# error, crit, alert, emerg. -# It is also possible to configure the log level for particular modules, e.g. -# "LogLevel info ssl:warn" -# -LogLevel warn - -# Include module configuration: -IncludeOptional mods-enabled/*.load -IncludeOptional mods-enabled/*.conf - -# Include list of ports to listen on -Include ports.conf - - -# Sets the default security model of the Apache2 HTTPD server. It does -# not allow access to the root filesystem outside of /usr/share and /var/www. -# The former is used by web applications packaged in Debian, -# the latter may be used for local directories served by the web server. If -# your system is serving content from a sub-directory in /srv you must allow -# access here, or in any related virtual host. - - Options FollowSymLinks - AllowOverride None - Require all denied - - - - AllowOverride None - Require all granted - - - - Options Indexes FollowSymLinks - AllowOverride None - Require all granted - - - -# AccessFileName: The name of the file to look for in each directory -# for additional configuration directives. See also the AllowOverride -# directive. -# -AccessFileName .htaccess - -# -# The following lines prevent .htaccess and .htpasswd files from being -# viewed by Web clients. -# - - Require all denied - - - -# -# The following directives define some format nicknames for use with -# a CustomLog directive. -# -# These deviate from the Common Log Format definitions in that they use %O -# (the actual bytes sent including headers) instead of %b (the size of the -# requested file), because the latter makes it impossible to detect partial -# requests. -# -# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended. -# Use mod_remoteip instead. -# -LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined -LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined -LogFormat "%h %l %u %t \"%r\" %>s %O" common -LogFormat "%{Referer}i -> %U" referer -LogFormat "%{User-agent}i" agent - -# Include of directories ignores editors' and dpkg's backup files, -# see README.Debian for details. - -# Include generic snippets of statements -IncludeOptional conf-enabled/*.conf - -# Include the virtual host configurations: -IncludeOptional sites-enabled/*.conf - -# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/codebuild/bin/apache2/ports.conf b/codebuild/bin/apache2/ports.conf deleted file mode 100644 index 10231882d77..00000000000 --- a/codebuild/bin/apache2/ports.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Server ports should not conflict with the range of ports used for -# integration tests (8000 to 30000) - -Define RENEGOTIATE_SERVER_PORT 7777 -Listen ${RENEGOTIATE_SERVER_PORT} - -# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/codebuild/bin/apache2/sites-enabled/renegotiate.conf b/codebuild/bin/apache2/sites-enabled/renegotiate.conf deleted file mode 100644 index 4cf0373792a..00000000000 --- a/codebuild/bin/apache2/sites-enabled/renegotiate.conf +++ /dev/null @@ -1,54 +0,0 @@ - - # The ServerName directive sets the request scheme, hostname and port that - # the server uses to identify itself. This is used when creating - # redirection URLs. In the context of virtual hosts, the ServerName - # specifies what hostname must appear in the request's Host: header to - # match this virtual host. For the default virtual host (this file) this - # value is not decisive as it is used as a last resort host regardless. - # However, you must set it for any further virtual host explicitly. - ServerName localhost - - ServerAdmin webmaster@localhost - DocumentRoot ${APACHE_SERVER_ROOT}/www/html - - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - - # SSL Engine Switch: - # Enable/Disable SSL for this virtual host. - SSLEngine on - - SSLCertificateFile ${APACHE_CERT_DIR}/apache_server_cert.pem - SSLCertificateKeyFile ${APACHE_CERT_DIR}/apache_server_key.pem - - # Certificate Authority (CA): - # Set the CA certificate verification path where to find CA - # certificates for client authentication or alternatively one - # huge file containing all of them (file must be PEM encoded) - # Note: Inside SSLCACertificatePath you need hash symlinks - # to point to the certificate files. Use the provided - # Makefile to update the hash symlinks after changes. - SSLCACertificateFile ${APACHE_CERT_DIR}/apache_client_cert.pem - - SSLProtocol -ALL +TLSv1.2 - SSLHonorCipherOrder On - SSLCipherSuite HIGH:!aNULL:!MD5 - SSLCompression Off - SSLInsecureRenegotiation Off - - Alias /change_cipher_suite ${APACHE_SERVER_ROOT}/www/change_cipher_suite - - Require all granted - SSLCipherSuite AES128-SHA - - - Alias /mutual_auth ${APACHE_SERVER_ROOT}/www/mutual_auth - - Require all granted - SSLVerifyClient require - SSLVerifyDepth 10 - - - - -# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/codebuild/bin/apache2/www/change_cipher_suite/index.html b/codebuild/bin/apache2/www/change_cipher_suite/index.html deleted file mode 100644 index 96ccd69650c..00000000000 --- a/codebuild/bin/apache2/www/change_cipher_suite/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Change Cipher Suite - - -

Success.

- - diff --git a/codebuild/bin/apache2/www/html/index.html b/codebuild/bin/apache2/www/html/index.html deleted file mode 100755 index 69f57f0b4a1..00000000000 --- a/codebuild/bin/apache2/www/html/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - Renegotiation Testing Server - - -

Welcome to the s2n renegotiation testing server! See the following endpoints:

- -
    -
  • - /change_cipher_suite forces a renegotiation by changing the negotiated - cipher suite to AES-128-SHA. -
  • -
  • - /mutual_auth forces a renegotiation by enforcing mutual authentication. -
  • -
- - diff --git a/codebuild/bin/apache2/www/mutual_auth/index.html b/codebuild/bin/apache2/www/mutual_auth/index.html deleted file mode 100644 index aff1ad6f7af..00000000000 --- a/codebuild/bin/apache2/www/mutual_auth/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Mutual Auth - - -

Success.

- - diff --git a/codebuild/bin/build_aws_crt_cpp.sh b/codebuild/bin/build_aws_crt_cpp.sh deleted file mode 100755 index 1e873fe7f07..00000000000 --- a/codebuild/bin/build_aws_crt_cpp.sh +++ /dev/null @@ -1,56 +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 -ex -pushd "$(pwd)" - -usage() { - echo "build_aws_crt_cpp.sh build_dir install_dir" - exit 1 -} - -if [ "$#" -ne "2" ]; then - usage -fi - -source codebuild/bin/s2n_setup_env.sh - -BUILD_DIR=$1 -INSTALL_DIR=$2 - -# Make sure there isn't another source tree hanging around. -rm -rf /opt/s2n-tls || true -mkdir -p "$BUILD_DIR/s2n" -# In case $BUILD_DIR is a subdirectory of current directory -for file in *;do test "$file" != "$BUILD_DIR" && cp -r "$file" "$BUILD_DIR/s2n";done -cd "$BUILD_DIR" -git clone --depth 1 --shallow-submodules --recurse-submodules https://github.com/awslabs/aws-crt-cpp.git -# Replace S2N -rm -r aws-crt-cpp/crt/s2n -mv s2n aws-crt-cpp/crt/ - -cmake ./aws-crt-cpp \ - -Bbuild \ - -GNinja \ - -DENFORCE_SUBMODULE_VERSIONS=off \ - -DBUILD_DEPS=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" -ninja -C ./build install -CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) ninja -C ./build test - -popd - -exit 0 diff --git a/codebuild/bin/clang_format_changed_files.sh b/codebuild/bin/clang_format_changed_files.sh deleted file mode 100755 index 6dd5674132f..00000000000 --- a/codebuild/bin/clang_format_changed_files.sh +++ /dev/null @@ -1,27 +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. - -# Get a list of changed files -REMOTE="${1:-origin}" -BRANCH="${2:-main}" -changed_files=$(git diff "$REMOTE"/"$BRANCH" --name-only ) - -# Run clang-format on each changed file -for file in $changed_files -do - if [[ $file == *.c || $file == *.h ]]; then # Only run on .c and .h files - echo "clang formatting ${file}" - clang-format -i $file - fi -done diff --git a/codebuild/bin/coverage_report.sh b/codebuild/bin/coverage_report.sh deleted file mode 100755 index b5f5a753583..00000000000 --- a/codebuild/bin/coverage_report.sh +++ /dev/null @@ -1,34 +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 - -# merge profiling data -llvm-profdata merge -failure-mode=all -sparse tests/unit/ut_*.profraw -o merged.profdata - -# generate file-level summary -llvm-cov report build/lib/libs2n.so \ - -instr-profile=merged.profdata \ - > coverage_summary.txt - -# convert llvm information to lcov format for genhtml -llvm-cov export build/lib/libs2n.so \ - -instr-profile=merged.profdata \ - -format=lcov \ - > unit_test_coverage.info - -# generate html report with annotated source files -genhtml unit_test_coverage.info \ - --branch-coverage \ - -o coverage_report diff --git a/codebuild/bin/cpp_style_comment_linter.sh b/codebuild/bin/cpp_style_comment_linter.sh deleted file mode 100755 index b82bb1563b2..00000000000 --- a/codebuild/bin/cpp_style_comment_linter.sh +++ /dev/null @@ -1,36 +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. -# - -S2N_FILES=$(find "$PWD" -type f -name "s2n*.[ch]") - -FAILED=0 - -for file in $S2N_FILES; do - # There should be no c++ style comments: // - RESULT=`grep -rnv '\*' $file | grep '\B\/\/.*$' | grep -v '\".*\"'`; - if [ "${#RESULT}" != "0" ]; - then - FAILED=1; - printf "\e[1;34mC++ Comments Check Failed in $file:\e[0m\n$RESULT\n\n"; - fi -done - -if [ $FAILED == 1 ]; -then - printf "\\033[31;1mFAILED C++ Comments Check\\033[0m\\n" - exit -1 -else - printf "\\033[32;1mPASSED C++ Comments Check\\033[0m\\n" -fi \ No newline at end of file diff --git a/codebuild/bin/cppcheck_suppressions.txt b/codebuild/bin/cppcheck_suppressions.txt deleted file mode 100644 index c85c4bd9514..00000000000 --- a/codebuild/bin/cppcheck_suppressions.txt +++ /dev/null @@ -1,17 +0,0 @@ -// Message: (style:variableScope) The scope of the variable 'text' can be reduced. -// Reason: Don't error for being able to reduce scope of variables in tests -variableScope:tests/unit/* - -// cppcheck Message: (information:ConfigurationNotChecked) Skipping configuration 'SO_RCVLOWAT' since the value of 'SO_RCVLOWAT' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly. -// Reason: There are many Config options that aren't checked by Cppcheck, and it warns for each. Ignore these so that they don't clutter the output. -ConfigurationNotChecked:bin/s2nd.c -ConfigurationNotChecked:tls/s2n_x509_validator.c -ConfigurationNotChecked:utils/s2n_socket.c - -// cppcheck Message: (style:redundantAssignment) Variable 'mock_time' is reassigned a value before the old one has been used. -// Reason: s2n_config_set_monotonic_clock() takes a reference to mock_time so that whenever it's modified locally, the timer sees the update when it dereferences the pointer. -redundantAssignment:tests/unit/s2n_timer_test.c - -// cppcheck Message: (style:knownConditionTrueFalse) Condition 's2n_libcrypto_awslc_api_version()<17' is always true -// Reason: s2n_libcrypto_awslc_api_version() are implemented using macro's and for certain libcrypto's the preprocessor will produce a trivial function returning e.g. 1 always. -knownConditionTrueFalse:crypto/s2n_libcrypto.c \ No newline at end of file diff --git a/codebuild/bin/format.sh b/codebuild/bin/format.sh deleted file mode 100755 index 14be59435de..00000000000 --- a/codebuild/bin/format.sh +++ /dev/null @@ -1,27 +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 -CLANG_NINE=$(which clang-format-9) -CLANG_VER=${CLANG_NINE:-clang-format} -for i in $(find . -not -path "./test-deps/*" -name '*.h' -or -name '*.c' -or -name '*.cpp'); do - $CLANG_VER --verbose -i "$i" ; -done - -if [[ `git status --porcelain` ]]; then - echo "clang-format updated files, throwing an error" - exit 255 -else - echo "No files touched" -fi diff --git a/codebuild/bin/fuzz_corpus_download.sh b/codebuild/bin/fuzz_corpus_download.sh deleted file mode 100755 index 0da91abcbf6..00000000000 --- a/codebuild/bin/fuzz_corpus_download.sh +++ /dev/null @@ -1,33 +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. -# - -for FUZZ_TEST in tests/fuzz/*.c; do - # extract file name without extension - TEST_NAME=$(basename "$FUZZ_TEST") - TEST_NAME="${TEST_NAME%.*}" - - # temp corpus folder to store downloaded corpus files - TEMP_CORPUS_DIR="./tests/fuzz/temp_corpus_${TEST_NAME}" - - # 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 "s3://s2n-tls-fuzz-corpus/${TEST_NAME}/corpus.zip" > /dev/null 2>&1; then - aws s3 cp "s3://s2n-tls-fuzz-corpus/${TEST_NAME}/corpus.zip" "${TEMP_CORPUS_DIR}/corpus.zip" - unzip -o "${TEMP_CORPUS_DIR}/corpus.zip" -d "${TEMP_CORPUS_DIR}" > /dev/null 2>&1 - else - printf "corpus.zip not found for ${TEST_NAME}" - fi -done diff --git a/codebuild/bin/fuzz_corpus_upload.sh b/codebuild/bin/fuzz_corpus_upload.sh deleted file mode 100755 index 8100e074166..00000000000 --- a/codebuild/bin/fuzz_corpus_upload.sh +++ /dev/null @@ -1,25 +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. -# - -for FUZZ_TEST in tests/fuzz/*.c; do - # extract file name without extension - TEST_NAME=$(basename "$FUZZ_TEST") - TEST_NAME="${TEST_NAME%.*}" - - # Upload generated corpus files to the S3 bucket. - zip -r ./tests/fuzz/corpus/${TEST_NAME}.zip ./tests/fuzz/corpus/${TEST_NAME}/ > /dev/null 2>&1 - aws s3 cp ./tests/fuzz/corpus/${TEST_NAME}.zip s3://s2n-tls-fuzz-corpus/${TEST_NAME}/corpus.zip -done - diff --git a/codebuild/bin/fuzz_coverage_report.sh b/codebuild/bin/fuzz_coverage_report.sh deleted file mode 100755 index a35e4e77d4e..00000000000 --- a/codebuild/bin/fuzz_coverage_report.sh +++ /dev/null @@ -1,75 +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 "Usage: fuzz_coverage_report.sh" - exit 1 -} - -if [ "$#" -ne "0" ]; then - usage -fi - -FUZZ_TEST_DIR="tests/fuzz" -FUZZCOV_SOURCES="api bin crypto error stuffer tls utils" - -# generate coverage report for each fuzz test -printf "Generating coverage reports... \n" - -mkdir -p coverage/fuzz -for FUZZ_TEST in "$FUZZ_TEST_DIR"/*.c; do - # extract file name without extension - TEST_NAME=$(basename "$FUZZ_TEST") - TEST_NAME="${TEST_NAME%.*}" - - # merge multiple .profraw files into a single .profdata file - llvm-profdata merge \ - -sparse tests/fuzz/profiles/${TEST_NAME}/*.profraw \ - -o tests/fuzz/profiles/${TEST_NAME}/${TEST_NAME}.profdata - - # generate a coverage report in text format - llvm-cov report \ - -instr-profile=tests/fuzz/profiles/${TEST_NAME}/${TEST_NAME}.profdata build/lib/libs2n.so ${FUZZCOV_SOURCES} \ - -show-functions \ - > coverage/fuzz/${TEST_NAME}_cov.txt - - # exports coverage data in LCOV format - llvm-cov export \ - -instr-profile=tests/fuzz/profiles/${TEST_NAME}/${TEST_NAME}.profdata build/lib/libs2n.so ${FUZZCOV_SOURCES} \ - -format=lcov \ - > coverage/fuzz/${TEST_NAME}_cov.info - - # convert to HTML format - genhtml -q -o coverage/html/${TEST_NAME} coverage/fuzz/${TEST_NAME}_cov.info > /dev/null 2>&1 -done - -# merge all coverage reports into a single report that shows total s2n coverage -printf "Calculating total s2n coverage... \n" -llvm-profdata merge \ - -sparse tests/fuzz/profiles/*/*.profdata \ - -o tests/fuzz/profiles/merged_fuzz.profdata - -llvm-cov report \ - -instr-profile=tests/fuzz/profiles/merged_fuzz.profdata build/lib/libs2n.so ${FUZZCOV_SOURCES} \ - > s2n_fuzz_coverage.txt - -llvm-cov export \ - -instr-profile=tests/fuzz/profiles/merged_fuzz.profdata build/lib/libs2n.so ${FUZZCOV_SOURCES} \ - -format=lcov \ - > s2n_fuzz_cov.info - -genhtml s2n_fuzz_cov.info --branch-coverage -q -o coverage/fuzz/total_fuzz_coverage diff --git a/codebuild/bin/grep_simple_mistakes.sh b/codebuild/bin/grep_simple_mistakes.sh deleted file mode 100755 index 6d177f628b0..00000000000 --- a/codebuild/bin/grep_simple_mistakes.sh +++ /dev/null @@ -1,302 +0,0 @@ -#!/usr/bin/env 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. - -FAILED=0 - -############################################# -# Grep for command line defines without values -############################################# -EMPTY_DEFINES=$(grep -Eon "\-D[^=]+=?" CMakeLists.txt | grep -v =) -if [ ! -z "${EMPTY_DEFINES}" ]; then - FAILED=1 - printf "\e[1;34mCommand line define is missing value:\e[0m " - printf "Compilers SHOULD set a default value of 1 when no default is given, " - printf "but that behavior is not required by any official spec. Set a value just in case. " - printf "For example: -DS2N_FOO=1 instead of -DS2N_FOO.\n" - printf "Found: \n" - echo "$EMPTY_DEFINES" -fi - -############################################# -# Grep for bindings methods without C documentation links. -############################################# -BINDINGS="bindings/rust/extended/s2n-tls/src" -C_APIS=$(grep -rEo "S2N_API( extern)? [^ ]+ [^(]+\(" api | sed -E "s/^.*? \*?(.*?)\(/\1/") -# Sanity checks -echo $C_APIS | grep -q "s2n_error_get_type" || { echo "Not detecting APIs" ; exit 1; } -echo $C_APIS | grep -q "s2n_connection_new" || { echo "Not detecting pointer APIs" ; exit 1; } -echo $C_APIS | grep -q "s2n_config_set_npn" || { echo "Not detecting unstable APIs" ; exit 1; } -KNOWN_MISSES=( - "s2n_errno_location" - "s2n_cert_chain_and_key_get_private_key" - "s2n_config_set_ctx" - "s2n_client_hello_has_extension" - "s2n_async_pkey_op_perform" - "s2n_config_get_client_auth_type" -) -C_DOCS_FAILED=0 -for api in $C_APIS; do - if [[ "${KNOWN_MISSES[*]}" =~ "$api" ]]; then continue; fi - CALLS=`grep -ro "$api(" $BINDINGS | wc -l` - if [ "$CALLS" == 0 ]; then continue; fi - DOCS=$(grep -ro "///.* \[\`$api\`\]" $BINDINGS | wc -l) - if [ "$DOCS" == 0 ]; then - if [ $C_DOCS_FAILED == 0 ]; then - C_DOCS_FAILED=1 - FAILED=1 - printf "\e[1;34mRust bindings are missing documentation links:\e[0m " - printf "Where possible, the Rust bindings should link to existing documentation. " - printf "Links can be written like \"[\`s2n_connection_new\`]\".\n" - fi - echo "- $api" - fi -done - -############################################# -# Grep for any instances of raw memcpy() function. s2n code should instead be -# using one of the *_ENSURE_MEMCPY macros. -############################################# -S2N_FILES_ASSERT_NOT_USING_MEMCPY=$(find "$PWD" -type f -name "s2n*.[ch]" -not -path "*/tests/*") -for file in $S2N_FILES_ASSERT_NOT_USING_MEMCPY; do - RESULT_NUM_LINES=`grep 'memcpy(' $file | wc -l` - if [ "${RESULT_NUM_LINES}" != 0 ]; then - echo "Found ${RESULT_NUM_LINES} raw 'memcpy' calls in $file" - FAILED=1 - fi -done - -############################################# -# Grep for any instances of raw memcmp() function. s2n code should instead be -# using s2n_constant_time_equals() -# -# KNOWN_MEMCMP_USAGE is used to capture all known uses of memcmp and acts as a -# safeguard against any new uses of memcmp. -############################################# -S2N_FILES_ASSERT_NOT_USING_MEMCMP=$(find "$PWD" -type f -name "s2n*.[ch]" -not -path "*/tests/*" -not -path "*/bindings/*") -declare -A KNOWN_MEMCMP_USAGE -KNOWN_MEMCMP_USAGE["$PWD/stuffer/s2n_stuffer_text.c"]=1 -KNOWN_MEMCMP_USAGE["$PWD/tls/s2n_psk.c"]=1 -KNOWN_MEMCMP_USAGE["$PWD/tls/s2n_protocol_preferences.c"]=1 -KNOWN_MEMCMP_USAGE["$PWD/tls/s2n_cipher_suites.c"]=1 -KNOWN_MEMCMP_USAGE["$PWD/utils/s2n_map.c"]=3 - -for file in $S2N_FILES_ASSERT_NOT_USING_MEMCMP; do - # NOTE: this matches on 'memcmp', which will also match comments. However, there - # are no uses of 'memcmp' in comments so we opt for this stricter check. - RESULT_NUM_LINES=`grep -n 'memcmp' $file | wc -l` - - # set default KNOWN_MEMCMP_USAGE value - [ -z "${KNOWN_MEMCMP_USAGE["$file"]}" ] && KNOWN_MEMCMP_USAGE["$file"]="0" - - # check if memcmp usage is 0 or a known value - if [ "${RESULT_NUM_LINES}" != "${KNOWN_MEMCMP_USAGE["$file"]}" ]; then - echo "Expected: ${KNOWN_MEMCMP_USAGE["$file"]} Found: ${RESULT_NUM_LINES} usage of 'memcmp' in $file" - FAILED=1 - fi -done - -############################################# -# Assert that functions do not return -1 or S2N_ERR* codes directly. -# To indicate failure, functions should use the S2N_ERROR* macros defined -# in s2n_errno.h. -############################################# -S2N_FILES_ASSERT_RETURN=$(find "$PWD" -type f -name "s2n*.c" -not -path "*/tests/*" -not -path "*/docs/examples/*") -for file in $S2N_FILES_ASSERT_RETURN; do - RESULT_NEGATIVE_ONE=`grep -rn 'return -1;' $file` - RESULT_S2N_ERR=`grep -rn 'return S2N_ERR*' $file` - RESULT_S2N_FAIL=`grep -rn 'return S2N_FAIL*' $file` - RESULT_S2N_RESULT_ERR=`grep -rn 'return S2N_RESULT_ERR*' $file` - - if [ "${#RESULT_NEGATIVE_ONE}" != "0" ]; then - FAILED=1 - printf "\e[1;34mGrep for 'return -1;' check failed in $file:\e[0m\n$RESULT_NEGATIVE_ONE\n\n" - fi - if [ "${#RESULT_S2N_ERR}" != "0" ]; then - FAILED=1 - printf "\e[1;34mGrep for 'return S2N_ERR*' check failed in $file:\e[0m\n$RESULT_S2N_ERR\n\n" - fi - if [ "${#RESULT_S2N_FAIL}" != "0" ]; then - FAILED=1 - printf "\e[1;34mGrep for 'return S2N_FAIL*' check failed in $file:\e[0m\n$RESULT_S2N_FAIL\n\n" - fi - if [ "${#RESULT_S2N_RESULT_ERR}" != "0" ]; then - FAILED=1 - printf "\e[1;34mGrep for 'return S2N_RESULT_ERR*' check failed in $file:\e[0m\n$RESULT_S2N_RESULT_ERR\n\n" - fi -done - -############################################# -# Detect any array size calculations that are not using the s2n_array_len() function. -############################################# -S2N_FILES_ARRAY_SIZING_RETURN=$(find "$PWD" -type f -name "s2n*.c" -path "*") -for file in $S2N_FILES_ARRAY_SIZING_RETURN; do - RESULT_ARR_DIV=`grep -Ern 'sizeof\(.*?\) / sizeof\(' $file` - if [ "${#RESULT_ARR_DIV}" != "0" ]; then - FAILED=1 - printf "\e[1;34mUsage of 'sizeof(array) / sizeof(T)' check failed. Use s2n_array_len instead in $file:\e[0m\n$RESULT_ARR_DIV\n\n" - fi -done - -############################################# -# Detect any suspicious loops not using s2n_array_len(). -# This is not necessarily a problem, but it's been a common source of errors, -# so we should just enforce stricter conventions. -############################################# -S2N_FILES_WITH_SIZEOF_LOOP=$(find "$PWD" -type f -name "s2n*.c" -path "*") -for file in $S2N_FILES_WITH_SIZEOF_LOOP; do - WITH_QUESTIONABLE_SIZEOF_LOOP=`grep -Ern 'for \(.+; .+ <=? sizeof\(.+\); .+\)' $file | \ - grep -vE '<=? sizeof\(.*bytes\);' | - grep -vE '<=? sizeof\(.*data\);' | - grep -vE '<=? sizeof\(.*u8\);'` - if [ "${#WITH_QUESTIONABLE_SIZEOF_LOOP}" != "0" ]; then - FAILED=1 - printf "\e[1;34mWarning: sizeof is only valid for arrays of chars or uint8_ts. " - printf "Use s2n_array_len for other types, " - printf "or append \"bytes\", \"data\", or \"u8\" to your variable name for clarity.\n" - printf "File: $file:\e[0m\n$WITH_QUESTIONABLE_SIZEOF_LOOP\n\n" - fi -done - -############################################# -# Assert that all assignments from s2n_stuffer_raw_read() have a -# notnull_check (or similar manual null check) on the same, or next, line. -# The assertion is shallow; this doesn't guarantee that we're doing the -# *correct* null check, just that we are doing *some* null check. -############################################# -S2N_FILES_ASSERT_NOTNULL_CHECK=$(find "$PWD" -type f -name "s2n*.[ch]" -not -path "*/tests/*") -for file in $S2N_FILES_ASSERT_NOTNULL_CHECK; do - while read -r line_one; do - # When called with the -A option, grep uses lines of "--" as delimiters. We ignore them. - if [[ $line_one == "--" ]]; then - continue - fi - - read -r line_two - - # $line_one definitely contains an assignment from s2n_stuffer_raw_read(), - # because that's what we grepped for. So verify that either $line_one or - # $line_two contains a null check. - null_check_regex="(.*(if|ENSURE).*=\ NULL)|(ENSURE_REF)" - if [[ $line_one =~ $null_check_regex ]] || [[ $line_two =~ $null_check_regex ]]; then - # Found a notnull_check - continue - else - FAILED=1 - printf "\e[1;34mFound a call to s2n_stuffer_raw_read without an ENSURE_REF in $file:\e[0m\n$line_one\n\n" - fi - done < <(grep -rnE -A 1 "=\ss2n_stuffer_raw_read\(.*\)" $file) -done - -############################################# -# Assert that "index" is not a variable name. An "index" function exists in strings.h, and older compilers (\n/' ./lib/programmemory.cpp - -# -DNO_UNIX_SIGNAL_HANDLING is added to support the cppcheck 2.3 build, and should also be removed -# after cppcheck is updated: https://github.com/aws/s2n-tls/issues/5239 -# These build instructions are based on https://github.com/danmar/cppcheck#gnu-make. -make -j $JOBS MATCHCOMPILER=yes CXXFLAGS="-O2 -DNDEBUG -DNO_UNIX_SIGNAL_HANDLING" - -mv cppcheck .. -mv cfg .. -cd .. -rm -rf cppcheck-src diff --git a/codebuild/bin/install_ctverif.sh b/codebuild/bin/install_ctverif.sh deleted file mode 100755 index 831a5d33ace..00000000000 --- a/codebuild/bin/install_ctverif.sh +++ /dev/null @@ -1,45 +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_ctverif.sh install_dir" - exit 1 -} - -if [ "$#" -ne "1" ]; then - usage -fi - -INSTALL_DIR=$1 - -cd "$INSTALL_DIR" - -#install smack -git clone https://github.com/smackers/smack.git -b develop -cd smack/bin -git checkout 45e1fc5 -./build.sh - -# Disabling ShellCheck using https://github.com/koalaman/shellcheck/wiki/Directive -# Turn of Warning in one line as https://github.com/koalaman/shellcheck/wiki/SC1090 -# shellcheck disable=SC1090 -source "$INSTALL_DIR"/smack.environment - -#install ctverif -cd "$INSTALL_DIR" -git clone --depth 1 https://github.com/imdea-software/verifying-constant-time.git -b test-automation - diff --git a/codebuild/bin/install_default_dependencies.sh b/codebuild/bin/install_default_dependencies.sh deleted file mode 100755 index ff0f02bed61..00000000000 --- a/codebuild/bin/install_default_dependencies.sh +++ /dev/null @@ -1,146 +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 -ex -source codebuild/bin/s2n_setup_env.sh - - - # Install latest version of clang, clang++, and llvm-symbolizer. Needed for fuzzing. -if [[ "$TESTS" == "fuzz" || "$TESTS" == "ALL" || "$LATEST_CLANG" == "true" ]]; then - mkdir -p "$LATEST_CLANG_INSTALL_DIR"||true - codebuild/bin/install_clang.sh "$(mktemp -d)" "$LATEST_CLANG_INSTALL_DIR" "$OS_NAME" > /dev/null ; -fi - -# Download and Install Openssl 1.1.1 -if [[ ("$S2N_LIBCRYPTO" == "openssl-1.1.1") || ( "$TESTS" == "integrationv2" || "$TESTS" == "ALL" ) ]]; then - if [[ ! -x "$OPENSSL_1_1_1_INSTALL_DIR/bin/openssl" ]]; then - mkdir -p "$OPENSSL_1_1_1_INSTALL_DIR"||true - codebuild/bin/install_openssl_1_1_1.sh "$(mktemp -d)" "$OPENSSL_1_1_1_INSTALL_DIR" "$OS_NAME" > /dev/null ; - fi -fi - -# Download and Install Openssl 3.0 -if [[ "$S2N_LIBCRYPTO" == "openssl-3.0" && ! -d "$OPENSSL_3_0_INSTALL_DIR" ]]; then - mkdir -p "$OPENSSL_3_0_INSTALL_DIR" - codebuild/bin/install_openssl_3_0.sh "$(mktemp -d)" "$OPENSSL_3_0_INSTALL_DIR" "$OS_NAME" > /dev/null ; -fi - -# Download and Install Openssl 3.0 FIPS -if [[ "$S2N_LIBCRYPTO" == "openssl-3.0-fips" && ! -d "$OPENSSL_3_FIPS_INSTALL_DIR" ]]; then - mkdir -p "$OPENSSL_3_FIPS_INSTALL_DIR" - codebuild/bin/install_openssl_3_0.sh "$(mktemp -d)" "$OPENSSL_3_FIPS_INSTALL_DIR" "$OS_NAME" fips > /dev/null ; -fi - -# Download and Install Openssl 1.0.2 -if [[ "$S2N_LIBCRYPTO" == "openssl-1.0.2" && ! -d "$OPENSSL_1_0_2_INSTALL_DIR" ]]; then - mkdir -p "$OPENSSL_1_0_2_INSTALL_DIR"||true - codebuild/bin/install_openssl_1_0_2.sh "$(mktemp -d)" "$OPENSSL_1_0_2_INSTALL_DIR" "$OS_NAME" > /dev/null ; -fi - -# Download and Install the Openssl FIPS module and Openssl 1.0.2-fips -if [[ "$S2N_LIBCRYPTO" == "openssl-1.0.2-fips" ]] && [[ ! -d "$OPENSSL_1_0_2_FIPS_INSTALL_DIR" ]]; then - codebuild/bin/install_openssl_1_0_2_fips.sh "$(mktemp -d)" "$OPENSSL_1_0_2_FIPS_INSTALL_DIR" "$OS_NAME" ; fi - -# Download and Install LibreSSL -if [[ "$S2N_LIBCRYPTO" == "libressl" && ! -d "$LIBRESSL_INSTALL_DIR" ]]; then - mkdir -p "$LIBRESSL_INSTALL_DIR"||true - codebuild/bin/install_libressl.sh "$(mktemp -d)" "$LIBRESSL_INSTALL_DIR" > /dev/null ; -fi - -# Download and Install BoringSSL -if [[ "$S2N_LIBCRYPTO" == "boringssl" && ! -d "$BORINGSSL_INSTALL_DIR" ]]; then - codebuild/bin/install_boringssl.sh "$(mktemp -d)" "$BORINGSSL_INSTALL_DIR" > /dev/null ; -fi - -# Download and Install AWS-LC -if [[ "$S2N_LIBCRYPTO" == "awslc" && ! -d "$AWSLC_INSTALL_DIR" ]]; then - codebuild/bin/install_awslc.sh "$(mktemp -d)" "$AWSLC_INSTALL_DIR" > /dev/null ; -fi - -if [[ "$S2N_LIBCRYPTO" == "awslc-fips-2022" && ! -d "$AWSLC_FIPS_2022_INSTALL_DIR" ]]; then - codebuild/bin/install_awslc.sh "$(mktemp -d)" "$AWSLC_FIPS_2022_INSTALL_DIR" "2022" > /dev/null ; -fi -if [[ "$S2N_LIBCRYPTO" == "awslc-fips-2024" && ! -d "$AWSLC_FIPS_2024_INSTALL_DIR" ]]; then - codebuild/bin/install_awslc_fips_2024.sh "$(mktemp -d)" "$AWSLC_FIPS_2024_INSTALL_DIR" "2024" > /dev/null ; -fi - -if [[ "$TESTS" == "integrationv2" || "$TESTS" == "ALL" ]]; then - # Install tox - if [[ "$DISTRO" == "ubuntu" ]]; then - if [[ ! -x `python3.9 -m tox --version` ]]; then - python3.9 -m pip install tox - fi - else - if [[ ! -x `which tox` ]]; then - case "$DISTRO" in - "amazon linux") - yum install -y python3-pip - python3 -m pip install --user tox ;; - "apple") - brew install python@3 - python3 -m pip install --user tox ;; - *) - echo "Unknown platform $DISTRO trying to install tox on $OS_NAME $ARCH" - exit 1 - ;; - esac - fi - fi - - if [[ ! -x "$GNUTLS37_INSTALL_DIR/bin/gnutls-cli" ]]; then - # Download and Install GnuTLS for integration tests - mkdir -p "$GNUTLS37_INSTALL_DIR"||true - codebuild/bin/install_gnutls37.sh "$(mktemp -d)" "$GNUTLS37_INSTALL_DIR" > /dev/null ; - fi - - if [[ "$DISTRO" == "ubuntu" ]]; then - # Install SSLyze for all Integration Tests on Ubuntu. - # There is a nassl dependency issue preventing this from working on on AL2 ARM (others?). - if [[ "$S2N_NO_SSLYZE" != "true" ]]; then - codebuild/bin/install_sslyze.sh - fi - - if [[ ! -x "$APACHE2_INSTALL_DIR/apache2.conf" ]]; then - codebuild/bin/install_apache2.sh codebuild/bin/apache2 "$APACHE2_INSTALL_DIR" - fi - fi -fi - -# Install SAW, Z3, and Yices for formal verification -if [[ "$SAW" == "true" || "$TESTS" == "ALL" ]]; then - mkdir -p "$SAW_INSTALL_DIR"||true - codebuild/bin/install_saw.sh "$(mktemp -d)" "$SAW_INSTALL_DIR" > /dev/null ; - - mkdir -p "$Z3_INSTALL_DIR"||true - codebuild/bin/install_z3_yices.sh "$(mktemp -d)" "$Z3_INSTALL_DIR" > /dev/null ; -fi - -if [[ ! -x `which cmake` ]]; then - case "$DISTRO" in - "ubuntu") - apt-get -y install cmake - ;; - "amazon linux") - yum install -y cmake3 - update-alternatives --install /usr/bin/cmake cmake /usr/bin/cmake3 30 - ;; - "apple") - brew install cmake - ;; - *) - echo "Unknown platform for cmake." - ;; - esac -fi diff --git a/codebuild/bin/install_gnutls37.sh b/codebuild/bin/install_gnutls37.sh deleted file mode 100755 index 7be4aa84149..00000000000 --- a/codebuild/bin/install_gnutls37.sh +++ /dev/null @@ -1,76 +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 -source codebuild/bin/s2n_setup_env.sh - -usage() { - echo "install_gnutls37.sh build_dir install_dir os_name" - exit 1 -} - -if [ "$#" -ne "2" ]; then - usage -fi - -GNUTLS_BUILD_DIR=$1 -GNUTLS_INSTALL_DIR=$2 - -source codebuild/bin/jobs.sh - -# libgmp is needed for libnettle -case "$DISTRO" in - "ubuntu") - sudo apt-get -qq install libgmp3-dev -y - ;; - "amazon linux") - sudo yum install -y gmp-devel - ;; -"darwin" ) - # Installing an existing package is a "failure" in brew - brew install gmp || true - ;; -*) - echo "Invalid platform! $OS_NAME" - usage - ;; -esac - -cd "$GNUTLS_BUILD_DIR" - -# Originally from: https://ftp.gnu.org/gnu/nettle/ -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/2021-01-04_nettle-3.7.tar.gz --output nettle-3.7.tar.gz -tar -xzf nettle-3.7.tar.gz -cd nettle-3.7 -./configure --prefix="$GNUTLS_INSTALL_DIR"/nettle \ - --disable-openssl \ - --enable-shared -make -j $JOBS -make -j $JOBS install -cd .. - -# Install GnuTLS -# Originally from: https://www.gnupg.org/ftp/gcrypt/gnutls/v3.7/ -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/2022-01-18_gnutls-3.7.3.tar.xz --output gnutls-3.7.3.tar.xz -tar -xJf gnutls-3.7.3.tar.xz -cd gnutls-3.7.3 -PKG_CONFIG_PATH="$GNUTLS_INSTALL_DIR"/nettle/lib/pkgconfig:$PKG_CONFIG_PATH \ - ./configure --prefix="$GNUTLS_INSTALL_DIR" \ - --without-p11-kit \ - --with-included-libtasn1 \ - --with-included-unistring -make -j $JOBS -make -j $JOBS install diff --git a/codebuild/bin/install_libressl.sh b/codebuild/bin/install_libressl.sh deleted file mode 100755 index 4a34c9468a8..00000000000 --- a/codebuild/bin/install_libressl.sh +++ /dev/null @@ -1,37 +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_libressl.sh build_dir install_dir" - exit 1 -} - -if [ "$#" -ne "2" ]; then - usage -fi - -BUILD_DIR=$1 -INSTALL_DIR=$2 -source codebuild/bin/jobs.sh - -cd "$BUILD_DIR" -# Originally from https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.4.3.tar.gz -curl https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/2022-12-01_libressl-3.6.1.tar.gz > libressl-3.6.1.tar.gz -tar -xzvf libressl-3.6.1.tar.gz -cd libressl-3.6.1 -./configure --prefix="$INSTALL_DIR" -make -j $JOBS CFLAGS=-fPIC install diff --git a/codebuild/bin/install_openssl_1_0_2.sh b/codebuild/bin/install_openssl_1_0_2.sh deleted file mode 100755 index d6f6793ecd4..00000000000 --- a/codebuild/bin/install_openssl_1_0_2.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env 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 -ex -pushd "$(pwd)" - -usage() { - echo "install_openssl_1_0_2.sh build_dir install_dir os_name" - exit 1 -} - -if [ "$#" -ne "3" ]; then - usage -fi - -BUILD_DIR=$1 -INSTALL_DIR=$2 -OS_NAME=$3 -source codebuild/bin/jobs.sh - -mkdir -p $BUILD_DIR -cd "$BUILD_DIR" -curl --retry 3 -L https://github.com/openssl/openssl/archive/OpenSSL_1_0_2-stable.zip --output openssl-OpenSSL_1_0_2-stable.zip -unzip openssl-OpenSSL_1_0_2-stable.zip -cd openssl-OpenSSL_1_0_2-stable - -if [ "$OS_NAME" == "linux" ]; then - CONFIGURE="./config -d" -elif [ "$OS_NAME" == "osx" ]; then - CONFIGURE="./Configure darwin64-x86_64-cc" -else - echo "Invalid platform! $OS_NAME" - usage -fi - -mkdir -p $INSTALL_DIR -$CONFIGURE shared -g3 -fPIC no-libunbound no-gmp no-jpake no-krb5 no-md2 no-rc5 no-rfc3779 no-sctp no-ssl-trace \ - no-store no-zlib no-hw no-mdc2 no-seed no-idea enable-ec_nistp_64_gcc_128 no-camellia no-bf no-ripemd \ - no-dsa no-ssl2 no-capieng -DSSL_FORBID_ENULL -DOPENSSL_NO_DTLS1 -DOPENSSL_NO_HEARTBEATS \ - --prefix="$INSTALL_DIR" - -make -j $JOBS depend -make -j $JOBS -make -j $JOBS install_sw - -popd - -exit 0 - diff --git a/codebuild/bin/install_openssl_1_0_2_fips.sh b/codebuild/bin/install_openssl_1_0_2_fips.sh deleted file mode 100755 index fd7521e5d2a..00000000000 --- a/codebuild/bin/install_openssl_1_0_2_fips.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env 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 -ex -pushd "$(pwd)" - -usage() { - echo "install_openssl_1_0_2_fips.sh build_dir install_dir os_name" - exit 1 -} - -if [ "$#" -ne "3" ]; then - usage -fi - -BUILD_DIR=$1 -INSTALL_DIR=$2 -OS_NAME=$3 - -if [ "$OS_NAME" == "linux" ]; then - CONFIGURE="./config -d" -elif [ "$OS_NAME" == "osx" ]; then - echo "WARNING: FIPS and MacOS is not officially supported. This build should only be used for local debugging." - echo "See: http://openssl.6102.n7.nabble.com/Openssl-Fips-build-for-Mac-OSX-64-bit-td44716.html" - CONFIGURE="./Configure darwin64-x86_64-cc" -else - echo "Invalid platform! $OS_NAME" - usage -fi - -# Install the FIPS object module in accordance with OpenSSL FIPS 140-2 Security Policy Annex A. -# https://www.openssl.org/docs/fips/SecurityPolicy-2.0.pdf -# This installation is not FIPS compliant as we do not own the build system architecture. -# It may only be used for testing purposes. -# -# There is no 'latest' download URL for the FIPS object modules -cd "$BUILD_DIR" -# Originally from: http://www.openssl.org/source/openssl-fips-2.0.13.tar.gz -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/2017-08-31_openssl-fips-2.0.13.tar.gz --output openssl-fips-2.0.13.tar.gz -gunzip -c openssl-fips-2.0.13.tar.gz | tar xf - -rm openssl-fips-2.0.13.tar.gz -cd openssl-fips-2.0.13 -mkdir ../OpensslFipsModule -FIPSDIR="$(pwd)/../OpensslFipsModule" -export FIPSDIR -chmod +x ./Configure -$CONFIGURE -make -make install - -cd "$BUILD_DIR" -curl --retry 3 -L https://github.com/openssl/openssl/archive/OpenSSL_1_0_2-stable.zip --output openssl-OpenSSL_1_0_2-stable.zip -unzip openssl-OpenSSL_1_0_2-stable.zip -cd openssl-OpenSSL_1_0_2-stable - -FIPS_OPTIONS="fips --with-fipsdir=$FIPSDIR shared" - -$CONFIGURE $FIPS_OPTIONS -g3 -fPIC no-libunbound no-gmp no-jpake no-krb5 no-md2 no-rc5 \ - no-rfc3779 no-sctp no-ssl-trace no-store no-zlib no-hw no-mdc2 no-seed no-idea \ - enable-ec_nistp_64_gcc_128 no-camellia no-bf no-ripemd no-dsa no-ssl2 no-capieng -DSSL_FORBID_ENULL \ - -DOPENSSL_NO_DTLS1 -DOPENSSL_NO_HEARTBEATS --prefix="$INSTALL_DIR" - -make depend -make -make install_sw - -popd - -exit 0 - diff --git a/codebuild/bin/install_openssl_1_1_1.sh b/codebuild/bin/install_openssl_1_1_1.sh deleted file mode 100755 index 269d017f1cd..00000000000 --- a/codebuild/bin/install_openssl_1_1_1.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env 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 -ex -pushd "$(pwd)" - -usage() { - echo "install_openssl_1_1_1.sh build_dir install_dir os_name" - exit 1 -} - -if [ "$#" -ne "3" ]; then - usage -fi - -BUILD_DIR=$1 -INSTALL_DIR=$2 -OS_NAME=$3 -source codebuild/bin/jobs.sh -RELEASE=1_1_1-stable - -mkdir -p $BUILD_DIR -cd "$BUILD_DIR" -curl --retry 3 -L https://github.com/openssl/openssl/archive/OpenSSL_${RELEASE}.zip --output OpenSSL_${RELEASE}.zip -unzip OpenSSL_${RELEASE}.zip -cd openssl-OpenSSL_${RELEASE} - -if [ "$OS_NAME" == "linux" ]; then - CONFIGURE="./config -d" -elif [[ "$OS_NAME" == "osx" || "$OS_NAME" == "darwin" ]]; then - CONFIGURE="./Configure darwin64-x86_64-cc" -else - echo "Invalid platform! $OS_NAME" - usage -fi - -mkdir -p $INSTALL_DIR -# Use g3 to get debug symbols in libcrypto to chase memory leaks -$CONFIGURE shared -g3 -fPIC \ - no-md2 no-rc5 no-rfc3779 no-sctp no-ssl-trace no-zlib \ - no-hw no-mdc2 no-seed no-idea enable-ec_nistp_64_gcc_128 no-camellia\ - no-bf no-ripemd no-dsa no-ssl2 no-ssl3 no-capieng \ - -DSSL_FORBID_ENULL -DOPENSSL_NO_DTLS1 -DOPENSSL_NO_HEARTBEATS \ - --prefix="$INSTALL_DIR" - -make -j $JOBS depend -make -j $JOBS -make -j $JOBS install_sw - -popd - -exit 0 diff --git a/codebuild/bin/install_openssl_3_0.sh b/codebuild/bin/install_openssl_3_0.sh deleted file mode 100755 index 51accc8e8ad..00000000000 --- a/codebuild/bin/install_openssl_3_0.sh +++ /dev/null @@ -1,94 +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 -ex -pushd "$(pwd)" - -usage() { - echo "install_openssl_3_0.sh build_dir install_dir os_name [fips]" - exit 1 -} - -if [ "$#" -eq "3" ]; then - FIPS=false -elif [ "$#" -eq "4" ] && [ "$4" = "fips" ]; then - FIPS=true -else - usage -fi - -BUILD_DIR=$1 -INSTALL_DIR=$2 -OS_NAME=$3 -source codebuild/bin/jobs.sh -config=$(cat codebuild/bin/s2n_fips_openssl.cnf) - -# Only some versions of Openssl-3 are FIPS validated. -# The list can be found at https://openssl-library.org/source/ -# Maintain separate release versions so that we can change the non-FIPS version -# without worrying about whether or not the new version is FIPS validated. -if $FIPS; then - RELEASE=3.0.9 -else - RELEASE=3.0.7 -fi - -mkdir -p $BUILD_DIR -cd "$BUILD_DIR" -curl --retry 3 -L --output OpenSSL_${RELEASE}.zip \ - https://github.com/openssl/openssl/archive/refs/tags/openssl-${RELEASE}.zip -unzip OpenSSL_${RELEASE}.zip -cd openssl-openssl-${RELEASE} - -if $FIPS; then - CONFIGURE="./Configure enable-fips" -else - CONFIGURE="./Configure" -fi - -mkdir -p $INSTALL_DIR -# Use g3 to get debug symbols in libcrypto to chase memory leaks -$CONFIGURE shared -g3 -fPIC \ - no-md2 no-rc5 no-rfc3779 no-sctp no-ssl-trace no-zlib \ - no-hw no-mdc2 no-seed no-idea enable-ec_nistp_64_gcc_128 no-camellia\ - no-bf no-ripemd no-dsa no-ssl2 no-ssl3 no-capieng no-dtls \ - -DSSL_FORBID_ENULL -DOPENSSL_NO_DTLS1 -DOPENSSL_NO_HEARTBEATS \ - --prefix="$INSTALL_DIR" - -make -j $JOBS -make -j $JOBS test -make -j $JOBS install - -popd - -# sym-link lib -> lib64 since codebuild assumes /lib path -pushd $INSTALL_DIR -ln -s lib64 lib -popd - -# Openssl3 uses the openssl config file to enable fips -# See https://docs.openssl.org/master/man7/fips_module/#making-all-applications-use-the-fips-module-by-default -if $FIPS; then - # We assume that the configs are in the /ssl directory of $INSTALL_DIR - pushd $INSTALL_DIR - config_path=./ssl/openssl.cnf - # We need an absolute path for the fips config - fips_config_path=$(pwd)/ssl/fipsmodule.cnf - config=$(echo "$config" | sed "s,S2N_FIPS_CONFIG_PATH,$fips_config_path,") - echo "$config" > $config_path - popd -fi - -exit 0 diff --git a/codebuild/bin/install_prlimit.sh b/codebuild/bin/install_prlimit.sh deleted file mode 100755 index 284749f8469..00000000000 --- a/codebuild/bin/install_prlimit.sh +++ /dev/null @@ -1,56 +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_prlimit.sh download_dir install_dir" - exit 1 -} - -if [ "$#" -ne "2" ]; then - usage -fi - -BUILD_DIR=$1 -INSTALL_DIR=$2 -source codebuild/bin/jobs.sh - -sudo apt-get install -y libncurses5-dev - -cd "$BUILD_DIR" -# Originally from: https://www.kernel.org/pub/linux/utils/util-linux/v2.25/util-linux-2.25.2.tar.gz -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/2017-08-29_util-linux-2.25.2.tar.gz --output util-linux-2.25.2.tar.gz -tar -xzvf util-linux-2.25.2.tar.gz -cd util-linux-2.25.2 -./configure ADJTIME_PATH=/var/lib/hwclock/adjtime \ - --disable-chfn-chsh \ - --disable-login \ - --disable-nologin \ - --disable-su \ - --disable-setpriv \ - --disable-runuser \ - --disable-pylibmount \ - --disable-static \ - --without-python \ - --without-systemd \ - --disable-makeinstall-chown \ - --without-systemdsystemunitdir \ - --without-ncurses \ - --prefix="$INSTALL_DIR" || cat config.log - -make -j $JOBS > /dev/null -make -j $JOBS install > /dev/null diff --git a/codebuild/bin/install_python.sh b/codebuild/bin/install_python.sh deleted file mode 100755 index 8167b10b184..00000000000 --- a/codebuild/bin/install_python.sh +++ /dev/null @@ -1,36 +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. -# -#!/bin/bash - -set -e - -if [ "$#" -ne 3 ]; then - echo "install_python.sh libcrypto_root build_dir install_dir" - exit 1 -fi - -LIBCRYPTO_ROOT=$1 -BUILD_DIR=$2 -INSTALL_DIR=$3 -source codebuild/bin/jobs.sh - -cd "$BUILD_DIR" -# Originally from: https://www.python.org/ftp/python/3.6.0/Python-3.6.0.tgz -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/2017-08-29_Python-3.6.0.tgz --output Python-3.6.0.tgz -tar xzf Python-3.6.0.tgz -cd Python-3.6.0 - CPPFLAGS="-I$LIBCRYPTO_ROOT/include" LDFLAGS="-Wl,-rpath,$LIBCRYPTO_ROOT/lib -L$LIBCRYPTO_ROOT/lib" ./configure --prefix="$INSTALL_DIR" -make -j $JOBS -make -j $JOBS install diff --git a/codebuild/bin/install_s2n_head.sh b/codebuild/bin/install_s2n_head.sh deleted file mode 100755 index 21505de1bf1..00000000000 --- a/codebuild/bin/install_s2n_head.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env 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 -eux - -usage() { - echo "install_s2n_head.sh build_dir" - exit 1 -} - -BUILD_DIR=$1 -SRC_ROOT=${SRC_ROOT:-$(pwd)} - -if [ "$#" -ne "1" ]; then - usage -fi - -# CMake(nix) and Make are using different directory structures. -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="." - # Make sure main is available in our workspace. - # This is a workaround for the merge queue workflow. - git fetch origin - git checkout main - git checkout $CODEBUILD_SOURCE_VERSION -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 -eu - -s2nc_head="$DEST_DIR/s2nc_head" -if [[ -f "$s2nc_head" ]]; then - now=$(date +%s) - last_modified=$(stat -c %Y "$s2nc_head") - days_old=$(( (now - last_modified) / 86400)) - if ((days_old <= 1)); then - echo "Reusing s2n_head: s2nc_head exists and is $days_old days old." - exit 0 - fi -fi - -git clone --branch "main" --single-branch "$CLONE_SRC" "$BUILD_DIR" - -cmake "$BUILD_DIR" -B"$BUILD_DIR"/build "$EXTRA_BUILD_FLAGS" \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DBUILD_SHARED_LIBS=on \ - -DBUILD_TESTING=on -cmake --build "$BUILD_DIR"/build --target s2nc -- -j $(nproc) -cmake --build "$BUILD_DIR"/build --target s2nd -- -j $(nproc) - -cp -f "$BUILD_DIR"/build/bin/s2nc "$s2nc_head" -cp -f "$BUILD_DIR"/build/bin/s2nd "$DEST_DIR"/s2nd_head - -if [[ -f "$s2nc_head" ]]; then - echo "Successfully installed s2n?_head" -else - echo "$s2nc_head not found, head build failed" - exit 255 -fi - -exit 0 diff --git a/codebuild/bin/install_saw.sh b/codebuild/bin/install_saw.sh deleted file mode 100755 index a1cff08e82e..00000000000 --- a/codebuild/bin/install_saw.sh +++ /dev/null @@ -1,45 +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 -xe - -usage() { - echo "install_saw.sh download_dir install_dir" - exit 1 -} - -if [ "$#" -ne "2" ]; then - usage -fi - -DOWNLOAD_DIR=$1 -INSTALL_DIR=$2 - -if [ -x "$INSTALL_DIR/bin/saw" ]; then - echo "Saw already installed at $INSTALL_DIR/bin/saw"; - exit 0; -fi - -mkdir -p "$DOWNLOAD_DIR" -cd "$DOWNLOAD_DIR" - -#download saw binaries -curl --retry 3 https://s2n-public-test-dependencies.s3.us-west-2.amazonaws.com/saw-0.9.0.99-Linux-x86_64.tar.gz --output saw.tar.gz - -mkdir -p saw && tar -xzf saw.tar.gz --strip-components=1 -C saw -mkdir -p "$INSTALL_DIR" && mv saw/* "$INSTALL_DIR" - -"$INSTALL_DIR"/bin/saw --version diff --git a/codebuild/bin/install_shellcheck.sh b/codebuild/bin/install_shellcheck.sh deleted file mode 100755 index 3635896b7e0..00000000000 --- a/codebuild/bin/install_shellcheck.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 -source codebuild/bin/s2n_setup_env.sh - -usage() { - echo "install_shellcheck.sh" - exit 1 -} - -install_shellcheck() { - wget "https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.$ARCH.tar.xz" -O /tmp/shellcheck.tar.xz - tar -Jxf /tmp/shellcheck.tar.xz -C /tmp - mv /tmp/shellcheck-v*/shellcheck /usr/local/bin/ - chmod 755 /usr/local/bin/shellcheck -} - -if [ "$#" -ne "0" ]; then - usage -fi - -case "$OS_NAME" in - "amazon linux"|"linux") - which shellcheck || install_shellcheck - ;; - "darwin" ) - brew install shellcheck || true ; - ;; - *) - echo "Unknown platform" - exit 255 - ;; -esac diff --git a/codebuild/bin/install_sidetrail.sh b/codebuild/bin/install_sidetrail.sh deleted file mode 100755 index 78a5dcfadfa..00000000000 --- a/codebuild/bin/install_sidetrail.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# Copyright 2017 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 -set -x - -usage() { - echo "install_sidetrail.sh install_dir" - exit 1 -} - -if [ "$#" -ne "1" ]; then - usage -fi - -INSTALL_DIR=$1 - -cd "$INSTALL_DIR" - -#install smack -git clone https://github.com/danielsn/smack.git -b sidewinder-debug -cd smack/bin - -clang --version -which clang - -./build.sh - -# Disabling ShellCheck using https://github.com/koalaman/shellcheck/wiki/Directive -# Turn of Warning in one line as https://github.com/koalaman/shellcheck/wiki/SC1090 -# shellcheck disable=SC1090 -source "$INSTALL_DIR"/smack.environment - -#install ctverif -cd "$INSTALL_DIR" -git clone --depth 1 https://github.com/imdea-software/verifying-constant-time.git -b test-automation - diff --git a/codebuild/bin/install_sidetrail_dependencies.sh b/codebuild/bin/install_sidetrail_dependencies.sh deleted file mode 100755 index 6d69563ae6a..00000000000 --- a/codebuild/bin/install_sidetrail_dependencies.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash -# Copyright 2017 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 -set -x - -#Figlet is required for ctverif printing -sudo apt-get install -y figlet - -#Install boogieman -gem install bam-bam-boogieman -which bam - -#Install the apt-get dependencies from the smack build script: this way they will still be there -#when we get things from cache -DEPENDENCIES="git cmake python-yaml python-psutil unzip wget python3-yaml" -DEPENDENCIES+=" mono-complete libz-dev libedit-dev" -DEPENDENCIES+=" clang-3.9 llvm-3.9 llvm-3.9-dev" - -# Adding MONO repository -sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF -echo "deb http://download.mono-project.com/repo/ubuntu trusty main" | sudo tee /etc/apt/sources.list.d/mono-official.list - -sudo apt-get update -o Acquire::CompressionTypes::Order::=gz -sudo apt-get install -y ${DEPENDENCIES} -pip install pyyaml - -LLVM_SHORT_VERSION=3.9 - -sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_SHORT_VERSION} 30 -sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_SHORT_VERSION} 30 -sudo update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-${LLVM_SHORT_VERSION} 30 -sudo update-alternatives --install /usr/bin/llvm-link llvm-link /usr/bin/llvm-link-${LLVM_SHORT_VERSION} 30 -sudo update-alternatives --install /usr/bin/llvm-dis llvm-dis /usr/bin/llvm-dis-${LLVM_SHORT_VERSION} 30 - -which clang -clang --version -clang-3.9 --version - -mkdir -p ~/override_clang -ln -s /usr/bin/clang ~/override_clang/clang -ln -s /usr/bin/clang++ ~/override_clang/clang++ -ln -s /usr/bin/llvm-config ~/override_clang/llvm-config -ln -s /usr/bin/llvm-link ~/override_clang/llvm-link -ln -s /usr/bin/llvm-dis ~/override_clang/llvm-dis -sudo chmod +x ~/override_clang/* - -export PATH="$HOME/override_clang/:${PATH}" -which clang -clang --version -clang-3.9 --version - -which python -python --version -pip install psutil diff --git a/codebuild/bin/install_sslyze.sh b/codebuild/bin/install_sslyze.sh deleted file mode 100755 index 5636780641c..00000000000 --- a/codebuild/bin/install_sslyze.sh +++ /dev/null @@ -1,37 +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 -. codebuild/bin/s2n_setup_env.sh - -aarch64_install() { - echo "sslyze has a dependency on nassl, which will not build on ARM." -} - -case "$ARCH" in - "aarch64") - aarch64_install - exit 1 - ;; - *) - python3 -m pip install --user --upgrade pip setuptools - # Version 3.0.0 introduces backwards incompatible changes in the JSON we parse. - # TODO: unpin the sslyze version and update the json parsing sslyze output. - python3 -m pip install --user "sslyze<3.0.0" - sudo ln -s /root/.local/bin/sslyze /usr/bin/sslyze || true - which sslyze - sslyze --version - ;; -esac diff --git a/codebuild/bin/install_ubuntu_dependencies.sh b/codebuild/bin/install_ubuntu_dependencies.sh deleted file mode 100755 index 8321661ce15..00000000000 --- a/codebuild/bin/install_ubuntu_dependencies.sh +++ /dev/null @@ -1,68 +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. -# - -# Shim code to get local docker/ec2 instances bootstrapped like a CodeBuild instance. -# Not actually used by CodeBuild. - -# This script is now targeting Ubuntu 24 not Ubuntu 18. - -source codebuild/bin/s2n_setup_env.sh - -set -e - -github_apt(){ - apt update -y - apt install -y gh -} -get_rust() { - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - source $HOME/.cargo/env - rustup default nightly -} - -base_packages() { - echo "Installing repositories and base packages" - apt update -y - apt install -y software-properties-common - add-apt-repository ppa:ubuntu-toolchain-r/test -y - add-apt-repository ppa:longsleep/golang-backports -y - apt-get update -o Acquire::CompressionTypes::Order::=gz - - DEPENDENCIES="unzip make indent iproute2 kwstyle libssl-dev net-tools tcpdump valgrind lcov m4 nettle-dev nettle-bin pkg-config psmisc gcc g++ zlib1g-dev python3-pip python3-testresources llvm libclang-dev curl shellcheck git tox cmake libtool ninja-build golang-go quilt jq apache2" - if [[ -n "${GCC_VERSION:-}" ]] && [[ "${GCC_VERSION:-}" != "NONE" ]]; then - DEPENDENCIES+=" gcc-$GCC_VERSION g++-$GCC_VERSION"; - fi - - apt-get -y install --no-install-recommends ${DEPENDENCIES} -} - -base_packages -github_apt -get_rust - -# If prlimit is not on our current PATH, download and compile prlimit manually. s2n needs prlimit to memlock pages -if ! type prlimit > /dev/null && [[ ! -d "$PRLIMIT_INSTALL_DIR" ]]; then - mkdir -p "$PRLIMIT_INSTALL_DIR"; - codebuild/bin/install_prlimit.sh "$(mktemp -d)" "$PRLIMIT_INSTALL_DIR"; -fi - -if [[ "$TESTS" == "ctverif" || "$TESTS" == "ALL" ]] && [[ ! -d "$CTVERIF_INSTALL_DIR" ]]; then - mkdir -p "$CTVERIF_INSTALL_DIR" && codebuild/bin/install_ctverif.sh "$CTVERIF_INSTALL_DIR" > /dev/null ; fi - -if [[ "$TESTS" == "sidetrail" || "$TESTS" == "ALL" ]] ; then - codebuild/bin/install_sidetrail_dependencies.sh ; fi - -if [[ "$TESTS" == "sidetrail" || "$TESTS" == "ALL" ]] && [[ ! -d "$SIDETRAIL_INSTALL_DIR" ]]; then - mkdir -p "$SIDETRAIL_INSTALL_DIR" && codebuild/bin/install_sidetrail.sh "$SIDETRAIL_INSTALL_DIR" > /dev/null ; fi diff --git a/codebuild/bin/install_z3_yices.sh b/codebuild/bin/install_z3_yices.sh deleted file mode 100755 index ff0b8c64d3c..00000000000 --- a/codebuild/bin/install_z3_yices.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_z3_yices.sh download_dir install_dir" - exit 1 -} - -if [ "$#" -ne "2" ]; then - usage -fi - -DOWNLOAD_DIR=$1 -INSTALL_DIR=$2 - -mkdir -p "$DOWNLOAD_DIR" -cd "$DOWNLOAD_DIR" - -#download z3 and yices -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/yices-2.6.1-x86_64-pc-linux-gnu-static-gmp.tar.gz --output yices.tar.gz -tar -xf yices.tar.gz - -curl --retry 3 https://s3-us-west-2.amazonaws.com/s2n-public-test-dependencies/z3-4.8.8-x64-ubuntu-16.04.zip --output z3.zip -unzip z3.zip - -mkdir -p "$INSTALL_DIR"/bin -mv z3-4.8.8-x64-ubuntu-16.04/bin/* "$INSTALL_DIR"/bin -mv yices-2.6.1/bin/* "$INSTALL_DIR"/bin -chmod +x "$INSTALL_DIR"/bin/* - -"$INSTALL_DIR"/bin/yices-smt2 --version -"$INSTALL_DIR"/bin/yices --version -"$INSTALL_DIR"/bin/z3 --version diff --git a/codebuild/bin/jobs.sh b/codebuild/bin/jobs.sh deleted file mode 100644 index 98e72960eeb..00000000000 --- a/codebuild/bin/jobs.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env 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. -# - -# Find if the environment has more than 8 cores -JOBS=8 -if [[ -x "$(command -v nproc)" ]]; then - UNITS=$(nproc); - if [[ $UNITS -gt $JOBS ]]; then - JOBS=$UNITS; - fi -fi diff --git a/codebuild/bin/run_cppcheck.sh b/codebuild/bin/run_cppcheck.sh deleted file mode 100755 index f9bcc0ffdc7..00000000000 --- a/codebuild/bin/run_cppcheck.sh +++ /dev/null @@ -1,40 +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 "run_cppcheck.sh install_dir" - exit 1 -} - -if [ "$#" -ne "1" ]; then - usage -fi - -INSTALL_DIR=$1 - -CPPCHECK_EXECUTABLE=${INSTALL_DIR}/cppcheck - -FAILED=0 -$CPPCHECK_EXECUTABLE --version -$CPPCHECK_EXECUTABLE --std=c99 --error-exitcode=-1 --quiet --force -j 8 --enable=all --template='[{file}:{line}]: ({severity}:{id}) {message}' --inline-suppr --suppressions-list=codebuild/bin/cppcheck_suppressions.txt -I . -I ./tests api bin crypto error stuffer ./tests/unit tls utils || FAILED=1 -if [ $FAILED == 1 ]; -then - printf "\\033[31;1mFAILED cppcheck\\033[0m\\n" - exit -1 -else - printf "\\033[32;1mPASSED cppcheck\\033[0m\\n" -fi diff --git a/codebuild/bin/run_ctverif.sh b/codebuild/bin/run_ctverif.sh deleted file mode 100755 index f421927f2ad..00000000000 --- a/codebuild/bin/run_ctverif.sh +++ /dev/null @@ -1,60 +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 "run_ctverif.sh install_dir" - exit 1 -} - -if [ "$#" -ne "1" ]; then - usage -fi - -INSTALL_DIR=$1 -export CTVERIF_DIR="${1}/verifying-constant-time" -SMACK_DIR="${1}/smack" - -#Put the dependencies are on the path - -# Disabling ShellCheck using https://github.com/koalaman/shellcheck/wiki/Directive -# Turn of Warning in one line as https://github.com/koalaman/shellcheck/wiki/SC1090 -# shellcheck disable=SC1090 -source "${INSTALL_DIR}/smack.environment" -export PATH="${SMACK_DIR}/bin:${SMACK_DIR}/build:${PATH}" -#Test that they are really there -which smack || echo "can't find smack" -which boogie || echo "can't find z3" -which llvm2bpl || echo "can't find llvm2bpl" - -#copy the current version of the file to the test -cd "${BASE_S2N_DIR}/tests/ctverif" -cp "${BASE_S2N_DIR}/utils/s2n_safety.c" . -make clean - -#run the test. We expect both to pass, and none to fail -FAILED=0 -EXPECTED_PASS=2 -EXPECTED_FAIL=0 -make 2>&1 | ./count_success.pl $EXPECTED_PASS $EXPECTED_FAIL || FAILED=1 - -if [ $FAILED == 1 ]; -then - printf "\\033[31;1mFAILED ctverif\\033[0m\\n" - exit -1 -else - printf "\\033[32;1mPASSED ctverif\\033[0m\\n" -fi diff --git a/codebuild/bin/run_kwstyle.sh b/codebuild/bin/run_kwstyle.sh deleted file mode 100755 index 6fdd8c6eb98..00000000000 --- a/codebuild/bin/run_kwstyle.sh +++ /dev/null @@ -1,40 +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 -eu - -which KWStyle - -S2N_FILES=$(find "$PWD" -type f -name "s2n_*.[ch]" | grep -v "test") -FAILED=0 - -for file in $S2N_FILES; do - set +e - ERROR_LIST=$(KWStyle -gcc -v -xml codebuild/bin/KWStyle.xml "$file") - set -e - if [ "$ERROR_LIST" != "" ] ; - then - echo "$ERROR_LIST" - FAILED=1 - fi -done - -if [ $FAILED == 1 ]; -then - printf "\\033[31;1mFAILED kwstyle\\033[0m\\n" - exit -1 -else - printf "\\033[32;1mPASSED kwstyle\\033[0m\\n" -fi diff --git a/codebuild/bin/run_sidetrail.sh b/codebuild/bin/run_sidetrail.sh deleted file mode 100755 index 336c23f0711..00000000000 --- a/codebuild/bin/run_sidetrail.sh +++ /dev/null @@ -1,71 +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 -ex - -usage() { - echo "run_sidetrail.sh install_dir s2n_dir" - exit 1 -} - -runSingleTest() { - cd "${BASE_S2N_DIR}/tests/sidetrail/working/${1}" - ./copy_as_needed.sh - make clean - make 2>&1 | tee out.txt - - ../../count_success.pl 1 0 out.txt -} - -runNegativeTest() { - cd "${BASE_S2N_DIR}/tests/sidetrail/working/${1}" - ./copy_as_needed.sh - make clean - make 2>&1 | tee out.txt - - ../../count_success.pl 0 1 out.txt -} - -if [[ "$#" -ne "2" ]]; then - usage -fi - -INSTALL_DIR=$1 -SMACK_DIR="${1}/smack" -BASE_S2N_DIR=$2 - -#Put the dependencies on the path - -# Disabling ShellCheck using https://github.com/koalaman/shellcheck/wiki/Directive -# Turn of Warning in one line as https://github.com/koalaman/shellcheck/wiki/SC1090 -# shellcheck disable=SC1090 -source "${INSTALL_DIR}/smack.environment" -export PATH="${SMACK_DIR}/bin:${SMACK_DIR}/build:${PATH}" -#Test that they are really there -which smack || echo "can't find smack" -which boogie || echo "can't find z3" -which llvm2bpl || echo "can't find llvm2bpl" -which clang -clang --version -echo $BOOGIE -echo $CORRAL - -runNegativeTest "s2n-record-read-cbc-negative-test" -runSingleTest "s2n-cbc" # Takes 6m 30s -runSingleTest "s2n-record-read-aead" -runSingleTest "s2n-record-read-cbc" -runSingleTest "s2n-record-read-composite" -runSingleTest "s2n-record-read-stream" - diff --git a/codebuild/bin/s2n_apache2.sh b/codebuild/bin/s2n_apache2.sh deleted file mode 100644 index 0ad94eb2730..00000000000 --- a/codebuild/bin/s2n_apache2.sh +++ /dev/null @@ -1,50 +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 -eu - -apache2_config() { - cert_dir="$1" - command="$2" - echo "apache2: ${command}" - - APACHE_SERVER_ROOT="$APACHE2_INSTALL_DIR" \ - APACHE_RUN_USER=www-data \ - APACHE_RUN_GROUP=www-data \ - APACHE_PID_FILE="${APACHE2_INSTALL_DIR}/run/apache2.pid" \ - APACHE_RUN_DIR="${APACHE2_INSTALL_DIR}/run" \ - APACHE_LOCK_DIR="${APACHE2_INSTALL_DIR}/lock" \ - APACHE_LOG_DIR="${APACHE2_INSTALL_DIR}/log" \ - APACHE_CERT_DIR="${cert_dir}" \ - apache2 -k "${command}" -f "${APACHE2_INSTALL_DIR}/apache2.conf" -} - -apache2_stop() { - cert_dir="$1" - apache2_config "${cert_dir}" stop -} - -apache2_start() { - if [[ ! -f "$APACHE2_INSTALL_DIR/apache2.conf" ]]; then - echo "apache2 not installed" - exit 1 - fi - - cert_dir="$1" - apache2_config "${cert_dir}" start - - # Stop the apache server after tests finish, even if an error occurs - trap 'apache2_stop "${cert_dir}"' ERR EXIT -} diff --git a/codebuild/bin/s2n_codebuild.sh b/codebuild/bin/s2n_codebuild.sh deleted file mode 100755 index 7d5d43b1506..00000000000 --- a/codebuild/bin/s2n_codebuild.sh +++ /dev/null @@ -1,113 +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 - -source codebuild/bin/s2n_setup_env.sh - -# Use prlimit to set the memlock limit to unlimited for linux. OSX is unlimited by default -# Codebuild Containers aren't allowing prlimit changes (and aren't being caught with the usual cgroup check) -if [[ "$OS_NAME" == "linux" && -n "$CODEBUILD_BUILD_ARN" ]]; then - PRLIMIT_LOCATION=`which prlimit` - sudo -E ${PRLIMIT_LOCATION} --pid "$$" --memlock=unlimited:unlimited; -fi - -# Set the version of GCC as Default if it's required -if [[ -n "$GCC_VERSION" ]] && [[ "$GCC_VERSION" != "NONE" ]]; then - alias gcc=$(which gcc-$GCC_VERSION); -fi - -# Find if the environment has more than 8 cores -JOBS=8 -if [[ -x "$(command -v nproc)" ]]; then - UNITS=$(nproc); - if [[ $UNITS -gt $JOBS ]]; then - JOBS=$UNITS; - fi -fi - -make clean; - -echo "Using $JOBS jobs for make.."; -echo "running with libcrypto: ${S2N_LIBCRYPTO}, gcc_version: ${GCC_VERSION}" - -test_linked_libcrypto() { - s2n_executable="$1" - so_path="${LIBCRYPTO_ROOT}/lib/libcrypto.so" - echo "Testing for linked libcrypto: ${so_path}" - echo "ldd:" - ldd "${s2n_executable}" - ldd "${s2n_executable}" | grep "${so_path}" || \ - { echo "Linked libcrypto is incorrect."; exit 1; } - echo "Test succeeded!" -} - -setup_apache_server() { - # Start the apache server if the list of tests isn't defined, meaning all tests - # are to be run, or if the renegotiate test is included in the list of tests. - if [[ -z $TOX_TEST_NAME ]] || [[ "${TOX_TEST_NAME}" == *"test_renegotiate_apache"* ]]; then - source codebuild/bin/s2n_apache2.sh - APACHE_CERT_DIR="$(pwd)/tests/pems" - - apache2_start "${APACHE_CERT_DIR}" - fi -} - -run_integration_v2_tests() { - setup_apache_server - "$CB_BIN_DIR/install_s2n_head.sh" "$(mktemp -d)" - cmake . -Bbuild \ - -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT \ - -DBUILD_SHARED_LIBS=on \ - -DS2N_INTEG_TESTS=on \ - -DPython3_EXECUTABLE=$(which python3) - cmake --build ./build --clean-first -- -j $(nproc) - test_linked_libcrypto ./build/bin/s2nc - test_linked_libcrypto ./build/bin/s2nd - cp -f ./build/bin/s2nc "$BASE_S2N_DIR"/bin/s2nc - cp -f ./build/bin/s2nd "$BASE_S2N_DIR"/bin/s2nd - cd ./build/ - for test_name in $TOX_TEST_NAME; do - test="${test_name//test_/}" - echo "Running... ctest --no-tests=error --output-on-failure --verbose -R ^integrationv2_${test}$" - ctest --no-tests=error --output-on-failure --verbose -R ^integrationv2_${test}$ - done -} - -run_unit_tests() { - cmake . -Bbuild \ - -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT \ - -DBUILD_SHARED_LIBS=on \ - -DS2N_ENFORCE_PROPER_LIBCRYPTO_FEATURE_PROBE=1 - cmake --build ./build -- -j $(nproc) - test_linked_libcrypto ./build/bin/s2nc - cmake --build build/ --target test -- ARGS="-L unit --output-on-failure -j $(nproc)" -} - -# Run Multiple tests on one flag. -if [[ "$TESTS" == "ALL" || "$TESTS" == "sawHMACPlus" ]] && [[ "$OS_NAME" == "linux" ]]; then make -C tests/saw tmp/verify_HMAC.log tmp/verify_drbg.log failure-tests; fi - -# Run Individual tests -if [[ "$TESTS" == "ALL" || "$TESTS" == "unit" ]]; then run_unit_tests; fi -if [[ "$TESTS" == "ALL" || "$TESTS" == "interning" ]]; then ./codebuild/bin/test_libcrypto_interning.sh; fi -if [[ "$TESTS" == "ALL" || "$TESTS" == "exec_leak" ]]; then ./codebuild/bin/test_exec_leak.sh; fi -if [[ "$TESTS" == "ALL" || "$TESTS" == "integrationv2" ]]; then run_integration_v2_tests; fi -if [[ "$TESTS" == "ALL" || "$TESTS" == "crt" ]]; then ./codebuild/bin/build_aws_crt_cpp.sh $(mktemp -d) $(mktemp -d); fi -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" == "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 -if [[ "$TESTS" == "sawHMACFailure" ]]; then make -C tests/saw failure-tests ; fi diff --git a/codebuild/bin/s2n_codebuild_al.sh b/codebuild/bin/s2n_codebuild_al.sh deleted file mode 100755 index 0a7cd2d9b63..00000000000 --- a/codebuild/bin/s2n_codebuild_al.sh +++ /dev/null @@ -1,49 +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 -eu - -source codebuild/bin/s2n_setup_env.sh -# Used to test if we're running in CodeBuild -CODEBUILD_BUILD_ARN_="${CODEBUILD_BUILD_ARN:-}" - -if [[ ${DISTRO} != "amazon linux" ]]; then - echo "Target Amazon Linux, but running on $DISTRO: Nothing to do." - exit 1; -else - # AL2023 case - BUILD_FLAGS="-DCMAKE_BUILD_TYPE=RelWithDebInfo" - # AL2 case; Linker flags are a workaround for system openssl - if [[ ${VERSION_ID} == '2' ]]; then - BUILD_FLAGS=$(echo -e '-DCMAKE_EXE_LINKER_FLAGS="-lcrypto -lz" \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON') - fi -fi - -# Use prlimit to set the memlock limit to unlimited for linux. OSX is unlimited by default -# Codebuild Containers aren't allowing prlimit changes (and aren't being caught with the usual cgroup check) -if [[ "$OS_NAME" == "linux" && -z "$CODEBUILD_BUILD_ARN_" ]]; then - PRLIMIT_LOCATION=$(which prlimit) - sudo -E ${PRLIMIT_LOCATION} --pid "$$" --memlock=unlimited:unlimited; -fi - -case "$TESTS" in - "unit") - eval cmake . -Bbuild "${BUILD_FLAGS}" - cmake --build ./build -j "$(nproc)" - CTEST_PARALLEL_LEVEL="$(nproc)" cmake --build ./build --target test -- ARGS="-L unit --output-on-failure" - ;; - *) echo "Unknown test"; exit 1;; -esac diff --git a/codebuild/bin/s2n_dynamic_load_test.c b/codebuild/bin/s2n_dynamic_load_test.c deleted file mode 100644 index d8bc929982a..00000000000 --- a/codebuild/bin/s2n_dynamic_load_test.c +++ /dev/null @@ -1,112 +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 - -static void *s2n_load_dynamic_lib(void *ctx) -{ - const char *s2n_so_path = ctx; - - void *s2n_so = dlopen(s2n_so_path, RTLD_NOW); - if (!s2n_so) { - printf("Error dynamically loading libs2n\n"); - printf("%s\n", dlerror()); - exit(1); - } - - int (*s2n_init_dl)(void) = NULL; - *(void **) (&s2n_init_dl) = dlsym(s2n_so, "s2n_init"); - if (dlerror()) { - printf("Error dynamically loading s2n_init\n"); - exit(1); - } - - int (*s2n_cleanup_final_dl)(void) = NULL; - *(void **) (&s2n_cleanup_final_dl) = dlsym(s2n_so, "s2n_cleanup_final"); - if (dlerror()) { - printf("Error dynamically loading s2n_cleanup_final\n"); - exit(1); - } - - int (*s2n_errno_location_dl)(void) = NULL; - *(void **) (&s2n_errno_location_dl) = dlsym(s2n_so, "s2n_errno_location"); - if (dlerror()) { - printf("Error dynamically loading s2n_errno_location\n"); - exit(1); - } - - const char *(*s2n_strerror_debug_dl)(int error, const char *lang) = NULL; - *(void **) (&s2n_strerror_debug_dl) = dlsym(s2n_so, "s2n_strerror_debug"); - if (dlerror()) { - printf("Error dynamically loading s2n_strerror_debug\n"); - exit(1); - } - - if ((*s2n_init_dl)()) { - int s2n_errno = (*s2n_errno_location_dl)(); - fprintf(stderr, "Error calling s2n_init: '%s'\n", (*s2n_strerror_debug_dl)(s2n_errno, "EN")); - exit(1); - } - if ((*s2n_cleanup_final_dl)()) { - int s2n_errno = (*s2n_errno_location_dl)(); - fprintf(stderr, "Error calling s2n_cleanup_final: '%s'\n", (*s2n_strerror_debug_dl)(s2n_errno, "EN")); - exit(1); - } - - /* TODO: https://github.com/aws/s2n-tls/issues/4827 - * This dlclose call invokes the pthread key destructor that - * asserts that the s2n-tls library is initialized, which at this point - * is not, due to the s2n_cleanup_final call. This is a bug. - if (dlclose(s2n_so)) { - printf("Error closing libs2n\n"); - printf("%s\n", dlerror()); - exit(1); - } - */ - - return NULL; -} - -int main(int argc, char *argv[]) -{ - if (argc != 2) { - printf("Usage: s2n_dynamic_load_test \n"); - exit(1); - } - - /* s2n-tls library can be dynamically loaded and cleaned up safely - * - * We can't use any s2n test macros because this test doesn't get linked to - * s2n during compile-time. This test is in a loop to make sure that we are - * cleaning up pthread keys properly. - */ - for (size_t i = 0; i <= PTHREAD_KEYS_MAX + 1; i++) { - pthread_t thread_id = { 0 }; - if (pthread_create(&thread_id, NULL, &s2n_load_dynamic_lib, argv[1])) { - printf("Error creating thread at loop index: %li\n", i); - exit(1); - } - if (pthread_join(thread_id, NULL)) { - printf("Error joining thread at loop index: %li\n", i); - exit(1); - } - } - - return 0; -} diff --git a/codebuild/bin/s2n_fips_openssl.cnf b/codebuild/bin/s2n_fips_openssl.cnf deleted file mode 100644 index 1017e3792a7..00000000000 --- a/codebuild/bin/s2n_fips_openssl.cnf +++ /dev/null @@ -1,383 +0,0 @@ -# A copy of the default OpenSSL config file with the FIPS provider enabled. -# See the instructions at: -# https://docs.openssl.org/master/man7/fips_module/#making-all-applications-use-the-fips-module-by-default - -# -# OpenSSL example configuration file. -# See doc/man5/config.pod for more info. -# -# This is mostly being used for generation of certificate requests, -# but may be used for auto loading of providers - -# Note that you can include other files from the main configuration -# file using the .include directive. -#.include filename - -# This definition stops the following lines choking if HOME isn't -# defined. -HOME = . - - # Use this in order to automatically load providers. -openssl_conf = openssl_init - -# Comment out the next line to ignore configuration errors -config_diagnostics = 1 - -# Extra OBJECT IDENTIFIER info: -# oid_file = $ENV::HOME/.oid -oid_section = new_oids - -# To use this configuration file with the "-extfile" option of the -# "openssl x509" utility, name here the section containing the -# X.509v3 extensions to use: -# extensions = -# (Alternatively, use a configuration file that has only -# X.509v3 extensions in its main [= default] section.) - -[ new_oids ] -# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. -# Add a simple OID like this: -# testoid1=1.2.3.4 -# Or use config file substitution like this: -# testoid2=${testoid1}.5.6 - -# Policies used by the TSA examples. -tsa_policy1 = 1.2.3.4.1 -tsa_policy2 = 1.2.3.4.5.6 -tsa_policy3 = 1.2.3.4.5.7 - -# For FIPS -# Refer to the OpenSSL security policy for more information. -.include S2N_FIPS_CONFIG_PATH - -[openssl_init] -providers = provider_sect -alg_section = algorithm_sect - -# List of providers to load -[provider_sect] -default = default_sect -fips = fips_sect - -[default_sect] -activate = 1 - -[algorithm_sect] -default_properties = fips=yes - -#################################################################### -[ ca ] -default_ca = CA_default # The default ca section - -#################################################################### -[ CA_default ] - -dir = ./demoCA # Where everything is kept -certs = $dir/certs # Where the issued certs are kept -crl_dir = $dir/crl # Where the issued crl are kept -database = $dir/index.txt # database index file. -#unique_subject = no # Set to 'no' to allow creation of - # several certs with same subject. -new_certs_dir = $dir/newcerts # default place for new certs. - -certificate = $dir/cacert.pem # The CA certificate -serial = $dir/serial # The current serial number -crlnumber = $dir/crlnumber # the current crl number - # must be commented out to leave a V1 CRL -crl = $dir/crl.pem # The current CRL -private_key = $dir/private/cakey.pem# The private key - -x509_extensions = usr_cert # The extensions to add to the cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -# Extension copying option: use with caution. -# copy_extensions = copy - -# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs -# so this is commented out by default to leave a V1 CRL. -# crlnumber must also be commented out to leave a V1 CRL. -# crl_extensions = crl_ext - -default_days = 365 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = default # use public key default MD -preserve = no # keep passed DN ordering - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_match - -# For the CA policy -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 2048 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extensions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -# req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgets Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - - -# Extensions for a typical CA - - -# PKIX recommendation. - -subjectKeyIdentifier=hash - -authorityKeyIdentifier=keyid:always,issuer - -basicConstraints = critical,CA:true - -# Key usage: this is typical for a CA certificate. However since it will -# prevent it being used as an test self-signed certificate it is best -# left out by default. -# keyUsage = cRLSign, keyCertSign - -# Include email address in subject alt name: another PKIX recommendation -# subjectAltName=email:copy -# Copy issuer details -# issuerAltName=issuer:copy - -# DER hex encoding of an extension: beware experts only! -# obj=DER:02:03 -# Where 'obj' is a standard or added object -# You can even override a supported extension: -# basicConstraints= critical, DER:30:03:01:01:FF - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ proxy_cert_ext ] -# These extensions should be added when creating a proxy certificate - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -# This really needs to be in place for it to be a proxy certificate. -proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo - -#################################################################### -[ tsa ] - -default_tsa = tsa_config1 # the default TSA section - -[ tsa_config1 ] - -# These are used by the TSA reply generation only. -dir = ./demoCA # TSA root directory -serial = $dir/tsaserial # The current serial number (mandatory) -crypto_device = builtin # OpenSSL engine to use for signing -signer_cert = $dir/tsacert.pem # The TSA signing certificate - # (optional) -certs = $dir/cacert.pem # Certificate chain to include in reply - # (optional) -signer_key = $dir/private/tsakey.pem # The TSA private key (optional) -signer_digest = sha256 # Signing digest to use. (Optional) -default_policy = tsa_policy1 # Policy if request did not specify it - # (optional) -other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) -digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) -accuracy = secs:1, millisecs:500, microsecs:100 # (optional) -clock_precision_digits = 0 # number of digits after dot. (optional) -ordering = yes # Is ordering defined for timestamps? - # (optional, default: no) -tsa_name = yes # Must the TSA name be included in the reply? - # (optional, default: no) -ess_cert_id_chain = no # Must the ESS cert id chain be included? - # (optional, default: no) -ess_cert_id_alg = sha1 # algorithm to compute certificate - # identifier (optional, default: sha1) - -[insta] # CMP using Insta Demo CA -# Message transfer -server = pki.certificate.fi:8700 -# proxy = # set this as far as needed, e.g., http://192.168.1.1:8080 -# tls_use = 0 -path = pkix/ - -# Server authentication -recipient = "/C=FI/O=Insta Demo/CN=Insta Demo CA" # or set srvcert or issuer -ignore_keyusage = 1 # potentially needed quirk -unprotected_errors = 1 # potentially needed quirk -extracertsout = insta.extracerts.pem - -# Client authentication -ref = 3078 # user identification -secret = pass:insta # can be used for both client and server side - -# Generic message options -cmd = ir # default operation, can be overridden on cmd line with, e.g., kur - -# Certificate enrollment -subject = "/CN=openssl-cmp-test" -newkey = insta.priv.pem -out_trusted = apps/insta.ca.crt # does not include keyUsage digitalSignature -certout = insta.cert.pem - -[pbm] # Password-based protection for Insta CA -# Server and client authentication -ref = $insta::ref # 3078 -secret = $insta::secret # pass:insta - -[signature] # Signature-based protection for Insta CA -# Server authentication -trusted = $insta::out_trusted # apps/insta.ca.crt - -# Client authentication -secret = # disable PBM -key = $insta::newkey # insta.priv.pem -cert = $insta::certout # insta.cert.pem - -[ir] -cmd = ir - -[cr] -cmd = cr - -[kur] -# Certificate update -cmd = kur -oldcert = $insta::certout # insta.cert.pem - -[rr] -# Certificate revocation -cmd = rr -oldcert = $insta::certout # insta.cert.pem diff --git a/codebuild/bin/s2n_install_test_dependencies.sh b/codebuild/bin/s2n_install_test_dependencies.sh deleted file mode 100755 index f6b95e2d726..00000000000 --- a/codebuild/bin/s2n_install_test_dependencies.sh +++ /dev/null @@ -1,44 +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 -ex - -source codebuild/bin/s2n_setup_env.sh - -# Install missing test dependencies. If the install directory already exists, cached artifacts will be used -# for that dependency. - -if [[ ! -d test-deps ]]; then - mkdir test-deps ; -fi - -#Install & Run shell check before installing dependencies -echo "Installing ShellCheck..." -codebuild/bin/install_shellcheck.sh -echo "Running ShellCheck..." -find ./codebuild -type f -name '*.sh' -exec shellcheck -Cnever -s bash {} \; - -if [[ "$OS_NAME" == "linux" ]]; then - codebuild/bin/install_ubuntu_dependencies.sh; -fi - -if [[ "$OS_NAME" == "darwin" ]]; then - codebuild/bin/install_osx_dependencies.sh; -fi - -codebuild/bin/install_default_dependencies.sh - -echo "Success" diff --git a/codebuild/bin/s2n_open_fds_test.py b/codebuild/bin/s2n_open_fds_test.py deleted file mode 100644 index 06cf5bffee9..00000000000 --- a/codebuild/bin/s2n_open_fds_test.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -# This script parses the LastDynamicAnalysis file generated by Valgrind running through CTest memcheck. -# It identifies any leaking file descriptors and triggers an error when detected. -# This enhances the capabilities of existing Valgrind checks. -# Output snippet for open file descriptors: -# ==6652== FILE DESCRIPTORS: 6 open (3 std) at exit. -# ==6652== Open AF_INET socket 6: 127.0.0.1:36915 <-> unbound -# ==6652== at 0x498B2EB: socket (syscall-template.S:120) -# ==6652== by 0x16CD16: s2n_new_inet_socket_pair (s2n_self_talk_ktls_test.c:69) -# ==6652== by 0x15DBB2: main (s2n_self_talk_ktls_test.c:168) -# ==6652== -import os -import sys - -EXIT_SUCCESS = 0 -# Exit with error code 1 if leaking fds are detected. -ERROR_EXIT_CODE = 1 -# This test is designed to be informational only, so we only print fifteen lines of error messages when a leak is detected. -NUM_OF_LINES_TO_PRINT = 15 - - -def find_log_file(path): - for f in os.listdir(path): - if "LastDynamicAnalysis" in f: - return os.path.join(path, f) - - raise FileNotFoundError("LastDynamicAnalysis log file is not found!") - - -def detect_leak(file): - fd_leak_detected = False - lines = file.readlines() - for i in range(len(lines)): - if "FILE DESCRIPTORS:" in lines[i]: - # Example line: `==6096== FILE DESCRIPTORS: 4 open (3 std) at exit.` - line_elements = lines[i].split() - open_fd_count = line_elements[line_elements.index("DESCRIPTORS:") + 1] - std_fd_count = line_elements[line_elements.index("std)") - 1][1:] - # CTest memcheck writes to a LastDynamicAnslysis log file. - # We allow that fd to remain opened. - if int(open_fd_count) > int(std_fd_count) + 1: - for j in range(NUM_OF_LINES_TO_PRINT): - print(lines[i + j], end="") - print() - fd_leak_detected = True - return fd_leak_detected - - -def main(): - # Print banner of the test - print("############################################################################") - print("################# Test for Leaking File Descriptors ########################") - print("############################################################################") - - with open(find_log_file(sys.argv[1]), 'r') as file: - if detect_leak(file): - sys.exit(ERROR_EXIT_CODE) - - return EXIT_SUCCESS - - -if __name__ == '__main__': - main() diff --git a/codebuild/bin/s2n_override_paths.sh b/codebuild/bin/s2n_override_paths.sh deleted file mode 100755 index 7939dfa6c53..00000000000 --- a/codebuild/bin/s2n_override_paths.sh +++ /dev/null @@ -1,21 +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 -ex - -# Add all of our test dependencies to the PATH. Use Openssl 1.1.1 so the latest openssl is used for s_client -# integration tests. -export PATH=$PYTHON_INSTALL_DIR/bin:$OPENSSL_1_1_1_INSTALL_DIR/bin:$SAW_INSTALL_DIR/bin:$Z3_INSTALL_DIR/bin:$SCAN_BUILD_INSTALL_DIR/bin:$PRLIMIT_INSTALL_DIR/bin:$LATEST_CLANG_INSTALL_DIR/bin:`pwd`/codebuild/bin:~/.local/bin:$PATH -export LD_LIBRARY_PATH=$OPENSSL_1_1_1_INSTALL_DIR/lib:$LD_LIBRARY_PATH; -export DYLD_LIBRARY_PATH=$OPENSSL_1_1_1_INSTALL_DIR/lib:$LD_LIBRARY_PATH; diff --git a/codebuild/bin/s2n_set_build_preset.sh b/codebuild/bin/s2n_set_build_preset.sh deleted file mode 100755 index e3b2bb27c1f..00000000000 --- a/codebuild/bin/s2n_set_build_preset.sh +++ /dev/null @@ -1,83 +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. - -case "${S2N_BUILD_PRESET-default}" in - "awslc_gcc4-8") - : "${S2N_LIBCRYPTO:=awslc}" - : "${GCC_VERSION:=4.8}" - ;; - "awslc_gcc9") - : "${S2N_LIBCRYPTO:=awslc}" - : "${GCC_VERSION:=9}" - ;; - "awslc-fips-2022_gcc4-8") - : "${S2N_LIBCRYPTO:=awslc-fips-2022}" - : "${GCC_VERSION:=4.8}" - ;; - "awslc-fips-2022_gcc9") - : "${S2N_LIBCRYPTO:=awslc-fips-2022}" - : "${GCC_VERSION:=9}" - ;; - "awslc-fips-2022_gcc6") - : "${S2N_LIBCRYPTO:=awslc-fips-2022}" - : "${GCC_VERSION:=6}" - ;; - "libressl_gcc6") - : "${S2N_LIBCRYPTO:=libressl}" - : "${GCC_VERSION:=6}" - ;; - "libressl_gcc9") - : "${S2N_LIBCRYPTO:=libressl}" - : "${GCC_VERSION:=9}" - ;; - "boringssl") - : "${S2N_LIBCRYPTO:=boringssl}" - : "${GCC_VERSION:=9}" - ;; - "openssl-1.0.2") - : "${S2N_LIBCRYPTO:=openssl-1.0.2}" - : "${GCC_VERSION:=6}" - ;; - "openssl-1.0.2-fips") - : "${S2N_LIBCRYPTO:=openssl-1.0.2-fips}" - : "${GCC_VERSION:=4.8}" - ;; - "openssl-1.1.1_gcc4-8") - : "${S2N_LIBCRYPTO:=openssl-1.1.1}" - : "${GCC_VERSION:=4.8}" - ;; - "openssl-1.1.1_gcc6") - : "${S2N_LIBCRYPTO:=openssl-1.1.1}" - : "${GCC_VERSION:=6}" - : "${S2N_CORKED_IO:=true}" - ;; - "openssl-1.1.1_gcc6_softcrypto") - : "${S2N_LIBCRYPTO:=openssl-1.1.1}" - : "${GCC_VERSION:=6}" - : "${OPENSSL_ia32cap:=~0x200000200000000}" - ;; - "openssl-1.1.1_gcc9") - : "${S2N_LIBCRYPTO:=openssl-1.1.1}" - : "${GCC_VERSION:=9}" - ;; - "openssl-3.0") - : "${S2N_LIBCRYPTO:=openssl-3.0}" - : "${GCC_VERSION:=9}" - ;; - "openssl-3.0-fips") - : "${S2N_LIBCRYPTO:=openssl-3.0-fips}" - : "${GCC_VERSION:=9}" - ;; -esac - diff --git a/codebuild/bin/s2n_setup_env.sh b/codebuild/bin/s2n_setup_env.sh deleted file mode 100755 index 602862258a7..00000000000 --- a/codebuild/bin/s2n_setup_env.sh +++ /dev/null @@ -1,223 +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. -# - -# TODO: Flag user if they didn't source this, values won't stick. - -source codebuild/bin/s2n_set_build_preset.sh - -# Setup Default Build Config -: "${S2N_LIBCRYPTO:=openssl-1.1.1}" -: "${BUILD_S2N:=false}" -: "${GCC_VERSION:=NONE}" -: "${LATEST_CLANG:=false}" -: "${TESTS:=unit}" -: "${S2N_COVERAGE:=false}" -: "${LD_LIBRARY_PATH:=NONE}" - -# Setup the cache directory paths. -# Set Env Variables with defaults if they aren't already set -: "${BASE_S2N_DIR:=$(pwd)}" -: "${TEST_DEPS_DIR:=$BASE_S2N_DIR/test-deps}" -: "${PYTHON_INSTALL_DIR:=$TEST_DEPS_DIR/python}" -: "${GNUTLS37_INSTALL_DIR:=$TEST_DEPS_DIR/gnutls37}" -: "${PRLIMIT_INSTALL_DIR:=$TEST_DEPS_DIR/prlimit}" -: "${SAW_INSTALL_DIR:=$TEST_DEPS_DIR/saw}" -: "${Z3_INSTALL_DIR:=$TEST_DEPS_DIR/z3}" -: "${LIBFUZZER_INSTALL_DIR:=$TEST_DEPS_DIR/libfuzzer}" -: "${LATEST_CLANG_INSTALL_DIR:=$TEST_DEPS_DIR/clang}" -: "${SCAN_BUILD_INSTALL_DIR:=$TEST_DEPS_DIR/scan-build}" -: "${OPENSSL_1_1_1_INSTALL_DIR:=$TEST_DEPS_DIR/openssl-1.1.1}" -: "${OPENSSL_3_0_INSTALL_DIR:=$TEST_DEPS_DIR/openssl-3.0}" -: "${OPENSSL_3_FIPS_INSTALL_DIR:=$TEST_DEPS_DIR/openssl-3.0-fips}" -: "${OPENSSL_1_0_2_INSTALL_DIR:=$TEST_DEPS_DIR/openssl-1.0.2}" -: "${OPENSSL_1_0_2_FIPS_INSTALL_DIR:=$TEST_DEPS_DIR/openssl-1.0.2-fips}" -: "${BORINGSSL_INSTALL_DIR:=$TEST_DEPS_DIR/boringssl}" -: "${AWSLC_INSTALL_DIR:=$TEST_DEPS_DIR/awslc}" -: "${AWSLC_FIPS_2022_INSTALL_DIR:=$TEST_DEPS_DIR/awslc-fips-2022}" -: "${AWSLC_FIPS_2024_INSTALL_DIR:=$TEST_DEPS_DIR/awslc-fips-2024}" -: "${AWSLC_FIPS_NEXT_INSTALL_DIR:=$TEST_DEPS_DIR/awslc-fips-next}" -: "${LIBRESSL_INSTALL_DIR:=$TEST_DEPS_DIR/libressl}" -: "${CPPCHECK_INSTALL_DIR:=$TEST_DEPS_DIR/cppcheck}" -: "${CTVERIF_INSTALL_DIR:=$TEST_DEPS_DIR/ctverif}" -: "${SIDETRAIL_INSTALL_DIR:=$TEST_DEPS_DIR/sidetrail}" -: "${GB_INSTALL_DIR:=$TEST_DEPS_DIR/gb}" -: "${APACHE2_INSTALL_DIR:=$TEST_DEPS_DIR/apache2}" -: "${FUZZ_TIMEOUT_SEC:=10}" - -# Set some environment vars for OS, Distro and architecture. -# Standardized as part of systemd http://0pointer.de/blog/projects/os-release -# Samples: -# OS_NAME = "linux" -# DISTRO="ubuntu" -# VERSION_ID = "18.04" -# VERSION_CODENAME = "bionic" -if [[ -f "/etc/os-release" ]]; then - # AL2 doesn't provide a codename. - . /etc/os-release - export DISTRO=$(echo "$NAME"|tr "[:upper:]" "[:lower:]") - export VERSION_ID=${VERSION_ID:-"unknown"} - export VERSION_CODENAME=${VERSION_CODENAME:-"unknown"} -elif [[ -x "/usr/bin/sw_vers" ]]; then - export DISTRO="apple" - export VERSION_ID=$(sw_vers -productVersion|sed 's/:[[:space:]]*/=/g') - export VERSION_CODENAME="unknown" # not queryable via CLI -else - export DISTRO="unknown" - export VERSION_ID="unknown" - export VERSION_CODENAME="unknown" -fi -export OS_NAME=$(uname -s|tr "[:upper:]" "[:lower:]") -export ARCH=$(uname -m) - -# Export all Env Variables -export S2N_LIBCRYPTO -export BUILD_S2N -export GCC_VERSION -export LATEST_CLANG -export TESTS -export BASE_S2N_DIR -export TEST_DEPS_DIR -export PYTHON_INSTALL_DIR -export GNUTLS37_INSTALL_DIR -export PRLIMIT_INSTALL_DIR -export SAW_INSTALL_DIR -export Z3_INSTALL_DIR -export LIBFUZZER_INSTALL_DIR -export LATEST_CLANG_INSTALL_DIR -export SCAN_BUILD_INSTALL_DIR -export OPENSSL_1_1_1_INSTALL_DIR -export OPENSSL_3_0_INSTALL_DIR -export OPENSSL_3_FIPS_INSTALL_DIR -export OPENSSL_1_0_2_INSTALL_DIR -export OPENSSL_1_0_2_FIPS_INSTALL_DIR -export BORINGSSL_INSTALL_DIR -export AWSLC_INSTALL_DIR -export AWSLC_FIPS_INSTALL_DIR -export AWSLC_FIPS_2022_INSTALL_DIR -export LIBRESSL_INSTALL_DIR -export CPPCHECK_INSTALL_DIR -export CTVERIF_INSTALL_DIR -export SIDETRAIL_INSTALL_DIR -export OPENSSL_1_1_X_MASTER_INSTALL_DIR -export FUZZ_TIMEOUT_SEC -export GB_INSTALL_DIR -export OS_NAME -export S2N_CORKED_IO -# For use by criterion/ci run reports -export AWS_S3_URL="s3://s2n-tls-logs/release/" - -# S2N_COVERAGE should not be used with fuzz tests, use FUZZ_COVERAGE instead -if [[ "$S2N_COVERAGE" == "true" && "$TESTS" == "fuzz" ]]; then - export S2N_COVERAGE="false" - export FUZZ_COVERAGE="true" -fi - -# Select the libcrypto to build s2n against. If this is unset, default to the latest stable version(Openssl 1.1.1) -if [[ -z $S2N_LIBCRYPTO ]]; then export LIBCRYPTO_ROOT=$OPENSSL_1_1_1_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "openssl-1.1.1" ]]; then export LIBCRYPTO_ROOT=$OPENSSL_1_1_1_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "openssl-3.0" ]]; then export LIBCRYPTO_ROOT=$OPENSSL_3_0_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "openssl-3.0-fips" ]]; then export LIBCRYPTO_ROOT=$OPENSSL_3_FIPS_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "openssl-1.0.2" ]]; then export LIBCRYPTO_ROOT=$OPENSSL_1_0_2_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "openssl-1.0.2-fips" ]]; then - export LIBCRYPTO_ROOT=$OPENSSL_1_0_2_FIPS_INSTALL_DIR ; - export S2N_TEST_IN_FIPS_MODE=1 ; -fi -if [[ "$S2N_LIBCRYPTO" == "boringssl" ]]; then export LIBCRYPTO_ROOT=$BORINGSSL_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "awslc" ]]; then export LIBCRYPTO_ROOT=$AWSLC_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "awslc-fips" ]]; then export LIBCRYPTO_ROOT=$AWSLC_FIPS_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "awslc-fips-2022" ]]; then export LIBCRYPTO_ROOT=$AWSLC_FIPS_2022_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "awslc-fips-2024" ]]; then export LIBCRYPTO_ROOT=$AWSLC_FIPS_2024_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "awslc-fips-next" ]]; then export LIBCRYPTO_ROOT=$AWSLC_FIPS_NEXT_INSTALL_DIR ; fi -if [[ "$S2N_LIBCRYPTO" == "libressl" ]]; then export LIBCRYPTO_ROOT=$LIBRESSL_INSTALL_DIR ; fi - -if [[ -n "${LIBCRYPTO_ROOT:-}" ]]; then - # Create a link to the selected libcrypto. This shouldn't be needed when LIBCRYPTO_ROOT is set, but some tests - # have the "libcrypto-root" directory path hardcoded. - rm -rf libcrypto-root && ln -s "$LIBCRYPTO_ROOT" libcrypto-root -fi - -# Set the libfuzzer to use for fuzz tests -export LIBFUZZER_ROOT=$LIBFUZZER_INSTALL_DIR - -#check if the path contains test dep X, if not and X exists, add to path -# The AWSLC binary(bssl) is only used for the PQ test, with the integration BoringSSL provider, and does not need to match the libcrypto used to build s2n. -# The OpenSSL 1.1.1 binary is used by the integ tests, and does not need to match the libcrypto used to build s2n. -path_overrides="$AWSLC_INSTALL_DIR/bin -$PYTHON_INSTALL_DIR/bin -$OPENSSL_1_1_1_INSTALL_DIR/bin -$SAW_INSTALL_DIR/bin -$Z3_INSTALL_DIR/bin -$SCAN_BUILD_INSTALL_DIR/bin -$PRLIMIT_INSTALL_DIR/bin -$LATEST_CLANG_INSTALL_DIR/bin -`pwd`/codebuild/bin -~/.local/bin" - -testdeps_path(){ - echo -ne "checking $1 is in the path..." - if [[ ! "$PATH" =~ "$1" ]]; then - if [[ -d "$1" ]]; then - export PATH="$1:$PATH" - echo -e "added" - else - echo -e "doesn't exist" - fi - else - echo -e "already in path" - fi -} - -for i in $path_overrides; do testdeps_path "$i" ;done - -# Just recording in the output for debugging. -if [ -f "/etc/lsb-release" ]; then - cat /etc/lsb-release -fi - -# Translate our custom variables into full paths to the compiler. -set_cc(){ - if [ -z ${GCC_VERSION:-} -o ${GCC_VERSION} = "NONE" ]; then - echo "No GCC_VERSION set" - if [ ${LATEST_CLANG:-} = "true" ]; then - echo "LATEST_CLANG is ${LATEST_CLANG}" - if [ -d ${LATEST_CLANG_INSTALL_DIR:-} ]; then - export CC=${LATEST_CLANG_INSTALL_DIR}/bin/clang - export CXX=${LATEST_CLANG_INSTALL_DIR}/bin/clang++ - echo "CC set to ${CC}" - echo "CXX set to ${CXX}" - else - echo "Could not find a clang installation $LATEST_CLANG_INSTALL_DIR" - fi - fi - else - echo "GCC_VERSION is ${GCC_VERSION}" - export CC=$(which gcc-${GCC_VERSION}) - export CXX=$(which g++-${GCC_VERSION}) - echo "CC set to ${CC}" - echo "CXX set to ${CXX}" - fi -} -set_cc - -echo "UID=$UID" -echo "OS_NAME=$OS_NAME" -echo "S2N_LIBCRYPTO=$S2N_LIBCRYPTO" -echo "LIBCRYPTO_ROOT=${LIBCRYPTO_ROOT:-}" -echo "BUILD_S2N=$BUILD_S2N" -echo "GCC_VERSION=$GCC_VERSION" -echo "LATEST_CLANG=$LATEST_CLANG" -echo "TESTS=$TESTS" -echo "PATH=$PATH" -echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" diff --git a/codebuild/bin/setup_ec2.sh b/codebuild/bin/setup_ec2.sh deleted file mode 100755 index 0ca216b1a3d..00000000000 --- a/codebuild/bin/setup_ec2.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -set -euo pipefail - -display_usage() { - cat <> ~/.config/nix/nix.conf' - # This sidesteps the need to update PATH for every user. - sudo ln -s /home/nix/.nix-profile/bin/nix /usr/local/bin - echo "=== Setting up Nix configs for the root user ===" - sudo -u root bash -c "ln -s /home/nix/.nix-profile ~/" - sudo -u root bash -c "ln -s /home/nix/.config ~/" -} - -setup_sudo() { - echo "=== Setting up sudo for the nix user, needed for installation ===" - # The nix installer refuses to install as root, so we need to set up sudo for the nix user. - sudo bash -c "echo 'nix ALL=NOPASSWD: ALL' > /etc/sudoers.d/nix" -} - -check_gnutls_config() { - if [[ -f "/etc/gnutls/config" ]]; then - echo "Turning off gnuTLS overrides" - sudo rm -f /etc/gnutls/config - fi -} - -update_ubuntu_packages() { - sudo apt update - sudo apt upgrade -y -} -# main -for arg in "$@"; do - case $arg in - -h|--help) - display_usage - exit 0 - ;; - esac -done -check_gnutls_config -update_ubuntu_packages -setup_sudo -setup_nix diff --git a/codebuild/bin/start_codebuild.sh b/codebuild/bin/start_codebuild.sh deleted file mode 100755 index 17e20361876..00000000000 --- a/codebuild/bin/start_codebuild.sh +++ /dev/null @@ -1,67 +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. - -# Codebuild does not run in the Github CI if certain files are modified. -# Launching each individual build from the Codebuild UI is slow and tedious. -# Instead, this script will launch all the Codebuild builds at once. -# You will need to setup the AWS CLI with the proper authentication. - -set -e - -BUILDS=( - "AddressSanitizer" - "S2nIntegrationV2SmallBatch" - "Valgrind" - "s2nFuzzBatch" - "s2nGeneralBatch" - "s2nUnitNix" - "IntegRustNixBatch" - "Integv2NixBatch" - "kTLS us-west-2 no-batch" - "kTLSKeyUpdate us-west-2 no-batch" -) - -usage() { - echo "start_codebuild.sh " - echo " example: start_codebuild.sh pr/1111" - echo " example: start_codebuild.sh 1234abcd" - echo " example: start_codebuild.sh test_branch lrstewart/s2n" -} - -if [ "$#" -lt "1" ]; then - usage - exit 1 -fi -SOURCE_VERSION=$1 -REPO=${2:-aws/s2n-tls} - -start_build() { - NAME=$1 - REGION=${2:-"us-west-2"} - BATCH=${3:-"batch"} - - START_COMMAND="start-build-batch" - if [ "$BATCH" = "no-batch" ]; then - START_COMMAND="start-build" - fi - aws --region $REGION codebuild $START_COMMAND \ - --project-name $NAME \ - --source-location-override https://github.com/$REPO \ - --source-version $SOURCE_VERSION | jq -re "(.buildBatch.id // .build.id)" -} - -for args in "${BUILDS[@]}"; do - start_build $args -done -echo "All builds successfully started." diff --git a/codebuild/bin/test_dynamic_load.sh b/codebuild/bin/test_dynamic_load.sh deleted file mode 100755 index e75554ab668..00000000000 --- a/codebuild/bin/test_dynamic_load.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env 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. -# - -# This script compiles s2n-tls as a shared library and compiles a test -# without linking to the library. This enables us to test behavior when -# s2n-tls is dynamically loaded. - -WORK_DIR=$1 - -if [ ! -z "$NIX_STORE" ]; then - OPENSSL=$(which openssl) - LIBCRYPTO_ROOT=$(nix-store --query $OPENSSL) -else - source codebuild/bin/s2n_setup_env.sh -fi - -S2N_BUILD_ARGS=(-H. -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT -DBUILD_TESTING=OFF) - -# create installation dir with libs2n.so -if [ ! -d $WORK_DIR/s2n-install-shared ]; then - (set -x; cmake -B$WORK_DIR/s2n-build-shared -DCMAKE_INSTALL_PREFIX=$WORK_DIR/s2n-install-shared -DBUILD_SHARED_LIBS=ON ${S2N_BUILD_ARGS[@]}) - (set -x; cmake --build $WORK_DIR/s2n-build-shared --target install -- -j $(nproc)) -fi - -# Compile the test file -$CC -Wl,-rpath $LIBCRYPTO_ROOT -o s2n_dynamic_load_test codebuild/bin/s2n_dynamic_load_test.c -ldl -lpthread - -LDD_OUTPUT=$(ldd s2n_dynamic_load_test) - -# Confirm executable doesn't have libs2n.so loaded -if echo "$LDD_OUTPUT" | grep -q libs2n; then - echo "test failure: libs2n should not appear in ldd output" - exit 1 -fi - -# Run the test with the path to libs2n -echo "Running s2n_dynamic_load_test" -LD_LIBRARY_PATH=$LIBCRYPTO_ROOT/lib ./s2n_dynamic_load_test $WORK_DIR/s2n-install-shared/lib/libs2n.so -returncode=$? -if [ $returncode -ne 0 ]; then - echo "test failure: s2n_dynamic_load_test did not succeed" - exit 1 -fi -echo "Passed s2n_dynamic_load_test" diff --git a/codebuild/bin/test_exec_leak.sh b/codebuild/bin/test_exec_leak.sh deleted file mode 100755 index 367652546a8..00000000000 --- a/codebuild/bin/test_exec_leak.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env 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 - -# Test that all file descriptors are properly cleaned up when `exec`ing from -# from an initialized s2n-tls process. - -source codebuild/bin/s2n_setup_env.sh -source codebuild/bin/jobs.sh - -function build() { - echo "=== BUILDING $1 ===" - cmake . -B$1 -DCMAKE_PREFIX_PATH=$TARGET_LIBCRYPTO_PATH ${@:2} - cmake --build $1 -- -j $JOBS -} - -function fail() { - echo "test failure: $1" - exit 1 -} - -function write_exec_app() { -cat < build/detect_exec_leak.c -#include -#include "unistd.h" - -int main() { - s2n_init(); - execl("build/bin/detect_exec_leak_finish", "", NULL); - return 0; -} -EOF -} - -function write_exec_finish_app() { -cat < build/detect_exec_leak_finish.c -#include - -int main() { - s2n_init(); - s2n_cleanup_final(); - - /* close std* file descriptors so valgrind output is less noisy */ - fclose(stdin); - fclose(stdout); - fclose(stderr); - return 0; -} -EOF -} - -# download libcrypto if its not available -TARGET_LIBCRYPTO="${S2N_LIBCRYPTO//[-.]/_}" -TARGET_LIBCRYPTO_PATH="${TEST_DEPS_DIR}/${S2N_LIBCRYPTO}" -if [ ! -f $TARGET_LIBCRYPTO_PATH/lib/libcrypto.a ]; then - ./codebuild/bin/install_${TARGET_LIBCRYPTO}.sh $TARGET_LIBCRYPTO_PATH/src $TARGET_LIBCRYPTO_PATH linux -fi - -# build s2n-tls -build build -DBUILD_SHARED_LIBS=on -DBUILD_TESTING=on - -# compile the test app for exec leak test -mkdir -p build/valgrind_log_dir -write_exec_app -write_exec_finish_app -cc -Iapi build/detect_exec_leak.c build/lib/libs2n.so -o build/bin/detect_exec_leak -cc -Iapi build/detect_exec_leak_finish.c build/lib/libs2n.so -o build/bin/detect_exec_leak_finish - -# run valgrind with track-fds enabled -valgrind_log_dir=valgrind_log_dir -for test_file in detect_exec_leak detect_exec_leak_finish; do - LD_LIBRARY_PATH="build/lib:$TARGET_LIBCRYPTO_PATH/lib:$LD_LIBRARY_PATH" S2N_VALGRIND=1 \ - valgrind --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all \ - --run-libc-freeres=yes -q --gen-suppressions=all --track-fds=yes \ - --leak-resolution=high --undef-value-errors=no --trace-children=yes \ - --suppressions=tests/unit/valgrind.suppressions --log-file="build/$valgrind_log_dir/$test_file" \ - build/bin/$test_file - - # search for all leaked file descriptors, excluding the valgrind_log_dir file - cat build/$valgrind_log_dir/$test_file | \ - grep "Open file descriptor" | \ - grep --invert-match $valgrind_log_dir \ - && fail "file leak detected while running $test_file" -done - -echo pass diff --git a/codebuild/bin/test_install_shared_and_static.sh b/codebuild/bin/test_install_shared_and_static.sh deleted file mode 100755 index b506185e606..00000000000 --- a/codebuild/bin/test_install_shared_and_static.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env 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 -eo pipefail - -usage() { - echo "test_install_shared_and_static.sh build_dir" - echo "Checks that installed s2n-config.cmake chooses appropriately between shared and static." - echo "Note that you MUST build against the version of libcrypto that's actually installed on the system," - echo "because installing libs2n.so forces it to use the system's libcrypto.so." - exit 1 -} - -if [ "$#" -ne 1 ]; then - usage -fi - -WORK_DIR=$1 - -source codebuild/bin/s2n_setup_env.sh -source codebuild/bin/jobs.sh - -COMMON_S2N_BUILD_ARGS=(-H. -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT -DBUILD_TESTING=OFF) - -# create installation dir with libs2n.so -if [ ! -d $WORK_DIR/s2n-install-shared ]; then - (set -x; cmake -B$WORK_DIR/s2n-build-shared -DCMAKE_INSTALL_PREFIX=$WORK_DIR/s2n-install-shared -DBUILD_SHARED_LIBS=ON ${COMMON_S2N_BUILD_ARGS[@]}) - (set -x; cmake --build $WORK_DIR/s2n-build-shared --target install -- -j $JOBS) -fi - -# create installation dir with libs2n.a -if [ ! -d $WORK_DIR/s2n-install-static ]; then - (set -x; cmake -B$WORK_DIR/s2n-build-static -DCMAKE_INSTALL_PREFIX=$WORK_DIR/s2n-install-static -DBUILD_SHARED_LIBS=OFF ${COMMON_S2N_BUILD_ARGS[@]}) - (set -x; cmake --build $WORK_DIR/s2n-build-static --target install -- -j $JOBS) -fi - -# create installation dir with both libs2n.so and libs2n.a -if [ ! -d $WORK_DIR/s2n-install-both ]; then - (set -x; cmake -B$WORK_DIR/s2n-build-shared-both -DCMAKE_INSTALL_PREFIX=$WORK_DIR/s2n-install-both -DBUILD_SHARED_LIBS=ON ${COMMON_S2N_BUILD_ARGS[@]}) - (set -x; cmake --build $WORK_DIR/s2n-build-shared-both --target install -- -j $JOBS) - - (set -x; cmake -B$WORK_DIR/s2n-build-static-both -DCMAKE_INSTALL_PREFIX=$WORK_DIR/s2n-install-both -DBUILD_SHARED_LIBS=OFF ${COMMON_S2N_BUILD_ARGS[@]}) - (set -x; cmake --build $WORK_DIR/s2n-build-static-both --target install -- -j $JOBS) -fi - -# write out source of a small cmake project, containing: -# - mylib: a library that uses s2n -# - myapp: executable that uses mylib -rm -rf $WORK_DIR/myapp-src -mkdir -p $WORK_DIR/myapp-src - -cat < $WORK_DIR/myapp-src/mylib.c -extern int s2n_init(void); - -void mylib_init(void) { - s2n_init(); -} -EOF - -cat < $WORK_DIR/myapp-src/myapp.c -extern void mylib_init(void); - -int main() { - mylib_init(); -} -EOF - -cat < $WORK_DIR/myapp-src/CMakeLists.txt -cmake_minimum_required (VERSION 3.0) -project (myapp C) - -add_library(mylib mylib.c) -find_package(s2n REQUIRED) -target_link_libraries(mylib PRIVATE AWS::s2n) - -add_executable(myapp myapp.c) -target_link_libraries(myapp PRIVATE mylib) -EOF - -# build myapp and mylib, confirm that expected type of libs2n is used -build_myapp() { - local BUILD_SHARED_LIBS=$1 # ("BUILD_SHARED_LIBS=ON" or "BUILD_SHARED_LIBS=OFF") - local S2N_INSTALL_DIR=$2 # which s2n-install dir should be used - local LIBS2N_EXPECTED=$3 # ("libs2n.so" or "libs2n.a") which type of libs2n is expected to be used - - echo "---------------------------------------------------------------------" - echo "building myapp with $BUILD_SHARED_LIBS looking-in:$S2N_INSTALL_DIR should-use:$LIBS2N_EXPECTED" - - local MYAPP_BUILD_DIR=$WORK_DIR/myapp-build - rm -rf $MYAPP_BUILD_DIR/ - - local S2N_INSTALL_PATH=$(realpath $WORK_DIR/$S2N_INSTALL_DIR) - - (set -x; cmake -H$WORK_DIR/myapp-src -B$MYAPP_BUILD_DIR -D$BUILD_SHARED_LIBS "-DCMAKE_PREFIX_PATH=$S2N_INSTALL_PATH;$LIBCRYPTO_ROOT") - (set -x; cmake --build $MYAPP_BUILD_DIR) - - LDD_OUTPUT=$(ldd $MYAPP_BUILD_DIR/myapp) - echo "$LDD_OUTPUT" - - if echo "$LDD_OUTPUT" | grep -q libs2n.so; then - local LIBS2N_ACTUAL=libs2n.so - else - local LIBS2N_ACTUAL=libs2n.a - fi - - if [ $LIBS2N_ACTUAL != $LIBS2N_EXPECTED ]; then - echo "test failure: used $LIBS2N_ACTUAL, but expected to use $LIBS2N_EXPECTED" - exit 1 - fi -} - -# if only shared libs2n.so is available, that's what should get used -build_myapp BUILD_SHARED_LIBS=ON s2n-install-shared libs2n.so -build_myapp BUILD_SHARED_LIBS=OFF s2n-install-shared libs2n.so - -# if only static libs2n.a is available, that's what should get used -build_myapp BUILD_SHARED_LIBS=ON s2n-install-static libs2n.a -build_myapp BUILD_SHARED_LIBS=OFF s2n-install-static libs2n.a - -# if both libs2n.so and libs2n.a are available... -build_myapp BUILD_SHARED_LIBS=ON s2n-install-both libs2n.so # should choose libs2n.so -build_myapp BUILD_SHARED_LIBS=OFF s2n-install-both libs2n.a # should choose libs2n.a diff --git a/codebuild/bin/test_libcrypto_interning.sh b/codebuild/bin/test_libcrypto_interning.sh deleted file mode 100755 index 256e538a2fb..00000000000 --- a/codebuild/bin/test_libcrypto_interning.sh +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env 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 - -source codebuild/bin/s2n_setup_env.sh -source codebuild/bin/jobs.sh - -# build 2 different version of libcrypto to make it easy to break the application if -# interning doesn't work as expected -WHICH_LIBCRYPTO=$(echo "${S2N_LIBCRYPTO:-"openssl-1.1.1"}") -TARGET_LIBCRYPTO="${WHICH_LIBCRYPTO//[-.]/_}" -TARGET_LIBCRYPTO_PATH="${TEST_DEPS_DIR}/${WHICH_LIBCRYPTO}" -OPENSSL_1_0="$OPENSSL_1_0_2_INSTALL_DIR" -if [ ! -f $OPENSSL_1_0/lib/libcrypto.a ]; then - ./codebuild/bin/install_openssl_1_0_2.sh $OPENSSL_1_0/src $OPENSSL_1_0 linux -fi -if [ ! -f $TARGET_LIBCRYPTO_PATH/lib/libcrypto.a ]; then - if [ "$TARGET_LIBCRYPTO" == "awslc" ]; then - ./codebuild/bin/install_${TARGET_LIBCRYPTO}.sh $TARGET_LIBCRYPTO_PATH/src $TARGET_LIBCRYPTO_PATH 0 - else - ./codebuild/bin/install_${TARGET_LIBCRYPTO}.sh $TARGET_LIBCRYPTO_PATH/src $TARGET_LIBCRYPTO_PATH linux - fi -fi - -COMMON_FLAGS="-DCMAKE_PREFIX_PATH=$TARGET_LIBCRYPTO_PATH -DCMAKE_BUILD_TYPE=RelWithDebInfo" -LTO_FLAGS="-DS2N_LTO=on" - -# use LTO-aware commands if possible -if [ -x "$(command -v gcc-ar)" ]; then - LTO_FLAGS+=" -DCMAKE_AR=$(which gcc-ar) -DCMAKE_NM=$(which gcc-nm) -DCMAKE_RANLIB=$(which gcc-ranlib)" -fi - -function fail() { - echo "test failure: $1" - exit 1 -} - -function write_app() { -cat < $1 -#include -#include - -int main() { - s2n_init(); - BN_CTX_new(); - return 0; -} -EOF -} - -function build() { - echo "=== BUILDING $1 ===" - cmake . -B$1 $COMMON_FLAGS ${@:2} - cmake --build $1 -- -j $JOBS -} - -function tests() { - echo "=== TESTING $1 ===" - make -C $1 test ARGS="-j $JOBS -L unit" -} - -################## -# Dynamic builds # -################## - -# build a default version to test what happens without interning -build build/shared-default -DBUILD_SHARED_LIBS=on -DBUILD_TESTING=on -ldd ./build/shared-default/lib/libs2n.so | grep -q libcrypto || fail "shared-default: libcrypto was not linked" - -# ensure libcrypto interning works with shared libs and no testing -build build/shared -DBUILD_SHARED_LIBS=on -DBUILD_TESTING=off -DS2N_INTERN_LIBCRYPTO=on -# s2n should not publicly depend on libcrypto -ldd ./build/shared/lib/libs2n.so | grep -q libcrypto && fail "shared: libcrypto was not interned" - -# ensure libcrypto interning works with shared libs, LTO and no testing -# NOTE: interning+LTO+testing doesn't currently work -build build/shared-lto -DBUILD_SHARED_LIBS=on -DBUILD_TESTING=off -DS2N_INTERN_LIBCRYPTO=on $LTO_FLAGS -# s2n should not publicly depend on libcrypto -ldd ./build/shared-lto/lib/libs2n.so | grep -q libcrypto && fail "shared-lto: libcrypto was not interned" - -# ensure libcrypto interning works with shared libs and testing -build build/shared-testing -DBUILD_SHARED_LIBS=on -DBUILD_TESTING=on -DS2N_INTERN_LIBCRYPTO=on -# s2n should not publicly depend on libcrypto -ldd ./build/shared-testing/lib/libs2n.so | grep -q libcrypto && fail "shared-testing: libcrypto was not interned" -# run the tests and make sure they all pass with the prefixed version -tests build/shared-testing -# load the wrong version of libcrypto and the tests should still pass -LD_PRELOAD=$OPENSSL_1_0/lib/libcrypto.so tests build/shared-testing - -# ensure the small app will compile with both versions of openssl without any linking issues -for build in shared shared-lto; do - # create a small app that links against both s2n and libcrypto - write_app build/$build/app.c - - for target in $OPENSSL_1_0 $TARGET_LIBCRYPTO_PATH; do - echo "testing $build linking with $target" - mkdir -p $target/bin - cc -fPIE -Iapi -I$target/include build/$build/app.c build/$build/lib/libs2n.so $target/lib/libcrypto.a -lpthread -ldl -o $target/bin/test-app - # make sure the app doesn't crash - LD_LIBRARY_PATH="build/$build/lib:$target/lib:$LD_LIBRARY_PATH" $target/bin/test-app - done -done - -################## -# Static builds # -################## - -# ensure libcrypto interning works with static libs -# NOTE: static builds don't vary based on testing being enabled -build build/static -DBUILD_SHARED_LIBS=off -DBUILD_TESTING=on -DS2N_INTERN_LIBCRYPTO=on -tests build/static - -# TODO figure out how to get static-lto+interning builds working - -# ensure the small app will compile with both versions of openssl without any linking issues -for build in static; do - # create a small app that links against both s2n and libcrypto - write_app build/$build/app.c - - for target in $OPENSSL_1_0 $TARGET_LIBCRYPTO_PATH; do - echo "testing $build linking with $target" - mkdir -p $target/bin - cc -fPIE -Iapi -I$target/include build/$build/app.c build/$build/lib/libs2n.a $target/lib/libcrypto.a -lpthread -ldl -o $target/bin/test-app - nm $target/bin/test-app | grep -q 'T s2n$BN_CTX_new' || fail "$target: libcrypto symbols were not prefixed" - nm $target/bin/test-app | grep -q 'T BN_CTX_new' || fail "$target: libcrypto was not linked in application" - # make sure the app doesn't crash - $target/bin/test-app - done -done - -################## -# Runtime tests # -################## - -run_connection_test() { - local TARGET="$1" - - LD_PRELOAD=$OPENSSL_1_0/lib/libcrypto.so ./build/$TARGET/bin/s2nd -c default_tls13 localhost 4433 &> /dev/null & - local SERVER_PID=$! - - # Wait for the server to start up before connecting - sleep 5s - - LD_PRELOAD=$OPENSSL_1_0/lib/libcrypto.so ./build/$TARGET/bin/s2nc -i -c default_tls13 localhost 4433 | tee build/client.log - kill $SERVER_PID &> /dev/null || true - - # ensure a TLS 1.3 session was negotiated - echo "checking for TLS 1.3" - grep -q "Actual protocol version: 34" build/client.log -} - -# without interning, the connection should fail when linking the wrong version of libcrypto -echo "Running test: attempt TLS1.3 handshake without interning" -run_connection_test shared-default && fail "TLS 1.3 handshake was expected to fail" -echo "TLS1.3 handshake failed as expected" -echo "" - -# with interning, the connection should succeed even though we've linked the wrong version of libcrypto -echo "Running test: attempt TLS1.3 handshake with interning" -run_connection_test shared-testing || fail "TLS 1.3 handshake was expected to succeed" -echo "TLS1.3 handshake succeeded as expected" - -echo "SUCCESS!" diff --git a/codebuild/bin/utils.sh b/codebuild/bin/utils.sh deleted file mode 100755 index fa165a09d54..00000000000 --- a/codebuild/bin/utils.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env 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 - -# Utility functions -get_latest_release(){ - local LATEST_RELEASE_URL=$(gh api /repos/aws/s2n-tls/releases/latest|jq -r '.tarball_url') - local LATEST_RELEASE_VER=$(echo "${LATEST_RELEASE_URL}" | sed 's|.*/||') - echo "${LATEST_RELEASE_VER}" -} - -gh_login(){ - # Takes secrets manager key as an argument - # This GH personal access token must have 'repo' permissions to work. - gh auth status || aws secretsmanager get-secret-value --secret-id "$1" --query 'SecretString' --output text |jq -r '.secret_key'| gh auth login --with-token - - #gh auth status -} - -criterion_install_deps(){ - make install - source "$HOME"/.cargo/env - make -C bindings/rust -} - - -usage(){ - echo -e "Usage:\n\tget_latest_release: returns just the latest v.N.N.N version" - echo -e "\tgh_login : retrieves a GitHub PAT from secrets manager and logs into GitHub.\n" -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - case "${1:-}" in - "gh_login") - gh_login "${2:-}";; - "get_latest_release") - get_latest_release - echo "$LATEST_RELEASE_VER";; - *) usage; - esac -fi diff --git a/codebuild/spec/buildspec_32bit_cross_compile.yml b/codebuild/spec/buildspec_32bit_cross_compile.yml deleted file mode 100644 index 67eb9450fac..00000000000 --- a/codebuild/spec/buildspec_32bit_cross_compile.yml +++ /dev/null @@ -1,31 +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. -version: 0.2 - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - build: - on-failure: ABORT - commands: - - cmake . -Bbuild -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/32-bit.toolchain - - cmake --build ./build -j $(nproc) - post_build: - on-failure: ABORT - commands: - - CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) make -C build test diff --git a/codebuild/spec/buildspec_amazonlinux.yml b/codebuild/spec/buildspec_amazonlinux.yml deleted file mode 100644 index 53d0907012e..00000000000 --- a/codebuild/spec/buildspec_amazonlinux.yml +++ /dev/null @@ -1,35 +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. -version: 0.2 - -env: - variables: - # CODEBUILD_ is a reserved namespace. - CB_BIN_DIR: "./codebuild/bin" - -phases: - install: - runtime-versions: - python: 3.x - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - - ./codebuild/bin/install_al_dependencies.sh - build: - commands: - - printenv - - $CB_BIN_DIR/s2n_codebuild_al.sh diff --git a/codebuild/spec/buildspec_disable_rand_override.yml b/codebuild/spec/buildspec_disable_rand_override.yml deleted file mode 100644 index e9871549a11..00000000000 --- a/codebuild/spec/buildspec_disable_rand_override.yml +++ /dev/null @@ -1,57 +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. -version: 0.2 - -env: - shell: bash - variables: - # Select a libcrypto to test the backwards-compatible RAND engine override flag. - # The flag is now a no-op (the custom RAND engine was removed), but we verify - # that builds with the flag set to 0 still pass all tests. - S2N_LIBCRYPTO: "openssl-1.0.2" - CTEST_OUTPUT_ON_FAILURE: 1 - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - build: - on-failure: ABORT - commands: - - | - cmake . -Brand_override_enabled \ - -DCMAKE_PREFIX_PATH=/usr/local/"${S2N_LIBCRYPTO}" \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo - - cmake --build ./rand_override_enabled -- -j $(nproc) - - | - cmake . -Brand_override_disabled \ - -DCMAKE_PREFIX_PATH=/usr/local/"${S2N_LIBCRYPTO}" \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DS2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE=0 - - cmake --build ./rand_override_disabled -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - # CTEST_PARALLEL_LEVEL is set outside of env/variables to ensure that `nproc` is evaluated. - - export CTEST_PARALLEL_LEVEL=$(nproc) - # Verify that s2n-tls tests pass with the RAND engine override disabled. - # The S2N_OVERRIDE_LIBCRYPTO_RAND_ENGINE flag is kept for backwards - # compatibility, but the custom RAND engine was removed as part of the - # DRBG removal. The flag is now a no-op. - - make -C rand_override_disabled test - # Verify that s2n-tls tests also pass with the default (override enabled) build. - - make -C rand_override_enabled test -- ARGS="-R 's2n_random_test'" diff --git a/codebuild/spec/buildspec_fuzz.yml b/codebuild/spec/buildspec_fuzz.yml deleted file mode 100644 index 55faa40afdc..00000000000 --- a/codebuild/spec/buildspec_fuzz.yml +++ /dev/null @@ -1,36 +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. -version: 0.2 - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - - /usr/bin/$COMPILER --version - build: - on-failure: ABORT - commands: - - | - cmake . -Bbuild \ - -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ - -DS2N_FUZZ_TEST=on - - cmake --build ./build -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - # -L: Restrict tests to names matching the pattern 'fuzz' - - cmake --build build/ --target test -- ARGS="-L fuzz --output-on-failure -j $(nproc)" diff --git a/codebuild/spec/buildspec_fuzz_batch.yml b/codebuild/spec/buildspec_fuzz_batch.yml deleted file mode 100644 index 5b77a0fbe7b..00000000000 --- a/codebuild/spec/buildspec_fuzz_batch.yml +++ /dev/null @@ -1,81 +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. -version: 0.2 - -# This buildspec runs on an Ubuntu22 image. That configuration is a property of -# the codebuild job itself. - -# Codebuild's matrix jobs have non-differentiated names so use batch-list -# instead. - -# Parameter motivation - -# LIBCRYPTOS -# awslc: happy path libcrypto for s2n-tls -# openssl 1.0.2: old version of libcrypto that is still supported by s2n-tls -# openssl 1.1.1: old version of libcrypto that is still supported by s2n-tls -# openssl 3: libcrypto that is widely used - -batch: - build-list: - - identifier: clang_awslc - buildspec: codebuild/spec/buildspec_fuzz.yml - debug-session: true - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - privileged-mode: true - variables: - S2N_LIBCRYPTO: awslc - COMPILER: clang - - identifier: clang_openssl_1_0_2 - buildspec: codebuild/spec/buildspec_fuzz.yml - debug-session: true - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - privileged-mode: true - variables: - S2N_LIBCRYPTO: openssl-1.0.2 - COMPILER: clang - - identifier: clang_openssl_1_1_1 - buildspec: codebuild/spec/buildspec_fuzz.yml - debug-session: true - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - privileged-mode: true - variables: - S2N_LIBCRYPTO: openssl-1.1.1 - COMPILER: clang - - identifier: clang_openssl_3_0 - buildspec: codebuild/spec/buildspec_fuzz.yml - debug-session: true - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - privileged-mode: true - variables: - S2N_LIBCRYPTO: openssl-3.0 - COMPILER: clang - - identifier: clang_openssl_1_0_2_fips - buildspec: codebuild/spec/buildspec_fuzz.yml - debug-session: true - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - privileged-mode: true - variables: - S2N_LIBCRYPTO: openssl-1.0.2-fips - COMPILER: clang diff --git a/codebuild/spec/buildspec_fuzz_scheduled.yml b/codebuild/spec/buildspec_fuzz_scheduled.yml deleted file mode 100644 index 29c9c8b354e..00000000000 --- a/codebuild/spec/buildspec_fuzz_scheduled.yml +++ /dev/null @@ -1,53 +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. -version: 0.2 - -env: - variables: - S2N_LIBCRYPTO: "awslc" - COMPILER: clang - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - build: - on-failure: ABORT - commands: - - | - cmake . -Bbuild \ - -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ - -DCMAKE_C_COMPILER=/usr/bin/$COMPILER \ - -DS2N_FUZZ_TEST=on \ - -DCOVERAGE=on \ - -DBUILD_SHARED_LIBS=on - - cmake --build ./build -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - - ./codebuild/bin/fuzz_corpus_download.sh - # -L: Restrict tests to labels matching the pattern 'fuzz' - # --timeout: override ctest's default timeout of 1500 - - cmake --build build/ --target test -- ARGS="-L fuzz --output-on-failure -j $(nproc) --timeout 28800" - - ./codebuild/bin/fuzz_corpus_upload.sh - - ./codebuild/bin/fuzz_coverage_report.sh - -artifacts: - # upload all files in the fuzz_coverage_report directory - files: - - '**/*' - base-directory: coverage/fuzz/total_fuzz_coverage diff --git a/codebuild/spec/buildspec_generalbatch.yml b/codebuild/spec/buildspec_generalbatch.yml deleted file mode 100644 index 42697566032..00000000000 --- a/codebuild/spec/buildspec_generalbatch.yml +++ /dev/null @@ -1,316 +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. - -version: 0.2 - -batch: - build-list: - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - GCC_VERSION: NONE - SAW: true - TESTS: tls - identifier: s2nSawTls - - buildspec: codebuild/spec/buildspec_sidetrail.yml - env: - compute-type: BUILD_GENERAL1_2XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu14codebuild - privileged-mode: true - variables: - TESTS: sidetrail - identifier: s2nSidetrail - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - TESTS: exec_leak - identifier: s2nExecLeak - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - GCC_VERSION: '9' - S2N_LIBCRYPTO: 'openssl-1.1.1' - TESTS: unit - identifier: s2nUnitOpenssl111Gcc9 - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: true - GCC_VERSION: 9 - S2N_COVERAGE: true - S2N_LIBCRYPTO: openssl-3.0 - TESTS: unit - identifier: s2nUnitOpenssl3Gcc9 - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: true - GCC_VERSION: 9 - S2N_COVERAGE: true - S2N_LIBCRYPTO: openssl-3.0-fips - TESTS: unit - identifier: s2nUnitOpenssl3FIPSGcc9 - ### Ubuntu24 ### - # Openssl-1.1.1 + gcc-13: Prefer more widely used Openssl on the default - # Ubuntu24 compiler. - # Aws-lc + clang-18: aws-lc is being built with clang; keep parity while building - # s2n-tls on a newer compiler. - - buildspec: codebuild/spec/buildspec_ubuntu_cmake.yml - env: - compute-type: BUILD_GENERAL1_MEDIUM - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - privileged-mode: true - variables: - COMPILER: 'gcc-13' - S2N_LIBCRYPTO: 'openssl-1.1.1' - identifier: s2nUnitOpenssl111Gcc13 - - buildspec: codebuild/spec/buildspec_ubuntu_cmake.yml - env: - compute-type: BUILD_GENERAL1_MEDIUM - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - privileged-mode: true - variables: - COMPILER: 'clang-18' - S2N_LIBCRYPTO: 'awslc' - identifier: s2nUnitAwslcClang18 - - buildspec: codebuild/spec/buildspec_amazonlinux.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: aws/codebuild/amazonlinux2-aarch64-standard:2.0 - privileged-mode: true - type: ARM_CONTAINER - variables: - TESTS: unit - identifier: s2nUnitAl2Arm - - buildspec: codebuild/spec/buildspec_amazonlinux.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 - privileged-mode: true - variables: - TESTS: unit - S2N_LIBCRYPTO: default - identifier: s2nUnitAL2 - - buildspec: codebuild/spec/buildspec_amazonlinux.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 - privileged-mode: true - variables: - TESTS: unit - S2N_LIBCRYPTO: openssl-1.1.1 - identifier: s2nUnitAl2Openssl111 - - buildspec: codebuild/spec/buildspec_amazonlinux.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: aws/codebuild/amazonlinux2-x86_64-standard:5.0 - privileged-mode: true - variables: - TESTS: unit - S2N_LIBCRYPTO: default - identifier: UnitAl2023x86 - - buildspec: codebuild/spec/buildspec_amazonlinux.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: aws/codebuild/amazonlinux2-aarch64-standard:3.0 - privileged-mode: true - type: ARM_CONTAINER - variables: - TESTS: unit - S2N_LIBCRYPTO: default - identifier: UnitAl2023arm - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - TESTS: interning - identifier: s2nLibcryptoInterningOpenSSL - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - S2N_LIBCRYPTO: awslc - TESTS: interning - identifier: s2nLibcryptoInterningAwslc - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - S2N_LIBCRYPTO: awslc-fips-2022 - TESTS: interning - identifier: s2nLibcryptoInterningAwslcFips2022 - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - privileged-mode: true - variables: - GCC_VERSION: '13' - TESTS: crt - identifier: s2nUnitCRT - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - S2N_LIBCRYPTO: openssl-1.1.1 - TESTS: sharedandstatic - identifier: s2nInstallSharedAndStatic - - identifier: s2nDynamicLoad - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - TESTS: dynamicload - S2N_LIBCRYPTO: openssl-1.1.1 - GCC_VERSION: '9' - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: true - GCC_VERSION: 6 - S2N_COVERAGE: true - S2N_LIBCRYPTO: openssl-1.1.1 - TESTS: unit - identifier: s2nUnitOpenSSL111Gcc6Coverage - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - GCC_VERSION: '6' - S2N_LIBCRYPTO: 'libressl' - TESTS: unit - identifier: s2nUnitLibressl - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - GCC_VERSION: '4.8' - S2N_LIBCRYPTO: 'openssl-1.0.2-fips' - TESTS: unit - identifier: s2nUnitOpenssl102FipsGcc48 - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - GCC_VERSION: '9' - S2N_LIBCRYPTO: 'boringssl' - TESTS: unit - identifier: s2nUnitBoringssl - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - GCC_VERSION: '9' - S2N_LIBCRYPTO: 'awslc-fips-2022' - TESTS: unit - identifier: s2nUnitAwslcFips2022 - - buildspec: codebuild/spec/buildspec_ubuntu.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - privileged-mode: true - variables: - BUILD_S2N: 'true' - CC: '/usr/bin/clang' - CXX: '/usr/bin/clang++' - S2N_LIBCRYPTO: 'awslc' - TESTS: unit - identifier: s2nUnitClang15 - - identifier: 32BitBuildAndUnit - buildspec: codebuild/spec/buildspec_32bit_cross_compile.yml - env: - privileged-mode: true - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - - identifier: ThreadSanitizer - buildspec: codebuild/spec/buildspec_tsan.yml - env: - privileged-mode: true - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - - identifier: musl - buildspec: codebuild/spec/buildspec_musl.yml - env: - privileged-mode: true - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - - identifier: DisableRandOverride - buildspec: codebuild/spec/buildspec_disable_rand_override.yml - env: - privileged-mode: true - compute-type: BUILD_GENERAL1_LARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - - identifier: s2nMemUsageTest_awslc - buildspec: codebuild/spec/buildspec_mem.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - privileged-mode: true - variables: - S2N_LIBCRYPTO: 'awslc' - S2N_EXPECTED_CONNECTION_MEMORY_KB: 47 - - identifier: s2nMemUsageTest_awslcfips - buildspec: codebuild/spec/buildspec_mem.yml - env: - compute-type: BUILD_GENERAL1_SMALL - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - privileged-mode: true - variables: - S2N_LIBCRYPTO: 'awslc-fips-2022' - S2N_EXPECTED_CONNECTION_MEMORY_KB: 46 diff --git a/codebuild/spec/buildspec_integ_rust.yml b/codebuild/spec/buildspec_integ_rust.yml deleted file mode 100644 index 413586b1678..00000000000 --- a/codebuild/spec/buildspec_integ_rust.yml +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -# Because the Ec2 reserved instance disks persist between runs, -# we need to do periodic clean up; The `nix store gc` command runs on Sunday to -# prevent the disk from filling up. ---- -version: 0.2 -env: - shell: bash - -batch: - build-graph: - # Cache job for x86 - - identifier: nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_ARGS: --max-jobs auto - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # Cache Job for aarch64 - - identifier: nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_ARGS: --max-jobs auto - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # OpenSSL 1.0.2 x86 - - identifier: Rust_openssl102_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_openssl102 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # OpenSSL 1.0.2 aarch64 - - identifier: Rust_openssl102_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_openssl102 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # OpenSSL 1.1.1 x86 - - identifier: Rust_openssl111_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_openssl111 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # OpenSSL 1.1.1 aarch64 - - identifier: Rust_openssl111_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_openssl111 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # OpenSSL 3.0 x86 - - identifier: Rust_openssl30_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_openssl30 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # OpenSSL 3.0 aarch64 - - identifier: Rust_openssl30_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_openssl30 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # AWS-LC x86 - - identifier: Rust_awslc_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_awslc - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # AWS-LC aarch64 - - identifier: Rust_awslc_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_awslc - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # AWS-LC FIPS 2024 x86 - - identifier: Rust_awslcfips2024_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_awslcfips2024 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # AWS-LC FIPS 2024 aarch64 - - identifier: Rust_awslcfips2024_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#rust_awslcfips2024 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - -phases: - install: - commands: - - if [[ $(date +%u) -eq 0 ]]; then nix store gc; fi - - | - if [[ $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - echo "Refreshing nix cache..." - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - nix build .#devShell - nix copy --to $NIX_CACHE_BUCKET .#devShell - else - echo "Downloading cache" - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - fi - pre_build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; rust_configure" - fi - build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; rust_build" - fi - post_build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - echo "Running Rust integration tests with $NIXDEV_LIBCRYPTO" - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; rust_test" - fi diff --git a/codebuild/spec/buildspec_integv2_nix.yml b/codebuild/spec/buildspec_integv2_nix.yml deleted file mode 100644 index d24c7d012af..00000000000 --- a/codebuild/spec/buildspec_integv2_nix.yml +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -# Because the Ec2 reserved instance disks persist between runs, -# we need to do periodic clean up; The `nix store gc` command runs on Sunday to -# prevent the disk from filling up. ---- -version: 0.2 -env: - shell: bash - -batch: - build-graph: - # Cache job for x86 - - identifier: nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - # max-jobs tell nix to use all available cores for building derivations. - NIXDEV_ARGS: --max-jobs auto - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # Cache Job for aarch64 - - identifier: nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - # max-jobs tell nix to use all available cores for building derivations. - NIXDEV_ARGS: --max-jobs auto - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # AWSLC x86 - - identifier: Integ_awslc_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#awslc - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # AWSLC aarch64 - - identifier: Integ_awslc_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#awslc - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # AWSLC-FIPS-2022 - - identifier: Integ_awslcfips2022_x86_64_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#awslcfips2022 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # AWSLC-FIPS-2024 - - identifier: Integ_awslcfips2024_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#awslcfips2024 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # Openssl30 x86 - - identifier: Integ_openssl30_x86_0 - depend-on: - - nixCache_x86_64 - env: - fleet: ubuntu24_x86_64_nix - variables: - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # Openssl30 aarch64 - - identifier: Integ_openssl30_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # Openssl111 aarch64 only - - identifier: Integ_openssl111_aarch64_0 - depend-on: - - nixCache_aarch64 - env: - fleet: ubuntu24_aarch64_nix - variables: - NIXDEV_LIBCRYPTO: .#openssl111 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - -phases: - install: - commands: - - if [[ $(date +%u) -eq 0 ]]; then nix store gc; fi - - | - if [[ $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - echo "Refreshing nix cache..." - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - nix build .#devShell - nix copy --to $NIX_CACHE_BUCKET .#devShell - else - echo "Downloading cache" - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - fi - pre_build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; configure" - fi - build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; build" - fi - post_build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh;uvinteg" - fi diff --git a/codebuild/spec/buildspec_ktls.yml b/codebuild/spec/buildspec_ktls.yml deleted file mode 100644 index dc4d094be0a..00000000000 --- a/codebuild/spec/buildspec_ktls.yml +++ /dev/null @@ -1,55 +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. - -version: 0.2 -# This is designed to work with CodeBuild's reserved instances fleet and -# curated Ec2 AMI for AL2023. -# -# Because the Ec2 reserved instance disks persist between runs, -# we need to do periodic clean up; The `nix store gc` command runs on Sunday to -# prevent the disk from filling up. -env: - shell: bash - variables: - NIX_CACHE_BUCKET: "s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2" - NIX_INSTALLER: "https://nixos.org/nix/install" - S2N_KTLS_TESTING_EXPECTED: 1 -phases: - install: - commands: - - yum update -y; yum upgrade -y - pre_build: - commands: - - id; groupadd nixbld||true - - useradd -m -g nixbld -G nixbld nix || true - - | - echo "Working around the faulty yaml parser..." - echo 'nix ALL=NOPASSWD: ALL' > /etc/sudoers.d/nix - # (Re)Install nix - - sh <(curl -L "$NIX_INSTALLER") --no-daemon - # Make sure nix exists in the PATH - - export PATH=$HOME/.nix-profile/bin:$PATH - # Turn on flakes - - mkdir -p ~/.config/nix; echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf - - if [[ $(date +%u) -eq 0 ]]; then nix store gc;fi - # Populate the store from the nix cache - - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - # Load the TLS kernel module - - sudo modprobe tls - - echo "Checking that the TLS kernel mod loaded..."; test $(sudo lsmod|grep -c tls) = 1 - build: - commands: - - nix develop .#awslc --command bash -c 'source ./nix/shell.sh && clean && configure && unit' - - S2N_CMAKE_OPTIONS="-DASAN=ON" nix develop .#awslc --command bash -c 'source ./nix/shell.sh && clean && configure && unit' - diff --git a/codebuild/spec/buildspec_ktls_keyupdate.yml b/codebuild/spec/buildspec_ktls_keyupdate.yml deleted file mode 100644 index 34bde0c0a70..00000000000 --- a/codebuild/spec/buildspec_ktls_keyupdate.yml +++ /dev/null @@ -1,42 +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. - -version: 0.2 -# This is designed to work with CodeBuild's reserved instances fleet and -# curated Ec2 AMI for Ubuntu25. -# -# Because the Ec2 reserved instance disks persist between runs, -# we need to do periodic clean up; The `nix store gc` command runs on Sunday to -# prevent the disk from filling up. -env: - shell: bash - variables: - NIX_CACHE_BUCKET: "s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2" - NIX_INSTALLER: "https://nixos.org/nix/install" - S2N_KTLS_TESTING_EXPECTED: 1 - S2N_KTLS_KEYUPDATE_TESTING_EXPECTED: 1 -phases: - pre_build: - commands: - - if [[ $(date +%u) -eq 0 ]]; then nix store gc;fi - # Populate the store from the nix cache - - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - # Load the TLS kernel module - - sudo modprobe tls - - echo "Checking that the TLS kernel mod loaded..."; test $(sudo lsmod|grep -c tls) = 1 - build: - commands: - - nix develop .#awslc --command bash -c 'source ./nix/shell.sh && clean && configure && unit' - - S2N_CMAKE_OPTIONS="-DASAN=ON" nix develop .#awslc --command bash -c 'source ./nix/shell.sh && clean && configure && unit' - diff --git a/codebuild/spec/buildspec_mem.yml b/codebuild/spec/buildspec_mem.yml deleted file mode 100644 index 4326cb89019..00000000000 --- a/codebuild/spec/buildspec_mem.yml +++ /dev/null @@ -1,46 +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. -version: 0.2 - -env: - shell: bash - variables: - CTEST_OUTPUT_ON_FAILURE: 1 - S2N_TEST_NAME: s2n_mem_usage_test - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - build: - on-failure: ABORT - commands: - - export CTEST_PARALLEL_LEVEL=$(nproc) - # Test for expected memory - - | - cmake . -Bbuild \ - -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO - - cmake --build build -j $(nproc) --target $S2N_TEST_NAME - - echo ">>>> POSITIVE TEST - EXPECTED TO SUCCEED <<<<" - - make -C build test -- ARGS="-R $S2N_TEST_NAME" - # Test for unexpected memory to confirm failure possible - # Use an unrealistically high number - - cmake --build build -j $(nproc) --target $S2N_TEST_NAME - - echo ">>>> NEGATIVE TESTS - EXPECTED TO FAIL <<<<" - - | - ! S2N_EXPECTED_CONNECTION_MEMORY_KB=3 make -C build test -- ARGS="-R $S2N_TEST_NAME" - ! S2N_EXPECTED_CONNECTION_MEMORY_KB=500 make -C build test -- ARGS="-R $S2N_TEST_NAME" diff --git a/codebuild/spec/buildspec_musl.yml b/codebuild/spec/buildspec_musl.yml deleted file mode 100644 index f9220cf44ad..00000000000 --- a/codebuild/spec/buildspec_musl.yml +++ /dev/null @@ -1,46 +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. -version: 0.2 - -env: - variables: - MUSL_DIR: "test-deps/musl" - LIBCRYPTO_DIR: "test-deps/musl-awslc" - -phases: - pre_build: - on-failure: ABORT - commands: - # Install musl libc - - git clone --depth=1 https://git.musl-libc.org/git/musl $MUSL_DIR - - echo "Installing musl to $CODEBUILD_SRC_DIR/$MUSL_DIR" - - cd $MUSL_DIR - - ./configure --prefix=$CODEBUILD_SRC_DIR/$MUSL_DIR - - make install - - cd $CODEBUILD_SRC_DIR - # Install libcrypto. - # We need to modify the usual install so that the library can link to musl. - # If this becomes a problem, we can switch to more official cross compilation. - - CFLAGS="-U_FORTIFY_SOURCE -D_FILE_OFFSET_BITS=32" - - ./codebuild/bin/install_awslc.sh $(mktemp -d) $CODEBUILD_SRC_DIR/$LIBCRYPTO_DIR - build: - on-failure: ABORT - commands: - - CC="$CODEBUILD_SRC_DIR/$MUSL_DIR/bin/musl-gcc" - - cmake . -Bbuild -DCMAKE_PREFIX_PATH=$CODEBUILD_SRC_DIR/$LIBCRYPTO_DIR - - cmake --build ./build - post_build: - on-failure: ABORT - commands: - - CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) make -C build test diff --git a/codebuild/spec/buildspec_s2n_tls_bench.yml b/codebuild/spec/buildspec_s2n_tls_bench.yml deleted file mode 100644 index b7edaf41430..00000000000 --- a/codebuild/spec/buildspec_s2n_tls_bench.yml +++ /dev/null @@ -1,53 +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. -version: 0.2 - -env: - shell: bash - -phases: - install: - commands: - # Install Rust dependencies - - ./codebuild/bin/install_ubuntu_dependencies.sh - - source $HOME/.cargo/env - - rustup toolchain install stable - - rustup override set stable - - cargo install cargo-criterion - - # Install Python dependencies - - sudo apt install -y python3.12-venv - - python3 -m venv .venv - - source .venv/bin/activate - - pip3 install "boto3[crt]" - - pre_build: - commands: - - cd bindings/rust/extended - - ./generate.sh --skip-tests - - cd $CODEBUILD_SRC_DIR - - build: - commands: - - cd bindings/rust/standard/benchmarks - - cargo criterion --message-format json > criterion_output.log - - cd $CODEBUILD_SRC_DIR - - post_build: - commands: - - | - python3 .github/bin/criterion_to_cloudwatch.py \ - --criterion_output_path bindings/rust/standard/benchmarks/criterion_output.log \ - --namespace s2n-tls-bench \ - --platform Linux-X64 diff --git a/codebuild/spec/buildspec_sanitizer.yml b/codebuild/spec/buildspec_sanitizer.yml deleted file mode 100644 index 0be909263b9..00000000000 --- a/codebuild/spec/buildspec_sanitizer.yml +++ /dev/null @@ -1,150 +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. -version: 0.2 - -# This buildspec runs on an Ubuntu22 image. That configuration is a property of -# the codebuild job itself. - -# Codebuild's matrix jobs have non-differentiated names so use batch-list -# instead. - -# Parameter motivation - -# COMPILERS -# We run asan on both gcc and clang because of different features sets for their -# address sanitizers. Specifically there was a case where GCC was able to detect -# a memcpy-param-overlap that Clang did not. - -# LIBCRYPTOS -# awslc: happy path libcrypto for s2n-tls -# openssl 3: s2n-tls takes different code paths for ossl3, so make sure we run -# asan on it. See pr 4033 for a historical motivating example. -# openssl 1.1.1: a widely deployed version of openssl. -# openssl 1.0.2: the default libcrypto on AL2, and AL2 is still widely deployed. - -# CMAKE_BUILD_TYPE -# RelWithDebInfo: This instructs CMake to do all optimizations (Rel -> Release) -# along with debug info (DebInfo). Debug info is necessary to get line numbers -# in the stack traces that ASAN reports. -batch: - build-list: - - identifier: clang_awslc - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: awslc - COMPILER: clang - - identifier: clang_openssl_3_0 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-3.0 - COMPILER: clang - - identifier: clang_openssl_3_fips - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-3.0-fips - COMPILER: clang - - identifier: clang_openssl_1_1_1 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-1.1.1 - COMPILER: clang - - identifier: clang_openssl_1_0_2 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-1.0.2 - COMPILER: clang - - identifier: gcc_awslc - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: awslc - COMPILER: gcc - - identifier: gcc_awslc_fips_2024 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: awslc-fips-2024 - COMPILER: gcc - - identifier: gcc_awslc_fips_next - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: awslc-fips-next - COMPILER: gcc - - identifier: gcc_openssl_3_0 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-3.0 - COMPILER: gcc - - identifier: gcc_openssl_3_fips - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-3.0-fips - COMPILER: gcc - - identifier: gcc_openssl_1_1_1 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-1.1.1 - COMPILER: gcc - - identifier: gcc_openssl_1_0_2 - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-1.0.2 - COMPILER: gcc - - identifier: clang_openssl_1_0_2_fips - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-1.0.2-fips - COMPILER: clang - - identifier: gcc_openssl_1_0_2_fips - env: - compute-type: BUILD_GENERAL1_LARGE - variables: - S2N_LIBCRYPTO: openssl-1.0.2-fips - COMPILER: gcc - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - ln -s /usr/local $CODEBUILD_SRC_DIR/third-party-src/test-deps; - fi - - /usr/bin/$COMPILER --version - build: - on-failure: ABORT - commands: - - | - cmake . -Bbuild \ - -DCMAKE_C_COMPILER=/usr/bin/$COMPILER \ - -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DASAN=ON \ - -DUBSAN=ON - - cmake --build ./build -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - - CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) make -C build test diff --git a/codebuild/spec/buildspec_sidetrail.yml b/codebuild/spec/buildspec_sidetrail.yml deleted file mode 100644 index 5e7176697fa..00000000000 --- a/codebuild/spec/buildspec_sidetrail.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -# 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. -version: 0.2 - -env: - variables: - # CODEBUILD_ is a reserved namespace. - CB_BIN_DIR: "./codebuild/bin" - -phases: - build: - commands: - - printenv - - | - if [ -d "third-party-src" ]; then - cd third-party-src - $CB_BIN_DIR/run_sidetrail.sh /sidetrail-install-dir ${CODEBUILD_SRC_DIR}/third-party-src; - else - $CB_BIN_DIR/run_sidetrail.sh /sidetrail-install-dir ${CODEBUILD_SRC_DIR}; - fi - post_build: - commands: - - echo Build completed on `date` diff --git a/codebuild/spec/buildspec_timing.yml b/codebuild/spec/buildspec_timing.yml deleted file mode 100644 index 71bf6669752..00000000000 --- a/codebuild/spec/buildspec_timing.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -version: 0.2 -env: - shell: bash - variables: - # This assumes you have a Rust toolchain installed - CARGO: "cargo +nightly" - OPENSSL_DIR: "/usr/local/openssl-3.0" - RUST_TOOLCHAIN: "1.72.0-x86_64-unknown-linux-gnu" -phases: - install: - commands: - - echo "Installing Rust ..." - - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - - . $HOME/.cargo/env - - rustup toolchain install $RUST_TOOLCHAIN - pre_build: - commands: - - | - cd bindings/rust/extended - ./generate.sh - cargo clean - build: - commands: - - cargo build --timings - post_build: - commands: - - cargo test --timings - -artifacts: - # upload timing reports - files: - - "**/*" - base-directory: bindings/rust/extended/target/cargo-timings diff --git a/codebuild/spec/buildspec_tsan.yml b/codebuild/spec/buildspec_tsan.yml deleted file mode 100644 index 460d9cbc4c0..00000000000 --- a/codebuild/spec/buildspec_tsan.yml +++ /dev/null @@ -1,31 +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. -version: 0.2 - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - build: - on-failure: ABORT - commands: - - cmake . -Bbuild -DTSAN=on - - cmake --build ./build -j $(nproc) - post_build: - on-failure: ABORT - commands: - - CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) make -C build test diff --git a/codebuild/spec/buildspec_ubuntu.yml b/codebuild/spec/buildspec_ubuntu.yml deleted file mode 100644 index 956de1a3693..00000000000 --- a/codebuild/spec/buildspec_ubuntu.yml +++ /dev/null @@ -1,33 +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. -version: 0.2 - -env: - variables: - # CODEBUILD_ is a reserved namespace. - CB_BIN_DIR: "./codebuild/bin" - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - ln -s /usr/local $CODEBUILD_SRC_DIR/third-party-src/test-deps; - fi - build: - commands: - - printenv - - ln -s /usr/local $CODEBUILD_SRC_DIR/test-deps - - $CB_BIN_DIR/s2n_codebuild.sh diff --git a/codebuild/spec/buildspec_ubuntu_cmake.yml b/codebuild/spec/buildspec_ubuntu_cmake.yml deleted file mode 100644 index 0ce71aa558e..00000000000 --- a/codebuild/spec/buildspec_ubuntu_cmake.yml +++ /dev/null @@ -1,35 +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. -version: 0.2 - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - build: - on-failure: ABORT - commands: - - | - cmake . -Bbuild \ - -DCMAKE_C_COMPILER=/usr/bin/$COMPILER \ - -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo - - cmake --build ./build -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - - CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) make -C build test diff --git a/codebuild/spec/buildspec_ubuntu_integrationv2.yml b/codebuild/spec/buildspec_ubuntu_integrationv2.yml deleted file mode 100644 index a88528f7015..00000000000 --- a/codebuild/spec/buildspec_ubuntu_integrationv2.yml +++ /dev/null @@ -1,68 +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. -version: 0.2 - -# This codebuild job just runs the integration test which aren't already -# running under nix. -batch: - build-matrix: - static: - env: - privileged-mode: true - dynamic: - env: - compute-type: - - BUILD_GENERAL1_XLARGE - image: - - 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu18codebuild - variables: - S2N_BUILD_PRESET: - - openssl-1.0.2 - - openssl-1.0.2-fips - - openssl-3.0-fips - # Group 1 and Group 2 (test_happy_path and test_buffered_send) each take approximately 1300 seconds to run. - # Group 3 is kept under 1000 seconds (bottlenecked by the slowest testcase which is 1300) - # Group 4 contains the remaining faster test cases. - INTEGV2_TEST: - - "test_happy_path" - - "test_buffered_send" - - "test_signature_algorithms test_session_resumption test_early_data test_external_psk" - - "test_version_negotiation test_ocsp test_renegotiate test_serialization test_record_padding - test_npn test_cross_compatibility test_renegotiate_apache test_hello_retry_requests - test_sni_match test_fragmentation test_key_update - test_client_authentication test_sslyze test_sslv2_client_hello" - -env: - variables: - # CODEBUILD_ is a reserved namespace. - CB_BIN_DIR: "./codebuild/bin" - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - ln -s /usr/local $CODEBUILD_SRC_DIR/third-party-src/test-deps; - fi - build: - commands: - # e.g. awslc-fips-2022_gcc6 - - echo "CI Configuration - s2n build preset is $S2N_BUILD_PRESET" - # e.g. "test_cross_compatibility test_client_authentication" - - echo "CI Configuration - the integ tests are $INTEGV2_TEST" - # For jdk integration test - - javac tests/integrationv2/bin/SSLSocketClient.java - - ln -s /usr/local $CODEBUILD_SRC_DIR/test-deps - - TOX_TEST_NAME=$INTEGV2_TEST TESTS=integrationv2 $CB_BIN_DIR/s2n_codebuild.sh diff --git a/codebuild/spec/buildspec_unit_coverage.yml b/codebuild/spec/buildspec_unit_coverage.yml deleted file mode 100644 index 633e491abf0..00000000000 --- a/codebuild/spec/buildspec_unit_coverage.yml +++ /dev/null @@ -1,45 +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. -version: 0.2 - -env: - variables: - # CODEBUILD_ is a reserved namespace. - CB_BIN_DIR: "./codebuild/bin" - CC: "/usr/bin/clang" - CXX: "/usr/bin/clang++" - -phases: - build: - on-failure: ABORT - commands: - # LLVM complains about corrupt coverage information - # for static targets, so compile to a shared lib - # instead. - - | - cmake . -Bbuild \ - -DCOVERAGE=ON \ - -DCMAKE_PREFIX_PATH=$LIBCRYPTO_ROOT \ - -DBUILD_SHARED_LIBS=ON - - cmake --build ./build -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - - LLVM_PROFILE_FILE="ut_%8m.profraw" CTEST_PARALLEL_LEVEL=$(nproc) cmake --build ./build --target test ARGS="--output-on-failure -L unit" - - $CB_BIN_DIR/coverage_report.sh -artifacts: - # upload all files in the coverage_report directory - files: - - '**/*' - base-directory: coverage_report diff --git a/codebuild/spec/buildspec_unit_nix.yml b/codebuild/spec/buildspec_unit_nix.yml deleted file mode 100644 index 8f1c479f6cc..00000000000 --- a/codebuild/spec/buildspec_unit_nix.yml +++ /dev/null @@ -1,181 +0,0 @@ -# -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -# ---- -version: 0.2 -env: - shell: bash - variables: - NIXDEV_ARGS: --max-jobs auto - S2N_NO_HEADBUILD: "1" - -batch: - build-graph: - # Cache job for x86 - - identifier: nixCache_x86_64 - comment: identifiers can not contain dashes - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild:latest - variables: - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # Cache job for aarch64 - - identifier: nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - type: ARM_CONTAINER - variables: - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # Openssl30 x86 - - identifier: UnitOpenssl30_x86_64 - depend-on: - - nixCache_x86_64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild:latest - privileged-mode: true - variables: - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # Openssl30 aarch64 - - identifier: UnitOpenssl30_aarch64 - depend-on: - - nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - privileged-mode: true - type: ARM_CONTAINER - variables: - NIXDEV_LIBCRYPTO: .#default - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # Openssl111 aarch64 - - identifier: UnitOpenssl111_aarch64 - depend-on: - - nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - privileged-mode: true - type: ARM_CONTAINER - variables: - NIXDEV_LIBCRYPTO: .#openssl111 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # Openssl102 aarch64 - - identifier: UnitOpenssl102_aarch64 - depend-on: - - nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - privileged-mode: true - type: ARM_CONTAINER - variables: - NIXDEV_LIBCRYPTO: .#openssl102 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # libressl x86 - - identifier: UnitLibressl_x86_64 - depend-on: - - nixCache_x86_64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild:latest - privileged-mode: true - variables: - NIXDEV_LIBCRYPTO: .#libressl - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # awslc x86 - - identifier: UnitAwslc_x86_64 - depend-on: - - nixCache_x86_64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild:latest - privileged-mode: true - variables: - NIXDEV_LIBCRYPTO: .#awslc - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-x86-64?region=us-west-2 - - # awslc aarch64 - - identifier: UnitAwslc_aarch64 - depend-on: - - nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - privileged-mode: true - type: ARM_CONTAINER - variables: - NIXDEV_LIBCRYPTO: .#awslc - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # awslcfips 2022 aarch64 - - identifier: UnitAwslcFips2022_aarch64 - depend-on: - - nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - privileged-mode: true - type: ARM_CONTAINER - variables: - NIXDEV_LIBCRYPTO: .#awslcfips2022 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - - # awslcfips 2024 aarch64 - - identifier: UnitAwslcFips2024_aarch64 - depend-on: - - nixCache_aarch64 - env: - compute-type: BUILD_GENERAL1_LARGE - image: public.ecr.aws/l1b2r3y5/nix-aws-codebuild-aarch64:next - privileged-mode: true - type: ARM_CONTAINER - variables: - NIXDEV_LIBCRYPTO: .#awslcfips2024 - NIX_CACHE_BUCKET: s3://s2n-tls-nixcachebucket-aarch64?region=us-west-2 - -phases: - install: - commands: - - | - if [[ "$CODEBUILD_BATCH_BUILD_IDENTIFIER" =~ .*"nixCache".* ]]; then - echo "Refreshing nix cache..." - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - nix build $NIXDEV_ARGS .#devShell - nix copy --to $NIX_CACHE_BUCKET .#devShell; - else - echo "Downloading cache" - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - fi - pre_build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix copy --from $NIX_CACHE_BUCKET --all --no-check-sigs - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; configure"; - fi - build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; build"; - fi - post_build: - commands: - - | - set -e - if [[ ! $CODEBUILD_BATCH_BUILD_IDENTIFIER =~ .*"nixCache".* ]]; then - nix develop $NIXDEV_ARGS $NIXDEV_LIBCRYPTO --command bash -c "source ./nix/shell.sh; unit" - fi diff --git a/codebuild/spec/buildspec_valgrind.yml b/codebuild/spec/buildspec_valgrind.yml deleted file mode 100644 index 5f1ba1d62ac..00000000000 --- a/codebuild/spec/buildspec_valgrind.yml +++ /dev/null @@ -1,95 +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. -version: 0.2 - -batch: - build-list: - - identifier: gcc_awslc - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - variables: - S2N_LIBCRYPTO: awslc - COMPILER: gcc - - identifier: gcc_awslc_fips - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - variables: - S2N_LIBCRYPTO: awslc-fips-2022 - COMPILER: gcc - - identifier: gcc_openssl_3_0 - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - variables: - S2N_LIBCRYPTO: openssl-3.0 - COMPILER: gcc - - identifier: gcc_openssl_3_fips - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - variables: - S2N_LIBCRYPTO: openssl-3.0-fips - COMPILER: gcc - - identifier: gcc_openssl_1_1_1 - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24 - variables: - S2N_LIBCRYPTO: openssl-1.1.1 - COMPILER: gcc - - identifier: gcc_openssl_1_0_2 - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - variables: - S2N_LIBCRYPTO: openssl-1.0.2 - COMPILER: gcc - - identifier: gcc_openssl_1_0_2_fips - env: - compute-type: BUILD_GENERAL1_XLARGE - image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild - variables: - S2N_LIBCRYPTO: openssl-1.0.2-fips - COMPILER: gcc - -phases: - pre_build: - commands: - - | - if [ -d "third-party-src" ]; then - cd third-party-src; - fi - - /usr/bin/$COMPILER --version - build: - on-failure: ABORT - commands: - - | - cmake . -Bbuild \ - -DCMAKE_C_COMPILER=/usr/bin/$COMPILER \ - -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo - - cmake --build ./build -- -j $(nproc) - post_build: - on-failure: ABORT - commands: - - | - S2N_VALGRIND=1 \ - CTEST_PARALLEL_LEVEL=$(nproc) \ - CTEST_OUTPUT_ON_FAILURE=1 \ - cmake --build build/ --target test \ - -- ARGS="--test-action memcheck" - - cd codebuild/bin - - python3 s2n_open_fds_test.py $CODEBUILD_SRC_DIR/build/Testing/Temporary diff --git a/libcrypto-build/README.md b/libcrypto-build/README.md deleted file mode 100644 index 554873b1612..00000000000 --- a/libcrypto-build/README.md +++ /dev/null @@ -1,4 +0,0 @@ -This directory is used to store build artifacts (tarballs and source) for a locally -built copy of libcrypto, either from OpenSSL, LibreSSL or BoringSSL. - -See the s2n [Build Guide](../docs/BUILD.md#building-with-a-specific-libcrypto) for more details.